Bug 4922

Summary: Bug in netfilter.c when drivers do hardware checksum generation.
Product: Networking Reporter: Thomas F. Herbert (herbert)
Component: Netfilter/IptablesAssignee: Stephen Hemminger (stephen)
Status: RESOLVED CODE_FIX    
Severity: high CC: kernelbugs
Priority: P2    
Hardware: i386   
OS: Linux   
Kernel Version: 2.6.12.2 Subsystem:
Regression: --- Bisected commit-id:

Description Thomas F. Herbert 2005-07-21 11:39:40 UTC
Distribution:

Has been reproduced in Fedora Core 2, Fedora Core 3, YDL 4.0.
with 2.6.8, 2.6.8.1 2.6.9, 2.6.10, 2.6.12.2

Hardware Environment:

x86 (tigon3 driver for BMC5705 tg3.c and ppc systems (mac mini) having ethernet
drivers that do hardware IP checksums.

Software Environment:

See kernel revs above.
Standard distributions. 
Problem Description:

There is a bug in the Linux kernel from 2.6.7 through 2.6.12.2.

The problem occurs when packets are being diverted to user space through
ipq/netlink sockets on systems that have ethernet drivers with hardware IP
checksum capability. It has been reproduced when user code is mangling the
packet headers. 

Where hardware has set ip_summed field in the skb and falsely indicates that the
checksum does not need to be re-generated after IP headers are mangled.

   This bug was originally introduced with a change to net/core/netfilter.c in
the 2.6.8 distribution.

Steps to reproduce:

   To reproduce the bug, divert packets through netlink ipq and change IP header
information.

The following patch fixes the problem on 2.6.12.2:

--- linux-2.6.12.2/net/core/netfilter.c.orig	2005-06-29 19:00:53.000000000 -0400
+++ linux-2.6.12.2/net/core/netfilter.c	2005-07-19 19:07:18.000000000 -0400
@@ -485,6 +485,14 @@
 	unsigned int verdict;
 	int ret = 0;
 
+        if ((*pskb)->ip_summed == CHECKSUM_HW) {
+                if (outdev == NULL) {
+                        (*pskb)->ip_summed = CHECKSUM_NONE;
+                } else {
+                        skb_checksum_help(*pskb, 0);
+                }
+        }
+
 	/* We may already have this, but read-locks nest anyway */
 	rcu_read_lock();
 

The following patch fixes the problem on 2.6.6 - 2.6.11

--- linux-2.6.7/net/core/netfilter.c	2005-07-19 13:02:11.000000000 -0400
+++ linux-2.6.7-netfilter/net/core/netfilter.c	2005-07-19 15:56:51.000000000 -0400
@@ -504,6 +504,14 @@
 	unsigned int verdict;
 	int ret = 0;
 
+        if (skb->ip_summed == CHECKSUM_HW) {
+                if (outdev == NULL) {
+                        skb->ip_summed = CHECKSUM_NONE;
+                } else {
+                        skb_checksum_help(&skb, 0);
+                }
+        }
+
 	/* We may already have this, but read-locks nest anyway */
 	rcu_read_lock();
 

Thanks in advance for your consideration of this bug,

--Tom Herbert

herbert@tfherbert.net  therbert@certicom.com
Comment 1 Stephen Hemminger 2007-09-11 01:13:50 UTC
The hardware checksum support was fixed in 2.6.19