views:

127

answers:

1
unsigned short  /* this function generates header checksums */
csum (unsigned short *buf, int nwords)
{
  unsigned long sum;
  for (sum = 0; nwords > 0; nwords--) // add words(16bits) together
  {
      sum += *buf++;
  }
  sum = (sum >> 16) + (sum & 0xffff);  //add carry over
  sum += (sum >> 16);                  //MY question: what exactly does this step do??? add possible left-over   
                                       //byte? But hasn't it already been added in the loop (if 
                                       //any)?
  return ((unsigned short) ~sum);
}
  1. I assume nwords in the number of 16bits word, not 8bits byte (if there are odd byte, nword is rounded to next large), is it correct? Say ip_hdr has 27 bytes totally, then nword will be 14 instead of 13, right?
  2. The line sum = (sum >> 16) + (sum & 0xffff) is to add carry over to make 16bit complement
  3. sum += (sum >> 16); What's the purpose of this step? Add left-over byte? But left-over byte has already been added in the loop?

Thanks!

A: 

You're correct. Step 3 condenses sum, a 32-bit long, into a 16-bit unsigned short, which is the length of the checksum. This is for performance purposes, allowing one to calculate the checksum without tracking overflow until the end. It does this both in step 2 and step 3 because it may have overflowed from step 2. Then it returns just the inverted lower 16 bits of sum.

This is a bit clearer: http://www.sysnet.ucsd.edu/~cfleizac/iptcphdr.html

WhirlWind
The assumptions here are that the length of a long is 32 bits and the length of a short is 16 bits. But there are platforms where neither is true.
GregS
True, but this is not platform-independent code; it's in the kernel, where you know what the sizes are, so this is a reasonable assumption.
WhirlWind
You can see the BSD source uses similar "non-portable" declarations: http://fxr.watson.org/fxr/source/netinet/in_cksum.c
WhirlWind