Commit 5d0c2b95 authored by Tom Herbert's avatar Tom Herbert Committed by David S. Miller

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: default avatarTom Herbert <therbert@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 1054cc15
...@@ -572,7 +572,8 @@ struct sk_buff { ...@@ -572,7 +572,8 @@ struct sk_buff {
*/ */
__u8 encapsulation:1; __u8 encapsulation:1;
__u8 encap_hdr_csum: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); kmemcheck_bitfield_end(flags2);
#if defined CONFIG_NET_DMA || defined CONFIG_NET_RX_BUSY_POLL #if defined CONFIG_NET_DMA || defined CONFIG_NET_RX_BUSY_POLL
...@@ -2735,7 +2736,7 @@ __sum16 __skb_checksum_complete(struct sk_buff *skb); ...@@ -2735,7 +2736,7 @@ __sum16 __skb_checksum_complete(struct sk_buff *skb);
static inline int skb_csum_unnecessary(const 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, ...@@ -2769,10 +2770,8 @@ static inline bool __skb_checksum_validate_needed(struct sk_buff *skb,
bool zero_okay, bool zero_okay,
__sum16 check) __sum16 check)
{ {
if (skb_csum_unnecessary(skb)) { if (skb_csum_unnecessary(skb) || (zero_okay && !check)) {
return false; skb->csum_valid = 1;
} else if (zero_okay && !check) {
skb->ip_summed = CHECKSUM_UNNECESSARY;
return false; return false;
} }
...@@ -2799,15 +2798,20 @@ static inline __sum16 __skb_checksum_validate_complete(struct sk_buff *skb, ...@@ -2799,15 +2798,20 @@ static inline __sum16 __skb_checksum_validate_complete(struct sk_buff *skb,
{ {
if (skb->ip_summed == CHECKSUM_COMPLETE) { if (skb->ip_summed == CHECKSUM_COMPLETE) {
if (!csum_fold(csum_add(psum, skb->csum))) { if (!csum_fold(csum_add(psum, skb->csum))) {
skb->ip_summed = CHECKSUM_UNNECESSARY; skb->csum_valid = 1;
return 0; return 0;
} }
} }
skb->csum = psum; skb->csum = psum;
if (complete || skb->len <= CHECKSUM_BREAK) if (complete || skb->len <= CHECKSUM_BREAK) {
return __skb_checksum_complete(skb); __sum16 csum;
csum = __skb_checksum_complete(skb);
skb->csum_valid = !csum;
return csum;
}
return 0; return 0;
} }
...@@ -2831,6 +2835,7 @@ static inline __wsum null_compute_pseudo(struct sk_buff *skb, int proto) ...@@ -2831,6 +2835,7 @@ static inline __wsum null_compute_pseudo(struct sk_buff *skb, int proto)
zero_okay, check, compute_pseudo) \ zero_okay, check, compute_pseudo) \
({ \ ({ \
__sum16 __ret = 0; \ __sum16 __ret = 0; \
skb->csum_valid = 0; \
if (__skb_checksum_validate_needed(skb, zero_okay, check)) \ if (__skb_checksum_validate_needed(skb, zero_okay, check)) \
__ret = __skb_checksum_validate_complete(skb, \ __ret = __skb_checksum_validate_complete(skb, \
complete, compute_pseudo(skb, proto)); \ complete, compute_pseudo(skb, proto)); \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment