net: Clarification of CHECKSUM_UNNECESSARY
authorTom Herbert <therbert@google.com>
Thu, 28 Aug 2014 04:26:46 +0000 (21:26 -0700)
committerDavid S. Miller <davem@davemloft.net>
Sat, 30 Aug 2014 03:41:11 +0000 (20:41 -0700)
This patch:
 - Clarifies the specific requirements of devices returning
   CHECKSUM_UNNECESSARY (comments in skbuff.h).
 - Adds csum_level field to skbuff. This is used to express how
   many checksums are covered by CHECKSUM_UNNECESSARY (stores n - 1).
   This replaces the overloading of skb->encapsulation, that field is
   is now only used to indicate inner headers are valid.
 - Change __skb_checksum_validate_needed to "consume" each checksum
   as indicated by csum_level as layers of the the packet are parsed.
 - Remove skb_pop_rcv_encapsulation, no longer needed in the new
   csum_level model.

Signed-off-by: Tom Herbert <therbert@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/vxlan.c
include/linux/skbuff.h
net/ipv4/gre_demux.c

index beb377b2d4b78e67e15fa2c7fe6404bef240eba0..67527f3d3be2ad8e1984f434ea7b375765724435 100644 (file)
@@ -1158,8 +1158,6 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
        if (!vs)
                goto drop;
 
-       skb_pop_rcv_encapsulation(skb);
-
        vs->rcv(vs, skb, vxh->vx_vni);
        return 0;
 
index 3c9574c80933ac526e929aa1ada52598d165ed09..c93b5859a772b9dd58eab291892af34e1da8fd07 100644 (file)
  *
  *   The hardware you're dealing with doesn't calculate the full checksum
  *   (as in CHECKSUM_COMPLETE), but it does parse headers and verify checksums
- *   for specific protocols e.g. TCP/UDP/SCTP, then, for such packets it will
- *   set CHECKSUM_UNNECESSARY if their checksums are okay. skb->csum is still
- *   undefined in this case though. It is a bad option, but, unfortunately,
- *   nowadays most vendors do this. Apparently with the secret goal to sell
- *   you new devices, when you will add new protocol to your host, f.e. IPv6 8)
+ *   for specific protocols. For such packets it will set CHECKSUM_UNNECESSARY
+ *   if their checksums are okay. skb->csum is still undefined in this case
+ *   though. It is a bad option, but, unfortunately, nowadays most vendors do
+ *   this. Apparently with the secret goal to sell you new devices, when you
+ *   will add new protocol to your host, f.e. IPv6 8)
+ *
+ *   CHECKSUM_UNNECESSARY is applicable to following protocols:
+ *     TCP: IPv6 and IPv4.
+ *     UDP: IPv4 and IPv6. A device may apply CHECKSUM_UNNECESSARY to a
+ *       zero UDP checksum for either IPv4 or IPv6, the networking stack
+ *       may perform further validation in this case.
+ *     GRE: only if the checksum is present in the header.
+ *     SCTP: indicates the CRC in SCTP header has been validated.
+ *
+ *   skb->csum_level indicates the number of consecutive checksums found in
+ *   the packet minus one that have been verified as CHECKSUM_UNNECESSARY.
+ *   For instance if a device receives an IPv6->UDP->GRE->IPv4->TCP packet
+ *   and a device is able to verify the checksums for UDP (possibly zero),
+ *   GRE (checksum flag is set), and TCP-- skb->csum_level would be set to
+ *   two. If the device were only able to verify the UDP checksum and not
+ *   GRE, either because it doesn't support GRE checksum of because GRE
+ *   checksum is bad, skb->csum_level would be set to zero (TCP checksum is
+ *   not considered in this case).
  *
  * CHECKSUM_COMPLETE:
  *
 #define CHECKSUM_COMPLETE      2
 #define CHECKSUM_PARTIAL       3
 
+/* Maximum value in skb->csum_level */
+#define SKB_MAX_CSUM_LEVEL     3
+
 #define SKB_DATA_ALIGN(X)      ALIGN(X, SMP_CACHE_BYTES)
 #define SKB_WITH_OVERHEAD(X)   \
        ((X) - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
@@ -571,11 +592,7 @@ struct sk_buff {
        __u8                    wifi_acked:1;
        __u8                    no_fcs:1;
        __u8                    head_frag:1;
-       /* Encapsulation protocol and NIC drivers should use
-        * this flag to indicate to each other if the skb contains
-        * encapsulated packet or not and maybe use the inner packet
-        * headers if needed
-        */
+       /* Indicates the inner headers are valid in the skbuff. */
        __u8                    encapsulation:1;
        __u8                    encap_hdr_csum:1;
        __u8                    csum_valid:1;
@@ -599,7 +616,8 @@ struct sk_buff {
        };
 
        kmemcheck_bitfield_begin(flags3);
-       /* 16 bit hole */
+       __u8                    csum_level:2;
+       /* 14 bit hole */
        kmemcheck_bitfield_end(flags3);
 
        __be16                  inner_protocol;
@@ -1866,18 +1884,6 @@ static inline int pskb_network_may_pull(struct sk_buff *skb, unsigned int len)
        return pskb_may_pull(skb, skb_network_offset(skb) + len);
 }
 
-static inline void skb_pop_rcv_encapsulation(struct sk_buff *skb)
-{
-       /* Only continue with checksum unnecessary if device indicated
-        * it is valid across encapsulation (skb->encapsulation was set).
-        */
-       if (skb->ip_summed == CHECKSUM_UNNECESSARY && !skb->encapsulation)
-               skb->ip_summed = CHECKSUM_NONE;
-
-       skb->encapsulation = 0;
-       skb->csum_valid = 0;
-}
-
 /*
  * CPUs often take a performance hit when accessing unaligned memory
  * locations. The actual performance hit varies, it can be small if the
@@ -2798,6 +2804,27 @@ static inline __sum16 skb_checksum_complete(struct sk_buff *skb)
               0 : __skb_checksum_complete(skb);
 }
 
+static inline void __skb_decr_checksum_unnecessary(struct sk_buff *skb)
+{
+       if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
+               if (skb->csum_level == 0)
+                       skb->ip_summed = CHECKSUM_NONE;
+               else
+                       skb->csum_level--;
+       }
+}
+
+static inline void __skb_incr_checksum_unnecessary(struct sk_buff *skb)
+{
+       if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
+               if (skb->csum_level < SKB_MAX_CSUM_LEVEL)
+                       skb->csum_level++;
+       } else if (skb->ip_summed == CHECKSUM_NONE) {
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+               skb->csum_level = 0;
+       }
+}
+
 /* Check if we need to perform checksum complete validation.
  *
  * Returns true if checksum complete is needed, false otherwise
@@ -2809,6 +2836,7 @@ static inline bool __skb_checksum_validate_needed(struct sk_buff *skb,
 {
        if (skb_csum_unnecessary(skb) || (zero_okay && !check)) {
                skb->csum_valid = 1;
+               __skb_decr_checksum_unnecessary(skb);
                return false;
        }
 
index 7c1a8ff974dd1b5a6beb016082c7af2682b65950..0485bf7f8f030d59bc6e9ee499051e99d9ab53d6 100644 (file)
@@ -125,7 +125,6 @@ static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
                        *csum_err = true;
                        return -EINVAL;
                }
-               skb_pop_rcv_encapsulation(skb);
                options++;
        }