diff options
| -rw-r--r-- | include/linux/netfilter.h | 11 | ||||
| -rw-r--r-- | include/linux/netfilter_ipv6.h | 3 | ||||
| -rw-r--r-- | net/core/netfilter.c | 66 | ||||
| -rw-r--r-- | net/ipv4/netfilter.c | 64 | ||||
| -rw-r--r-- | net/ipv4/netfilter/ip_queue.c | 27 | ||||
| -rw-r--r-- | net/ipv6/af_inet6.c | 7 | ||||
| -rw-r--r-- | net/ipv6/netfilter.c | 62 | ||||
| -rw-r--r-- | net/ipv6/netfilter/ip6_queue.c | 24 | 
8 files changed, 199 insertions, 65 deletions
| diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 54b97a1baba5..d163e20ca8d9 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h | |||
| @@ -198,6 +198,17 @@ extern void nf_invalidate_cache(int pf); | |||
| 198 | Returns true or false. */ | 198 | Returns true or false. */ | 
| 199 | extern int skb_make_writable(struct sk_buff **pskb, unsigned int writable_len); | 199 | extern int skb_make_writable(struct sk_buff **pskb, unsigned int writable_len); | 
| 200 | 200 | ||
| 201 | struct nf_queue_rerouter { | ||
| 202 | void (*save)(const struct sk_buff *skb, struct nf_info *info); | ||
| 203 | int (*reroute)(struct sk_buff **skb, const struct nf_info *info); | ||
| 204 | int rer_size; | ||
| 205 | }; | ||
| 206 | |||
| 207 | #define nf_info_reroute(x) ((void *)x + sizeof(struct nf_info)) | ||
| 208 | |||
| 209 | extern int nf_register_queue_rerouter(int pf, struct nf_queue_rerouter *rer); | ||
| 210 | extern int nf_unregister_queue_rerouter(int pf); | ||
| 211 | |||
| 201 | #else /* !CONFIG_NETFILTER */ | 212 | #else /* !CONFIG_NETFILTER */ | 
| 202 | #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb) | 213 | #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb) | 
| 203 | static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {} | 214 | static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {} | 
| diff --git a/include/linux/netfilter_ipv6.h b/include/linux/netfilter_ipv6.h index 20c069a5e4ac..5d204ee7a312 100644 --- a/include/linux/netfilter_ipv6.h +++ b/include/linux/netfilter_ipv6.h | |||
| @@ -71,4 +71,7 @@ enum nf_ip6_hook_priorities { | |||
| 71 | NF_IP6_PRI_LAST = INT_MAX, | 71 | NF_IP6_PRI_LAST = INT_MAX, | 
| 72 | }; | 72 | }; | 
| 73 | 73 | ||
| 74 | int ipv6_netfilter_init(void); | ||
| 75 | void ipv6_netfilter_fini(void); | ||
| 76 | |||
| 74 | #endif /*__LINUX_IP6_NETFILTER_H*/ | 77 | #endif /*__LINUX_IP6_NETFILTER_H*/ | 
| diff --git a/net/core/netfilter.c b/net/core/netfilter.c index 9849357f6129..1ed4f3110421 100644 --- a/net/core/netfilter.c +++ b/net/core/netfilter.c | |||
| @@ -53,6 +53,9 @@ static struct nf_queue_handler_t { | |||
| 53 | nf_queue_outfn_t outfn; | 53 | nf_queue_outfn_t outfn; | 
| 54 | void *data; | 54 | void *data; | 
| 55 | } queue_handler[NPROTO]; | 55 | } queue_handler[NPROTO]; | 
| 56 | |||
| 57 | static struct nf_queue_rerouter *queue_rerouter; | ||
| 58 | |||
| 56 | static DEFINE_RWLOCK(queue_handler_lock); | 59 | static DEFINE_RWLOCK(queue_handler_lock); | 
| 57 | 60 | ||
| 58 | int nf_register_hook(struct nf_hook_ops *reg) | 61 | int nf_register_hook(struct nf_hook_ops *reg) | 
| @@ -260,11 +263,34 @@ int nf_unregister_queue_handler(int pf) | |||
| 260 | return 0; | 263 | return 0; | 
| 261 | } | 264 | } | 
| 262 | 265 | ||
| 266 | int nf_register_queue_rerouter(int pf, struct nf_queue_rerouter *rer) | ||
| 267 | { | ||
| 268 | if (pf >= NPROTO) | ||
| 269 | return -EINVAL; | ||
| 270 | |||
| 271 | write_lock_bh(&queue_handler_lock); | ||
| 272 | memcpy(&queue_rerouter[pf], rer, sizeof(queue_rerouter[pf])); | ||
| 273 | write_unlock_bh(&queue_handler_lock); | ||
| 274 | |||
| 275 | return 0; | ||
| 276 | } | ||
| 277 | |||
| 278 | int nf_unregister_queue_rerouter(int pf) | ||
| 279 | { | ||
| 280 | if (pf >= NPROTO) | ||
| 281 | return -EINVAL; | ||
| 282 | |||
| 283 | write_lock_bh(&queue_handler_lock); | ||
| 284 | memset(&queue_rerouter[pf], 0, sizeof(queue_rerouter[pf])); | ||
| 285 | write_unlock_bh(&queue_handler_lock); | ||
| 286 | return 0; | ||
| 287 | } | ||
| 288 | |||
| 263 | /* | 289 | /* | 
| 264 | * Any packet that leaves via this function must come back | 290 | * Any packet that leaves via this function must come back | 
| 265 | * through nf_reinject(). | 291 | * through nf_reinject(). | 
| 266 | */ | 292 | */ | 
| 267 | static int nf_queue(struct sk_buff *skb, | 293 | static int nf_queue(struct sk_buff **skb, | 
| 268 | struct list_head *elem, | 294 | struct list_head *elem, | 
| 269 | int pf, unsigned int hook, | 295 | int pf, unsigned int hook, | 
| 270 | struct net_device *indev, | 296 | struct net_device *indev, | 
| @@ -282,17 +308,17 @@ static int nf_queue(struct sk_buff *skb, | |||
| 282 | read_lock(&queue_handler_lock); | 308 | read_lock(&queue_handler_lock); | 
| 283 | if (!queue_handler[pf].outfn) { | 309 | if (!queue_handler[pf].outfn) { | 
| 284 | read_unlock(&queue_handler_lock); | 310 | read_unlock(&queue_handler_lock); | 
| 285 | kfree_skb(skb); | 311 | kfree_skb(*skb); | 
| 286 | return 1; | 312 | return 1; | 
| 287 | } | 313 | } | 
| 288 | 314 | ||
| 289 | info = kmalloc(sizeof(*info), GFP_ATOMIC); | 315 | info = kmalloc(sizeof(*info)+queue_rerouter[pf].rer_size, GFP_ATOMIC); | 
| 290 | if (!info) { | 316 | if (!info) { | 
| 291 | if (net_ratelimit()) | 317 | if (net_ratelimit()) | 
| 292 | printk(KERN_ERR "OOM queueing packet %p\n", | 318 | printk(KERN_ERR "OOM queueing packet %p\n", | 
| 293 | skb); | 319 | *skb); | 
| 294 | read_unlock(&queue_handler_lock); | 320 | read_unlock(&queue_handler_lock); | 
| 295 | kfree_skb(skb); | 321 | kfree_skb(*skb); | 
| 296 | return 1; | 322 | return 1; | 
| 297 | } | 323 | } | 
| 298 | 324 | ||
| @@ -311,15 +337,21 @@ static int nf_queue(struct sk_buff *skb, | |||
| 311 | if (outdev) dev_hold(outdev); | 337 | if (outdev) dev_hold(outdev); | 
| 312 | 338 | ||
| 313 | #ifdef CONFIG_BRIDGE_NETFILTER | 339 | #ifdef CONFIG_BRIDGE_NETFILTER | 
| 314 | if (skb->nf_bridge) { | 340 | if ((*skb)->nf_bridge) { | 
| 315 | physindev = skb->nf_bridge->physindev; | 341 | physindev = (*skb)->nf_bridge->physindev; | 
| 316 | if (physindev) dev_hold(physindev); | 342 | if (physindev) dev_hold(physindev); | 
| 317 | physoutdev = skb->nf_bridge->physoutdev; | 343 | physoutdev = (*skb)->nf_bridge->physoutdev; | 
| 318 | if (physoutdev) dev_hold(physoutdev); | 344 | if (physoutdev) dev_hold(physoutdev); | 
| 319 | } | 345 | } | 
| 320 | #endif | 346 | #endif | 
| 347 | if (queue_rerouter[pf].save) | ||
| 348 | queue_rerouter[pf].save(*skb, info); | ||
| 349 | |||
| 350 | status = queue_handler[pf].outfn(*skb, info, queue_handler[pf].data); | ||
| 351 | |||
| 352 | if (status >= 0 && queue_rerouter[pf].reroute) | ||
| 353 | status = queue_rerouter[pf].reroute(skb, info); | ||
| 321 | 354 | ||
| 322 | status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data); | ||
| 323 | read_unlock(&queue_handler_lock); | 355 | read_unlock(&queue_handler_lock); | 
| 324 | 356 | ||
| 325 | if (status < 0) { | 357 | if (status < 0) { | 
| @@ -332,9 +364,11 @@ static int nf_queue(struct sk_buff *skb, | |||
| 332 | #endif | 364 | #endif | 
| 333 | module_put(info->elem->owner); | 365 | module_put(info->elem->owner); | 
| 334 | kfree(info); | 366 | kfree(info); | 
| 335 | kfree_skb(skb); | 367 | kfree_skb(*skb); | 
| 368 | |||
| 336 | return 1; | 369 | return 1; | 
| 337 | } | 370 | } | 
| 371 | |||
| 338 | return 1; | 372 | return 1; | 
| 339 | } | 373 | } | 
| 340 | 374 | ||
| @@ -365,7 +399,7 @@ next_hook: | |||
| 365 | ret = -EPERM; | 399 | ret = -EPERM; | 
| 366 | } else if (verdict == NF_QUEUE) { | 400 | } else if (verdict == NF_QUEUE) { | 
| 367 | NFDEBUG("nf_hook: Verdict = QUEUE.\n"); | 401 | NFDEBUG("nf_hook: Verdict = QUEUE.\n"); | 
| 368 | if (!nf_queue(*pskb, elem, pf, hook, indev, outdev, okfn)) | 402 | if (!nf_queue(pskb, elem, pf, hook, indev, outdev, okfn)) | 
| 369 | goto next_hook; | 403 | goto next_hook; | 
| 370 | } | 404 | } | 
| 371 | unlock: | 405 | unlock: | 
| @@ -428,7 +462,7 @@ void nf_reinject(struct sk_buff *skb, struct nf_info *info, | |||
| 428 | break; | 462 | break; | 
| 429 | 463 | ||
| 430 | case NF_QUEUE: | 464 | case NF_QUEUE: | 
| 431 | if (!nf_queue(skb, elem, info->pf, info->hook, | 465 | if (!nf_queue(&skb, elem, info->pf, info->hook, | 
| 432 | info->indev, info->outdev, info->okfn)) | 466 | info->indev, info->outdev, info->okfn)) | 
| 433 | goto next_hook; | 467 | goto next_hook; | 
| 434 | break; | 468 | break; | 
| @@ -555,6 +589,12 @@ void __init netfilter_init(void) | |||
| 555 | { | 589 | { | 
| 556 | int i, h; | 590 | int i, h; | 
| 557 | 591 | ||
| 592 | queue_rerouter = kmalloc(NPROTO * sizeof(struct nf_queue_rerouter), | ||
| 593 | GFP_KERNEL); | ||
| 594 | if (!queue_rerouter) | ||
| 595 | panic("netfilter: cannot allocate queue rerouter array\n"); | ||
| 596 | memset(queue_rerouter, 0, NPROTO * sizeof(struct nf_queue_rerouter)); | ||
| 597 | |||
| 558 | for (i = 0; i < NPROTO; i++) { | 598 | for (i = 0; i < NPROTO; i++) { | 
| 559 | for (h = 0; h < NF_MAX_HOOKS; h++) | 599 | for (h = 0; h < NF_MAX_HOOKS; h++) | 
| 560 | INIT_LIST_HEAD(&nf_hooks[i][h]); | 600 | INIT_LIST_HEAD(&nf_hooks[i][h]); | 
| @@ -573,4 +613,6 @@ EXPORT_SYMBOL(nf_reinject); | |||
| 573 | EXPORT_SYMBOL(nf_setsockopt); | 613 | EXPORT_SYMBOL(nf_setsockopt); | 
| 574 | EXPORT_SYMBOL(nf_unregister_hook); | 614 | EXPORT_SYMBOL(nf_unregister_hook); | 
| 575 | EXPORT_SYMBOL(nf_unregister_queue_handler); | 615 | EXPORT_SYMBOL(nf_unregister_queue_handler); | 
| 616 | EXPORT_SYMBOL_GPL(nf_register_queue_rerouter); | ||
| 617 | EXPORT_SYMBOL_GPL(nf_unregister_queue_rerouter); | ||
| 576 | EXPORT_SYMBOL(nf_unregister_sockopt); | 618 | EXPORT_SYMBOL(nf_unregister_sockopt); | 
| diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c index 6594d1c9697e..ae0779d82c5d 100644 --- a/net/ipv4/netfilter.c +++ b/net/ipv4/netfilter.c | |||
| @@ -1,10 +1,11 @@ | |||
| 1 | #include <linux/config.h> | 1 | /* IPv4 specific functions of netfilter core */ | 
| 2 | 2 | ||
| 3 | #include <linux/config.h> | ||
| 3 | #ifdef CONFIG_NETFILTER | 4 | #ifdef CONFIG_NETFILTER | 
| 4 | 5 | ||
| 5 | /* IPv4 specific functions of netfilter core */ | ||
| 6 | #include <linux/kernel.h> | 6 | #include <linux/kernel.h> | 
| 7 | #include <linux/netfilter.h> | 7 | #include <linux/netfilter.h> | 
| 8 | #include <linux/netfilter_ipv4.h> | ||
| 8 | 9 | ||
| 9 | #include <linux/tcp.h> | 10 | #include <linux/tcp.h> | 
| 10 | #include <linux/udp.h> | 11 | #include <linux/udp.h> | 
| @@ -76,4 +77,63 @@ int ip_route_me_harder(struct sk_buff **pskb) | |||
| 76 | return 0; | 77 | return 0; | 
| 77 | } | 78 | } | 
| 78 | EXPORT_SYMBOL(ip_route_me_harder); | 79 | EXPORT_SYMBOL(ip_route_me_harder); | 
| 80 | |||
| 81 | /* | ||
| 82 | * Extra routing may needed on local out, as the QUEUE target never | ||
| 83 | * returns control to the table. | ||
| 84 | */ | ||
| 85 | |||
| 86 | struct ip_rt_info { | ||
| 87 | u_int32_t daddr; | ||
| 88 | u_int32_t saddr; | ||
| 89 | u_int8_t tos; | ||
| 90 | }; | ||
| 91 | |||
| 92 | static void queue_save(const struct sk_buff *skb, struct nf_info *info) | ||
| 93 | { | ||
| 94 | struct ip_rt_info *rt_info = nf_info_reroute(info); | ||
| 95 | |||
| 96 | if (info->hook == NF_IP_LOCAL_OUT) { | ||
| 97 | const struct iphdr *iph = skb->nh.iph; | ||
| 98 | |||
| 99 | rt_info->tos = iph->tos; | ||
| 100 | rt_info->daddr = iph->daddr; | ||
| 101 | rt_info->saddr = iph->saddr; | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | static int queue_reroute(struct sk_buff **pskb, const struct nf_info *info) | ||
| 106 | { | ||
| 107 | const struct ip_rt_info *rt_info = nf_info_reroute(info); | ||
| 108 | |||
| 109 | if (info->hook == NF_IP_LOCAL_OUT) { | ||
| 110 | struct iphdr *iph = (*pskb)->nh.iph; | ||
| 111 | |||
| 112 | if (!(iph->tos == rt_info->tos | ||
| 113 | && iph->daddr == rt_info->daddr | ||
| 114 | && iph->saddr == rt_info->saddr)) | ||
| 115 | return ip_route_me_harder(pskb); | ||
| 116 | } | ||
| 117 | return 0; | ||
| 118 | } | ||
| 119 | |||
| 120 | static struct nf_queue_rerouter ip_reroute = { | ||
| 121 | .rer_size = sizeof(struct ip_rt_info), | ||
| 122 | .save = queue_save, | ||
| 123 | .reroute = queue_reroute, | ||
| 124 | }; | ||
| 125 | |||
| 126 | static int init(void) | ||
| 127 | { | ||
| 128 | return nf_register_queue_rerouter(PF_INET, &ip_reroute); | ||
| 129 | } | ||
| 130 | |||
| 131 | static void fini(void) | ||
| 132 | { | ||
| 133 | nf_unregister_queue_rerouter(PF_INET); | ||
| 134 | } | ||
| 135 | |||
| 136 | module_init(init); | ||
| 137 | module_exit(fini); | ||
| 138 | |||
| 79 | #endif /* CONFIG_NETFILTER */ | 139 | #endif /* CONFIG_NETFILTER */ | 
| diff --git a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c index b237f7fcad92..78892980f42c 100644 --- a/net/ipv4/netfilter/ip_queue.c +++ b/net/ipv4/netfilter/ip_queue.c | |||
| @@ -43,17 +43,10 @@ | |||
| 43 | #define NET_IPQ_QMAX 2088 | 43 | #define NET_IPQ_QMAX 2088 | 
| 44 | #define NET_IPQ_QMAX_NAME "ip_queue_maxlen" | 44 | #define NET_IPQ_QMAX_NAME "ip_queue_maxlen" | 
| 45 | 45 | ||
| 46 | struct ipq_rt_info { | ||
| 47 | __u8 tos; | ||
| 48 | __u32 daddr; | ||
| 49 | __u32 saddr; | ||
| 50 | }; | ||
| 51 | |||
| 52 | struct ipq_queue_entry { | 46 | struct ipq_queue_entry { | 
| 53 | struct list_head list; | 47 | struct list_head list; | 
| 54 | struct nf_info *info; | 48 | struct nf_info *info; | 
| 55 | struct sk_buff *skb; | 49 | struct sk_buff *skb; | 
| 56 | struct ipq_rt_info rt_info; | ||
| 57 | }; | 50 | }; | 
| 58 | 51 | ||
| 59 | typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long); | 52 | typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long); | 
| @@ -305,14 +298,6 @@ ipq_enqueue_packet(struct sk_buff *skb, struct nf_info *info, void *data) | |||
| 305 | entry->info = info; | 298 | entry->info = info; | 
| 306 | entry->skb = skb; | 299 | entry->skb = skb; | 
| 307 | 300 | ||
| 308 | if (entry->info->hook == NF_IP_LOCAL_OUT) { | ||
| 309 | struct iphdr *iph = skb->nh.iph; | ||
| 310 | |||
| 311 | entry->rt_info.tos = iph->tos; | ||
| 312 | entry->rt_info.daddr = iph->daddr; | ||
| 313 | entry->rt_info.saddr = iph->saddr; | ||
| 314 | } | ||
| 315 | |||
| 316 | nskb = ipq_build_packet_message(entry, &status); | 301 | nskb = ipq_build_packet_message(entry, &status); | 
| 317 | if (nskb == NULL) | 302 | if (nskb == NULL) | 
| 318 | goto err_out_free; | 303 | goto err_out_free; | 
| @@ -393,18 +378,6 @@ ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct ipq_queue_entry *e) | |||
| 393 | memcpy(e->skb->data, v->payload, v->data_len); | 378 | memcpy(e->skb->data, v->payload, v->data_len); | 
| 394 | e->skb->ip_summed = CHECKSUM_NONE; | 379 | e->skb->ip_summed = CHECKSUM_NONE; | 
| 395 | 380 | ||
| 396 | /* | ||
| 397 | * Extra routing may needed on local out, as the QUEUE target never | ||
| 398 | * returns control to the table. | ||
| 399 | */ | ||
| 400 | if (e->info->hook == NF_IP_LOCAL_OUT) { | ||
| 401 | struct iphdr *iph = e->skb->nh.iph; | ||
| 402 | |||
| 403 | if (!(iph->tos == e->rt_info.tos | ||
| 404 | && iph->daddr == e->rt_info.daddr | ||
| 405 | && iph->saddr == e->rt_info.saddr)) | ||
| 406 | return ip_route_me_harder(&e->skb); | ||
| 407 | } | ||
| 408 | return 0; | 381 | return 0; | 
| 409 | } | 382 | } | 
| 410 | 383 | ||
| diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 28d9bcab0970..574047353628 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c | |||
| @@ -44,6 +44,7 @@ | |||
| 44 | #include <linux/netdevice.h> | 44 | #include <linux/netdevice.h> | 
| 45 | #include <linux/icmpv6.h> | 45 | #include <linux/icmpv6.h> | 
| 46 | #include <linux/smp_lock.h> | 46 | #include <linux/smp_lock.h> | 
| 47 | #include <linux/netfilter_ipv6.h> | ||
| 47 | 48 | ||
| 48 | #include <net/ip.h> | 49 | #include <net/ip.h> | 
| 49 | #include <net/ipv6.h> | 50 | #include <net/ipv6.h> | 
| @@ -757,6 +758,9 @@ static int __init inet6_init(void) | |||
| 757 | err = igmp6_init(&inet6_family_ops); | 758 | err = igmp6_init(&inet6_family_ops); | 
| 758 | if (err) | 759 | if (err) | 
| 759 | goto igmp_fail; | 760 | goto igmp_fail; | 
| 761 | err = ipv6_netfilter_init(); | ||
| 762 | if (err) | ||
| 763 | goto netfilter_fail; | ||
| 760 | /* Create /proc/foo6 entries. */ | 764 | /* Create /proc/foo6 entries. */ | 
| 761 | #ifdef CONFIG_PROC_FS | 765 | #ifdef CONFIG_PROC_FS | 
| 762 | err = -ENOMEM; | 766 | err = -ENOMEM; | 
| @@ -813,6 +817,8 @@ proc_tcp6_fail: | |||
| 813 | raw6_proc_exit(); | 817 | raw6_proc_exit(); | 
| 814 | proc_raw6_fail: | 818 | proc_raw6_fail: | 
| 815 | #endif | 819 | #endif | 
| 820 | ipv6_netfilter_fini(); | ||
| 821 | netfilter_fail: | ||
| 816 | igmp6_cleanup(); | 822 | igmp6_cleanup(); | 
| 817 | igmp_fail: | 823 | igmp_fail: | 
| 818 | ndisc_cleanup(); | 824 | ndisc_cleanup(); | 
| @@ -852,6 +858,7 @@ static void __exit inet6_exit(void) | |||
| 852 | ip6_route_cleanup(); | 858 | ip6_route_cleanup(); | 
| 853 | ipv6_packet_cleanup(); | 859 | ipv6_packet_cleanup(); | 
| 854 | igmp6_cleanup(); | 860 | igmp6_cleanup(); | 
| 861 | ipv6_netfilter_fini(); | ||
| 855 | ndisc_cleanup(); | 862 | ndisc_cleanup(); | 
| 856 | icmpv6_cleanup(); | 863 | icmpv6_cleanup(); | 
| 857 | #ifdef CONFIG_SYSCTL | 864 | #ifdef CONFIG_SYSCTL | 
| diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c index 5656d0959aba..c8daef97cf56 100644 --- a/net/ipv6/netfilter.c +++ b/net/ipv6/netfilter.c | |||
| @@ -5,6 +5,8 @@ | |||
| 5 | 5 | ||
| 6 | #include <linux/kernel.h> | 6 | #include <linux/kernel.h> | 
| 7 | #include <linux/ipv6.h> | 7 | #include <linux/ipv6.h> | 
| 8 | #include <linux/netfilter.h> | ||
| 9 | #include <linux/netfilter_ipv6.h> | ||
| 8 | #include <net/dst.h> | 10 | #include <net/dst.h> | 
| 9 | #include <net/ipv6.h> | 11 | #include <net/ipv6.h> | 
| 10 | #include <net/ip6_route.h> | 12 | #include <net/ip6_route.h> | 
| @@ -40,4 +42,64 @@ int ip6_route_me_harder(struct sk_buff *skb) | |||
| 40 | } | 42 | } | 
| 41 | EXPORT_SYMBOL(ip6_route_me_harder); | 43 | EXPORT_SYMBOL(ip6_route_me_harder); | 
| 42 | 44 | ||
| 45 | /* | ||
| 46 | * Extra routing may needed on local out, as the QUEUE target never | ||
| 47 | * returns control to the table. | ||
| 48 | */ | ||
| 49 | |||
| 50 | struct ip6_rt_info { | ||
| 51 | struct in6_addr daddr; | ||
| 52 | struct in6_addr saddr; | ||
| 53 | }; | ||
| 54 | |||
| 55 | static void save(const struct sk_buff *skb, struct nf_info *info) | ||
| 56 | { | ||
| 57 | struct ip6_rt_info *rt_info = nf_info_reroute(info); | ||
| 58 | |||
| 59 | if (info->hook == NF_IP6_LOCAL_OUT) { | ||
| 60 | struct ipv6hdr *iph = skb->nh.ipv6h; | ||
| 61 | |||
| 62 | rt_info->daddr = iph->daddr; | ||
| 63 | rt_info->saddr = iph->saddr; | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | static int reroute(struct sk_buff **pskb, const struct nf_info *info) | ||
| 68 | { | ||
| 69 | struct ip6_rt_info *rt_info = nf_info_reroute(info); | ||
| 70 | |||
| 71 | if (info->hook == NF_IP6_LOCAL_OUT) { | ||
| 72 | struct ipv6hdr *iph = (*pskb)->nh.ipv6h; | ||
| 73 | if (!ipv6_addr_equal(&iph->daddr, &rt_info->daddr) || | ||
| 74 | !ipv6_addr_equal(&iph->saddr, &rt_info->saddr)) | ||
| 75 | return ip6_route_me_harder(*pskb); | ||
| 76 | } | ||
| 77 | return 0; | ||
| 78 | } | ||
| 79 | |||
| 80 | static struct nf_queue_rerouter ip6_reroute = { | ||
| 81 | .rer_size = sizeof(struct ip6_rt_info), | ||
| 82 | .save = &save, | ||
| 83 | .reroute = &reroute, | ||
| 84 | }; | ||
| 85 | |||
| 86 | int __init ipv6_netfilter_init(void) | ||
| 87 | { | ||
| 88 | return nf_register_queue_rerouter(PF_INET6, &ip6_reroute); | ||
| 89 | } | ||
| 90 | |||
| 91 | void ipv6_netfilter_fini(void) | ||
| 92 | { | ||
| 93 | nf_unregister_queue_rerouter(PF_INET6); | ||
| 94 | } | ||
| 95 | |||
| 96 | #else /* CONFIG_NETFILTER */ | ||
| 97 | int __init ipv6_netfilter_init(void) | ||
| 98 | { | ||
| 99 | return 0; | ||
| 100 | } | ||
| 101 | |||
| 102 | void ipv6_netfilter_fini(void) | ||
| 103 | { | ||
| 104 | } | ||
| 43 | #endif /* CONFIG_NETFILTER */ | 105 | #endif /* CONFIG_NETFILTER */ | 
| diff --git a/net/ipv6/netfilter/ip6_queue.c b/net/ipv6/netfilter/ip6_queue.c index 1c3d247a22cc..c45d8f8815de 100644 --- a/net/ipv6/netfilter/ip6_queue.c +++ b/net/ipv6/netfilter/ip6_queue.c | |||
| @@ -47,16 +47,10 @@ | |||
| 47 | #define NET_IPQ_QMAX 2088 | 47 | #define NET_IPQ_QMAX 2088 | 
| 48 | #define NET_IPQ_QMAX_NAME "ip6_queue_maxlen" | 48 | #define NET_IPQ_QMAX_NAME "ip6_queue_maxlen" | 
| 49 | 49 | ||
| 50 | struct ipq_rt_info { | ||
| 51 | struct in6_addr daddr; | ||
| 52 | struct in6_addr saddr; | ||
| 53 | }; | ||
| 54 | |||
| 55 | struct ipq_queue_entry { | 50 | struct ipq_queue_entry { | 
| 56 | struct list_head list; | 51 | struct list_head list; | 
| 57 | struct nf_info *info; | 52 | struct nf_info *info; | 
| 58 | struct sk_buff *skb; | 53 | struct sk_buff *skb; | 
| 59 | struct ipq_rt_info rt_info; | ||
| 60 | }; | 54 | }; | 
| 61 | 55 | ||
| 62 | typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long); | 56 | typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long); | 
| @@ -302,13 +296,6 @@ ipq_enqueue_packet(struct sk_buff *skb, struct nf_info *info, void *data) | |||
| 302 | entry->info = info; | 296 | entry->info = info; | 
| 303 | entry->skb = skb; | 297 | entry->skb = skb; | 
| 304 | 298 | ||
| 305 | if (entry->info->hook == NF_IP_LOCAL_OUT) { | ||
| 306 | struct ipv6hdr *iph = skb->nh.ipv6h; | ||
| 307 | |||
| 308 | entry->rt_info.daddr = iph->daddr; | ||
| 309 | entry->rt_info.saddr = iph->saddr; | ||
| 310 | } | ||
| 311 | |||
| 312 | nskb = ipq_build_packet_message(entry, &status); | 299 | nskb = ipq_build_packet_message(entry, &status); | 
| 313 | if (nskb == NULL) | 300 | if (nskb == NULL) | 
| 314 | goto err_out_free; | 301 | goto err_out_free; | 
| @@ -389,17 +376,6 @@ ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct ipq_queue_entry *e) | |||
| 389 | memcpy(e->skb->data, v->payload, v->data_len); | 376 | memcpy(e->skb->data, v->payload, v->data_len); | 
| 390 | e->skb->ip_summed = CHECKSUM_NONE; | 377 | e->skb->ip_summed = CHECKSUM_NONE; | 
| 391 | 378 | ||
| 392 | /* | ||
| 393 | * Extra routing may needed on local out, as the QUEUE target never | ||
| 394 | * returns control to the table. | ||
| 395 | * Not a nice way to cmp, but works | ||
| 396 | */ | ||
| 397 | if (e->info->hook == NF_IP_LOCAL_OUT) { | ||
| 398 | struct ipv6hdr *iph = e->skb->nh.ipv6h; | ||
| 399 | if (!ipv6_addr_equal(&iph->daddr, &e->rt_info.daddr) || | ||
| 400 | !ipv6_addr_equal(&iph->saddr, &e->rt_info.saddr)) | ||
| 401 | return ip6_route_me_harder(e->skb); | ||
| 402 | } | ||
| 403 | return 0; | 379 | return 0; | 
| 404 | } | 380 | } | 
| 405 | 381 | ||
