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