PDA

View Full Version : Problem with calculating Checksum for TCP header.


Encrypted
04-29-2003, 04:48 PM
Hi,

im trying to build a program that sends out a SYN packet to some server.
But whenever i run my program, i see this with tcpdump:

10:26:41.131045 (tos 0x0, ttl 64, length: 40) 192.168.***.***.23456 > beast.dierentuin.com.http: S [bad tcp cksum 93e8 (->a749)!] 752684582:752684582(0) win 12000


And heres the code:

#define __USE_BSD
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <arpa/inet.h>
#define __FAVOR_BSD
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>

unsigned short in_cksum( unsigned short *addr, int len );

int main()
{
int sockfd, packet_size, sport, dport;
int on = 1, data_len = 0;

struct in_addr srcaddr, dstaddr;
struct sockaddr_in sock_raw;
struct tcphdr *tcp;
struct ip *iphdr;
struct in_addr saddr, daddr;
char *packet;

saddr.s_addr = inet_addr("192.168.168.251");
daddr.s_addr = inet_addr("194.109.192.114");
sport = 23456;
dport = 80;

if(getuid() != 0)
{
printf("YOU MUST BE r00t!!\n");
exit(1);
}

if( ( sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW )) < 0 )
{
perror("socket");
printf("Prob socket\n");
exit(1);
}

if(setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL,(char *)&on,sizeof(on)) < 0)
{
perror("setsockopt");
printf("Prob setsockopt\n");
exit(1);
}

memset(&sock_raw, '\0', sizeof(sock_raw) );

packet_size = (sizeof(struct ip) + sizeof(struct tcphdr));
packet = malloc(packet_size);

iphdr = (struct ip *)packet;
iphdr->ip_v = 4;
iphdr->ip_hl = 5;
iphdr->ip_len = packet_size;
iphdr->ip_off = 0;
iphdr->ip_ttl = IPDEFTTL;
iphdr->ip_p = IPPROTO_TCP;
iphdr->ip_src = saddr;
iphdr->ip_dst = daddr;
iphdr->ip_sum = (unsigned short)in_cksum((unsigned short *)iphdr, sizeof(struct ip));

tcp = (struct tcphdr *)(packet + sizeof ( struct ip ));
memset((char *)tcp,'\0',sizeof(struct tcphdr));

tcp->th_sport = htons(sport);
tcp->th_dport = htons(dport);
tcp->th_seq = htonl(random()%time(NULL));
tcp->th_ack = htonl(random()%time(NULL));
tcp->th_off = 5;
/* We won't use th_x2 (i don't know what it is) */
tcp->th_flags = TH_SYN;
tcp->th_win = htons(12000);
tcp->th_sum = (unsigned short)in_cksum((unsigned short *)tcp, (sizeof(struct tcphdr)));

sock_raw.sin_family = AF_INET;
sock_raw.sin_port = htons(dport);
sock_raw.sin_addr = daddr;

sendto(sockfd, packet, packet_size, 0x0, (struct sockaddr *)&sock_raw, sizeof(sock_raw));

exit(0);
}

unsigned short in_cksum(unsigned short *addr,int len)
{
register int sum = 0;
u_short answer = 0;
register u_short *w = addr;
register int nleft = len;
/*
* * Our algorithm is simple, using a 32 bit accumulator (sum), we add
* * sequential 16 bit words to it, and at the end, fold back all the
* * carry bits from the top 16 bits into the lower 16 bits.
* */
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}

/* mop up an odd byte, if necessary */
if (nleft == 1) {
*(u_char *)(&answer) = *(u_char *)w ;
sum += answer;
}

/* add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return(answer);
}


I saw in some code that you have to use a pseudo header or something, but i dont really get that since i've never heard about a 'pseudo header' in a packet...
any help is apprectiated ;)

thanks,
encrypted

RobSeace
04-29-2003, 09:25 PM
Yeah, the TCP header checksum is a bit wacky... In addition
to the actual TCP header, it also covers a 12-byte pseudo-header
conceptually tacked onto the beginning, which consists of the
source and destination IPs, the protocol, and the combined
TCP header length + data length... See RFC-793 (http://www.faqs.org/rfcs/rfc793.html) for more
info... (It's down a ways, but just search for "checksum", and
you'll find the detailed explanation at the 3rd match...)

Encrypted
04-30-2003, 01:58 PM
Hi,

I didnt found this pseudo header in any header files (at least, not the header files that relate to networking etc). (i mean the C programming header files)
But after that i looked in the nmap source, i saw that the maker of nmap had declared a pseudo header himself:

struct pseudo_header {
/*for computing TCP checksum, see TCP/IP Illustrated p. 145 */
unsigned long s_addr;
unsigned long d_addr;
char zer0;
unsigned char protocol;
unsigned short length;
};

and after that initialised

struct pseudo_header *pseudo = (struct pseudo_header *) (packet + sizeof(struct iphdr) - sizeof(struct pseudo_header));

pseudo->s_addr = source->s_addr;
pseudo->d_addr = victim->s_addr;
pseudo->protocol = IPPROTO_TCP;
pseudo->length = htons(sizeof(struct tcphdr) + datalen);


But i dont get this part

(packet + sizeof(struct iphdr) - sizeof(struct pseudo_header));

Packet is the datagram that is to be send, and this pseudo header comes after the IP header right? or does the IP header contain this pseudo?

/*for computing TCP checksum, see TCP/IP Illustrated p. 145 */

http://yenigul.net/tcpip/udp_user.htm#11_3
That link is that page the maker of nmap referred to.

If i get that piece of text right, this is how a packet would look like:

+--------------+--------------+--------------+------------+
| Ethernet | IP Header | Pseudohdr | TCP/UDP |
+--------------+--------------+--------------+------------+

If this is correct, then why didnt i knew about this :?
and why didnt the writer of this tutorial said anything about a checksum?
http://mixter.void.ru/rawip.html

anyway, im trying this stuff out later today, when i succeed, i'll post the whole code here 8)

thanks,
encrypted

RobSeace
04-30-2003, 02:18 PM
No, the pseudo-header is just that: a PSEUDO-header... It's
not actually transmitted at all... It's a purely conceptual construct
designed solely for computing the TCP checksum... In concept,
it comes immediately before the TCP header; but, in reality, it
just doesn't exist, at all... (Confused yet? ;-))

In that nmap code you posted, it's just using the buffer space
immediately prior to the TCP header (actually part of the IP
header) for temporarily holding the pseudo-header info, while
it performs the checksum... If you notice in later bits of the code,
it can get away with that, because it FIRST fills in the TCP
header and pseudo-header, THEN moves onto the IP header
(thereby overwriting the pseudo-header junk that was occupying
the last 12 bytes)...

RobSeace
04-30-2003, 02:27 PM
Also, at that last URL you mention, they DO actually mention
the pseudo-header, when describing th_sum... But, in the code,
they just set it to 0, with the comment that your TCP stack should
handle calculating it for you, in that case...

Encrypted
04-30-2003, 11:06 PM
hey i got it working 8)

After i had filled in the pseudo fields, and did exactly as showed in the nmap source, it still didnt work, but then i check a newer version of nmap, and the thing that was causing the trouble was this: (i assume so, because after i changed it, it worked)

psuedo->length = sizeof(struct tcphdr);

It has to be

pseudo->length = htons(sizeof(struct tcphdr));


And here is the full code, if you are interested

#define __USE_BSD
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <arpa/inet.h>
#define __FAVOR_BSD
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>

struct pseudo_header
{
/*for computing TCP checksum, see TCP/IP Illustrated p. 145 */
unsigned long s_addr;
unsigned long d_addr;
char zer0;
unsigned char protocol;
unsigned short length;
};

unsigned short in_cksum( unsigned short *addr, int len );

int main()
{
int sockfd, packet_size, sport, dport;
int on = 1, data_len = 0;
char *packet;

packet_size = (sizeof(struct ip) + sizeof(struct tcphdr));
packet = malloc(packet_size);

struct in_addr srcaddr, dstaddr;
struct sockaddr_in sock_raw;
struct ip *iphdr = (struct ip *)packet;
struct in_addr saddr, daddr;
struct pseudo_header *psuedo = (struct pseudo_header *) (packet + sizeof(struct iphdr) - sizeof(struct pseudo_header));
struct tcphdr *tcp = (struct tcphdr *)(packet + sizeof( struct ip));

bzero(packet, sizeof(struct iphdr) + sizeof(struct tcphdr));

saddr.s_addr = inet_addr("192.168.168.251");
daddr.s_addr = inet_addr("194.109.192.114");


psuedo->protocol = IPPROTO_TCP;
psuedo->length = htons(sizeof(struct tcphdr));
psuedo->s_addr = inet_addr("192.168.168.251");
psuedo->d_addr = inet_addr("194.109.192.114");


sport = 23456;
dport = 80;

if(getuid() != 0)
{
printf("YOU MUST BE r00t!!\n");
exit(1);
}

if( ( sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW )) < 0 )
{
perror("socket");
printf("Prob socket\n");
exit(1);
}

if(setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL,(char *)&on,sizeof(on)) < 0)
{
perror("setsockopt");
printf("Prob setsockopt\n");
exit(1);
}

memset(&sock_raw, '\0', sizeof(sock_raw) );


memset((char *)tcp,'\0',sizeof(struct tcphdr));

tcp->th_sport = htons(sport);
tcp->th_dport = htons(dport);
tcp->th_seq = htonl(random()%time(NULL));
tcp->th_ack = htonl(random()%time(NULL));
tcp->th_off = 5;
/* We won't use th_x2 (i don't know what it is) */
tcp->th_flags = TH_FIN;
tcp->th_win = htons(12000);
tcp->th_sum = 0;
tcp->th_sum = in_cksum((unsigned short *)psuedo, sizeof(struct tcphdr) + sizeof(struct pseudo_header));

bzero(packet, sizeof(struct iphdr));
iphdr->ip_v = 4;
iphdr->ip_hl = 5;
iphdr->ip_len = packet_size;
iphdr->ip_off = 0;
iphdr->ip_ttl = IPDEFTTL;
iphdr->ip_p = IPPROTO_TCP;
iphdr->ip_src = saddr;
iphdr->ip_dst = daddr;
iphdr->ip_sum = (unsigned short)in_cksum((unsigned short *)iphdr, sizeof(struct ip));


sock_raw.sin_family = AF_INET;
sock_raw.sin_port = htons(dport);
sock_raw.sin_addr = daddr;

sendto(sockfd, packet, packet_size, 0x0, (struct sockaddr *)&sock_raw, sizeof(sock_raw));

exit(0);
}

unsigned short in_cksum(unsigned short *addr,int len)
{
register int sum = 0;
u_short answer = 0;
register u_short *w = addr;
register int nleft = len;
/*
* * Our algorithm is simple, using a 32 bit accumulator (sum), we add
* * sequential 16 bit words to it, and at the end, fold back all the
* * carry bits from the top 16 bits into the lower 16 bits.
* */
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}

/* mop up an odd byte, if necessary */
if (nleft == 1) {
*(u_char *)(&answer) = *(u_char *)w ;
sum += answer;
}

/* add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return(answer);
}


hey thanks for all the help. ( at least i know now how to calculate a TCP checksum properly ;) )
And now im going to experiment more with raw sockets ;)

later,
encrypted