2 * Kernel module to capture and hold incoming TCP connections using
3 * no local per-connection resources.
5 * Based on ipt_REJECT.c and offering functionality similar to
6 * LaBrea <http://www.hackbusters.net/LaBrea/>.
8 * Copyright (c) 2002 Aaron Hopkins <tools@die.net>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 * - Allow incoming TCP connections to be established.
26 * - Passing data should result in the connection being switched to the
27 * persist state (0 byte window), in which the remote side stops sending
28 * data and asks to continue every 60 seconds.
29 * - Attempts to shut down the connection should be ignored completely, so
30 * the remote side ends up having to time it out.
33 * - Reply to TCP SYN,!ACK,!RST,!FIN with SYN-ACK, window 5 bytes
34 * - Reply to TCP SYN,ACK,!RST,!FIN with RST to prevent spoofing
35 * - Reply to TCP !SYN,!RST,!FIN with ACK, window 0 bytes, rate-limited
38 #include <linux/version.h>
39 #include <linux/module.h>
40 #include <linux/skbuff.h>
46 #include <net/route.h>
47 #include <linux/random.h>
48 #include <linux/netfilter_ipv4/ip_tables.h>
50 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
51 #define ip_hdr(s) (s->nh.iph)
54 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
55 #define NF_INET_PRE_ROUTING NF_IP_PRE_ROUTING
56 #define NF_INET_LOCAL_IN NF_IP_LOCAL_IN
57 #define NF_INET_FORWARD NF_IP_FORWARD
58 #define NF_INET_LOCAL_OUT NF_IP_LOCAL_OUT
59 #define NF_INET_POST_ROUTING NF_IP_POST_ROUTING
65 #define DEBUGP(format, args...)
68 MODULE_LICENSE("GPL");
69 MODULE_AUTHOR("Aaron Hopkins <tools@die.net>");
71 /* Stolen from ip_finish_output2 */
72 static int ip_direct_send(struct sk_buff
*skb
)
74 struct dst_entry
*dst
= skb
->dst
;
77 return neigh_hh_output(dst
->hh
, skb
);
78 else if (dst
->neighbour
)
79 return dst
->neighbour
->output(skb
);
82 printk(KERN_DEBUG
"TARPIT ip_direct_send: no header cache and no neighbor!\n");
89 static void tarpit_tcp(struct sk_buff
*oskb
,struct rtable
*ort
,int local
)
93 struct tcphdr
*otcph
, *ntcph
;
98 /* A truncated TCP header isn't going to be useful */
99 if (oskb
->len
< (ip_hdr(oskb
)->ihl
*4) + sizeof(struct tcphdr
))
102 otcph
= (struct tcphdr
*)((u_int32_t
*)ip_hdr(oskb
)
103 + ip_hdr(oskb
)->ihl
);
104 otcplen
= oskb
->len
- ip_hdr(oskb
)->ihl
*4;
106 /* No replies for RST or FIN */
107 if (otcph
->rst
|| otcph
->fin
)
110 /* No reply to !SYN,!ACK. Rate-limit replies to !SYN,ACKs */
111 if (!otcph
->syn
&& (!otcph
->ack
|| !xrlim_allow(&ort
->u
.dst
, 1*HZ
)))
114 /* Check checksum. */
115 if (tcp_v4_check(otcplen
, ip_hdr(oskb
)->saddr
,
117 csum_partial((char *)otcph
, otcplen
, 0)) != 0)
120 /* Copy skb (even if skb is about to be dropped, we can't just
121 clone it because there may be other things, such as tcpdump,
123 nskb
= skb_copy(oskb
, GFP_ATOMIC
);
127 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
128 /* This packet will not be the same as the other: clear nf fields */
129 nf_conntrack_put(nskb
->nfct
);
131 #endif /* CONFIG_NF_CONNTRACK */
133 ntcph
= (struct tcphdr
*)((u_int32_t
*)ip_hdr(nskb
) + ip_hdr(nskb
)->ihl
);
135 /* Truncate to length (no data) */
136 ntcph
->doff
= sizeof(struct tcphdr
)/4;
137 skb_trim(nskb
, ip_hdr(nskb
)->ihl
*4 + sizeof(struct tcphdr
));
138 ip_hdr(nskb
)->tot_len
= htons(nskb
->len
);
140 /* Swap source and dest */
141 ip_hdr(nskb
)->daddr
= xchg(&ip_hdr(nskb
)->saddr
, ip_hdr(nskb
)->daddr
);
143 ntcph
->source
= ntcph
->dest
;
146 /* Use supplied sequence number or make a new one */
147 ntcph
->seq
= otcph
->ack
? otcph
->ack_seq
148 : htonl(secure_tcp_sequence_number(ip_hdr(nskb
)->saddr
,
153 /* Our SYN-ACKs must have a >0 window */
154 ntcph
->window
= (otcph
->syn
&& !otcph
->ack
) ? htons(5) : 0;
159 ((u_int8_t
*)ntcph
)[13] = 0;
161 if (otcph
->syn
&& otcph
->ack
) {
165 ntcph
->syn
= otcph
->syn
;
167 ntcph
->ack_seq
= htonl(ntohl(otcph
->seq
) + otcph
->syn
);
170 /* Adjust TCP checksum */
172 ntcph
->check
= tcp_v4_check(sizeof(struct tcphdr
),
175 csum_partial((char *)ntcph
,
176 sizeof(struct tcphdr
), 0));
178 fl
.nl_u
.ip4_u
.daddr
= ip_hdr(nskb
)->daddr
;
179 fl
.nl_u
.ip4_u
.saddr
= local
? ip_hdr(nskb
)->saddr
: 0;
180 fl
.nl_u
.ip4_u
.tos
= RT_TOS(ip_hdr(nskb
)->tos
) | RTO_CONN
;
183 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
184 if (ip_route_output_key(&init_net
, &nrt
, &fl
))
186 if (ip_route_output_key(&nrt
, &fl
))
190 dst_release(nskb
->dst
);
191 nskb
->dst
= &nrt
->u
.dst
;
194 ip_hdr(nskb
)->ttl
= dst_metric(nskb
->dst
, RTAX_HOPLIMIT
);
197 ip_hdr(nskb
)->frag_off
= htons(IP_DF
);
198 ip_hdr(nskb
)->id
= 0;
200 /* Adjust IP checksum */
201 ip_hdr(nskb
)->check
= 0;
202 ip_hdr(nskb
)->check
= ip_fast_csum((unsigned char *)ip_hdr(nskb
),
205 /* "Never happens" */
206 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
207 if (nskb
->len
> dst_mtu(nskb
->dst
))
209 if (nskb
->len
> dst_pmtu(nskb
->dst
))
213 ip_direct_send (nskb
);
222 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
223 target(struct sk_buff
**pskb
,
224 unsigned int hooknum
,
225 const struct net_device
*in
,
226 const struct net_device
*out
,
227 const void *targinfo
,
229 #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
230 target(struct sk_buff
**pskb
,
231 const struct net_device
*in
,
232 const struct net_device
*out
,
233 unsigned int hooknum
,
234 const void *targinfo
,
236 #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
237 target(struct sk_buff
**pskb
,
238 const struct net_device
*in
,
239 const struct net_device
*out
,
240 unsigned int hooknum
,
241 const struct xt_target
*target
,
242 const void *targinfo
,
244 #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
245 target(struct sk_buff
**pskb
,
246 const struct net_device
*in
,
247 const struct net_device
*out
,
248 unsigned int hooknum
,
249 const struct xt_target
*target
,
250 const void *targinfo
)
251 #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
252 target(struct sk_buff
*skb
,
253 const struct net_device
*in
,
254 const struct net_device
*out
,
255 unsigned int hooknum
,
256 const struct xt_target
*target
,
257 const void *targinfo
)
258 #else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) */
259 target(struct sk_buff
*skb
,
260 const struct xt_target_param
*par
)
263 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
264 unsigned int hooknum
= par
->hooknum
;
266 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
267 struct sk_buff
*skb
= *pskb
;
269 struct rtable
*rt
= (struct rtable
*)skb
->dst
;
271 /* Do we have an input route cache entry? */
275 /* No replies to physical multicast/broadcast */
276 if (skb
->pkt_type
!= PACKET_HOST
&& skb
->pkt_type
!= PACKET_OTHERHOST
)
279 /* Now check at the protocol level */
280 if (rt
->rt_flags
&(RTCF_BROADCAST
|RTCF_MULTICAST
))
283 /* Our naive response construction doesn't deal with IP
284 options, and probably shouldn't try. */
285 if (ip_hdr(skb
)->ihl
*4 != sizeof(struct iphdr
))
288 /* We aren't interested in fragments */
289 if (ip_hdr(skb
)->frag_off
& htons(IP_OFFSET
))
292 tarpit_tcp(skb
,rt
,hooknum
== NF_INET_LOCAL_IN
);
297 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
299 checkentry(const char *tablename
,
300 const struct ipt_entry
*e
,
302 unsigned int targinfosize
,
303 unsigned int hook_mask
)
304 #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
306 checkentry(const char *tablename
,
309 unsigned int targinfosize
,
310 unsigned int hook_mask
)
311 #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
313 checkentry(const char *tablename
,
315 const struct xt_target
*target
,
317 unsigned int targinfosize
,
318 unsigned int hook_mask
)
319 #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
321 checkentry(const char *tablename
,
323 const struct xt_target
*target
,
325 unsigned int hook_mask
)
326 #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
328 checkentry(const char *tablename
,
330 const struct xt_target
*target
,
332 unsigned int hook_mask
)
333 #else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) */
335 checkentry(const struct xt_tgchk_param
*par
)
338 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
339 const struct ipt_entry
*entry
= e
;
341 const char *tablename
= par
->table
;
342 const struct ipt_entry
*entry
= par
->entryinfo
;
343 unsigned int hook_mask
= par
-> hook_mask
;
346 /* Only allow these for input/forward packet filtering. */
347 if (strcmp(tablename
, "filter") != 0) {
348 DEBUGP("TARPIT: bad table %s'.\n", tablename
);
351 if ((hook_mask
& ~((1 << NF_INET_LOCAL_IN
)
352 | (1 << NF_INET_FORWARD
))) != 0) {
353 DEBUGP("TARPIT: bad hook mask %X\n", hook_mask
);
357 /* Must specify that it's a TCP packet */
358 if (entry
->ip
.proto
!= IPPROTO_TCP
|| (entry
->ip
.invflags
& IPT_INV_PROTO
)) {
359 DEBUGP("TARPIT: not valid for non-tcp\n");
366 static struct xt_target ipt_tarpit_reg
= {
370 .checkentry
= checkentry
,
374 static int __init
init(void)
376 return xt_register_target(&ipt_tarpit_reg
);
379 static void __exit
fini(void)
381 xt_unregister_target(&ipt_tarpit_reg
);