datapath: Attempt to checksum packets sent to controller on non-Xen kernel.
authorBen Pfaff <blp@nicira.com>
Thu, 16 Apr 2009 22:16:28 +0000 (15:16 -0700)
committerBen Pfaff <blp@nicira.com>
Thu, 16 Apr 2009 22:16:28 +0000 (15:16 -0700)
Commit 96660ad113 "datapath: Fix up checksum on Xen before forwarding to
controller" made sure that packets sent to the controller were properly
checksummed on Xen, where it happens pretty commonly that they are not.
It seems possible that on non-Xen machines this could also happen, even
though it does not seem to be common, so this commit tries to fix up
packets in that case also.

This commit adds a WARN_ON_ONCE() call to make it clear that it has
triggered.  If we ever see this warning, then we should figure out what
triggered it and make sure that this code actually works properly.

datapath/datapath.c

index eb2c7db04c712687077edae10542665b874f303d..ce0042bb6708728c0cf7edadaee90d145f8230e5 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/wait.h>
 #include <asm/system.h>
 #include <asm/div64.h>
+#include <asm/bug.h>
 #include <linux/netfilter_bridge.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/inetdevice.h>
@@ -682,13 +683,33 @@ dp_output_control(struct datapath *dp, struct sk_buff *skb, int queue_no,
        if (skb_queue_len(queue) >= DP_MAX_QUEUE_LEN)
                goto err;
 
-#ifdef CONFIG_XEN
        /* If a checksum-deferred packet is forwarded to the controller,
-        * correct the pointers and checksum.
+        * correct the pointers and checksum.  This happens on a regular basis
+        * only on Xen (the CHECKSUM_HW case), on which VMs can pass up packets
+        * that do not have their checksum computed.  We also implement it for
+        * the non-Xen case, but it is difficult to trigger or test this case
+        * there, hence the WARN_ON_ONCE().
         */
        err = skb_checksum_setup(skb);
        if (err)
                goto err;
+#ifndef CHECKSUM_HW
+       if (skb->ip_summed == CHECKSUM_PARTIAL) {
+               WARN_ON_ONCE(1);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
+               /* Until 2.6.22, the start of the transport header was also the
+                * start of data to be checksummed.  Linux 2.6.22 introduced
+                * the csum_start field for this purpose, but we should point
+                * the transport header to it anyway for backward
+                * compatibility, as dev_queue_xmit() does even in 2.6.28. */
+               skb_set_transport_header(skb, skb->csum_start -
+                                             skb_headroom(skb));
+#endif
+               err = skb_checksum_help(skb);
+               if (err)
+                       goto err;
+       }
+#else
        if (skb->ip_summed == CHECKSUM_HW) {
                err = skb_checksum_help(skb, 0);
                if (err)