net: Preserve CHECKSUM_COMPLETE at validation
authorTom Herbert <therbert@google.com>
Wed, 11 Jun 2014 01:54:13 +0000 (18:54 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 11 Jun 2014 22:46:12 +0000 (15:46 -0700)
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>
include/linux/skbuff.h

index 1f50bfe..72a5380 100644 (file)
@@ -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));  \