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