2 * This implements the ROUTE v6 target, which enables you to setup unusual
3 * routes not supported by the standard kernel routing table.
5 * Copyright (C) 2003 Cedric de Launois <delaunois@info.ucl.ac.be>
9 * This software is distributed under GNU GPL v2, 1991
12 #include <linux/module.h>
13 #include <linux/skbuff.h>
14 #include <linux/version.h>
16 #include <linux/ipv6.h>
17 #include <linux/netfilter_ipv6/ip6_tables.h>
18 #include <linux/netfilter_ipv6/ip6t_ROUTE.h>
19 #include <linux/netdevice.h>
20 #include <linux/version.h>
22 #include <net/ndisc.h>
23 #include <net/ip6_route.h>
24 #include <linux/icmpv6.h>
26 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
27 #define ipv6_hdr(s) (s->nh.ipv6h)
30 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
31 #include <net/net_namespace.h>
34 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
35 #define NF_INET_PRE_ROUTING NF_IP6_PRE_ROUTING
36 #define NF_INET_LOCAL_IN NF_IP6_LOCAL_IN
37 #define NF_INET_FORWARD NF_IP6_FORWARD
38 #define NF_INET_LOCAL_OUT NF_IP6_LOCAL_OUT
39 #define NF_INET_POST_ROUTING NF_IP6_POST_ROUTING
45 #define DEBUGP(format, args...)
49 ntohs((addr).s6_addr16[0]), \
50 ntohs((addr).s6_addr16[1]), \
51 ntohs((addr).s6_addr16[2]), \
52 ntohs((addr).s6_addr16[3]), \
53 ntohs((addr).s6_addr16[4]), \
54 ntohs((addr).s6_addr16[5]), \
55 ntohs((addr).s6_addr16[6]), \
56 ntohs((addr).s6_addr16[7])
58 /* Route the packet according to the routing keys specified in
59 * route_info. Keys are :
61 * 0 if no oif preferred,
62 * otherwise set to the index of the desired oif
64 * 0 if no gateway specified,
65 * otherwise set to the next host to which the pkt must be routed
66 * If success, skb->dev is the output device to which the packet must
67 * be sent and skb->dst is not NULL
69 * RETURN: 1 if the packet was succesfully routed to the
71 * 0 if the kernel routing table could not route the packet
72 * according to the keys specified
75 route6(struct sk_buff
*skb
,
77 const struct ip6t_route_target_info
*route_info
)
79 struct rt6_info
*rt
= NULL
;
80 struct ipv6hdr
*ipv6h
= ipv6_hdr(skb
);
81 struct in6_addr
*gw
= (struct in6_addr
*)&route_info
->gw
;
83 DEBUGP("ip6t_ROUTE: called with: ");
84 DEBUGP("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(ipv6h
->daddr
));
85 DEBUGP("GATEWAY=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(*gw
));
86 DEBUGP("OUT=%s\n", route_info
->oif
);
88 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
89 if (ipv6_addr_any(gw
))
90 rt
= rt6_lookup(&init_net
, &ipv6h
->daddr
, &ipv6h
->saddr
, ifindex
, 1);
92 rt
= rt6_lookup(&init_net
, gw
, &ipv6h
->saddr
, ifindex
, 1);
94 if (ipv6_addr_any(gw
))
95 rt
= rt6_lookup(&ipv6h
->daddr
, &ipv6h
->saddr
, ifindex
, 1);
97 rt
= rt6_lookup(gw
, &ipv6h
->saddr
, ifindex
, 1);
103 DEBUGP("ip6t_ROUTE: routing gives: ");
104 DEBUGP("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(rt
->rt6i_dst
.addr
));
105 DEBUGP("GATEWAY=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(rt
->rt6i_gateway
));
106 DEBUGP("OUT=%s\n", rt
->rt6i_dev
->name
);
108 if (ifindex
&& rt
->rt6i_dev
->ifindex
!=ifindex
)
111 if (!rt
->rt6i_nexthop
) {
112 DEBUGP("ip6t_ROUTE: discovering neighbor\n");
113 rt
->rt6i_nexthop
= ndisc_get_neigh(rt
->rt6i_dev
, &rt
->rt6i_dst
.addr
);
116 /* Drop old route. */
117 dst_release(skb
->dst
);
118 skb
->dst
= &rt
->u
.dst
;
119 skb
->dev
= rt
->rt6i_dev
;
123 dst_release(&rt
->u
.dst
);
125 if (!net_ratelimit())
128 printk("ip6t_ROUTE: no explicit route found ");
130 printk("via interface %s ", route_info
->oif
);
131 if (!ipv6_addr_any(gw
))
132 printk("via gateway %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", NIP6(*gw
));
138 /* Stolen from ip6_output_finish
139 * PRE : skb->dev is set to the device we are leaving by
140 * skb->dst is not NULL
141 * POST: the packet is sent with the link layer header pushed
142 * the packet is destroyed
144 static void ip_direct_send(struct sk_buff
*skb
)
146 struct dst_entry
*dst
= skb
->dst
;
147 struct hh_cache
*hh
= dst
->hh
;
152 seq
= read_seqbegin(&hh
->hh_lock
);
153 memcpy(skb
->data
- 16, hh
->hh_data
, 16);
154 } while (read_seqretry(&hh
->hh_lock
, seq
));
155 skb_push(skb
, hh
->hh_len
);
157 } else if (dst
->neighbour
)
158 dst
->neighbour
->output(skb
);
161 DEBUGP(KERN_DEBUG
"ip6t_ROUTE: no hdr & no neighbour cache!\n");
168 route6_oif(const struct ip6t_route_target_info
*route_info
,
171 unsigned int ifindex
= 0;
172 struct net_device
*dev_out
= NULL
;
174 /* The user set the interface name to use.
175 * Getting the current interface index.
177 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
178 if ((dev_out
= dev_get_by_name(&init_net
, route_info
->oif
))) {
180 if ((dev_out
= dev_get_by_name(route_info
->oif
))) {
182 ifindex
= dev_out
->ifindex
;
184 /* Unknown interface name : packet dropped */
186 DEBUGP("ip6t_ROUTE: oif interface %s not found\n", route_info
->oif
);
188 if (route_info
->flags
& IP6T_ROUTE_CONTINUE
)
189 return IP6T_CONTINUE
;
194 /* Trying the standard way of routing packets */
195 if (route6(skb
, ifindex
, route_info
)) {
197 if (route_info
->flags
& IP6T_ROUTE_CONTINUE
)
198 return IP6T_CONTINUE
;
208 route6_gw(const struct ip6t_route_target_info
*route_info
,
211 if (route6(skb
, 0, route_info
)) {
212 if (route_info
->flags
& IP6T_ROUTE_CONTINUE
)
213 return IP6T_CONTINUE
;
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,22)
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 const struct ip6t_route_target_info
*route_info
= targinfo
;
266 const struct ip6t_route_target_info
*route_info
= par
->targinfo
;
267 unsigned int hooknum
= par
->hooknum
;
269 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
270 struct sk_buff
*skb
= *pskb
;
272 struct in6_addr
*gw
= (struct in6_addr
*)&route_info
->gw
;
275 if (route_info
->flags
& IP6T_ROUTE_CONTINUE
)
278 /* If we are at PREROUTING or INPUT hook
279 * the TTL isn't decreased by the IP stack
281 if (hooknum
== NF_INET_PRE_ROUTING
||
282 hooknum
== NF_INET_LOCAL_IN
) {
284 struct ipv6hdr
*ipv6h
= ipv6_hdr(skb
);
286 if (ipv6h
->hop_limit
<= 1) {
287 /* Force OUTPUT device used as source address */
288 skb
->dev
= skb
->dst
->dev
;
290 icmpv6_send(skb
, ICMPV6_TIME_EXCEED
,
291 ICMPV6_EXC_HOPLIMIT
, 0, skb
->dev
);
299 if ((route_info
->flags
& IP6T_ROUTE_TEE
)) {
301 * Copy the skb, and route the copy. Will later return
302 * IP6T_CONTINUE for the original skb, which should continue
303 * on its way as if nothing happened. The copy should be
304 * independantly delivered to the ROUTE --gw.
306 skb
= skb_copy(skb
, GFP_ATOMIC
);
309 DEBUGP(KERN_DEBUG
"ip6t_ROUTE: copy failed!\n");
310 return IP6T_CONTINUE
;
315 if (route_info
->oif
[0]) {
316 res
= route6_oif(route_info
, skb
);
317 } else if (!ipv6_addr_any(gw
)) {
318 res
= route6_gw(route_info
, skb
);
321 DEBUGP(KERN_DEBUG
"ip6t_ROUTE: no parameter !\n");
325 if ((route_info
->flags
& IP6T_ROUTE_TEE
))
332 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
334 checkentry(const char *tablename
,
335 const struct ip6t_entry
*e
,
337 unsigned int targinfosize
,
338 unsigned int hook_mask
)
339 #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
341 checkentry(const char *tablename
,
344 unsigned int targinfosize
,
345 unsigned int hook_mask
)
346 #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
348 checkentry(const char *tablename
,
350 const struct xt_target
*target
,
352 unsigned int targinfosize
,
353 unsigned int hook_mask
)
354 #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
356 checkentry(const char *tablename
,
358 const struct xt_target
*target
,
360 unsigned int hook_mask
)
361 #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
363 checkentry(const char *tablename
,
365 const struct xt_target
*target
,
367 unsigned int hook_mask
)
368 #else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) */
370 checkentry(const struct xt_tgchk_param
*par
)
373 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
374 const char *tablename
= par
->table
;
377 if (strcmp(tablename
, "mangle") != 0) {
378 printk("ip6t_ROUTE: can only be called from \"mangle\" table.\n");
382 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
383 if (targinfosize
!= IP6T_ALIGN(sizeof(struct ip6t_route_target_info
))) {
384 printk(KERN_WARNING
"ip6t_ROUTE: targinfosize %u != %Zu\n",
386 IP6T_ALIGN(sizeof(struct ip6t_route_target_info
)));
394 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
395 static struct xt_target ip6t_route_reg
= {
397 static struct ip6t_target ip6t_route_reg
= {
400 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
404 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
405 .targetsize
= sizeof(struct ip6t_route_target_info
),
407 .checkentry
= checkentry
,
412 static int __init
init(void)
414 printk(KERN_DEBUG
"registering ipv6 ROUTE target\n");
415 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
416 if (xt_register_target(&ip6t_route_reg
))
418 if (ip6t_register_target(&ip6t_route_reg
))
426 static void __exit
fini(void)
428 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
429 xt_unregister_target(&ip6t_route_reg
);
431 ip6t_unregister_target(&ip6t_route_reg
);
437 MODULE_LICENSE("GPL");