3d8768e92f5076f9487f4046d4f839ead9c5f534
[openwrt/openwrt.git] / target / linux / generic-2.6 / patches-2.6.22 / 160-netfilter_route.patch
1 Index: linux-2.6.22.19/include/linux/netfilter_ipv4/ipt_ROUTE.h
2 ===================================================================
3 --- /dev/null
4 +++ linux-2.6.22.19/include/linux/netfilter_ipv4/ipt_ROUTE.h
5 @@ -0,0 +1,23 @@
6 +/* Header file for iptables ipt_ROUTE target
7 + *
8 + * (C) 2002 by Cédric de Launois <delaunois@info.ucl.ac.be>
9 + *
10 + * This software is distributed under GNU GPL v2, 1991
11 + */
12 +#ifndef _IPT_ROUTE_H_target
13 +#define _IPT_ROUTE_H_target
14 +
15 +#define IPT_ROUTE_IFNAMSIZ 16
16 +
17 +struct ipt_route_target_info {
18 + char oif[IPT_ROUTE_IFNAMSIZ]; /* Output Interface Name */
19 + char iif[IPT_ROUTE_IFNAMSIZ]; /* Input Interface Name */
20 + u_int32_t gw; /* IP address of gateway */
21 + u_int8_t flags;
22 +};
23 +
24 +/* Values for "flags" field */
25 +#define IPT_ROUTE_CONTINUE 0x01
26 +#define IPT_ROUTE_TEE 0x02
27 +
28 +#endif /*_IPT_ROUTE_H_target*/
29 Index: linux-2.6.22.19/include/linux/netfilter_ipv6/ip6t_ROUTE.h
30 ===================================================================
31 --- /dev/null
32 +++ linux-2.6.22.19/include/linux/netfilter_ipv6/ip6t_ROUTE.h
33 @@ -0,0 +1,23 @@
34 +/* Header file for iptables ip6t_ROUTE target
35 + *
36 + * (C) 2003 by Cédric de Launois <delaunois@info.ucl.ac.be>
37 + *
38 + * This software is distributed under GNU GPL v2, 1991
39 + */
40 +#ifndef _IPT_ROUTE_H_target
41 +#define _IPT_ROUTE_H_target
42 +
43 +#define IP6T_ROUTE_IFNAMSIZ 16
44 +
45 +struct ip6t_route_target_info {
46 + char oif[IP6T_ROUTE_IFNAMSIZ]; /* Output Interface Name */
47 + char iif[IP6T_ROUTE_IFNAMSIZ]; /* Input Interface Name */
48 + u_int32_t gw[4]; /* IPv6 address of gateway */
49 + u_int8_t flags;
50 +};
51 +
52 +/* Values for "flags" field */
53 +#define IP6T_ROUTE_CONTINUE 0x01
54 +#define IP6T_ROUTE_TEE 0x02
55 +
56 +#endif /*_IP6T_ROUTE_H_target*/
57 Index: linux-2.6.22.19/net/ipv4/netfilter/ipt_ROUTE.c
58 ===================================================================
59 --- /dev/null
60 +++ linux-2.6.22.19/net/ipv4/netfilter/ipt_ROUTE.c
61 @@ -0,0 +1,483 @@
62 +/*
63 + * This implements the ROUTE target, which enables you to setup unusual
64 + * routes not supported by the standard kernel routing table.
65 + *
66 + * Copyright (C) 2002 Cedric de Launois <delaunois@info.ucl.ac.be>
67 + *
68 + * v 1.11 2004/11/23
69 + *
70 + * This software is distributed under GNU GPL v2, 1991
71 + */
72 +
73 +#include <linux/module.h>
74 +#include <linux/skbuff.h>
75 +#include <linux/ip.h>
76 +#include <linux/netfilter_ipv4/ip_tables.h>
77 +#include <net/netfilter/nf_conntrack.h>
78 +#include <linux/netfilter_ipv4/ipt_ROUTE.h>
79 +#include <linux/netdevice.h>
80 +#include <linux/route.h>
81 +#include <linux/version.h>
82 +#include <linux/if_arp.h>
83 +#include <net/ip.h>
84 +#include <net/route.h>
85 +#include <net/icmp.h>
86 +#include <net/checksum.h>
87 +
88 +#if 0
89 +#define DEBUGP printk
90 +#else
91 +#define DEBUGP(format, args...)
92 +#endif
93 +
94 +MODULE_LICENSE("GPL");
95 +MODULE_AUTHOR("Cedric de Launois <delaunois@info.ucl.ac.be>");
96 +MODULE_DESCRIPTION("iptables ROUTE target module");
97 +
98 +/* Try to route the packet according to the routing keys specified in
99 + * route_info. Keys are :
100 + * - ifindex :
101 + * 0 if no oif preferred,
102 + * otherwise set to the index of the desired oif
103 + * - route_info->gw :
104 + * 0 if no gateway specified,
105 + * otherwise set to the next host to which the pkt must be routed
106 + * If success, skb->dev is the output device to which the packet must
107 + * be sent and skb->dst is not NULL
108 + *
109 + * RETURN: -1 if an error occured
110 + * 1 if the packet was succesfully routed to the
111 + * destination desired
112 + * 0 if the kernel routing table could not route the packet
113 + * according to the keys specified
114 + */
115 +static int route(struct sk_buff *skb,
116 + unsigned int ifindex,
117 + const struct ipt_route_target_info *route_info)
118 +{
119 + int err;
120 + struct rtable *rt;
121 + struct iphdr *iph = ip_hdr(skb);
122 + struct flowi fl = {
123 + .oif = ifindex,
124 + .nl_u = {
125 + .ip4_u = {
126 + .daddr = iph->daddr,
127 + .saddr = 0,
128 + .tos = RT_TOS(iph->tos),
129 + .scope = RT_SCOPE_UNIVERSE,
130 + }
131 + }
132 + };
133 +
134 + /* The destination address may be overloaded by the target */
135 + if (route_info->gw)
136 + fl.fl4_dst = route_info->gw;
137 +
138 + /* Trying to route the packet using the standard routing table. */
139 + if ((err = ip_route_output_key(&rt, &fl))) {
140 + if (net_ratelimit())
141 + DEBUGP("ipt_ROUTE: couldn't route pkt (err: %i)",err);
142 + return -1;
143 + }
144 +
145 + /* Drop old route. */
146 + dst_release(skb->dst);
147 + skb->dst = NULL;
148 +
149 + /* Success if no oif specified or if the oif correspond to the
150 + * one desired */
151 + if (!ifindex || rt->u.dst.dev->ifindex == ifindex) {
152 + skb->dst = &rt->u.dst;
153 + skb->dev = skb->dst->dev;
154 + skb->protocol = htons(ETH_P_IP);
155 + return 1;
156 + }
157 +
158 + /* The interface selected by the routing table is not the one
159 + * specified by the user. This may happen because the dst address
160 + * is one of our own addresses.
161 + */
162 + if (net_ratelimit())
163 + DEBUGP("ipt_ROUTE: failed to route as desired gw=%u.%u.%u.%u oif=%i (got oif=%i)\n",
164 + NIPQUAD(route_info->gw), ifindex, rt->u.dst.dev->ifindex);
165 +
166 + return 0;
167 +}
168 +
169 +
170 +/* Stolen from ip_finish_output2
171 + * PRE : skb->dev is set to the device we are leaving by
172 + * skb->dst is not NULL
173 + * POST: the packet is sent with the link layer header pushed
174 + * the packet is destroyed
175 + */
176 +static void ip_direct_send(struct sk_buff *skb)
177 +{
178 + struct dst_entry *dst = skb->dst;
179 + struct hh_cache *hh = dst->hh;
180 + struct net_device *dev = dst->dev;
181 + int hh_len = LL_RESERVED_SPACE(dev);
182 +
183 + /* Be paranoid, rather than too clever. */
184 + if (unlikely(skb_headroom(skb) < hh_len && dev->hard_header)) {
185 + struct sk_buff *skb2;
186 +
187 + skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));
188 + if (skb2 == NULL) {
189 + kfree_skb(skb);
190 + return;
191 + }
192 + if (skb->sk)
193 + skb_set_owner_w(skb2, skb->sk);
194 + kfree_skb(skb);
195 + skb = skb2;
196 + }
197 +
198 + if (hh) {
199 + int hh_alen;
200 +
201 + read_lock_bh(&hh->hh_lock);
202 + hh_alen = HH_DATA_ALIGN(hh->hh_len);
203 + memcpy(skb->data - hh_alen, hh->hh_data, hh_alen);
204 + read_unlock_bh(&hh->hh_lock);
205 + skb_push(skb, hh->hh_len);
206 + hh->hh_output(skb);
207 + } else if (dst->neighbour)
208 + dst->neighbour->output(skb);
209 + else {
210 + if (net_ratelimit())
211 + DEBUGP(KERN_DEBUG "ipt_ROUTE: no hdr & no neighbour cache!\n");
212 + kfree_skb(skb);
213 + }
214 +}
215 +
216 +
217 +/* PRE : skb->dev is set to the device we are leaving by
218 + * POST: - the packet is directly sent to the skb->dev device, without
219 + * pushing the link layer header.
220 + * - the packet is destroyed
221 + */
222 +static inline int dev_direct_send(struct sk_buff *skb)
223 +{
224 + return dev_queue_xmit(skb);
225 +}
226 +
227 +
228 +static unsigned int route_oif(const struct ipt_route_target_info *route_info,
229 + struct sk_buff *skb)
230 +{
231 + unsigned int ifindex = 0;
232 + struct net_device *dev_out = NULL;
233 +
234 + /* The user set the interface name to use.
235 + * Getting the current interface index.
236 + */
237 + if ((dev_out = dev_get_by_name(route_info->oif))) {
238 + ifindex = dev_out->ifindex;
239 + } else {
240 + /* Unknown interface name : packet dropped */
241 + if (net_ratelimit())
242 + DEBUGP("ipt_ROUTE: oif interface %s not found\n", route_info->oif);
243 + return NF_DROP;
244 + }
245 +
246 + /* Trying the standard way of routing packets */
247 + switch (route(skb, ifindex, route_info)) {
248 + case 1:
249 + dev_put(dev_out);
250 + if (route_info->flags & IPT_ROUTE_CONTINUE)
251 + return IPT_CONTINUE;
252 +
253 + ip_direct_send(skb);
254 + return NF_STOLEN;
255 +
256 + case 0:
257 + /* Failed to send to oif. Trying the hard way */
258 + if (route_info->flags & IPT_ROUTE_CONTINUE)
259 + return NF_DROP;
260 +
261 + if (net_ratelimit())
262 + DEBUGP("ipt_ROUTE: forcing the use of %i\n",
263 + ifindex);
264 +
265 + /* We have to force the use of an interface.
266 + * This interface must be a tunnel interface since
267 + * otherwise we can't guess the hw address for
268 + * the packet. For a tunnel interface, no hw address
269 + * is needed.
270 + */
271 + if ((dev_out->type != ARPHRD_TUNNEL)
272 + && (dev_out->type != ARPHRD_IPGRE)) {
273 + if (net_ratelimit())
274 + DEBUGP("ipt_ROUTE: can't guess the hw addr !\n");
275 + dev_put(dev_out);
276 + return NF_DROP;
277 + }
278 +
279 + /* Send the packet. This will also free skb
280 + * Do not go through the POST_ROUTING hook because
281 + * skb->dst is not set and because it will probably
282 + * get confused by the destination IP address.
283 + */
284 + skb->dev = dev_out;
285 + dev_direct_send(skb);
286 + dev_put(dev_out);
287 + return NF_STOLEN;
288 +
289 + default:
290 + /* Unexpected error */
291 + dev_put(dev_out);
292 + return NF_DROP;
293 + }
294 +}
295 +
296 +
297 +static unsigned int route_iif(const struct ipt_route_target_info *route_info,
298 + struct sk_buff *skb)
299 +{
300 + struct net_device *dev_in = NULL;
301 +
302 + /* Getting the current interface index. */
303 + if (!(dev_in = dev_get_by_name(route_info->iif))) {
304 + if (net_ratelimit())
305 + DEBUGP("ipt_ROUTE: iif interface %s not found\n", route_info->iif);
306 + return NF_DROP;
307 + }
308 +
309 + skb->dev = dev_in;
310 + dst_release(skb->dst);
311 + skb->dst = NULL;
312 +
313 + netif_rx(skb);
314 + dev_put(dev_in);
315 + return NF_STOLEN;
316 +}
317 +
318 +
319 +static unsigned int route_gw(const struct ipt_route_target_info *route_info,
320 + struct sk_buff *skb)
321 +{
322 + if (route(skb, 0, route_info)!=1)
323 + return NF_DROP;
324 +
325 + if (route_info->flags & IPT_ROUTE_CONTINUE)
326 + return IPT_CONTINUE;
327 +
328 + ip_direct_send(skb);
329 + return NF_STOLEN;
330 +}
331 +
332 +
333 +/* To detect and deter routed packet loopback when using the --tee option,
334 + * we take a page out of the raw.patch book: on the copied skb, we set up
335 + * a fake ->nfct entry, pointing to the local &route_tee_track. We skip
336 + * routing packets when we see they already have that ->nfct.
337 + */
338 +
339 +static struct nf_conn route_tee_track;
340 +
341 +static unsigned int ipt_route_target(struct sk_buff **pskb,
342 + const struct net_device *in,
343 + const struct net_device *out,
344 + unsigned int hooknum,
345 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
346 + const struct xt_target *target,
347 +#endif
348 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
349 + const void *targinfo,
350 + void *userinfo)
351 +#else
352 + const void *targinfo)
353 +#endif
354 +{
355 + const struct ipt_route_target_info *route_info = targinfo;
356 + struct sk_buff *skb = *pskb;
357 + unsigned int res;
358 +
359 + if (skb->nfct == &route_tee_track.ct_general) {
360 + /* Loopback - a packet we already routed, is to be
361 + * routed another time. Avoid that, now.
362 + */
363 + if (net_ratelimit())
364 + DEBUGP(KERN_DEBUG "ipt_ROUTE: loopback - DROP!\n");
365 + return NF_DROP;
366 + }
367 +
368 + /* If we are at PREROUTING or INPUT hook
369 + * the TTL isn't decreased by the IP stack
370 + */
371 + if (hooknum == NF_IP_PRE_ROUTING ||
372 + hooknum == NF_IP_LOCAL_IN) {
373 +
374 + struct iphdr *iph = ip_hdr(skb);
375 +
376 + if (iph->ttl <= 1) {
377 + struct rtable *rt;
378 + struct flowi fl = {
379 + .oif = 0,
380 + .nl_u = {
381 + .ip4_u = {
382 + .daddr = iph->daddr,
383 + .saddr = iph->saddr,
384 + .tos = RT_TOS(iph->tos),
385 + .scope = ((iph->tos & RTO_ONLINK) ?
386 + RT_SCOPE_LINK :
387 + RT_SCOPE_UNIVERSE)
388 + }
389 + }
390 + };
391 +
392 + if (ip_route_output_key(&rt, &fl)) {
393 + return NF_DROP;
394 + }
395 +
396 + if (skb->dev == rt->u.dst.dev) {
397 + /* Drop old route. */
398 + dst_release(skb->dst);
399 + skb->dst = &rt->u.dst;
400 +
401 + /* this will traverse normal stack, and
402 + * thus call conntrack on the icmp packet */
403 + icmp_send(skb, ICMP_TIME_EXCEEDED,
404 + ICMP_EXC_TTL, 0);
405 + }
406 +
407 + return NF_DROP;
408 + }
409 +
410 + /*
411 + * If we are at INPUT the checksum must be recalculated since
412 + * the length could change as the result of a defragmentation.
413 + */
414 + if(hooknum == NF_IP_LOCAL_IN) {
415 + iph->ttl = iph->ttl - 1;
416 + iph->check = 0;
417 + iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
418 + } else {
419 + ip_decrease_ttl(iph);
420 + }
421 + }
422 +
423 + if ((route_info->flags & IPT_ROUTE_TEE)) {
424 + /*
425 + * Copy the *pskb, and route the copy. Will later return
426 + * IPT_CONTINUE for the original skb, which should continue
427 + * on its way as if nothing happened. The copy should be
428 + * independantly delivered to the ROUTE --gw.
429 + */
430 + skb = skb_copy(*pskb, GFP_ATOMIC);
431 + if (!skb) {
432 + if (net_ratelimit())
433 + DEBUGP(KERN_DEBUG "ipt_ROUTE: copy failed!\n");
434 + return IPT_CONTINUE;
435 + }
436 + }
437 +
438 + /* Tell conntrack to forget this packet since it may get confused
439 + * when a packet is leaving with dst address == our address.
440 + * Good idea ? Dunno. Need advice.
441 + *
442 + * NEW: mark the skb with our &route_tee_track, so we avoid looping
443 + * on any already routed packet.
444 + */
445 + if (!(route_info->flags & IPT_ROUTE_CONTINUE)) {
446 + nf_conntrack_put(skb->nfct);
447 + skb->nfct = &route_tee_track.ct_general;
448 + skb->nfctinfo = IP_CT_NEW;
449 + nf_conntrack_get(skb->nfct);
450 + }
451 +
452 + if (route_info->oif[0] != '\0') {
453 + res = route_oif(route_info, skb);
454 + } else if (route_info->iif[0] != '\0') {
455 + res = route_iif(route_info, skb);
456 + } else if (route_info->gw) {
457 + res = route_gw(route_info, skb);
458 + } else {
459 + if (net_ratelimit())
460 + DEBUGP(KERN_DEBUG "ipt_ROUTE: no parameter !\n");
461 + res = IPT_CONTINUE;
462 + }
463 +
464 + if ((route_info->flags & IPT_ROUTE_TEE))
465 + res = IPT_CONTINUE;
466 +
467 + return res;
468 +}
469 +
470 +
471 +static int ipt_route_checkentry(const char *tablename,
472 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
473 + const void *e,
474 +#else
475 + const struct ipt_ip *ip,
476 +#endif
477 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
478 + const struct xt_target *target,
479 +#endif
480 + void *targinfo,
481 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
482 + unsigned int targinfosize,
483 +#endif
484 + unsigned int hook_mask)
485 +{
486 + if (strcmp(tablename, "mangle") != 0) {
487 + printk("ipt_ROUTE: bad table `%s', use the `mangle' table.\n",
488 + tablename);
489 + return 0;
490 + }
491 +
492 + if (hook_mask & ~( (1 << NF_IP_PRE_ROUTING)
493 + | (1 << NF_IP_LOCAL_IN)
494 + | (1 << NF_IP_FORWARD)
495 + | (1 << NF_IP_LOCAL_OUT)
496 + | (1 << NF_IP_POST_ROUTING))) {
497 + printk("ipt_ROUTE: bad hook\n");
498 + return 0;
499 + }
500 +
501 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
502 + if (targinfosize != IPT_ALIGN(sizeof(struct ipt_route_target_info))) {
503 + printk(KERN_WARNING "ipt_ROUTE: targinfosize %u != %Zu\n",
504 + targinfosize,
505 + IPT_ALIGN(sizeof(struct ipt_route_target_info)));
506 + return 0;
507 + }
508 +#endif
509 +
510 + return 1;
511 +}
512 +
513 +
514 +static struct ipt_target ipt_route_reg = {
515 + .name = "ROUTE",
516 + .target = ipt_route_target,
517 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
518 + .targetsize = sizeof(struct ipt_route_target_info),
519 +#endif
520 + .checkentry = ipt_route_checkentry,
521 + .me = THIS_MODULE,
522 +};
523 +
524 +static int __init init(void)
525 +{
526 + /* Set up fake conntrack (stolen from raw.patch):
527 + - to never be deleted, not in any hashes */
528 + atomic_set(&route_tee_track.ct_general.use, 1);
529 + /* - and look it like as a confirmed connection */
530 + set_bit(IPS_CONFIRMED_BIT, &route_tee_track.status);
531 + /* Initialize fake conntrack so that NAT will skip it */
532 + route_tee_track.status |= IPS_NAT_DONE_MASK;
533 +
534 + return xt_register_target(&ipt_route_reg);
535 +}
536 +
537 +
538 +static void __exit fini(void)
539 +{
540 + xt_unregister_target(&ipt_route_reg);
541 +}
542 +
543 +module_init(init);
544 +module_exit(fini);
545 Index: linux-2.6.22.19/net/ipv4/netfilter/Kconfig
546 ===================================================================
547 --- linux-2.6.22.19.orig/net/ipv4/netfilter/Kconfig
548 +++ linux-2.6.22.19/net/ipv4/netfilter/Kconfig
549 @@ -552,5 +552,22 @@ config IP_NF_TARGET_SET
550 To compile it as a module, choose M here. If unsure, say N.
551
552
553 +config IP_NF_TARGET_ROUTE
554 + tristate 'ROUTE target support'
555 + depends on IP_NF_MANGLE
556 + help
557 + This option adds a `ROUTE' target, which enables you to setup unusual
558 + routes. For example, the ROUTE lets you route a received packet through
559 + an interface or towards a host, even if the regular destination of the
560 + packet is the router itself. The ROUTE target is also able to change the
561 + incoming interface of a packet.
562 +
563 + The target can be or not a final target. It has to be used inside the
564 + mangle table.
565 +
566 + If you want to compile it as a module, say M here and read
567 + Documentation/modules.txt. The module will be called ipt_ROUTE.o.
568 + If unsure, say `N'.
569 +
570 endmenu
571
572 Index: linux-2.6.22.19/net/ipv4/netfilter/Makefile
573 ===================================================================
574 --- linux-2.6.22.19.orig/net/ipv4/netfilter/Makefile
575 +++ linux-2.6.22.19/net/ipv4/netfilter/Makefile
576 @@ -60,6 +60,7 @@ obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_EC
577 obj-$(CONFIG_IP_NF_TARGET_IMQ) += ipt_IMQ.o
578 obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o
579 obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o
580 +obj-$(CONFIG_IP_NF_TARGET_ROUTE) += ipt_ROUTE.o
581 obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o
582 obj-$(CONFIG_IP_NF_TARGET_SAME) += ipt_SAME.o
583 obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o
584 Index: linux-2.6.22.19/net/ipv6/ndisc.c
585 ===================================================================
586 --- linux-2.6.22.19.orig/net/ipv6/ndisc.c
587 +++ linux-2.6.22.19/net/ipv6/ndisc.c
588 @@ -154,6 +154,8 @@ struct neigh_table nd_tbl = {
589 .gc_thresh3 = 1024,
590 };
591
592 +EXPORT_SYMBOL(nd_tbl);
593 +
594 /* ND options */
595 struct ndisc_options {
596 struct nd_opt_hdr *nd_opt_array[__ND_OPT_ARRAY_MAX];
597 Index: linux-2.6.22.19/net/ipv6/netfilter/ip6t_ROUTE.c
598 ===================================================================
599 --- /dev/null
600 +++ linux-2.6.22.19/net/ipv6/netfilter/ip6t_ROUTE.c
601 @@ -0,0 +1,330 @@
602 +/*
603 + * This implements the ROUTE v6 target, which enables you to setup unusual
604 + * routes not supported by the standard kernel routing table.
605 + *
606 + * Copyright (C) 2003 Cedric de Launois <delaunois@info.ucl.ac.be>
607 + *
608 + * v 1.1 2004/11/23
609 + *
610 + * This software is distributed under GNU GPL v2, 1991
611 + */
612 +
613 +#include <linux/module.h>
614 +#include <linux/skbuff.h>
615 +#include <linux/ipv6.h>
616 +#include <linux/netfilter_ipv6/ip6_tables.h>
617 +#include <linux/netfilter_ipv6/ip6t_ROUTE.h>
618 +#include <linux/netdevice.h>
619 +#include <linux/version.h>
620 +#include <net/ipv6.h>
621 +#include <net/ndisc.h>
622 +#include <net/ip6_route.h>
623 +#include <linux/icmpv6.h>
624 +
625 +#if 1
626 +#define DEBUGP printk
627 +#else
628 +#define DEBUGP(format, args...)
629 +#endif
630 +
631 +#define NIP6(addr) \
632 + ntohs((addr).s6_addr16[0]), \
633 + ntohs((addr).s6_addr16[1]), \
634 + ntohs((addr).s6_addr16[2]), \
635 + ntohs((addr).s6_addr16[3]), \
636 + ntohs((addr).s6_addr16[4]), \
637 + ntohs((addr).s6_addr16[5]), \
638 + ntohs((addr).s6_addr16[6]), \
639 + ntohs((addr).s6_addr16[7])
640 +
641 +/* Route the packet according to the routing keys specified in
642 + * route_info. Keys are :
643 + * - ifindex :
644 + * 0 if no oif preferred,
645 + * otherwise set to the index of the desired oif
646 + * - route_info->gw :
647 + * 0 if no gateway specified,
648 + * otherwise set to the next host to which the pkt must be routed
649 + * If success, skb->dev is the output device to which the packet must
650 + * be sent and skb->dst is not NULL
651 + *
652 + * RETURN: 1 if the packet was succesfully routed to the
653 + * destination desired
654 + * 0 if the kernel routing table could not route the packet
655 + * according to the keys specified
656 + */
657 +static int
658 +route6(struct sk_buff *skb,
659 + unsigned int ifindex,
660 + const struct ip6t_route_target_info *route_info)
661 +{
662 + struct rt6_info *rt = NULL;
663 + struct ipv6hdr *ipv6h = ipv6_hdr(skb);
664 + struct in6_addr *gw = (struct in6_addr*)&route_info->gw;
665 +
666 + DEBUGP("ip6t_ROUTE: called with: ");
667 + DEBUGP("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(ipv6h->daddr));
668 + DEBUGP("GATEWAY=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(*gw));
669 + DEBUGP("OUT=%s\n", route_info->oif);
670 +
671 + if (ipv6_addr_any(gw))
672 + rt = rt6_lookup(&ipv6h->daddr, &ipv6h->saddr, ifindex, 1);
673 + else
674 + rt = rt6_lookup(gw, &ipv6h->saddr, ifindex, 1);
675 +
676 + if (!rt)
677 + goto no_route;
678 +
679 + DEBUGP("ip6t_ROUTE: routing gives: ");
680 + DEBUGP("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(rt->rt6i_dst.addr));
681 + DEBUGP("GATEWAY=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(rt->rt6i_gateway));
682 + DEBUGP("OUT=%s\n", rt->rt6i_dev->name);
683 +
684 + if (ifindex && rt->rt6i_dev->ifindex!=ifindex)
685 + goto wrong_route;
686 +
687 + if (!rt->rt6i_nexthop) {
688 + DEBUGP("ip6t_ROUTE: discovering neighbour\n");
689 + rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_dst.addr);
690 + }
691 +
692 + /* Drop old route. */
693 + dst_release(skb->dst);
694 + skb->dst = &rt->u.dst;
695 + skb->dev = rt->rt6i_dev;
696 + return 1;
697 +
698 + wrong_route:
699 + dst_release(&rt->u.dst);
700 + no_route:
701 + if (!net_ratelimit())
702 + return 0;
703 +
704 + printk("ip6t_ROUTE: no explicit route found ");
705 + if (ifindex)
706 + printk("via interface %s ", route_info->oif);
707 + if (!ipv6_addr_any(gw))
708 + printk("via gateway %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", NIP6(*gw));
709 + printk("\n");
710 + return 0;
711 +}
712 +
713 +
714 +/* Stolen from ip6_output_finish
715 + * PRE : skb->dev is set to the device we are leaving by
716 + * skb->dst is not NULL
717 + * POST: the packet is sent with the link layer header pushed
718 + * the packet is destroyed
719 + */
720 +static void ip_direct_send(struct sk_buff *skb)
721 +{
722 + struct dst_entry *dst = skb->dst;
723 + struct hh_cache *hh = dst->hh;
724 +
725 + if (hh) {
726 + read_lock_bh(&hh->hh_lock);
727 + memcpy(skb->data - 16, hh->hh_data, 16);
728 + read_unlock_bh(&hh->hh_lock);
729 + skb_push(skb, hh->hh_len);
730 + hh->hh_output(skb);
731 + } else if (dst->neighbour)
732 + dst->neighbour->output(skb);
733 + else {
734 + if (net_ratelimit())
735 + DEBUGP(KERN_DEBUG "ip6t_ROUTE: no hdr & no neighbour cache!\n");
736 + kfree_skb(skb);
737 + }
738 +}
739 +
740 +
741 +static unsigned int
742 +route6_oif(const struct ip6t_route_target_info *route_info,
743 + struct sk_buff *skb)
744 +{
745 + unsigned int ifindex = 0;
746 + struct net_device *dev_out = NULL;
747 +
748 + /* The user set the interface name to use.
749 + * Getting the current interface index.
750 + */
751 + if ((dev_out = dev_get_by_name(route_info->oif))) {
752 + ifindex = dev_out->ifindex;
753 + } else {
754 + /* Unknown interface name : packet dropped */
755 + if (net_ratelimit())
756 + DEBUGP("ip6t_ROUTE: oif interface %s not found\n", route_info->oif);
757 +
758 + if (route_info->flags & IP6T_ROUTE_CONTINUE)
759 + return IP6T_CONTINUE;
760 + else
761 + return NF_DROP;
762 + }
763 +
764 + /* Trying the standard way of routing packets */
765 + if (route6(skb, ifindex, route_info)) {
766 + dev_put(dev_out);
767 + if (route_info->flags & IP6T_ROUTE_CONTINUE)
768 + return IP6T_CONTINUE;
769 +
770 + ip_direct_send(skb);
771 + return NF_STOLEN;
772 + } else
773 + return NF_DROP;
774 +}
775 +
776 +
777 +static unsigned int
778 +route6_gw(const struct ip6t_route_target_info *route_info,
779 + struct sk_buff *skb)
780 +{
781 + if (route6(skb, 0, route_info)) {
782 + if (route_info->flags & IP6T_ROUTE_CONTINUE)
783 + return IP6T_CONTINUE;
784 +
785 + ip_direct_send(skb);
786 + return NF_STOLEN;
787 + } else
788 + return NF_DROP;
789 +}
790 +
791 +
792 +static unsigned int
793 +ip6t_route_target(struct sk_buff **pskb,
794 + const struct net_device *in,
795 + const struct net_device *out,
796 + unsigned int hooknum,
797 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
798 + const struct xt_target *target,
799 +#endif
800 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
801 + const void *targinfo,
802 + void *userinfo)
803 +#else
804 + const void *targinfo)
805 +#endif
806 +{
807 + const struct ip6t_route_target_info *route_info = targinfo;
808 + struct sk_buff *skb = *pskb;
809 + struct in6_addr *gw = (struct in6_addr*)&route_info->gw;
810 + unsigned int res;
811 +
812 + if (route_info->flags & IP6T_ROUTE_CONTINUE)
813 + goto do_it;
814 +
815 + /* If we are at PREROUTING or INPUT hook
816 + * the TTL isn't decreased by the IP stack
817 + */
818 + if (hooknum == NF_IP6_PRE_ROUTING ||
819 + hooknum == NF_IP6_LOCAL_IN) {
820 +
821 + struct ipv6hdr *ipv6h = ipv6_hdr(skb);
822 +
823 + if (ipv6h->hop_limit <= 1) {
824 + /* Force OUTPUT device used as source address */
825 + skb->dev = skb->dst->dev;
826 +
827 + icmpv6_send(skb, ICMPV6_TIME_EXCEED,
828 + ICMPV6_EXC_HOPLIMIT, 0, skb->dev);
829 +
830 + return NF_DROP;
831 + }
832 +
833 + ipv6h->hop_limit--;
834 + }
835 +
836 + if ((route_info->flags & IP6T_ROUTE_TEE)) {
837 + /*
838 + * Copy the *pskb, and route the copy. Will later return
839 + * IP6T_CONTINUE for the original skb, which should continue
840 + * on its way as if nothing happened. The copy should be
841 + * independantly delivered to the ROUTE --gw.
842 + */
843 + skb = skb_copy(*pskb, GFP_ATOMIC);
844 + if (!skb) {
845 + if (net_ratelimit())
846 + DEBUGP(KERN_DEBUG "ip6t_ROUTE: copy failed!\n");
847 + return IP6T_CONTINUE;
848 + }
849 + }
850 +
851 +do_it:
852 + if (route_info->oif[0]) {
853 + res = route6_oif(route_info, skb);
854 + } else if (!ipv6_addr_any(gw)) {
855 + res = route6_gw(route_info, skb);
856 + } else {
857 + if (net_ratelimit())
858 + DEBUGP(KERN_DEBUG "ip6t_ROUTE: no parameter !\n");
859 + res = IP6T_CONTINUE;
860 + }
861 +
862 + if ((route_info->flags & IP6T_ROUTE_TEE))
863 + res = IP6T_CONTINUE;
864 +
865 + return res;
866 +}
867 +
868 +
869 +static int
870 +ip6t_route_checkentry(const char *tablename,
871 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
872 + const void *entry,
873 +#else
874 + const struct ip6t_entry *entry
875 +#endif
876 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
877 + const struct xt_target *target,
878 +#endif
879 + void *targinfo,
880 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
881 + unsigned int targinfosize,
882 +#endif
883 + unsigned int hook_mask)
884 +{
885 + if (strcmp(tablename, "mangle") != 0) {
886 + printk("ip6t_ROUTE: can only be called from \"mangle\" table.\n");
887 + return 0;
888 + }
889 +
890 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
891 + if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_route_target_info))) {
892 + printk(KERN_WARNING "ip6t_ROUTE: targinfosize %u != %Zu\n",
893 + targinfosize,
894 + IP6T_ALIGN(sizeof(struct ip6t_route_target_info)));
895 + return 0;
896 + }
897 +#endif
898 +
899 + return 1;
900 +}
901 +
902 +
903 +static struct ip6t_target ip6t_route_reg = {
904 + .name = "ROUTE",
905 + .target = ip6t_route_target,
906 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
907 + .targetsize = sizeof(struct ip6t_route_target_info),
908 +#endif
909 + .checkentry = ip6t_route_checkentry,
910 + .me = THIS_MODULE
911 +};
912 +
913 +
914 +static int __init init(void)
915 +{
916 + printk(KERN_DEBUG "registering ipv6 ROUTE target\n");
917 + if (xt_register_target(&ip6t_route_reg))
918 + return -EINVAL;
919 +
920 + return 0;
921 +}
922 +
923 +
924 +static void __exit fini(void)
925 +{
926 + xt_unregister_target(&ip6t_route_reg);
927 +}
928 +
929 +module_init(init);
930 +module_exit(fini);
931 +MODULE_LICENSE("GPL");
932 Index: linux-2.6.22.19/net/ipv6/netfilter/Kconfig
933 ===================================================================
934 --- linux-2.6.22.19.orig/net/ipv6/netfilter/Kconfig
935 +++ linux-2.6.22.19/net/ipv6/netfilter/Kconfig
936 @@ -209,5 +209,18 @@ config IP6_NF_RAW
937 If you want to compile it as a module, say M here and read
938 <file:Documentation/kbuild/modules.txt>. If unsure, say `N'.
939
940 +config IP6_NF_TARGET_ROUTE
941 + tristate 'ROUTE target support'
942 + depends on IP6_NF_MANGLE
943 + help
944 + This option adds a `ROUTE' target, which enables you to setup unusual
945 + routes. The ROUTE target is also able to change the incoming interface
946 + of a packet.
947 +
948 + The target can be or not a final target. It has to be used inside the
949 + mangle table.
950 +
951 + Not working as a module.
952 +
953 endmenu
954
955 Index: linux-2.6.22.19/net/ipv6/netfilter/Makefile
956 ===================================================================
957 --- linux-2.6.22.19.orig/net/ipv6/netfilter/Makefile
958 +++ linux-2.6.22.19/net/ipv6/netfilter/Makefile
959 @@ -20,6 +20,7 @@ obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_
960 obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o
961 obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o
962 obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o
963 +obj-$(CONFIG_IP6_NF_TARGET_ROUTE) += ip6t_ROUTE.o
964 obj-$(CONFIG_IP6_NF_MATCH_MH) += ip6t_mh.o
965
966 # objects for l3 independent conntrack