[Winpcap-users] Calculating TCP checksum after altering payload

Vidar Evenrud Seeberg vseeberg at netcom.no
Tue Feb 7 19:53:21 GMT 2006


Hello experts!

I am lost in my Masters Thesis and I hope some of you gurus can help me.
The issue is about calculating the right TCP checksum after changing payload
of a packet.
The function csum below calculates the checksum correctly for the (IP
header) and the (TCP header + no payload). However it calculates an
incorrect TCP checksum when payload is present. Can anybody help me getting
the checksum calculated correctly?
Here is my code:


#include <stdio.h> 
#include "string.h"
#include "stdlib.h"

// WinPCAP includes 
#include <pcap.h> 
#include <remote-ext.h> 

//Defines
#define SIZE_ETHERNET 14
#define ETHER_ADDR_LEN 6

//**********************************************************
// STRUCTURES
//**********************************************************

struct ethernet_header {
	u_char ether_dhost[ETHER_ADDR_LEN];    /* destination host address
*/
	u_char ether_shost[ETHER_ADDR_LEN];    /* source host address */
	u_short ether_type;                    /* IP? ARP? RARP? etc */
};

// 6 byte MAC Address 
typedef struct mac_address { 
    u_char byte1; 
    u_char byte2; 
    u_char byte3; 
    u_char byte4; 
    u_char byte5; 
    u_char byte6; 
}mac_address; 


// 4 bytes IP address 
typedef struct ip_address{ 
    u_char byte1; 
    u_char byte2; 
    u_char byte3; 
    u_char byte4; 
}ip_address; 


// 20 bytes IP Header 
typedef struct ip_header{ 
    u_char ver_ihl;		// Version (4 bits) + Internet header length
(4 bits) 
    u_char tos; 		// Type of service 
    u_short tlen; 		// Total length 
    u_short identification; 	// Identification 
    u_short flags_fo; 		// Flags (3 bits) + Fragment offset (13
bits) 
    u_char ttl; 		// Time to live 
    u_char proto; 		// Protocol 
    u_short crc; 		// Header checksum 
    ip_address saddr; 		// Source address 
    ip_address daddr; 		// Destination address 
    // u_int op_pad; 		// Option + Padding -- NOT NEEDED! 
}ip_header; 


typedef struct tcp_header { 
	u_short sport; 	// Source port 
	u_short dport; 	// Destination port 
	u_int seqnum; 	// Sequence Number 
	u_int acknum; 	// Acknowledgement number 
	u_char th_off; 	// Header length 
	u_char flags; 	// packet flags 
	u_short win; 	// Window size 
	u_short crc; 	// Header Checksum 
	u_short urgptr; // Urgent pointer...still don't know what this is...

}tcp_header; 



//**********************************************************
// Function prototypes
//**********************************************************
u_char *anonymizePayload(u_char *pkt_data);
u_short csum (unsigned short *buf, int nwords);



//**********************************************************
// FUNCTION: anonymizePayload
// Receives raw packets one by one from AnonMain.c
// Alters headerdata and/or payload
//**********************************************************
u_char *anonymizePayload(u_char *pkt_data)
{
	const struct ethernet_header *ethernet;	/* The ethernet header */
	const struct ip_header *ip;		/* The IP header */
	const struct tcp_header *tcp;		/* The TCP header */
	ip_header new_ip; 			/* IP header of new outgoing
packet*/
	tcp_header new_tcp; 			/* TCP header of new
outgoing packet*/

	char *payload;				/* Packet payload from
incoming packet*/
	u_char payloadstring[1530];		/* Payload string for new
packet*/
	int i, j;					/* Counters for
loops used e.g. when converting to short array*/
	u_int size_ip, size_tcp;		/* Size of ip and tcp
headers (without payload)*/
	int size_payload;				/* Size of payload*/
	static char * strpointer;		/* Points to a searched-for
string, part of "payloadstring"*/
	u_short tcplen;				/* Size of tcp header,
calculated*/
	u_short tcptos;				/* This is actually protocol
field in IP header (OK, BAD name...)*/
	
	u_short tcp_hdrcrc[1700];		/* Array built for
calculating tcp crc*/
	u_short payload_shortarray[1700];	/* This is "payloadstring"
converted to short-array*/
	u_short ip_hdrcrc[10];			/* Array built for
calculating ip crc*/
	u_short ip_tos = htons(0x0800); 	/* IP.protocol field
hardcoded*/
	u_char pkt[1600];				/* The assembled
packet returned to AnonMain.c for dumping to file*/
	u_char a, b;				/* Used to convert
"payloadstring" to a short-array*/
	
	mac_address *srcmac;			/* Source MAC*/
	mac_address *destmac; 			/* Destination MAC*/


	//-----------------------------------------------
	// MACs get values
	//-----------------------------------------------
	srcmac = (mac_address *)pkt_data; 
	destmac = (mac_address *)(pkt_data + 6); 
	
	
	//-----------------------------------------------
	// Fetching ethernet and IP headers from
	// the u_char pkt_data received from AnonMain.c
	//-----------------------------------------------
	ethernet = (struct ethernet_header*)(pkt_data);
	ip = (struct ip_header*)(pkt_data + SIZE_ETHERNET);
	size_ip = (ip->ver_ihl & 0xf) * 4; //Gets length of IP header with
options
	if (size_ip < 20) {
		printf("   * Invalid IP header length: %u bytes\n",
size_ip);
		return NULL;
	}
		

	//-----------------------------------------------
	// Working on TCP only (for now)
	//-----------------------------------------------
	if( ip->proto == 0x06 ) 
	{
		//-----------------------------------------------
		// Fetching TCP header and payload
		//-----------------------------------------------
		tcp = (struct tcp_header*)(pkt_data + SIZE_ETHERNET +
size_ip); //TCP header
		size_tcp = tcp->th_off/4;
		if (size_tcp < 20) {
			printf("   * Invalid TCP header length: %u bytes\n",
size_tcp);
			return  NULL;
		}
		payload = (u_char *)(pkt_data + SIZE_ETHERNET + size_ip +
size_tcp); //This is a pointer to the payload
		size_payload = ntohs(ip->tlen) - (size_ip + (size_tcp));


		//-----------------------------------------------
		// Converting *payload to a char string
		// According to Kernighan this should be done
		// for reliable character alterations
		// Results of alterations of *strings are "undefined"
		//-----------------------------------------------
		if (size_payload > 0)
		{
			for(i = 0; i < size_payload; i++)
			{
				if (isprint(*payload))
				{
					payloadstring[i] = *payload;
				}
				else if(iscntrl(*payload))
				{
					if(*payload == 0x0D)
					{
						payloadstring[i] = '\r';
					}
					else if(*payload == 0x0A)
					{
						payloadstring[i] = '\n';
					}
					else
						payloadstring[i] = '.';
				}
				else
				{
					payloadstring[i] = '.';
				}
				payload++;
			}
			payloadstring[i] = '\0'; //Terminating the string
		}
		else
			payloadstring[0] = '\0';

	}
	
	
	//-----------------------------------------------
	// An example of payload alteration
	// "gzip" is substituted by "tull"
	//-----------------------------------------------
	if(strpointer = strstr (payloadstring,"gzip"))
		strncpy(strpointer,"tull",4);
	

	
	//----------------------------------------------------------------
	// Construct packet
	// Repeating header fields from incoming packet
	// TCP and IP CRCs must be set to zero before calculating checksum 
	//----------------------------------------------------------------

	// Setup IP Header 
	new_ip.ver_ihl = ip->ver_ihl; 
	new_ip.tos = ip->tos; 
	new_ip.tlen = ip->tlen; 
	new_ip.identification = ip->identification; 
	new_ip.flags_fo = ip->flags_fo; 
	new_ip.ttl = ip->ttl; 
	new_ip.proto = ip->proto; 
	new_ip.crc = 0x00;
	new_ip.saddr = ip->saddr;
	new_ip.daddr = ip->daddr; 
  
	// Setup TCP Header 
	new_tcp.sport = tcp->sport;  
	new_tcp.dport = tcp->dport; 
	new_tcp.seqnum = tcp->seqnum; 
	new_tcp.acknum = tcp->acknum;
	new_tcp.th_off = tcp->th_off;
	//new_tcp.flags = 0x07; //Example of alterations. Sets FIN, SYN, RST
	new_tcp.flags = tcp->flags; 
	new_tcp.win = tcp->win; 
	new_tcp.urgptr = tcp->urgptr; 
	new_tcp.crc = 0x00; 
 

	//----------------------------------------------------------------
	// Calculate IP CRC
	// This works as it should
	//----------------------------------------------------------------
	memset(ip_hdrcrc, 0, 20); 
	memcpy(ip_hdrcrc, &new_ip, 20); 
	new_ip.crc = csum( ip_hdrcrc, 10 ); /* OBS: THIS WORKS AS IT SHOULD
*/


	//----------------------------------------------------------------
	// Calculate TCP CRC
	// This works as it should when there's no payload (the "else"-part)
	// Incorrect checksum calculated when payload is present (This is my
	// real problem)
	//----------------------------------------------------------------
	if(size_payload>0)
	{
		//Calculating TCP CRC
		//THIS PART OF THE IF STATEMENT DOES NOT WORK
		//PRODUCES INCORRECT CHECKSUM

	
//----------------------------------------------------------------
		// Convert payloadstring to short-array. I think this is
necessary.
		// Putting two characters into a 2-byte short
	
//----------------------------------------------------------------
		i=j=0;
		while(i<size_payload)
		{
			a = payloadstring[i];
			i++;
			b = payloadstring[i];
			i++;
			payload_shortarray[j] = a*256+b;
			j++;
		}
		

	
//----------------------------------------------------------------
		// Constructs the u_short array for TCP CRC calculations
	
//----------------------------------------------------------------
		tcplen = htons(new_tcp.th_off/4);
		tcptos = htons(new_ip.proto);

		memset(tcp_hdrcrc, 0, (ntohs(tcplen)+size_payload+12));
		memcpy(tcp_hdrcrc, &new_tcp, ntohs(tcplen)); 
		memcpy(&tcp_hdrcrc[ntohs(tcplen)/2], &new_ip.saddr, 4); 
		memcpy(&tcp_hdrcrc[(ntohs(tcplen)/2)+2], &new_ip.daddr, 4); 
		memcpy(&tcp_hdrcrc[(ntohs(tcplen)/2)+4], &tcptos, 2); 
		memcpy(&tcp_hdrcrc[(ntohs(tcplen)/2)+5], &tcplen, 2); 
		memcpy(&tcp_hdrcrc[(ntohs(tcplen)/2)+6], payload_shortarray,
size_payload); 
		
		//Calculating the TCP CRC. See function csum below
		new_tcp.crc = csum( tcp_hdrcrc,
(ntohs(tcplen)+size_payload+12)/2 ); 

	}
	else
	{
		//This part is triggered when there is no payload
		//THIS PART OF THE IF STATEMENT WORKS AS IT SHOULD
		//PRODUCES CORRECT CHECKSUM

		tcplen = htons(new_tcp.th_off/4);
		tcptos = htons(new_ip.proto);

		memset(tcp_hdrcrc, 0, (ntohs(tcplen)+12)); 
		memcpy(tcp_hdrcrc, &new_tcp, ntohs(tcplen)); 
		memcpy(&tcp_hdrcrc[ntohs(tcplen)/2], &new_ip.saddr, 4);
//Fra her
		memcpy(&tcp_hdrcrc[(ntohs(tcplen)/2)+2], &new_ip.daddr, 4); 
		memcpy(&tcp_hdrcrc[(ntohs(tcplen)/2)+4], &tcptos, 2); 
		memcpy(&tcp_hdrcrc[(ntohs(tcplen)/2)+5], &tcplen, 2); 
		new_tcp.crc = csum( tcp_hdrcrc, ((ntohs(tcplen)+12)/2) ); 
	}


	//----------------------------------------------------------------
	// Assembling the new outgoing packet 
	// Returns the new packet to AnonMain.c for dumping into a file
	//----------------------------------------------------------------
	memcpy( pkt, srcmac, 6 ); 
	memcpy( (pkt + 6), destmac, 6 ); 
	memcpy( (pkt + 12), &ip_tos, 2);
	memcpy( (pkt + 14), &new_ip, size_ip ); 
	memcpy( (pkt + 14 + size_ip), &new_tcp, ((new_tcp.th_off)/4) );
	memcpy( (pkt + 14 + size_ip + ((new_tcp.th_off)/4)), payloadstring,
size_payload );
 
	return &pkt;
}



//*****************************************************************
// Calculates the TCP Checksum 
//*****************************************************************
u_short csum (unsigned short *buf, int nwords) { 
	unsigned long sum=0; 
  for( sum=0; nwords > 0; nwords-- ) 
                sum += *buf++; 
        sum = (sum >> 16) + (sum & 0xffff); 
        sum += (sum >> 16); 
  return (u_short)~sum; 
} 


Best regards
Vidar E. Seeberg



More information about the Winpcap-users mailing list