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 #include <net/ip6_checksum.h> 11 12 int ip6_route_me_harder(struct sk_buff *skb) 13 { 14 struct ipv6hdr *iph = ipv6_hdr(skb); 15 struct dst_entry *dst; 16 struct flowi fl = { 17 .oif = skb->sk ? skb->sk->sk_bound_dev_if : 0, 18 .mark = skb->mark, 19 .nl_u = 20 { .ip6_u = 21 { .daddr = iph->daddr, 22 .saddr = iph->saddr, } }, 23 }; 24 25 dst = ip6_route_output(skb->sk, &fl); 26 27 #ifdef CONFIG_XFRM 28 if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && 29 xfrm_decode_session(skb, &fl, AF_INET6) == 0) 30 if (xfrm_lookup(&skb->dst, &fl, skb->sk, 0)) 31 return -1; 32 #endif 33 34 if (dst->error) { 35 IP6_INC_STATS(ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES); 36 LIMIT_NETDEBUG(KERN_DEBUG "ip6_route_me_harder: No more route.\n"); 37 dst_release(dst); 38 return -EINVAL; 39 } 40 41 /* Drop old route. */ 42 dst_release(skb->dst); 43 44 skb->dst = dst; 45 return 0; 46 } 47 EXPORT_SYMBOL(ip6_route_me_harder); 48 49 /* 50 * Extra routing may needed on local out, as the QUEUE target never 51 * returns control to the table. 52 */ 53 54 struct ip6_rt_info { 55 struct in6_addr daddr; 56 struct in6_addr saddr; 57 }; 58 59 static void nf_ip6_saveroute(const struct sk_buff *skb, struct nf_info *info) 60 { 61 struct ip6_rt_info *rt_info = nf_info_reroute(info); 62 63 if (info->hook == NF_IP6_LOCAL_OUT) { 64 struct ipv6hdr *iph = ipv6_hdr(skb); 65 66 rt_info->daddr = iph->daddr; 67 rt_info->saddr = iph->saddr; 68 } 69 } 70 71 static int nf_ip6_reroute(struct sk_buff **pskb, const struct nf_info *info) 72 { 73 struct ip6_rt_info *rt_info = nf_info_reroute(info); 74 75 if (info->hook == NF_IP6_LOCAL_OUT) { 76 struct ipv6hdr *iph = ipv6_hdr(*pskb); 77 if (!ipv6_addr_equal(&iph->daddr, &rt_info->daddr) || 78 !ipv6_addr_equal(&iph->saddr, &rt_info->saddr)) 79 return ip6_route_me_harder(*pskb); 80 } 81 return 0; 82 } 83 84 __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook, 85 unsigned int dataoff, u_int8_t protocol) 86 { 87 struct ipv6hdr *ip6h = ipv6_hdr(skb); 88 __sum16 csum = 0; 89 90 switch (skb->ip_summed) { 91 case CHECKSUM_COMPLETE: 92 if (hook != NF_IP6_PRE_ROUTING && hook != NF_IP6_LOCAL_IN) 93 break; 94 if (!csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, 95 skb->len - dataoff, protocol, 96 csum_sub(skb->csum, 97 skb_checksum(skb, 0, 98 dataoff, 0)))) { 99 skb->ip_summed = CHECKSUM_UNNECESSARY; 100 break; 101 } 102 /* fall through */ 103 case CHECKSUM_NONE: 104 skb->csum = ~csum_unfold( 105 csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, 106 skb->len - dataoff, 107 protocol, 108 csum_sub(0, 109 skb_checksum(skb, 0, 110 dataoff, 0)))); 111 csum = __skb_checksum_complete(skb); 112 } 113 return csum; 114 } 115 116 EXPORT_SYMBOL(nf_ip6_checksum); 117 118 static struct nf_afinfo nf_ip6_afinfo = { 119 .family = AF_INET6, 120 .checksum = nf_ip6_checksum, 121 .saveroute = nf_ip6_saveroute, 122 .reroute = nf_ip6_reroute, 123 .route_key_size = sizeof(struct ip6_rt_info), 124 }; 125 126 int __init ipv6_netfilter_init(void) 127 { 128 return nf_register_afinfo(&nf_ip6_afinfo); 129 } 130 131 /* This can be called from inet6_init() on errors, so it cannot 132 * be marked __exit. -DaveM 133 */ 134 void ipv6_netfilter_fini(void) 135 { 136 nf_unregister_afinfo(&nf_ip6_afinfo); 137 } 138