#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>
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)