summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Johnson <djohnson+linux-mips@sw.starentnetworks.com>2007-04-18 10:39:41 -0400
committerRalf Baechle <ralf@linux-mips.org>2007-04-20 14:58:37 +0100
commit1d464c26b5625215c4b35fb336c8f3c57d248c2e (patch)
tree88316e8149c4125dbdb55ff90a4e8d45a024940b
parentba755f8ec80fdbf2b5212622eabf7355464c6327 (diff)
[MIPS] Fix wrong checksum for split TCP packets on 64-bit MIPS
I've traced down an off-by-one TCP checksum calculation error under the following conditions: 1) The TCP code needs to split a full-sized packet due to a reduced MSS (typically due to the addition of TCP options mid-stream like SACK). _AND_ 2) The checksum of the 2nd fragment is larger than the checksum of the original packet. After subtraction this results in a checksum for the 1st fragment with bits 16..31 set to 1. (this is ok) _AND_ 3) The checksum of the 1st fragment's TCP header plus the previously 32bit checksum of the 1st fragment DOES NOT cause a 32bit overflow when added together. This results in a checksum of the TCP header plus TCP data that still has the upper 16 bits as 1's. _THEN_ 4) The TCP+data checksum is added to the checksum of the pseudo IP header with csum_tcpudp_nofold() incorrectly (the bug). The problem is the checksum of the TCP+data is passed to csum_tcpudp_nofold() as an 32bit unsigned value, however the assembly code acts on it as if it is a 64bit unsigned value. This causes an incorrect 32->64bit extension if the sum has bit 31 set. The resulting checksum is off by one. This problems is data and TCP header dependent due to #2 and #3 above so it doesn't occur on every TCP packet split. Signed-off-by: Dave Johnson <djohnson+linux-mips@sw.starentnetworks.com> Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
-rw-r--r--include/asm-mips/checksum.h2
1 files changed, 1 insertions, 1 deletions
diff --git a/include/asm-mips/checksum.h b/include/asm-mips/checksum.h
index 20a81e1548f..290485ac540 100644
--- a/include/asm-mips/checksum.h
+++ b/include/asm-mips/checksum.h
@@ -166,7 +166,7 @@ static inline __wsum csum_tcpudp_nofold(__be32 saddr,
#else
"r" (proto + len),
#endif
- "r" (sum));
+ "r" ((__force unsigned long)sum));
return sum;
}