Skip to content

Commit ac2e539

Browse files
pcercueiQuzarDC
authored andcommitted
net: Fix checksum algorithm
The recent commit to improve the checksum algorithm actually broke it, as shown by the NTP example not working anymore. Rework the algorithm to make it work again, and improve its performance by always processing two bytes at a time. Signed-off-by: Paul Cercueil <paul@crapouillou.net>
1 parent 7e43350 commit ac2e539

1 file changed

Lines changed: 18 additions & 25 deletions

File tree

kernel/net/net_ipv4.c

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -27,37 +27,30 @@
2727

2828
static net_ipv4_stats_t ipv4_stats = { 0 };
2929

30-
/* Perform an IP-style checksum on a block of data */
31-
uint16_t __pure net_ipv4_checksum(const uint8_t *data, size_t bytes, uint16_t start) {
32-
uint32_t sum = start;
33-
uintptr_t end = (uintptr_t)data + bytes;
30+
static inline uint16_t checksum_one(uint16_t val, uint16_t sum) {
31+
uint16_t result;
3432

35-
/* Make sure we don't do any unaligned memory accesses */
36-
if(((uint32_t)data) & 0x01) {
37-
const uint8_t *ptr = data;
33+
return __builtin_add_overflow(val, sum, &result) + result;
34+
}
3835

39-
while((size_t)(&ptr[2]) <= end) {
40-
sum += ptr[0] | ((ptr[1]) << 8);
41-
ptr += 2;
42-
}
43-
}
44-
/* Fast path assumes at least 2-byte alignment. */
45-
else {
46-
const uint16_t *ptr = (const uint16_t *)data;
47-
while((size_t)(&ptr[1]) <= end) {
48-
sum += *ptr++;
49-
}
36+
/* Perform an IP-style checksum on a block of data */
37+
uint16_t __pure net_ipv4_checksum(const uint8_t *data, size_t bytes, uint16_t sum) {
38+
/* Make sure we don't do any unaligned memory accesses */
39+
if((uintptr_t)data & 1) {
40+
sum = checksum_one(*data, sum);
41+
bytes--;
42+
data++;
5043
}
5144

52-
/* Handle the last byte, if we have an odd byte count */
53-
if(bytes & 0x1)
54-
sum += data[bytes - 1];
45+
/* Compute checksum two bytes at a time */
46+
for(; bytes > 1; bytes -= 2, data += 2)
47+
sum = checksum_one(*(const uint16_t *)data, sum);
5548

56-
/* Take care of any carry bits */
57-
while(sum >> 16)
58-
sum = (sum >> 16) + (sum & 0xFFFF);
49+
/* Handle the last byte, if we have an odd byte count */
50+
if(bytes)
51+
sum = checksum_one(*data, sum);
5952

60-
return (uint16_t)~sum;
53+
return ~sum;
6154
}
6255

6356
/* Determine if a given IP is in the current network */

0 commit comments

Comments
 (0)