From f11f36e76afcc89b6b16d3889644d4b10dbc3c25 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Mon, 25 Jun 2007 04:35:20 -0700 Subject: [PATCH] --- yaml --- r: 58925 b: refs/heads/master c: 334a8132d9950f769f390f0f35c233d099688e7a h: refs/heads/master i: 58923: 3ab31f2f8f7fbc62c9dae73222d2d66f0f2b17e5 v: v3 --- [refs] | 2 +- trunk/include/linux/skbuff.h | 24 ++++++++++++++++++++---- trunk/net/core/skbuff.c | 2 ++ trunk/net/netfilter/core.c | 4 +++- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/[refs] b/[refs] index cdefc3e926e4..6a1be1753f25 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: e50c41b53d7aa48152dd9c633b04fc7abd536f1f +refs/heads/master: 334a8132d9950f769f390f0f35c233d099688e7a diff --git a/trunk/include/linux/skbuff.h b/trunk/include/linux/skbuff.h index 6f0b2f7d0010..881fe80f01d0 100644 --- a/trunk/include/linux/skbuff.h +++ b/trunk/include/linux/skbuff.h @@ -147,8 +147,8 @@ struct skb_shared_info { /* We divide dataref into two halves. The higher 16 bits hold references * to the payload part of skb->data. The lower 16 bits hold references to - * the entire skb->data. It is up to the users of the skb to agree on - * where the payload starts. + * the entire skb->data. A clone of a headerless skb holds the length of + * the header in skb->hdr_len. * * All users must obey the rule that the skb->data reference count must be * greater than or equal to the payload reference count. @@ -206,6 +206,7 @@ typedef unsigned char *sk_buff_data_t; * @len: Length of actual data * @data_len: Data length * @mac_len: Length of link layer header + * @hdr_len: writable header length of cloned skb * @csum: Checksum (must include start/offset pair) * @csum_start: Offset from skb->head where checksumming should start * @csum_offset: Offset from csum_start where checksum should be stored @@ -260,8 +261,9 @@ struct sk_buff { char cb[48]; unsigned int len, - data_len, - mac_len; + data_len; + __u16 mac_len, + hdr_len; union { __wsum csum; struct { @@ -1321,6 +1323,20 @@ static inline struct sk_buff *netdev_alloc_skb(struct net_device *dev, return __netdev_alloc_skb(dev, length, GFP_ATOMIC); } +/** + * skb_clone_writable - is the header of a clone writable + * @skb: buffer to check + * @len: length up to which to write + * + * Returns true if modifying the header part of the cloned buffer + * does not requires the data to be copied. + */ +static inline int skb_clone_writable(struct sk_buff *skb, int len) +{ + return !skb_header_cloned(skb) && + skb_headroom(skb) + len <= skb->hdr_len; +} + /** * skb_cow - copy header of skb when it is required * @skb: buffer to cow diff --git a/trunk/net/core/skbuff.c b/trunk/net/core/skbuff.c index 3943c3ad9145..c989c3a0f907 100644 --- a/trunk/net/core/skbuff.c +++ b/trunk/net/core/skbuff.c @@ -415,6 +415,7 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask) C(csum); C(local_df); n->cloned = 1; + n->hdr_len = skb->nohdr ? skb_headroom(skb) : skb->hdr_len; n->nohdr = 0; C(pkt_type); C(ip_summed); @@ -676,6 +677,7 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, skb->network_header += off; skb->mac_header += off; skb->cloned = 0; + skb->hdr_len = 0; skb->nohdr = 0; atomic_set(&skb_shinfo(skb)->dataref, 1); return 0; diff --git a/trunk/net/netfilter/core.c b/trunk/net/netfilter/core.c index a84478ee2ded..3aaabec70d19 100644 --- a/trunk/net/netfilter/core.c +++ b/trunk/net/netfilter/core.c @@ -203,7 +203,9 @@ int skb_make_writable(struct sk_buff **pskb, unsigned int writable_len) return 0; /* Not exclusive use of packet? Must copy. */ - if (skb_shared(*pskb) || skb_cloned(*pskb)) + if (skb_cloned(*pskb) && !skb_clone_writable(*pskb, writable_len)) + goto copy_skb; + if (skb_shared(*pskb)) goto copy_skb; return pskb_may_pull(*pskb, writable_len);