diff options
author | Tom Herbert <therbert@google.com> | 2014-06-11 03:54:13 +0200 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-06-12 00:46:12 +0200 |
commit | 5d0c2b95bc57cf8fdc0e7b3e9d7e751eb65ad052 (patch) | |
tree | 7614df8920d998a75dda7c1530ec6fc4d558d1f4 | |
parent | Merge branch 'qlcnic-next' (diff) | |
download | linux-5d0c2b95bc57cf8fdc0e7b3e9d7e751eb65ad052.tar.xz linux-5d0c2b95bc57cf8fdc0e7b3e9d7e751eb65ad052.zip |
net: Preserve CHECKSUM_COMPLETE at validation
Currently when the first checksum in a packet is validated using
CHECKSUM_COMPLETE, ip_summed is overwritten to be CHECKSUM_UNNECESSARY
so that any subsequent checksums in the packet are not correctly
validated.
This patch adds csum_valid flag in sk_buff and uses that to indicate
validated checksum instead of setting CHECKSUM_UNNECESSARY. The bit
is set accordingly in the skb_checksum_validate_* functions. The flag
is checked in skb_checksum_complete, so that validation is communicated
between checksum_init and checksum_complete sequence in TCP and UDP.
Signed-off-by: Tom Herbert <therbert@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/skbuff.h | 23 |
1 files changed, 14 insertions, 9 deletions
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 1f50bfe2243d..72a53805858a 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -572,7 +572,8 @@ struct sk_buff { */ __u8 encapsulation:1; __u8 encap_hdr_csum:1; - /* 5/7 bit hole (depending on ndisc_nodetype presence) */ + __u8 csum_valid:1; + /* 4/6 bit hole (depending on ndisc_nodetype presence) */ kmemcheck_bitfield_end(flags2); #if defined CONFIG_NET_DMA || defined CONFIG_NET_RX_BUSY_POLL @@ -2735,7 +2736,7 @@ __sum16 __skb_checksum_complete(struct sk_buff *skb); static inline int skb_csum_unnecessary(const struct sk_buff *skb) { - return skb->ip_summed & CHECKSUM_UNNECESSARY; + return ((skb->ip_summed & CHECKSUM_UNNECESSARY) || skb->csum_valid); } /** @@ -2769,10 +2770,8 @@ static inline bool __skb_checksum_validate_needed(struct sk_buff *skb, bool zero_okay, __sum16 check) { - if (skb_csum_unnecessary(skb)) { - return false; - } else if (zero_okay && !check) { - skb->ip_summed = CHECKSUM_UNNECESSARY; + if (skb_csum_unnecessary(skb) || (zero_okay && !check)) { + skb->csum_valid = 1; return false; } @@ -2799,15 +2798,20 @@ static inline __sum16 __skb_checksum_validate_complete(struct sk_buff *skb, { if (skb->ip_summed == CHECKSUM_COMPLETE) { if (!csum_fold(csum_add(psum, skb->csum))) { - skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->csum_valid = 1; return 0; } } skb->csum = psum; - if (complete || skb->len <= CHECKSUM_BREAK) - return __skb_checksum_complete(skb); + if (complete || skb->len <= CHECKSUM_BREAK) { + __sum16 csum; + + csum = __skb_checksum_complete(skb); + skb->csum_valid = !csum; + return csum; + } return 0; } @@ -2831,6 +2835,7 @@ static inline __wsum null_compute_pseudo(struct sk_buff *skb, int proto) zero_okay, check, compute_pseudo) \ ({ \ __sum16 __ret = 0; \ + skb->csum_valid = 0; \ if (__skb_checksum_validate_needed(skb, zero_okay, check)) \ __ret = __skb_checksum_validate_complete(skb, \ complete, compute_pseudo(skb, proto)); \ |