diff options
Diffstat (limited to 'net/ipv4/ip_fragment.c')
| -rw-r--r-- | net/ipv4/ip_fragment.c | 27 | 
1 files changed, 17 insertions, 10 deletions
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 75347ea70ea..b7c41654dde 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -124,11 +124,8 @@ static int ip4_frag_match(struct inet_frag_queue *q, void *a)  }  /* Memory Tracking Functions. */ -static __inline__ void frag_kfree_skb(struct netns_frags *nf, -		struct sk_buff *skb, int *work) +static void frag_kfree_skb(struct netns_frags *nf, struct sk_buff *skb)  { -	if (work) -		*work -= skb->truesize;  	atomic_sub(skb->truesize, &nf->mem);  	kfree_skb(skb);  } @@ -309,7 +306,7 @@ static int ip_frag_reinit(struct ipq *qp)  	fp = qp->q.fragments;  	do {  		struct sk_buff *xp = fp->next; -		frag_kfree_skb(qp->q.net, fp, NULL); +		frag_kfree_skb(qp->q.net, fp);  		fp = xp;  	} while (fp); @@ -317,6 +314,7 @@ static int ip_frag_reinit(struct ipq *qp)  	qp->q.len = 0;  	qp->q.meat = 0;  	qp->q.fragments = NULL; +	qp->q.fragments_tail = NULL;  	qp->iif = 0;  	return 0; @@ -389,6 +387,11 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)  	 * in the chain of fragments so far.  We must know where to put  	 * this fragment, right?  	 */ +	prev = qp->q.fragments_tail; +	if (!prev || FRAG_CB(prev)->offset < offset) { +		next = NULL; +		goto found; +	}  	prev = NULL;  	for (next = qp->q.fragments; next != NULL; next = next->next) {  		if (FRAG_CB(next)->offset >= offset) @@ -396,6 +399,7 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)  		prev = next;  	} +found:  	/* We found where to put this one.  Check for overlap with  	 * preceding fragment, and, if needed, align things so that  	 * any overlaps are eliminated. @@ -446,7 +450,7 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)  				qp->q.fragments = next;  			qp->q.meat -= free_it->len; -			frag_kfree_skb(qp->q.net, free_it, NULL); +			frag_kfree_skb(qp->q.net, free_it);  		}  	} @@ -454,6 +458,8 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)  	/* Insert this fragment in the chain of fragments. */  	skb->next = next; +	if (!next) +		qp->q.fragments_tail = skb;  	if (prev)  		prev->next = skb;  	else @@ -507,6 +513,8 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,  			goto out_nomem;  		fp->next = head->next; +		if (!fp->next) +			qp->q.fragments_tail = fp;  		prev->next = fp;  		skb_morph(head, qp->q.fragments); @@ -556,7 +564,6 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,  	skb_shinfo(head)->frag_list = head->next;  	skb_push(head, head->data - skb_network_header(head)); -	atomic_sub(head->truesize, &qp->q.net->mem);  	for (fp=head->next; fp; fp = fp->next) {  		head->data_len += fp->len; @@ -566,8 +573,8 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,  		else if (head->ip_summed == CHECKSUM_COMPLETE)  			head->csum = csum_add(head->csum, fp->csum);  		head->truesize += fp->truesize; -		atomic_sub(fp->truesize, &qp->q.net->mem);  	} +	atomic_sub(head->truesize, &qp->q.net->mem);  	head->next = NULL;  	head->dev = dev; @@ -578,6 +585,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,  	iph->tot_len = htons(len);  	IP_INC_STATS_BH(net, IPSTATS_MIB_REASMOKS);  	qp->q.fragments = NULL; +	qp->q.fragments_tail = NULL;  	return 0;  out_nomem: @@ -624,6 +632,7 @@ int ip_defrag(struct sk_buff *skb, u32 user)  	kfree_skb(skb);  	return -ENOMEM;  } +EXPORT_SYMBOL(ip_defrag);  #ifdef CONFIG_SYSCTL  static int zero; @@ -777,5 +786,3 @@ void __init ipfrag_init(void)  	ip4_frags.secret_interval = 10 * 60 * HZ;  	inet_frags_init(&ip4_frags);  } - -EXPORT_SYMBOL(ip_defrag);  |