forked from mirrors/linux
		
	net: ipv4: add skbuff fraglist splitter
This patch adds the skbuff fraglist splitter. This API provides an iterator to transform the fraglist into single skbuff objects, it consists of: * ip_fraglist_init(), that initializes the internal state of the fraglist splitter. * ip_fraglist_prepare(), that restores the IPv4 header on the fragments. * ip_fraglist_next(), that retrieves the fragment from the fraglist and it updates the internal state of the splitter to point to the next fragment skbuff in the fraglist. The ip_fraglist_iter object stores the internal state of the iterator. This code has been extracted from ip_do_fragment(). Symbols are also exported to allow to reuse this iterator from the bridge codepath to build its own refragmentation routine by reusing the existing codebase. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									d48ecb40b5
								
							
						
					
					
						commit
						c8b17be0b7
					
				
					 2 changed files with 78 additions and 33 deletions
				
			
		|  | @ -165,6 +165,29 @@ int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb); | ||||||
| int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb); | int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb); | ||||||
| int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, | int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, | ||||||
| 		   int (*output)(struct net *, struct sock *, struct sk_buff *)); | 		   int (*output)(struct net *, struct sock *, struct sk_buff *)); | ||||||
|  | 
 | ||||||
|  | struct ip_fraglist_iter { | ||||||
|  | 	struct sk_buff	*frag_list; | ||||||
|  | 	struct sk_buff	*frag; | ||||||
|  | 	struct iphdr	*iph; | ||||||
|  | 	int		offset; | ||||||
|  | 	unsigned int	hlen; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void ip_fraglist_init(struct sk_buff *skb, struct iphdr *iph, | ||||||
|  | 		      unsigned int hlen, struct ip_fraglist_iter *iter); | ||||||
|  | void ip_fraglist_prepare(struct sk_buff *skb, struct ip_fraglist_iter *iter); | ||||||
|  | 
 | ||||||
|  | static inline struct sk_buff *ip_fraglist_next(struct ip_fraglist_iter *iter) | ||||||
|  | { | ||||||
|  | 	struct sk_buff *skb = iter->frag; | ||||||
|  | 
 | ||||||
|  | 	iter->frag = skb->next; | ||||||
|  | 	skb_mark_not_on_list(skb); | ||||||
|  | 
 | ||||||
|  | 	return skb; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void ip_send_check(struct iphdr *ip); | void ip_send_check(struct iphdr *ip); | ||||||
| int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb); | int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb); | ||||||
| int ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb); | int ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb); | ||||||
|  |  | ||||||
|  | @ -561,6 +561,54 @@ static int ip_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, | ||||||
| 	return ip_do_fragment(net, sk, skb, output); | 	return ip_do_fragment(net, sk, skb, output); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void ip_fraglist_init(struct sk_buff *skb, struct iphdr *iph, | ||||||
|  | 		      unsigned int hlen, struct ip_fraglist_iter *iter) | ||||||
|  | { | ||||||
|  | 	unsigned int first_len = skb_pagelen(skb); | ||||||
|  | 
 | ||||||
|  | 	iter->frag_list = skb_shinfo(skb)->frag_list; | ||||||
|  | 	iter->frag = iter->frag_list; | ||||||
|  | 	skb_frag_list_init(skb); | ||||||
|  | 
 | ||||||
|  | 	iter->offset = 0; | ||||||
|  | 	iter->iph = iph; | ||||||
|  | 	iter->hlen = hlen; | ||||||
|  | 
 | ||||||
|  | 	skb->data_len = first_len - skb_headlen(skb); | ||||||
|  | 	skb->len = first_len; | ||||||
|  | 	iph->tot_len = htons(first_len); | ||||||
|  | 	iph->frag_off = htons(IP_MF); | ||||||
|  | 	ip_send_check(iph); | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(ip_fraglist_init); | ||||||
|  | 
 | ||||||
|  | void ip_fraglist_prepare(struct sk_buff *skb, struct ip_fraglist_iter *iter) | ||||||
|  | { | ||||||
|  | 	unsigned int hlen = iter->hlen; | ||||||
|  | 	struct iphdr *iph = iter->iph; | ||||||
|  | 	struct sk_buff *frag; | ||||||
|  | 
 | ||||||
|  | 	frag = iter->frag; | ||||||
|  | 	frag->ip_summed = CHECKSUM_NONE; | ||||||
|  | 	skb_reset_transport_header(frag); | ||||||
|  | 	__skb_push(frag, hlen); | ||||||
|  | 	skb_reset_network_header(frag); | ||||||
|  | 	memcpy(skb_network_header(frag), iph, hlen); | ||||||
|  | 	iter->iph = ip_hdr(frag); | ||||||
|  | 	iph = iter->iph; | ||||||
|  | 	iph->tot_len = htons(frag->len); | ||||||
|  | 	ip_copy_metadata(frag, skb); | ||||||
|  | 	if (iter->offset == 0) | ||||||
|  | 		ip_options_fragment(frag); | ||||||
|  | 	iter->offset += skb->len - hlen; | ||||||
|  | 	iph->frag_off = htons(iter->offset >> 3); | ||||||
|  | 	if (frag->next) | ||||||
|  | 		iph->frag_off |= htons(IP_MF); | ||||||
|  | 	/* Ready, complete checksum */ | ||||||
|  | 	ip_send_check(iph); | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(ip_fraglist_prepare); | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  *	This IP datagram is too large to be sent in one piece.  Break it up into |  *	This IP datagram is too large to be sent in one piece.  Break it up into | ||||||
|  *	smaller pieces (each of size equal to IP header plus |  *	smaller pieces (each of size equal to IP header plus | ||||||
|  | @ -578,6 +626,7 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, | ||||||
| 	int offset; | 	int offset; | ||||||
| 	__be16 not_last_frag; | 	__be16 not_last_frag; | ||||||
| 	struct rtable *rt = skb_rtable(skb); | 	struct rtable *rt = skb_rtable(skb); | ||||||
|  | 	struct ip_fraglist_iter iter; | ||||||
| 	int err = 0; | 	int err = 0; | ||||||
| 
 | 
 | ||||||
| 	/* for offloaded checksums cleanup checksum before fragmentation */ | 	/* for offloaded checksums cleanup checksum before fragmentation */ | ||||||
|  | @ -642,49 +691,22 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		/* Everything is OK. Generate! */ | 		/* Everything is OK. Generate! */ | ||||||
| 
 | 		ip_fraglist_init(skb, iph, hlen, &iter); | ||||||
| 		err = 0; |  | ||||||
| 		offset = 0; |  | ||||||
| 		frag = skb_shinfo(skb)->frag_list; |  | ||||||
| 		skb_frag_list_init(skb); |  | ||||||
| 		skb->data_len = first_len - skb_headlen(skb); |  | ||||||
| 		skb->len = first_len; |  | ||||||
| 		iph->tot_len = htons(first_len); |  | ||||||
| 		iph->frag_off = htons(IP_MF); |  | ||||||
| 		ip_send_check(iph); |  | ||||||
| 
 | 
 | ||||||
| 		for (;;) { | 		for (;;) { | ||||||
| 			/* Prepare header of the next frame,
 | 			/* Prepare header of the next frame,
 | ||||||
| 			 * before previous one went down. */ | 			 * before previous one went down. */ | ||||||
| 			if (frag) { | 			if (iter.frag) | ||||||
| 				frag->ip_summed = CHECKSUM_NONE; | 				ip_fraglist_prepare(skb, &iter); | ||||||
| 				skb_reset_transport_header(frag); |  | ||||||
| 				__skb_push(frag, hlen); |  | ||||||
| 				skb_reset_network_header(frag); |  | ||||||
| 				memcpy(skb_network_header(frag), iph, hlen); |  | ||||||
| 				iph = ip_hdr(frag); |  | ||||||
| 				iph->tot_len = htons(frag->len); |  | ||||||
| 				ip_copy_metadata(frag, skb); |  | ||||||
| 				if (offset == 0) |  | ||||||
| 					ip_options_fragment(frag); |  | ||||||
| 				offset += skb->len - hlen; |  | ||||||
| 				iph->frag_off = htons(offset>>3); |  | ||||||
| 				if (frag->next) |  | ||||||
| 					iph->frag_off |= htons(IP_MF); |  | ||||||
| 				/* Ready, complete checksum */ |  | ||||||
| 				ip_send_check(iph); |  | ||||||
| 			} |  | ||||||
| 
 | 
 | ||||||
| 			err = output(net, sk, skb); | 			err = output(net, sk, skb); | ||||||
| 
 | 
 | ||||||
| 			if (!err) | 			if (!err) | ||||||
| 				IP_INC_STATS(net, IPSTATS_MIB_FRAGCREATES); | 				IP_INC_STATS(net, IPSTATS_MIB_FRAGCREATES); | ||||||
| 			if (err || !frag) | 			if (err || !iter.frag) | ||||||
| 				break; | 				break; | ||||||
| 
 | 
 | ||||||
| 			skb = frag; | 			skb = ip_fraglist_next(&iter); | ||||||
| 			frag = skb->next; |  | ||||||
| 			skb_mark_not_on_list(skb); |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (err == 0) { | 		if (err == 0) { | ||||||
|  | @ -692,7 +714,7 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, | ||||||
| 			return 0; | 			return 0; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		kfree_skb_list(frag); | 		kfree_skb_list(iter.frag_list); | ||||||
| 
 | 
 | ||||||
| 		IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS); | 		IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS); | ||||||
| 		return err; | 		return err; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Pablo Neira Ayuso
						Pablo Neira Ayuso