diff options
Diffstat (limited to 'net/core/skbuff.c')
| -rw-r--r-- | net/core/skbuff.c | 149 | 
1 files changed, 140 insertions, 9 deletions
diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 973a71f4bc89..e1101a4f90a6 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -68,6 +68,7 @@  #include <net/ip6_checksum.h>  #include <net/xfrm.h>  #include <net/mpls.h> +#include <net/mptcp.h>  #include <linux/uaccess.h>  #include <trace/events/skb.h> @@ -466,7 +467,6 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, unsigned int len,  		return NULL;  	} -	/* use OR instead of assignment to avoid clearing of bits in mask */  	if (pfmemalloc)  		skb->pfmemalloc = 1;  	skb->head_frag = 1; @@ -526,7 +526,6 @@ struct sk_buff *__napi_alloc_skb(struct napi_struct *napi, unsigned int len,  		return NULL;  	} -	/* use OR instead of assignment to avoid clearing of bits in mask */  	if (nc->page.pfmemalloc)  		skb->pfmemalloc = 1;  	skb->head_frag = 1; @@ -3638,6 +3637,97 @@ static inline skb_frag_t skb_head_frag_to_page_desc(struct sk_buff *frag_skb)  	return head_frag;  } +struct sk_buff *skb_segment_list(struct sk_buff *skb, +				 netdev_features_t features, +				 unsigned int offset) +{ +	struct sk_buff *list_skb = skb_shinfo(skb)->frag_list; +	unsigned int tnl_hlen = skb_tnl_header_len(skb); +	unsigned int delta_truesize = 0; +	unsigned int delta_len = 0; +	struct sk_buff *tail = NULL; +	struct sk_buff *nskb; + +	skb_push(skb, -skb_network_offset(skb) + offset); + +	skb_shinfo(skb)->frag_list = NULL; + +	do { +		nskb = list_skb; +		list_skb = list_skb->next; + +		if (!tail) +			skb->next = nskb; +		else +			tail->next = nskb; + +		tail = nskb; + +		delta_len += nskb->len; +		delta_truesize += nskb->truesize; + +		skb_push(nskb, -skb_network_offset(nskb) + offset); + +		 __copy_skb_header(nskb, skb); + +		skb_headers_offset_update(nskb, skb_headroom(nskb) - skb_headroom(skb)); +		skb_copy_from_linear_data_offset(skb, -tnl_hlen, +						 nskb->data - tnl_hlen, +						 offset + tnl_hlen); + +		if (skb_needs_linearize(nskb, features) && +		    __skb_linearize(nskb)) +			goto err_linearize; + +	} while (list_skb); + +	skb->truesize = skb->truesize - delta_truesize; +	skb->data_len = skb->data_len - delta_len; +	skb->len = skb->len - delta_len; + +	skb_gso_reset(skb); + +	skb->prev = tail; + +	if (skb_needs_linearize(skb, features) && +	    __skb_linearize(skb)) +		goto err_linearize; + +	skb_get(skb); + +	return skb; + +err_linearize: +	kfree_skb_list(skb->next); +	skb->next = NULL; +	return ERR_PTR(-ENOMEM); +} +EXPORT_SYMBOL_GPL(skb_segment_list); + +int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb) +{ +	if (unlikely(p->len + skb->len >= 65536)) +		return -E2BIG; + +	if (NAPI_GRO_CB(p)->last == p) +		skb_shinfo(p)->frag_list = skb; +	else +		NAPI_GRO_CB(p)->last->next = skb; + +	skb_pull(skb, skb_gro_offset(skb)); + +	NAPI_GRO_CB(p)->last = skb; +	NAPI_GRO_CB(p)->count++; +	p->data_len += skb->len; +	p->truesize += skb->truesize; +	p->len += skb->len; + +	NAPI_GRO_CB(skb)->same_flow = 1; + +	return 0; +} +EXPORT_SYMBOL_GPL(skb_gro_receive_list); +  /**   *	skb_segment - Perform protocol segmentation on skb.   *	@head_skb: buffer to segment @@ -4109,6 +4199,9 @@ static const u8 skb_ext_type_len[] = {  #if IS_ENABLED(CONFIG_NET_TC_SKB_EXT)  	[TC_SKB_EXT] = SKB_EXT_CHUNKSIZEOF(struct tc_skb_ext),  #endif +#if IS_ENABLED(CONFIG_MPTCP) +	[SKB_EXT_MPTCP] = SKB_EXT_CHUNKSIZEOF(struct mptcp_ext), +#endif  };  static __always_inline unsigned int skb_ext_total_length(void) @@ -4123,6 +4216,9 @@ static __always_inline unsigned int skb_ext_total_length(void)  #if IS_ENABLED(CONFIG_NET_TC_SKB_EXT)  		skb_ext_type_len[TC_SKB_EXT] +  #endif +#if IS_ENABLED(CONFIG_MPTCP) +		skb_ext_type_len[SKB_EXT_MPTCP] + +#endif  		0;  } @@ -4707,9 +4803,9 @@ static __sum16 *skb_checksum_setup_ip(struct sk_buff *skb,  				      typeof(IPPROTO_IP) proto,  				      unsigned int off)  { -	switch (proto) { -		int err; +	int err; +	switch (proto) {  	case IPPROTO_TCP:  		err = skb_maybe_pull_tail(skb, off + sizeof(struct tcphdr),  					  off + MAX_TCP_HDR_LEN); @@ -5472,12 +5568,15 @@ static void skb_mod_eth_type(struct sk_buff *skb, struct ethhdr *hdr,  }  /** - * skb_mpls_push() - push a new MPLS header after the mac header + * skb_mpls_push() - push a new MPLS header after mac_len bytes from start of + *                   the packet   *   * @skb: buffer   * @mpls_lse: MPLS label stack entry to push   * @mpls_proto: ethertype of the new MPLS header (expects 0x8847 or 0x8848)   * @mac_len: length of the MAC header + * @ethernet: flag to indicate if the resulting packet after skb_mpls_push is + *            ethernet   *   * Expects skb->data at mac header.   * @@ -5501,7 +5600,7 @@ int skb_mpls_push(struct sk_buff *skb, __be32 mpls_lse, __be16 mpls_proto,  		return err;  	if (!skb->inner_protocol) { -		skb_set_inner_network_header(skb, mac_len); +		skb_set_inner_network_header(skb, skb_network_offset(skb));  		skb_set_inner_protocol(skb, skb->protocol);  	} @@ -5510,6 +5609,7 @@ int skb_mpls_push(struct sk_buff *skb, __be32 mpls_lse, __be16 mpls_proto,  		mac_len);  	skb_reset_mac_header(skb);  	skb_set_network_header(skb, mac_len); +	skb_reset_mac_len(skb);  	lse = mpls_hdr(skb);  	lse->label_stack_entry = mpls_lse; @@ -5529,7 +5629,7 @@ EXPORT_SYMBOL_GPL(skb_mpls_push);   * @skb: buffer   * @next_proto: ethertype of header after popped MPLS header   * @mac_len: length of the MAC header - * @ethernet: flag to indicate if ethernet header is present in packet + * @ethernet: flag to indicate if the packet is ethernet   *   * Expects skb->data at mac header.   * @@ -5976,7 +6076,14 @@ static void *skb_ext_get_ptr(struct skb_ext *ext, enum skb_ext_id id)  	return (void *)ext + (ext->offset[id] * SKB_EXT_ALIGN_VALUE);  } -static struct skb_ext *skb_ext_alloc(void) +/** + * __skb_ext_alloc - allocate a new skb extensions storage + * + * Returns the newly allocated pointer. The pointer can later attached to a + * skb via __skb_ext_set(). + * Note: caller must handle the skb_ext as an opaque data. + */ +struct skb_ext *__skb_ext_alloc(void)  {  	struct skb_ext *new = kmem_cache_alloc(skbuff_ext_cache, GFP_ATOMIC); @@ -6017,6 +6124,30 @@ static struct skb_ext *skb_ext_maybe_cow(struct skb_ext *old,  }  /** + * __skb_ext_set - attach the specified extension storage to this skb + * @skb: buffer + * @id: extension id + * @ext: extension storage previously allocated via __skb_ext_alloc() + * + * Existing extensions, if any, are cleared. + * + * Returns the pointer to the extension. + */ +void *__skb_ext_set(struct sk_buff *skb, enum skb_ext_id id, +		    struct skb_ext *ext) +{ +	unsigned int newlen, newoff = SKB_EXT_CHUNKSIZEOF(*ext); + +	skb_ext_put(skb); +	newlen = newoff + skb_ext_type_len[id]; +	ext->chunks = newlen; +	ext->offset[id] = newoff; +	skb->extensions = ext; +	skb->active_extensions = 1 << id; +	return skb_ext_get_ptr(ext, id); +} + +/**   * skb_ext_add - allocate space for given extension, COW if needed   * @skb: buffer   * @id: extension to allocate space for @@ -6049,7 +6180,7 @@ void *skb_ext_add(struct sk_buff *skb, enum skb_ext_id id)  	} else {  		newoff = SKB_EXT_CHUNKSIZEOF(*new); -		new = skb_ext_alloc(); +		new = __skb_ext_alloc();  		if (!new)  			return NULL;  	}  |