X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fipv6%2Fxfrm6_output.c;h=e9ea33892e44db1c41d6cf83376b3905e612be3a;hb=9464c7cf61b9433057924c36e6e02f303a00e768;hp=40612ec543059f8ff09e4cf0035adf11ece5bc99;hpb=1a69f36a78895dad9a6b97ffa2d6d2b7c3975b5d;p=linux-2.6.git diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c index 40612ec54..e9ea33892 100644 --- a/net/ipv6/xfrm6_output.c +++ b/net/ipv6/xfrm6_output.c @@ -9,9 +9,11 @@ * 2 of the License, or (at your option) any later version. */ +#include #include #include #include +#include #include #include #include @@ -69,7 +71,7 @@ static void xfrm6_encap(struct sk_buff *skb) dsfield &= ~INET_ECN_MASK; ipv6_change_dsfield(top_iph, 0, dsfield); top_iph->nexthdr = IPPROTO_IPV6; - top_iph->hop_limit = dst_path_metric(dst, RTAX_HOPLIMIT); + top_iph->hop_limit = dst_metric(dst->child, RTAX_HOPLIMIT); ipv6_addr_copy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr); ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr); } @@ -79,11 +81,12 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb) int mtu, ret = 0; struct dst_entry *dst = skb->dst; - mtu = dst_pmtu(dst) - sizeof(struct ipv6hdr); + mtu = dst_mtu(dst); if (mtu < IPV6_MIN_MTU) mtu = IPV6_MIN_MTU; if (skb->len > mtu) { + skb->dev = dst->dev; icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev); ret = -EMSGSIZE; } @@ -91,49 +94,53 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb) return ret; } -int xfrm6_output(struct sk_buff **pskb) +static int xfrm6_output_one(struct sk_buff *skb) { - struct sk_buff *skb = *pskb; struct dst_entry *dst = skb->dst; struct xfrm_state *x = dst->xfrm; int err; if (skb->ip_summed == CHECKSUM_HW) { - err = skb_checksum_help(pskb, 0); - skb = *pskb; + err = skb_checksum_help(skb, 0); if (err) goto error_nolock; } - spin_lock_bh(&x->lock); - err = xfrm_state_check(x, skb); - if (err) - goto error; - if (x->props.mode) { err = xfrm6_tunnel_check_size(skb); if (err) - goto error; + goto error_nolock; } - xfrm6_encap(skb); + do { + spin_lock_bh(&x->lock); + err = xfrm_state_check(x, skb); + if (err) + goto error; - err = x->type->output(skb); - if (err) - goto error; + xfrm6_encap(skb); - x->curlft.bytes += skb->len; - x->curlft.packets++; + err = x->type->output(x, skb); + if (err) + goto error; - spin_unlock_bh(&x->lock); + x->curlft.bytes += skb->len; + x->curlft.packets++; - skb->nh.raw = skb->data; - - if (!(skb->dst = dst_pop(dst))) { - err = -EHOSTUNREACH; - goto error_nolock; - } - err = NET_XMIT_BYPASS; + spin_unlock_bh(&x->lock); + + skb->nh.raw = skb->data; + + if (!(skb->dst = dst_pop(dst))) { + err = -EHOSTUNREACH; + goto error_nolock; + } + dst = skb->dst; + x = dst->xfrm; + } while (x && !x->props.mode); + + IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED; + err = 0; out_exit: return err; @@ -143,3 +150,68 @@ 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 = nf_hook(PF_INET6, NF_IP6_LOCAL_OUT, &skb, NULL, + skb->dst->dev, dst_output); + 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); + + 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 = 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; +} + +int xfrm6_output(struct sk_buff *skb) +{ + return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb, NULL, skb->dst->dev, + xfrm6_output_finish); +}