diff options
Diffstat (limited to 'net/ipv6/ip6_output.c')
| -rw-r--r-- | net/ipv6/ip6_output.c | 18 |
1 files changed, 13 insertions, 5 deletions
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index d40b330c0ee6..980912ed7a38 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c | |||
| @@ -639,7 +639,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) | |||
| 639 | 639 | ||
| 640 | if (skb_has_frags(skb)) { | 640 | if (skb_has_frags(skb)) { |
| 641 | int first_len = skb_pagelen(skb); | 641 | int first_len = skb_pagelen(skb); |
| 642 | int truesizes = 0; | 642 | struct sk_buff *frag2; |
| 643 | 643 | ||
| 644 | if (first_len - hlen > mtu || | 644 | if (first_len - hlen > mtu || |
| 645 | ((first_len - hlen) & 7) || | 645 | ((first_len - hlen) & 7) || |
| @@ -651,18 +651,18 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) | |||
| 651 | if (frag->len > mtu || | 651 | if (frag->len > mtu || |
| 652 | ((frag->len & 7) && frag->next) || | 652 | ((frag->len & 7) && frag->next) || |
| 653 | skb_headroom(frag) < hlen) | 653 | skb_headroom(frag) < hlen) |
| 654 | goto slow_path; | 654 | goto slow_path_clean; |
| 655 | 655 | ||
| 656 | /* Partially cloned skb? */ | 656 | /* Partially cloned skb? */ |
| 657 | if (skb_shared(frag)) | 657 | if (skb_shared(frag)) |
| 658 | goto slow_path; | 658 | goto slow_path_clean; |
| 659 | 659 | ||
| 660 | BUG_ON(frag->sk); | 660 | BUG_ON(frag->sk); |
| 661 | if (skb->sk) { | 661 | if (skb->sk) { |
| 662 | frag->sk = skb->sk; | 662 | frag->sk = skb->sk; |
| 663 | frag->destructor = sock_wfree; | 663 | frag->destructor = sock_wfree; |
| 664 | truesizes += frag->truesize; | ||
| 665 | } | 664 | } |
| 665 | skb->truesize -= frag->truesize; | ||
| 666 | } | 666 | } |
| 667 | 667 | ||
| 668 | err = 0; | 668 | err = 0; |
| @@ -693,7 +693,6 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) | |||
| 693 | 693 | ||
| 694 | first_len = skb_pagelen(skb); | 694 | first_len = skb_pagelen(skb); |
| 695 | skb->data_len = first_len - skb_headlen(skb); | 695 | skb->data_len = first_len - skb_headlen(skb); |
| 696 | skb->truesize -= truesizes; | ||
| 697 | skb->len = first_len; | 696 | skb->len = first_len; |
| 698 | ipv6_hdr(skb)->payload_len = htons(first_len - | 697 | ipv6_hdr(skb)->payload_len = htons(first_len - |
| 699 | sizeof(struct ipv6hdr)); | 698 | sizeof(struct ipv6hdr)); |
| @@ -756,6 +755,15 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) | |||
| 756 | IPSTATS_MIB_FRAGFAILS); | 755 | IPSTATS_MIB_FRAGFAILS); |
| 757 | dst_release(&rt->dst); | 756 | dst_release(&rt->dst); |
| 758 | return err; | 757 | return err; |
| 758 | |||
| 759 | slow_path_clean: | ||
| 760 | skb_walk_frags(skb, frag2) { | ||
| 761 | if (frag2 == frag) | ||
| 762 | break; | ||
| 763 | frag2->sk = NULL; | ||
| 764 | frag2->destructor = NULL; | ||
| 765 | skb->truesize += frag2->truesize; | ||
| 766 | } | ||
| 759 | } | 767 | } |
| 760 | 768 | ||
| 761 | slow_path: | 769 | slow_path: |
