diff --git a/include/net/dst.h b/include/net/dst.h index 7a0b1bde8e28387663b3e22ff1e73855f3a7d199..e86b9a008ebf300ec118c2fd909f05eca1c10e11 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -98,6 +98,7 @@ struct dst_ops struct dst_entry * (*negative_advice)(struct dst_entry *); void (*link_failure)(struct sk_buff *); void (*update_pmtu)(struct dst_entry *dst, u32 mtu); + int (*local_out)(struct sk_buff *skb); int entry_size; atomic_t entries; diff --git a/include/net/xfrm.h b/include/net/xfrm.h index a9dbe091ae58f9081bcdca5b11d29a7fb9951b12..ab9e747340b4433a2ebc17df2a93354878d2163f 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -259,6 +259,7 @@ struct xfrm_state_afinfo { unsigned int family; unsigned int proto; unsigned int eth_proto; + unsigned int nf_post_routing; struct module *owner; struct xfrm_type *type_map[IPPROTO_MAX]; struct xfrm_mode *mode_map[XFRM_MODE_MAX]; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 137b8eb666b780d8718624b078fb1ae3270d8412..94ef788a2ac65f29cdc78210ff197087bc4d9d9b 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -166,6 +166,7 @@ static struct dst_ops ipv4_dst_ops = { .negative_advice = ipv4_negative_advice, .link_failure = ipv4_link_failure, .update_pmtu = ip_rt_update_pmtu, + .local_out = ip_local_out, .entry_size = sizeof(struct rtable), }; diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c index 0ffc3d07848942b78eb41025a175876d81b4fbe5..2fb4efa3ff2cf74cdd815d27ccf24fa51b98b417 100644 --- a/net/ipv4/xfrm4_output.c +++ b/net/ipv4/xfrm4_output.c @@ -59,7 +59,7 @@ int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb) return err; memset(IPCB(skb), 0, sizeof(*IPCB(skb))); - IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE; + IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED; skb->protocol = htons(ETH_P_IP); @@ -67,87 +67,19 @@ int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb) } EXPORT_SYMBOL(xfrm4_prepare_output); -static inline int xfrm4_output_one(struct sk_buff *skb) -{ - int err; - - err = xfrm_output(skb); - if (err) - goto error_nolock; - - IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED; - err = 0; - -out_exit: - return err; -error_nolock: - kfree_skb(skb); - goto out_exit; -} - -static int xfrm4_output_finish2(struct sk_buff *skb) -{ - int err; - - while (likely((err = xfrm4_output_one(skb)) == 0)) { - nf_reset(skb); - - err = __ip_local_out(skb); - if (unlikely(err != 1)) - break; - - if (!skb->dst->xfrm) - return dst_output(skb); - - err = nf_hook(PF_INET, NF_IP_POST_ROUTING, skb, NULL, - skb->dst->dev, xfrm4_output_finish2); - if (unlikely(err != 1)) - break; - } - - return err; -} - static int xfrm4_output_finish(struct sk_buff *skb) { - struct sk_buff *segs; - #ifdef CONFIG_NETFILTER if (!skb->dst->xfrm) { IPCB(skb)->flags |= IPSKB_REROUTED; return dst_output(skb); } -#endif - if (!skb_is_gso(skb)) - return xfrm4_output_finish2(skb); + IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED; +#endif skb->protocol = htons(ETH_P_IP); - segs = skb_gso_segment(skb, 0); - kfree_skb(skb); - if (unlikely(IS_ERR(segs))) - return PTR_ERR(segs); - - do { - struct sk_buff *nskb = segs->next; - int err; - - segs->next = NULL; - err = xfrm4_output_finish2(segs); - - if (unlikely(err)) { - while ((segs = nskb)) { - nskb = segs->next; - segs->next = NULL; - kfree_skb(segs); - } - return err; - } - - segs = nskb; - } while (segs); - - return 0; + return xfrm_output(skb); } int xfrm4_output(struct sk_buff *skb) diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 1d7524375b49a29f7b9faad71bb94eed743e9692..b4948c170b3ea37ac4c41a17901bdd4ff9ba2f35 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -237,6 +237,7 @@ static struct dst_ops xfrm4_dst_ops = { .update_pmtu = xfrm4_update_pmtu, .destroy = xfrm4_dst_destroy, .ifdown = xfrm4_dst_ifdown, + .local_out = __ip_local_out, .gc_thresh = 1024, .entry_size = sizeof(struct xfrm_dst), }; diff --git a/net/ipv4/xfrm4_state.c b/net/ipv4/xfrm4_state.c index 85f04b7b237f628eed2cfa54cd3690294eed948c..80292fbf221a967a7ea931192e3af5d0dc8d641a 100644 --- a/net/ipv4/xfrm4_state.c +++ b/net/ipv4/xfrm4_state.c @@ -11,6 +11,7 @@ #include #include #include +#include static struct xfrm_state_afinfo xfrm4_state_afinfo; @@ -66,6 +67,7 @@ static struct xfrm_state_afinfo xfrm4_state_afinfo = { .family = AF_INET, .proto = IPPROTO_IPIP, .eth_proto = htons(ETH_P_IP), + .nf_post_routing = NF_IP_POST_ROUTING, .owner = THIS_MODULE, .init_flags = xfrm4_init_flags, .init_tempsel = __xfrm4_init_tempsel, diff --git a/net/ipv6/route.c b/net/ipv6/route.c index ac70e2d3b10c76bb0a1a21cc1f39127d63d56642..4ef2cfaa3467d28d4e9d4bfb3b29c1938055e2d1 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -113,6 +113,7 @@ static struct dst_ops ip6_dst_ops = { .negative_advice = ip6_negative_advice, .link_failure = ip6_link_failure, .update_pmtu = ip6_rt_update_pmtu, + .local_out = ip6_local_out, .entry_size = sizeof(struct rt6_info), }; diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c index 0f0ff51f6dbadcb5119f5cd15d16a4ab32ac45c4..a0a924991c4f5db8e023832bd5a126e4d4856b22 100644 --- a/net/ipv6/xfrm6_output.c +++ b/net/ipv6/xfrm6_output.c @@ -66,6 +66,9 @@ int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb) return err; memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); +#ifdef CONFIG_NETFILTER + IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED; +#endif skb->protocol = htons(ETH_P_IPV6); @@ -73,80 +76,14 @@ int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb) } EXPORT_SYMBOL(xfrm6_prepare_output); -static inline int xfrm6_output_one(struct sk_buff *skb) -{ - int err; - - err = xfrm_output(skb); - if (err) - goto error_nolock; - - IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED; - err = 0; - -out_exit: - return err; -error_nolock: - kfree_skb(skb); - goto out_exit; -} - -static int xfrm6_output_finish2(struct sk_buff *skb) -{ - int err; - - while (likely((err = xfrm6_output_one(skb)) == 0)) { - nf_reset(skb); - - err = __ip6_local_out(skb); - if (unlikely(err != 1)) - break; - - if (!skb->dst->xfrm) - return dst_output(skb); - - err = nf_hook(PF_INET6, NF_IP6_POST_ROUTING, skb, NULL, - skb->dst->dev, xfrm6_output_finish2); - if (unlikely(err != 1)) - break; - } - - return err; -} - static int xfrm6_output_finish(struct sk_buff *skb) { - struct sk_buff *segs; - - if (!skb_is_gso(skb)) - return xfrm6_output_finish2(skb); +#ifdef CONFIG_NETFILTER + IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED; +#endif skb->protocol = htons(ETH_P_IPV6); - segs = skb_gso_segment(skb, 0); - kfree_skb(skb); - if (unlikely(IS_ERR(segs))) - return PTR_ERR(segs); - - do { - struct sk_buff *nskb = segs->next; - int err; - - segs->next = NULL; - err = xfrm6_output_finish2(segs); - - if (unlikely(err)) { - while ((segs = nskb)) { - nskb = segs->next; - segs->next = NULL; - kfree_skb(segs); - } - return err; - } - - segs = nskb; - } while (segs); - - return 0; + return xfrm_output(skb); } int xfrm6_output(struct sk_buff *skb) diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 63932c5fd3c71072028e49333e103b8d9abb57d6..a31dd531e191802dab91330293322202169f5856 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -252,6 +252,7 @@ static struct dst_ops xfrm6_dst_ops = { .update_pmtu = xfrm6_update_pmtu, .destroy = xfrm6_dst_destroy, .ifdown = xfrm6_dst_ifdown, + .local_out = __ip6_local_out, .gc_thresh = 1024, .entry_size = sizeof(struct xfrm_dst), }; diff --git a/net/ipv6/xfrm6_state.c b/net/ipv6/xfrm6_state.c index 90fef0a4726fb449a540b8f3e951900edeaf288a..bb09e85a336dd53fdad0b7d4522c36c9272b9120 100644 --- a/net/ipv6/xfrm6_state.c +++ b/net/ipv6/xfrm6_state.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -189,6 +190,7 @@ static struct xfrm_state_afinfo xfrm6_state_afinfo = { .family = AF_INET6, .proto = IPPROTO_IPV6, .eth_proto = htons(ETH_P_IPV6), + .nf_post_routing = NF_IP6_POST_ROUTING, .owner = THIS_MODULE, .init_tempsel = __xfrm6_init_tempsel, .tmpl_sort = __xfrm6_tmpl_sort, diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index b1efdc8850a7413b39aed94b51301a0a85180b9f..bcb3701c5cf3e27d34e3fdd37203e2fc3a79bdd1 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -40,7 +41,7 @@ static int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb) return err; } -int xfrm_output(struct sk_buff *skb) +static int xfrm_output_one(struct sk_buff *skb) { struct dst_entry *dst = skb->dst; struct xfrm_state *x = dst->xfrm; @@ -87,10 +88,73 @@ int xfrm_output(struct sk_buff *skb) err = 0; -error_nolock: +out_exit: return err; error: spin_unlock_bh(&x->lock); - goto error_nolock; +error_nolock: + kfree_skb(skb); + goto out_exit; +} + +static int xfrm_output2(struct sk_buff *skb) +{ + int err; + + while (likely((err = xfrm_output_one(skb)) == 0)) { + struct xfrm_state *x; + + nf_reset(skb); + + err = skb->dst->ops->local_out(skb); + if (unlikely(err != 1)) + break; + + x = skb->dst->xfrm; + if (!x) + return dst_output(skb); + + err = nf_hook(x->inner_mode->afinfo->family, + x->inner_mode->afinfo->nf_post_routing, skb, + NULL, skb->dst->dev, xfrm_output2); + if (unlikely(err != 1)) + break; + } + + return err; +} + +int xfrm_output(struct sk_buff *skb) +{ + struct sk_buff *segs; + + if (!skb_is_gso(skb)) + return xfrm_output2(skb); + + segs = skb_gso_segment(skb, 0); + kfree_skb(skb); + if (unlikely(IS_ERR(segs))) + return PTR_ERR(segs); + + do { + struct sk_buff *nskb = segs->next; + int err; + + segs->next = NULL; + err = xfrm_output2(segs); + + if (unlikely(err)) { + while ((segs = nskb)) { + nskb = segs->next; + segs->next = NULL; + kfree_skb(segs); + } + return err; + } + + segs = nskb; + } while (segs); + + return 0; } EXPORT_SYMBOL_GPL(xfrm_output);