int ip6_route_me_harder(struct sk_buff *skb)
{
+ struct net *net = dev_net(skb_dst(skb)->dev);
struct ipv6hdr *iph = ipv6_hdr(skb);
struct dst_entry *dst;
struct flowi fl = {
.saddr = iph->saddr, } },
};
- dst = ip6_route_output(skb->sk, &fl);
-
-#ifdef CONFIG_XFRM
- if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
- xfrm_decode_session(skb, &fl, AF_INET6) == 0)
- if (xfrm_lookup(&skb->dst, &fl, skb->sk, 0))
- return -1;
-#endif
-
+ dst = ip6_route_output(net, skb->sk, &fl);
if (dst->error) {
- IP6_INC_STATS(ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
+ IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
LIMIT_NETDEBUG(KERN_DEBUG "ip6_route_me_harder: No more route.\n");
dst_release(dst);
return -EINVAL;
}
/* Drop old route. */
- dst_release(skb->dst);
+ skb_dst_drop(skb);
+
+ skb_dst_set(skb, dst);
+
+#ifdef CONFIG_XFRM
+ if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
+ xfrm_decode_session(skb, &fl, AF_INET6) == 0) {
+ skb_dst_set(skb, NULL);
+ if (xfrm_lookup(net, &dst, &fl, skb->sk, 0))
+ return -1;
+ skb_dst_set(skb, dst);
+ }
+#endif
- skb->dst = dst;
return 0;
}
EXPORT_SYMBOL(ip6_route_me_harder);
struct ip6_rt_info {
struct in6_addr daddr;
struct in6_addr saddr;
+ u_int32_t mark;
};
static void nf_ip6_saveroute(const struct sk_buff *skb,
rt_info->daddr = iph->daddr;
rt_info->saddr = iph->saddr;
+ rt_info->mark = skb->mark;
}
}
if (entry->hook == NF_INET_LOCAL_OUT) {
struct ipv6hdr *iph = ipv6_hdr(skb);
if (!ipv6_addr_equal(&iph->daddr, &rt_info->daddr) ||
- !ipv6_addr_equal(&iph->saddr, &rt_info->saddr))
+ !ipv6_addr_equal(&iph->saddr, &rt_info->saddr) ||
+ skb->mark != rt_info->mark)
return ip6_route_me_harder(skb);
}
return 0;
static int nf_ip6_route(struct dst_entry **dst, struct flowi *fl)
{
- *dst = ip6_route_output(NULL, fl);
+ *dst = ip6_route_output(&init_net, NULL, fl);
return (*dst)->error;
}
}
return csum;
}
-
EXPORT_SYMBOL(nf_ip6_checksum);
+static __sum16 nf_ip6_checksum_partial(struct sk_buff *skb, unsigned int hook,
+ unsigned int dataoff, unsigned int len,
+ u_int8_t protocol)
+{
+ struct ipv6hdr *ip6h = ipv6_hdr(skb);
+ __wsum hsum;
+ __sum16 csum = 0;
+
+ switch (skb->ip_summed) {
+ case CHECKSUM_COMPLETE:
+ if (len == skb->len - dataoff)
+ return nf_ip6_checksum(skb, hook, dataoff, protocol);
+ /* fall through */
+ case CHECKSUM_NONE:
+ hsum = skb_checksum(skb, 0, dataoff, 0);
+ skb->csum = ~csum_unfold(csum_ipv6_magic(&ip6h->saddr,
+ &ip6h->daddr,
+ skb->len - dataoff,
+ protocol,
+ csum_sub(0, hsum)));
+ skb->ip_summed = CHECKSUM_NONE;
+ csum = __skb_checksum_complete_head(skb, dataoff + len);
+ if (!csum)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ }
+ return csum;
+};
+
static const struct nf_afinfo nf_ip6_afinfo = {
- .family = AF_INET6,
- .checksum = nf_ip6_checksum,
- .route = nf_ip6_route,
- .saveroute = nf_ip6_saveroute,
- .reroute = nf_ip6_reroute,
- .route_key_size = sizeof(struct ip6_rt_info),
+ .family = AF_INET6,
+ .checksum = nf_ip6_checksum,
+ .checksum_partial = nf_ip6_checksum_partial,
+ .route = nf_ip6_route,
+ .saveroute = nf_ip6_saveroute,
+ .reroute = nf_ip6_reroute,
+ .route_key_size = sizeof(struct ip6_rt_info),
};
int __init ipv6_netfilter_init(void)