Calc'ing TCP and IP header checksums

Started by
2 comments, last by Lutrosis 21 years, 11 months ago
Right out of RFC's 791 and 793: "The checksum field is the 16 bit one's complement of the one's complement sum of all 16 bit words in the header. For purposes of computing the checksum, the value of the checksum field is zero." I'm having trouble translating this to code. Let's say I want to calculate the tcp checksum. I make sure the tcpheader and data being transmitted are stuck together in a buffer, pad the buffer with a zero if there's an odd number of bytes, and (this is where I'm unsure) add up all the one's compliments of the 16-bit words in the header, and the checksum is the one's compliment of that sum? Here's my (poorly written) code:

unsigned short checksum(unsigned char *theData, unsigned int length)
// theData is a pointer to the tcpheader and data; th_sum in
// tcpheader should be zero for calculation purposes, and
// length is in bytes
{
	unsigned int numWords;
	unsigned short *buffer;

	if (length%2!=0)  
	{
		buffer=(unsigned short*)malloc(length+1);
		memcpy(buffer, theData, length);
		buffer[length]=0;	// padding
		numWords=length/2+1;
	}
	else
	{
		buffer=(unsigned short*)malloc(length);
		memcpy(buffer, theData, length);
		numWords=length/2;
	}

	unsigned int sum;
	for (int j=0;j < numWords; j++)
	{
		sum+= ~(buffer[j]);	// this what they mean by
							// ones compliment sum?
	}

	return ~sum;	// return ones compliment of sum
}
  
I'm trying to test it by reading captured packets from file (captured with NetXRay), and recalculating the checksum. I can't get it to match the original checksum (and yes, I do set it to 0 before calculating). Could anybody point me in the right direction? -Lutrosis [edited by - Lutrosis on June 4, 2002 12:23:24 PM]
-Lutrosis#define WHOOPS 0class DogClass {public: CDog() { printf("Ruff!"); } Run() { printf("Run!"); } Crash() { printf("%d",100/WOOPS); }};DogClass CDog;CDog.Run();CDog.Crash();
Advertisement
One''s complement sum

And you have a memory leak (buffer not freed).

Documents [ GDNet | MSDN | STL | OpenGL | Formats | RTFM | Asking Smart Questions ]
C++ Stuff [ MinGW | Loki | SDL | Boost. | STLport | FLTK | ACCU Recommended Books ]
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Here's a checksum calculator from a small IP stack I have written for a microcontroller. There are a few things left out from the code, but the one's complement summation should be complete.

    u_short checksum(const u_short *buffer, u_short length){    long sum = 0;        while(length > 1)    {        sum    += *buffer++;        length -= 2;    }        if(length == 1)    {        sum += (u_short)(*((u_char *)buffer));    }    while(sum & 0xFFFF0000)    {        sum = (sum & 0xFFFF) + (sum >> 16);    }    return (u_short)((~sum) & 0x0000FFFF);}    

I'm not 100% sure if the if statement works on big endian systems. If you have a big endian system (Intel is little in case you didn't know), you may have to multiply the value by 256 before adding in to sum.

By the way. To test the checksum calculator on a datagram, you don't have to clear the checksum field before passing it to the function, and then check the result agains the field you cleared. If you calculate the checksum including the checksum field, you should get a zero from the function. This is because of the way one's compelement addition works.

[edited by - Brother Bob on June 4, 2002 5:06:33 PM]
Thanks a lot for the pointers, I scratched what I had, and started over, using a packet consisting of just ip and tcp headers. This time I also tried to make allowance for the possible option infortmation and padding in each of the headers. I'm fairly certain my sumation is correct (it is less efficient than Bob's method, but I saw his after I wrote mine). I'm also leaving the checksum value as it was rather than clearing it (which should produce a zero). Can't manage to get zero.

The packet I'm testing with is an empty packet, so the tcp header is only 20 bytes long, and there's no data. If I understand correctly then, the buffer to be summed should contain the psuedoheader (32bit src and dest addresses, a zero byte, the protocol: 0x6 for tcp, and the length of the tcp header not counting the psuedoheader: 20 plus the data size: 0), plus the tcp header, and that's it. I debugged and checked the contents of the buffer after it had been put together, and matched up the information byte-by-byte from the information displayed in NetXRay - if I have the format correct, then my buffer is ok. Then I tried changing the words from the buffer to little-endian before adding them to the sum. Still no good.

Here's the code I'm using for the checksum routine; I've gone over it a number of times and I can't spot anything wrong, but I'm a newbie when it comes to network programming.


    USHORT Checksum(ipheader ipHeader, 				tcpheader tcpHeader,				UCHAR *thePayload,				USHORT datasize){	// build the psuedoheader	tcppsuedoheader psuedohdr;	psuedohdr.th_src=ipHeader.ip_src;	psuedohdr.th_dst=ipHeader.ip_dst;	psuedohdr.th_p=ipHeader.ip_p;	psuedohdr.zeros=0;	psuedohdr.th_len=htons(ntohs(ipHeader.ip_len)-ipHeader.ip_hl*4);	// we need to build a buffer with the psuedohdr, the tcpheader,	// and the data, and then add a padding byte if the total size	// is odd	// first lets get the total size of the buffer	USHORT bufsize=sizeof(psuedohdr)+tcpHeader.th_off*4+datasize;	UCHAR padding=0;	if (bufsize%2!=0)	{		padding=1;	}	// now get memory for the buffer	UCHAR *buffer;	buffer=(UCHAR*)malloc(bufsize+padding);	if (padding) buffer[bufsize+padding]=0;	// the padded byte should be 0	USHORT offset=0;	// ok, fill the buffer in	// first the psuedohdr	memcpy(buffer, &psuedohdr, sizeof(psuedohdr));	offset+=sizeof(psuedohdr);	// now the tcpheader	memcpy(buffer+offset, &tcpHeader, tcpHeader.th_off*4);	offset+=tcpHeader.th_off*4;	// and finally the data	memcpy(buffer+offset, thePayload, datasize);	// Now we've built our buffer, we need to do the actual checksum	// part. we need to treat buffer like a pointer to USHORT, and	// loop through bufsize/2 times, each time adding to sum via	// ocsum	USHORT sum=0;	USHORT *bufptr;	bufptr=(USHORT*)buffer;	sum=ntohs(bufptr[0]);	for (int j=1;j<bufsize/2;j++)	{		sum+=ocadd(sum, ntohs(bufptr[j]));	}	free(buffer);	return ~sum;	}unsigned short ocadd(unsigned short lhs, unsigned short rhs)// add the two together; if there's a carryover, add it to the other side{	unsigned short result;	__asm{		mov ax, lhs		mov bx, rhs		add ax, bx		jnb done		inc ax	done:		mov result, ax	}	return result;}    


Sorry about posting so much information, and thanks for the help!

**** EDIT ****

DOH!

Knew this would happen, I could feel it. Couldn't figure out what was wrong, so I took a break for an hour, watched some history channel Head back, take a look at my summation loop, and it punches me right in the eye:

   sum+=ocadd(sum, ntohs(bufptr[j])); 


Eheh. Scratch that plus sign and everything's gravy. Most embarrasing.

-Lutrosis

[edited by - Lutrosis on June 4, 2002 10:14:24 PM]
-Lutrosis#define WHOOPS 0class DogClass {public: CDog() { printf("Ruff!"); } Run() { printf("Run!"); } Crash() { printf("%d",100/WOOPS); }};DogClass CDog;CDog.Run();CDog.Crash();

This topic is closed to new replies.

Advertisement