diff options
Diffstat (limited to 'net/ipv4/ip_output.c')
-rw-r--r-- | net/ipv4/ip_output.c | 19 |
1 files changed, 13 insertions, 6 deletions
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 04b69896df5f..7649d7750075 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c | |||
@@ -488,9 +488,8 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) | |||
488 | * we can switch to copy when see the first bad fragment. | 488 | * we can switch to copy when see the first bad fragment. |
489 | */ | 489 | */ |
490 | if (skb_has_frags(skb)) { | 490 | if (skb_has_frags(skb)) { |
491 | struct sk_buff *frag; | 491 | struct sk_buff *frag, *frag2; |
492 | int first_len = skb_pagelen(skb); | 492 | int first_len = skb_pagelen(skb); |
493 | int truesizes = 0; | ||
494 | 493 | ||
495 | if (first_len - hlen > mtu || | 494 | if (first_len - hlen > mtu || |
496 | ((first_len - hlen) & 7) || | 495 | ((first_len - hlen) & 7) || |
@@ -503,18 +502,18 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) | |||
503 | if (frag->len > mtu || | 502 | if (frag->len > mtu || |
504 | ((frag->len & 7) && frag->next) || | 503 | ((frag->len & 7) && frag->next) || |
505 | skb_headroom(frag) < hlen) | 504 | skb_headroom(frag) < hlen) |
506 | goto slow_path; | 505 | goto slow_path_clean; |
507 | 506 | ||
508 | /* Partially cloned skb? */ | 507 | /* Partially cloned skb? */ |
509 | if (skb_shared(frag)) | 508 | if (skb_shared(frag)) |
510 | goto slow_path; | 509 | goto slow_path_clean; |
511 | 510 | ||
512 | BUG_ON(frag->sk); | 511 | BUG_ON(frag->sk); |
513 | if (skb->sk) { | 512 | if (skb->sk) { |
514 | frag->sk = skb->sk; | 513 | frag->sk = skb->sk; |
515 | frag->destructor = sock_wfree; | 514 | frag->destructor = sock_wfree; |
516 | } | 515 | } |
517 | truesizes += frag->truesize; | 516 | skb->truesize -= frag->truesize; |
518 | } | 517 | } |
519 | 518 | ||
520 | /* Everything is OK. Generate! */ | 519 | /* Everything is OK. Generate! */ |
@@ -524,7 +523,6 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) | |||
524 | frag = skb_shinfo(skb)->frag_list; | 523 | frag = skb_shinfo(skb)->frag_list; |
525 | skb_frag_list_init(skb); | 524 | skb_frag_list_init(skb); |
526 | skb->data_len = first_len - skb_headlen(skb); | 525 | skb->data_len = first_len - skb_headlen(skb); |
527 | skb->truesize -= truesizes; | ||
528 | skb->len = first_len; | 526 | skb->len = first_len; |
529 | iph->tot_len = htons(first_len); | 527 | iph->tot_len = htons(first_len); |
530 | iph->frag_off = htons(IP_MF); | 528 | iph->frag_off = htons(IP_MF); |
@@ -576,6 +574,15 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) | |||
576 | } | 574 | } |
577 | IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS); | 575 | IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS); |
578 | return err; | 576 | return err; |
577 | |||
578 | slow_path_clean: | ||
579 | skb_walk_frags(skb, frag2) { | ||
580 | if (frag2 == frag) | ||
581 | break; | ||
582 | frag2->sk = NULL; | ||
583 | frag2->destructor = NULL; | ||
584 | skb->truesize += frag2->truesize; | ||
585 | } | ||
579 | } | 586 | } |
580 | 587 | ||
581 | slow_path: | 588 | slow_path: |