Archived

This topic is now archived and is closed to further replies.

Calc'ing TCP and IP header checksums

This topic is 5669 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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]

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites