1 #include <linux/config.h> 2 #include <linux/init.h> 3 4 #ifdef CONFIG_NETFILTER 5 6 #include <linux/kernel.h> 7 #include <linux/ipv6.h> 8 #include <linux/netfilter.h> 9 #include <linux/netfilter_ipv6.h> 10 #include <net/dst.h> 11 #include <net/ipv6.h> 12 #include <net/ip6_route.h> 13 14 int ip6_route_me_harder(struct sk_buff *skb) 15 { 16 struct ipv6hdr *iph = skb->nh.ipv6h; 17 struct dst_entry *dst; 18 struct flowi fl = { 19 .oif = skb->sk ? skb->sk->sk_bound_dev_if : 0, 20 .nl_u = 21 { .ip6_u = 22 { .daddr = iph->daddr, 23 .saddr = iph->saddr, } }, 24 .proto = iph->nexthdr, 25 }; 26 27 dst = ip6_route_output(skb->sk, &fl); 28 29 if (dst->error) { 30 IP6_INC_STATS(IPSTATS_MIB_OUTNOROUTES); 31 LIMIT_NETDEBUG(KERN_DEBUG "ip6_route_me_harder: No more route.\n"); 32 dst_release(dst); 33 return -EINVAL; 34 } 35 36 /* Drop old route. */ 37 dst_release(skb->dst); 38 39 skb->dst = dst; 40 return 0; 41 } 42 EXPORT_SYMBOL(ip6_route_me_harder); 43 44 /* 45 * Extra routing may needed on local out, as the QUEUE target never 46 * returns control to the table. 47 */ 48 49 struct ip6_rt_info { 50 struct in6_addr daddr; 51 struct in6_addr saddr; 52 }; 53 54 static void save(const struct sk_buff *skb, struct nf_info *info) 55 { 56 struct ip6_rt_info *rt_info = nf_info_reroute(info); 57 58 if (info->hook == NF_IP6_LOCAL_OUT) { 59 struct ipv6hdr *iph = skb->nh.ipv6h; 60 61 rt_info->daddr = iph->daddr; 62 rt_info->saddr = iph->saddr; 63 } 64 } 65 66 static int reroute(struct sk_buff **pskb, const struct nf_info *info) 67 { 68 struct ip6_rt_info *rt_info = nf_info_reroute(info); 69 70 if (info->hook == NF_IP6_LOCAL_OUT) { 71 struct ipv6hdr *iph = (*pskb)->nh.ipv6h; 72 if (!ipv6_addr_equal(&iph->daddr, &rt_info->daddr) || 73 !ipv6_addr_equal(&iph->saddr, &rt_info->saddr)) 74 return ip6_route_me_harder(*pskb); 75 } 76 return 0; 77 } 78 79 static struct nf_queue_rerouter ip6_reroute = { 80 .rer_size = sizeof(struct ip6_rt_info), 81 .save = &save, 82 .reroute = &reroute, 83 }; 84 85 int __init ipv6_netfilter_init(void) 86 { 87 return nf_register_queue_rerouter(PF_INET6, &ip6_reroute); 88 } 89 90 void ipv6_netfilter_fini(void) 91 { 92 nf_unregister_queue_rerouter(PF_INET6); 93 } 94 95 #else /* CONFIG_NETFILTER */ 96 int __init ipv6_netfilter_init(void) 97 { 98 return 0; 99 } 100 101 void ipv6_netfilter_fini(void) 102 { 103 } 104 #endif /* CONFIG_NETFILTER */ 105