[kernel] generic-2.6/2.6.21: refresh patches
[openwrt.git] / target / linux / generic-2.6 / patches-2.6.21 / 171-netfilter_tarpit.patch
1 --- a/net/netfilter/Kconfig
2 +++ b/net/netfilter/Kconfig
3 @@ -414,6 +414,23 @@ config NETFILTER_XT_TARGET_CONNSECMARK
4  
5           To compile it as a module, choose M here.  If unsure, say N.
6  
7 +config NETFILTER_XT_TARGET_TARPIT
8 +       tristate '"TARPIT" target support'
9 +       depends on NETFILTER_XTABLES
10 +       ---help---
11 +         Adds a TARPIT target to iptables, which captures and holds
12 +         incoming TCP connections using no local per-connection resources.
13 +         Connections are accepted, but immediately switched to the persist
14 +         state (0 byte window), in which the remote side stops sending data
15 +         and asks to continue every 60-240 seconds. Attempts to close the
16 +         connection are ignored, forcing the remote side to time out the
17 +         connection in 12-24 minutes.
18 +
19 +         This offers similar functionality to LaBrea
20 +         <http://www.hackbusters.net/LaBrea/>, but does not require dedicated
21 +         hardware or IPs. Any TCP port that you would normally DROP or REJECT
22 +         can instead become a tarpit.
23 +
24  config NETFILTER_XT_TARGET_TCPMSS
25         tristate '"TCPMSS" target support'
26         depends on NETFILTER_XTABLES && (IPV6 || IPV6=n)
27 --- a/net/netfilter/Makefile
28 +++ b/net/netfilter/Makefile
29 @@ -45,6 +45,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE
30  obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o
31  obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o
32  obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o
33 +obj-$(CONFIG_NETFILTER_XT_TARGET_TARPIT) += xt_TARPIT.o
34  obj-$(CONFIG_NETFILTER_XT_TARGET_TCPMSS) += xt_TCPMSS.o
35  obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o
36  obj-$(CONFIG_NETFILTER_XT_TARGET_CHAOS) += xt_CHAOS.o
37 --- /dev/null
38 +++ b/net/netfilter/xt_TARPIT.c
39 @@ -0,0 +1,280 @@
40 +/*
41 + * Kernel module to capture and hold incoming TCP connections using
42 + * no local per-connection resources.
43 + *
44 + * Based on ipt_REJECT.c and offering functionality similar to
45 + * LaBrea <http://www.hackbusters.net/LaBrea/>.
46 + *
47 + * Copyright (c) 2002 Aaron Hopkins <tools@die.net>
48 + *
49 + * This program is free software; you can redistribute it and/or modify
50 + * it under the terms of the GNU General Public License as published by
51 + * the Free Software Foundation; either version 2 of the License, or
52 + * (at your option) any later version.
53 + *
54 + * This program is distributed in the hope that it will be useful,
55 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
56 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
57 + * GNU General Public License for more details.
58 + *
59 + * You should have received a copy of the GNU General Public License
60 + * along with this program; if not, write to the Free Software
61 + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
62 + *
63 + * Goal:
64 + * - Allow incoming TCP connections to be established.
65 + * - Passing data should result in the connection being switched to the
66 + *   persist state (0 byte window), in which the remote side stops sending
67 + *   data and asks to continue every 60 seconds.
68 + * - Attempts to shut down the connection should be ignored completely, so
69 + *   the remote side ends up having to time it out.
70 + *
71 + * This means:
72 + * - Reply to TCP SYN,!ACK,!RST,!FIN with SYN-ACK, window 5 bytes
73 + * - Reply to TCP SYN,ACK,!RST,!FIN with RST to prevent spoofing
74 + * - Reply to TCP !SYN,!RST,!FIN with ACK, window 0 bytes, rate-limited
75 + */
76 +
77 +#include <linux/version.h>
78 +#include <linux/module.h>
79 +#include <linux/skbuff.h>
80 +#include <linux/ip.h>
81 +#include <net/ip.h>
82 +#include <net/tcp.h>
83 +#include <net/icmp.h>
84 +struct in_device;
85 +#include <net/route.h>
86 +#include <linux/random.h>
87 +#include <linux/netfilter_ipv4/ip_tables.h>
88 +
89 +#if 0
90 +#define DEBUGP printk
91 +#else
92 +#define DEBUGP(format, args...)
93 +#endif
94 +
95 +/* Stolen from ip_finish_output2 */
96 +static int ip_direct_send(struct sk_buff *skb)
97 +{
98 +       struct dst_entry *dst = skb->dst;
99 +
100 +        if (dst->hh != NULL)
101 +               return neigh_hh_output(dst->hh, skb);
102 +       else if (dst->neighbour != NULL)
103 +               return dst->neighbour->output(skb);
104 +
105 +       if (net_ratelimit())
106 +               printk(KERN_DEBUG "TARPIT ip_direct_send: no header cache and no neighbor!\n");
107 +
108 +       kfree_skb(skb);
109 +       return -EINVAL;
110 +}
111 +
112 +
113 +/* Send reply */
114 +static void tarpit_tcp(const struct sk_buff *oskb, struct rtable *ort,
115 +                       unsigned int local)
116 +{
117 +       struct sk_buff *nskb;
118 +       struct rtable *nrt;
119 +       struct tcphdr *otcph, *ntcph;
120 +       struct flowi fl = {};
121 +       unsigned int otcplen;
122 +       u_int16_t tmp;
123 +
124 +       const struct iphdr *oiph = ip_hdr(oskb);
125 +       struct iphdr *niph;
126 +
127 +       /* A truncated TCP header is not going to be useful */
128 +       if (oskb->len < ip_hdrlen(oskb) + sizeof(struct tcphdr))
129 +               return;
130 +
131 +       otcph   = (void *)oiph + ip_hdrlen(oskb);
132 +       otcplen = oskb->len - ip_hdrlen(oskb);
133 +
134 +       /* No replies for RST or FIN */
135 +       if (otcph->rst || otcph->fin)
136 +               return;
137 +
138 +       /* No reply to !SYN,!ACK.  Rate-limit replies to !SYN,ACKs */
139 +       if (!otcph->syn && (!otcph->ack || !xrlim_allow(&ort->u.dst, 1*HZ)))
140 +               return;
141 +
142 +       /* Check checksum. */
143 +       if (tcp_v4_check(otcplen, oiph->saddr, oiph->daddr,
144 +           csum_partial((char *)otcph, otcplen, 0)) != 0)
145 +               return;
146 +
147 +       /*
148 +        * Copy skb (even if skb is about to be dropped, we cannot just
149 +        * clone it because there may be other things, such as tcpdump,
150 +        * interested in it)
151 +        */
152 +       nskb = skb_copy(oskb, GFP_ATOMIC);
153 +       if (nskb == NULL)
154 +               return;
155 +
156 +       niph = ip_hdr(nskb);
157 +
158 +       /* This packet will not be the same as the other: clear nf fields */
159 +       nf_conntrack_put(nskb->nfct);
160 +       nskb->nfct = NULL;
161 +#ifdef CONFIG_NETFILTER_DEBUG
162 +       nskb->nf_debug = 0;
163 +#endif
164 +
165 +       ntcph = (void *)niph + ip_hdrlen(nskb);
166 +
167 +       /* Truncate to length (no data) */
168 +       ntcph->doff = sizeof(struct tcphdr)/4;
169 +       skb_trim(nskb, ip_hdrlen(nskb) + sizeof(struct tcphdr));
170 +       niph->tot_len = htons(nskb->len);
171 +
172 +       /* Swap source and dest */
173 +       niph->daddr = xchg(&niph->saddr, niph->daddr);
174 +       tmp = ntcph->source;
175 +       ntcph->source = ntcph->dest;
176 +       ntcph->dest = tmp;
177 +
178 +       /* Use supplied sequence number or make a new one */
179 +       ntcph->seq = otcph->ack ? otcph->ack_seq
180 +               : htonl(secure_tcp_sequence_number(niph->saddr,
181 +                                                  niph->daddr,
182 +                                                  ntcph->source,
183 +                                                  ntcph->dest));
184 +
185 +       /* Our SYN-ACKs must have a >0 window */
186 +       ntcph->window = (otcph->syn && !otcph->ack) ? htons(5) : 0;
187 +
188 +       ntcph->urg_ptr = 0;
189 +
190 +       /* Reset flags */
191 +       ((u_int8_t *)ntcph)[13] = 0;
192 +
193 +       if (otcph->syn && otcph->ack) {
194 +               ntcph->rst = 1;
195 +               ntcph->ack_seq = 0;
196 +       } else {
197 +               ntcph->syn = otcph->syn;
198 +               ntcph->ack = 1;
199 +               ntcph->ack_seq = htonl(ntohl(otcph->seq) + otcph->syn);
200 +       }
201 +
202 +       /* Adjust TCP checksum */
203 +       ntcph->check = 0;
204 +       ntcph->check = tcp_v4_check(sizeof(struct tcphdr),
205 +                                  niph->saddr,
206 +                                  niph->daddr,
207 +                                  csum_partial((char *)ntcph,
208 +                                               sizeof(struct tcphdr), 0));
209 +
210 +       fl.nl_u.ip4_u.daddr = niph->daddr;
211 +       fl.nl_u.ip4_u.saddr = local ? niph->saddr : 0;
212 +       fl.nl_u.ip4_u.tos = RT_TOS(niph->tos) | RTO_CONN;
213 +       fl.oif = 0;
214 +
215 +       if (ip_route_output_key(&nrt, &fl))
216 +               goto free_nskb;
217 +
218 +       dst_release(nskb->dst);
219 +       nskb->dst = &nrt->u.dst;
220 +
221 +       /* Adjust IP TTL */
222 +       niph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT);
223 +
224 +       /* Set DF, id = 0 */
225 +       niph->frag_off = htons(IP_DF);
226 +       niph->id = 0;
227 +
228 +       /* Adjust IP checksum */
229 +       niph->check = 0;
230 +       niph->check = ip_fast_csum((unsigned char *)niph, niph->ihl);
231 +
232 +       /* "Never happens" */
233 +       if (nskb->len > dst_mtu(nskb->dst))
234 +               goto free_nskb;
235 +
236 +       ip_direct_send(nskb);
237 +       return;
238 +
239 + free_nskb:
240 +       kfree_skb(nskb);
241 +}
242 +
243 +static unsigned int xt_tarpit_target(struct sk_buff **pskb,
244 +                                     const struct net_device *in,
245 +                                     const struct net_device *out,
246 +                                     unsigned int hooknum,
247 +                                     const struct xt_target *target,
248 +                                     const void *targinfo)
249 +{
250 +       const struct sk_buff *skb = *pskb;
251 +       const struct iphdr *iph   = ip_hdr(skb);
252 +       struct rtable *rt         = (void *)skb->dst;
253 +
254 +       /* Do we have an input route cache entry? */
255 +       if (rt == NULL)
256 +               return NF_DROP;
257 +
258 +       /* No replies to physical multicast/broadcast */
259 +       if (skb->pkt_type != PACKET_HOST && skb->pkt_type != PACKET_OTHERHOST)
260 +               return NF_DROP;
261 +
262 +       /* Now check at the protocol level */
263 +       if (rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
264 +               return NF_DROP;
265 +
266 +       /*
267 +        * Our naive response construction does not deal with IP
268 +        * options, and probably should not try.
269 +        */
270 +       if (iph->ihl * 4 != sizeof(struct iphdr))
271 +               return NF_DROP;
272 +
273 +       /* We are not interested in fragments */
274 +       if (iph->frag_off & htons(IP_OFFSET))
275 +               return NF_DROP;
276 +
277 +       tarpit_tcp(skb, rt, hooknum == NF_IP_LOCAL_IN);
278 +       return NF_DROP;
279 +}
280 +
281 +static int xt_tarpit_check(const char *tablename, const void *entry,
282 +                            const struct xt_target *target, void *targinfo,
283 +                            unsigned int hook_mask)
284 +{
285 +       bool invalid;
286 +
287 +       if (strcmp(tablename, "raw") == 0 && hook_mask == NF_IP_PRE_ROUTING)
288 +               return true;
289 +       if (strcmp(tablename, "filter") != 0)
290 +               return false;
291 +       invalid = hook_mask & ~((1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD));
292 +       return !invalid;
293 +}
294 +
295 +static struct xt_target xt_tarpit_reg = {
296 +       .name       = "TARPIT",
297 +       .family     = AF_INET,
298 +       .proto      = IPPROTO_TCP,
299 +       .target     = xt_tarpit_target,
300 +       .checkentry = xt_tarpit_check,
301 +       .me         = THIS_MODULE,
302 +};
303 +
304 +static int __init xt_tarpit_init(void)
305 +{
306 +       return xt_register_target(&xt_tarpit_reg);
307 +}
308 +
309 +static void __exit xt_tarpit_exit(void)
310 +{
311 +       xt_unregister_target(&xt_tarpit_reg);
312 +}
313 +
314 +module_init(xt_tarpit_init);
315 +module_exit(xt_tarpit_exit);
316 +MODULE_DESCRIPTION("netfilter xt_TARPIT target module");
317 +MODULE_AUTHOR("Jan Engelhardt <jengelh@gmx.de>");
318 +MODULE_LICENSE("GPL");
319 +MODULE_ALIAS("ipt_TARPIT");