I believe that the TCP checksum function does the following:
- Break up the pseudoheader and the TCP segment header and data into 2 byte blocks.
- Add a one byte padding of 0s to the end of the last block if it's not 2 bytes long, to make it 2 bytes.
- Take the one's complement of the sum to get the TCP checksum.
Sounds simple enough. Hence I wrote my own generic checksum function:
#include <inttypes.h>
#include <arpa/inet.h>
uint16_t checksum(uint16_t * data, int size) {
uint16_t sum = 0;
int i = 0, length = size / 2;
while (i < length) sum += data[i++];
if (size % 2) sum += data[i] & 0xFF00;
return htons(~sum);
}
However other people have written checksum functions which seem to be more complicated. For example:
uint16_t checksum(uint16_t * addr, int len) {
int nleft = len;
int sum = 0;
uint16_t * w = addr;
uint16_t answer = 0;
while (nleft > 1) {
sum += *w++;
nleft -= sizeof(uint16_t);
}
if (nleft == 1) {
*(uint8_t *) (&answer) = *(uint8_t *) w;
sum += answer;
}
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
answer = ~sum;
return (answer);
}
I have a few questions regarding this code:
- What does the statement
*(uint8_t *) (&answer) = *(uint8_t *) w;actually do? Why do we take the sum as:
sum = (sum >> 16) + (sum & 0xFFFF); sum += (sum >> 16);Did the way to calculate the TCP checksum change?
I really don't see why we do sum = (sum >> 16) + (sum & 0xFFFF). Consider sum is 0xABCD:
0xABCD >> 16 == 0x0000
0xABCD & 0xFFFF == 0xABCD
0x0000 + 0xABCD == 0xABCD
It seems like a redundant step. Same goes for the next statement sum += (sum >> 16).
sum = (sum >> 16) + (sum & 0xFFFF)is for wrapping all the data into16bit integer, hence in your second example thesumis declared asint sum = 0;- deimussum += ...would do. - nos