diff options
-rw-r--r-- | net/ipv4/ip_fragment.c | 76 |
1 files changed, 55 insertions, 21 deletions
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index fabb86db763b..d7fa2bf3a0c1 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c | |||
@@ -109,6 +109,9 @@ static u32 ipfrag_hash_rnd; | |||
109 | static LIST_HEAD(ipq_lru_list); | 109 | static LIST_HEAD(ipq_lru_list); |
110 | int ip_frag_nqueues = 0; | 110 | int ip_frag_nqueues = 0; |
111 | 111 | ||
112 | static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev, | ||
113 | struct net_device *dev); | ||
114 | |||
112 | static __inline__ void __ipq_unlink(struct ipq *qp) | 115 | static __inline__ void __ipq_unlink(struct ipq *qp) |
113 | { | 116 | { |
114 | hlist_del(&qp->list); | 117 | hlist_del(&qp->list); |
@@ -464,17 +467,20 @@ static int ip_frag_reinit(struct ipq *qp) | |||
464 | } | 467 | } |
465 | 468 | ||
466 | /* Add new segment to existing queue. */ | 469 | /* Add new segment to existing queue. */ |
467 | static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb) | 470 | static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb) |
468 | { | 471 | { |
469 | struct sk_buff *prev, *next; | 472 | struct sk_buff *prev, *next; |
473 | struct net_device *dev; | ||
470 | int flags, offset; | 474 | int flags, offset; |
471 | int ihl, end; | 475 | int ihl, end; |
476 | int err = -ENOENT; | ||
472 | 477 | ||
473 | if (qp->last_in & COMPLETE) | 478 | if (qp->last_in & COMPLETE) |
474 | goto err; | 479 | goto err; |
475 | 480 | ||
476 | if (!(IPCB(skb)->flags & IPSKB_FRAG_COMPLETE) && | 481 | if (!(IPCB(skb)->flags & IPSKB_FRAG_COMPLETE) && |
477 | unlikely(ip_frag_too_far(qp)) && unlikely(ip_frag_reinit(qp))) { | 482 | unlikely(ip_frag_too_far(qp)) && |
483 | unlikely(err = ip_frag_reinit(qp))) { | ||
478 | ipq_kill(qp); | 484 | ipq_kill(qp); |
479 | goto err; | 485 | goto err; |
480 | } | 486 | } |
@@ -487,6 +493,7 @@ static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb) | |||
487 | 493 | ||
488 | /* Determine the position of this fragment. */ | 494 | /* Determine the position of this fragment. */ |
489 | end = offset + skb->len - ihl; | 495 | end = offset + skb->len - ihl; |
496 | err = -EINVAL; | ||
490 | 497 | ||
491 | /* Is this the final fragment? */ | 498 | /* Is this the final fragment? */ |
492 | if ((flags & IP_MF) == 0) { | 499 | if ((flags & IP_MF) == 0) { |
@@ -514,9 +521,12 @@ static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb) | |||
514 | if (end == offset) | 521 | if (end == offset) |
515 | goto err; | 522 | goto err; |
516 | 523 | ||
524 | err = -ENOMEM; | ||
517 | if (pskb_pull(skb, ihl) == NULL) | 525 | if (pskb_pull(skb, ihl) == NULL) |
518 | goto err; | 526 | goto err; |
519 | if (pskb_trim_rcsum(skb, end-offset)) | 527 | |
528 | err = pskb_trim_rcsum(skb, end - offset); | ||
529 | if (err) | ||
520 | goto err; | 530 | goto err; |
521 | 531 | ||
522 | /* Find out which fragments are in front and at the back of us | 532 | /* Find out which fragments are in front and at the back of us |
@@ -539,8 +549,10 @@ static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb) | |||
539 | 549 | ||
540 | if (i > 0) { | 550 | if (i > 0) { |
541 | offset += i; | 551 | offset += i; |
552 | err = -EINVAL; | ||
542 | if (end <= offset) | 553 | if (end <= offset) |
543 | goto err; | 554 | goto err; |
555 | err = -ENOMEM; | ||
544 | if (!pskb_pull(skb, i)) | 556 | if (!pskb_pull(skb, i)) |
545 | goto err; | 557 | goto err; |
546 | if (skb->ip_summed != CHECKSUM_UNNECESSARY) | 558 | if (skb->ip_summed != CHECKSUM_UNNECESSARY) |
@@ -548,6 +560,8 @@ static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb) | |||
548 | } | 560 | } |
549 | } | 561 | } |
550 | 562 | ||
563 | err = -ENOMEM; | ||
564 | |||
551 | while (next && FRAG_CB(next)->offset < end) { | 565 | while (next && FRAG_CB(next)->offset < end) { |
552 | int i = end - FRAG_CB(next)->offset; /* overlap is 'i' bytes */ | 566 | int i = end - FRAG_CB(next)->offset; /* overlap is 'i' bytes */ |
553 | 567 | ||
@@ -589,37 +603,62 @@ static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb) | |||
589 | else | 603 | else |
590 | qp->fragments = skb; | 604 | qp->fragments = skb; |
591 | 605 | ||
592 | if (skb->dev) | 606 | dev = skb->dev; |
593 | qp->iif = skb->dev->ifindex; | 607 | if (dev) { |
594 | skb->dev = NULL; | 608 | qp->iif = dev->ifindex; |
609 | skb->dev = NULL; | ||
610 | } | ||
595 | qp->stamp = skb->tstamp; | 611 | qp->stamp = skb->tstamp; |
596 | qp->meat += skb->len; | 612 | qp->meat += skb->len; |
597 | atomic_add(skb->truesize, &ip_frag_mem); | 613 | atomic_add(skb->truesize, &ip_frag_mem); |
598 | if (offset == 0) | 614 | if (offset == 0) |
599 | qp->last_in |= FIRST_IN; | 615 | qp->last_in |= FIRST_IN; |
600 | 616 | ||
617 | if (qp->last_in == (FIRST_IN | LAST_IN) && qp->meat == qp->len) | ||
618 | return ip_frag_reasm(qp, prev, dev); | ||
619 | |||
601 | write_lock(&ipfrag_lock); | 620 | write_lock(&ipfrag_lock); |
602 | list_move_tail(&qp->lru_list, &ipq_lru_list); | 621 | list_move_tail(&qp->lru_list, &ipq_lru_list); |
603 | write_unlock(&ipfrag_lock); | 622 | write_unlock(&ipfrag_lock); |
604 | 623 | return -EINPROGRESS; | |
605 | return; | ||
606 | 624 | ||
607 | err: | 625 | err: |
608 | kfree_skb(skb); | 626 | kfree_skb(skb); |
627 | return err; | ||
609 | } | 628 | } |
610 | 629 | ||
611 | 630 | ||
612 | /* Build a new IP datagram from all its fragments. */ | 631 | /* Build a new IP datagram from all its fragments. */ |
613 | 632 | ||
614 | static struct sk_buff *ip_frag_reasm(struct ipq *qp, struct net_device *dev) | 633 | static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev, |
634 | struct net_device *dev) | ||
615 | { | 635 | { |
616 | struct iphdr *iph; | 636 | struct iphdr *iph; |
617 | struct sk_buff *fp, *head = qp->fragments; | 637 | struct sk_buff *fp, *head = qp->fragments; |
618 | int len; | 638 | int len; |
619 | int ihlen; | 639 | int ihlen; |
640 | int err; | ||
620 | 641 | ||
621 | ipq_kill(qp); | 642 | ipq_kill(qp); |
622 | 643 | ||
644 | /* Make the one we just received the head. */ | ||
645 | if (prev) { | ||
646 | head = prev->next; | ||
647 | fp = skb_clone(head, GFP_ATOMIC); | ||
648 | |||
649 | if (!fp) | ||
650 | goto out_nomem; | ||
651 | |||
652 | fp->next = head->next; | ||
653 | prev->next = fp; | ||
654 | |||
655 | skb_morph(head, qp->fragments); | ||
656 | head->next = qp->fragments->next; | ||
657 | |||
658 | kfree_skb(qp->fragments); | ||
659 | qp->fragments = head; | ||
660 | } | ||
661 | |||
623 | BUG_TRAP(head != NULL); | 662 | BUG_TRAP(head != NULL); |
624 | BUG_TRAP(FRAG_CB(head)->offset == 0); | 663 | BUG_TRAP(FRAG_CB(head)->offset == 0); |
625 | 664 | ||
@@ -627,10 +666,12 @@ static struct sk_buff *ip_frag_reasm(struct ipq *qp, struct net_device *dev) | |||
627 | ihlen = ip_hdrlen(head); | 666 | ihlen = ip_hdrlen(head); |
628 | len = ihlen + qp->len; | 667 | len = ihlen + qp->len; |
629 | 668 | ||
669 | err = -E2BIG; | ||
630 | if (len > 65535) | 670 | if (len > 65535) |
631 | goto out_oversize; | 671 | goto out_oversize; |
632 | 672 | ||
633 | /* Head of list must not be cloned. */ | 673 | /* Head of list must not be cloned. */ |
674 | err = -ENOMEM; | ||
634 | if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC)) | 675 | if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC)) |
635 | goto out_nomem; | 676 | goto out_nomem; |
636 | 677 | ||
@@ -681,7 +722,7 @@ static struct sk_buff *ip_frag_reasm(struct ipq *qp, struct net_device *dev) | |||
681 | iph->tot_len = htons(len); | 722 | iph->tot_len = htons(len); |
682 | IP_INC_STATS_BH(IPSTATS_MIB_REASMOKS); | 723 | IP_INC_STATS_BH(IPSTATS_MIB_REASMOKS); |
683 | qp->fragments = NULL; | 724 | qp->fragments = NULL; |
684 | return head; | 725 | return 0; |
685 | 726 | ||
686 | out_nomem: | 727 | out_nomem: |
687 | LIMIT_NETDEBUG(KERN_ERR "IP: queue_glue: no memory for gluing " | 728 | LIMIT_NETDEBUG(KERN_ERR "IP: queue_glue: no memory for gluing " |
@@ -694,14 +735,13 @@ out_oversize: | |||
694 | NIPQUAD(qp->saddr)); | 735 | NIPQUAD(qp->saddr)); |
695 | out_fail: | 736 | out_fail: |
696 | IP_INC_STATS_BH(IPSTATS_MIB_REASMFAILS); | 737 | IP_INC_STATS_BH(IPSTATS_MIB_REASMFAILS); |
697 | return NULL; | 738 | return err; |
698 | } | 739 | } |
699 | 740 | ||
700 | /* Process an incoming IP datagram fragment. */ | 741 | /* Process an incoming IP datagram fragment. */ |
701 | struct sk_buff *ip_defrag(struct sk_buff *skb, u32 user) | 742 | struct sk_buff *ip_defrag(struct sk_buff *skb, u32 user) |
702 | { | 743 | { |
703 | struct ipq *qp; | 744 | struct ipq *qp; |
704 | struct net_device *dev; | ||
705 | 745 | ||
706 | IP_INC_STATS_BH(IPSTATS_MIB_REASMREQDS); | 746 | IP_INC_STATS_BH(IPSTATS_MIB_REASMREQDS); |
707 | 747 | ||
@@ -709,23 +749,17 @@ struct sk_buff *ip_defrag(struct sk_buff *skb, u32 user) | |||
709 | if (atomic_read(&ip_frag_mem) > sysctl_ipfrag_high_thresh) | 749 | if (atomic_read(&ip_frag_mem) > sysctl_ipfrag_high_thresh) |
710 | ip_evictor(); | 750 | ip_evictor(); |
711 | 751 | ||
712 | dev = skb->dev; | ||
713 | |||
714 | /* Lookup (or create) queue header */ | 752 | /* Lookup (or create) queue header */ |
715 | if ((qp = ip_find(ip_hdr(skb), user)) != NULL) { | 753 | if ((qp = ip_find(ip_hdr(skb), user)) != NULL) { |
716 | struct sk_buff *ret = NULL; | 754 | int ret; |
717 | 755 | ||
718 | spin_lock(&qp->lock); | 756 | spin_lock(&qp->lock); |
719 | 757 | ||
720 | ip_frag_queue(qp, skb); | 758 | ret = ip_frag_queue(qp, skb); |
721 | |||
722 | if (qp->last_in == (FIRST_IN|LAST_IN) && | ||
723 | qp->meat == qp->len) | ||
724 | ret = ip_frag_reasm(qp, dev); | ||
725 | 759 | ||
726 | spin_unlock(&qp->lock); | 760 | spin_unlock(&qp->lock); |
727 | ipq_put(qp, NULL); | 761 | ipq_put(qp, NULL); |
728 | return ret; | 762 | return ret ? NULL : skb; |
729 | } | 763 | } |
730 | 764 | ||
731 | IP_INC_STATS_BH(IPSTATS_MIB_REASMFAILS); | 765 | IP_INC_STATS_BH(IPSTATS_MIB_REASMFAILS); |