ssharma ssharma - 6 months ago 34
C Question

Bogus TCP Header Length When Examining Packets in Tshark

I was trying to send a TCP

SYN
packet to a server on my machine on port
8000
. Then, I wanted to check if the server responded with a
SYN ACK
. If this was the case, then I would send back a
RST
packet to abort the connection. However, when I sniff the
SYN
packet that I send out it tells me the TCP header has a bogus length of 0, which isn't the case. The sniffer I used was
tshark
, by the way. Here's my code:

In the
main
function, I run this:

FLAGS f = SYN;

tcp_scan("127.0.0.1",8000,f,0);


This function assembles the IP header:

struct iphdr* assemble_ip(char* dest,unsigned int proto) {

/* Assemble IP Layer */

struct iphdr* iph;

iph = malloc(sizeof(struct iphdr)); // allocate memory

if (iph == NULL) { // if the ip header is NULL

err();
return NULL;

}

srand((unsigned int)time(NULL)); // seed random number generator

/* Hardcoded values */

iph->version = 4; // the version
iph->tos = 0; // type of services
iph->ihl = 5; // internet header length
iph->id = htons(rand() % 65536); // random id
iph->ttl = rand() % 257; // ttl
iph->frag_off = 0; // fragment offset

if (iph->ttl < 64) iph->ttl += 64; // if TTL is not sufficient

iph->tot_len = htons(iph->ihl*4); // the internet header length

/* User defined values */

iph->saddr = inet_addr(client); // source address
iph->daddr = inet_addr(dest); // destination address
iph->protocol = proto; // protocol

iph->check = 0; // set to zero for later calculation

return iph;

}


This function assembles the TCP header:

struct tcphdr* assemble_tcp(unsigned int sport,unsigned int dport,FLAGS f) {

/* Assemble TCP layer */

struct tcphdr* tcph;

tcph = malloc(sizeof(struct tcphdr)); // allocate tcp header

if (tcph == NULL) { // if tcp is NULL

err();
return NULL;

}


bzero(tcph,sizeof(struct tcphdr));

srand((unsigned int)time(NULL)); // seed random number generator

/* Hardcoded values */

tcph->seq = htonl(rand() % 65001); // generate random sequence number
tcph->ack_seq = 0; // ack sequence should be 0
tcph->doff = 5; // set data offset
tcph->window = htons(rand() % 65536); // set window size

/* Increase values by random value above 64 */

if (ntohs(tcph->seq) < 64) tcph->seq += (rand() % 101 + 64);
if (ntohs(tcph->window) < 64) tcph->window += (rand() % 101 + 64);

/* User-defined values */

tcph->source = htons(sport); // source port
tcph->dest = htons(dport); // destination port
tcph = set_flags(tcph,f); // set the TCP flags

/* Set urgent ptr if URG flag is set*/

if (tcph->urg == 1) tcph->urg_ptr = 1;
else tcph->urg_ptr = 0;

tcph->check = 0; // set the checksum to 0 for other calculations


return tcph;

}


Also, I do compute the checksum of the headers. For my purposes, when calculating the checksum, the IP header is always 20 bytes long, since I'm not sending any data or options. That means that there are 10 16-bit words in the header. The TCP header is also going to be 20 bytes long since I didn't add any options or data. Here's the code:

unsigned short ip_checksum(struct iphdr* iph) {

/* Acquire IP checksum */

/*

Checksum for Internet Protocol:
One's complement of the one's complement sum of the 16 bit words in the header.
So we get the first 16 bits of the header then add it to the sum, and then
we get the next 16 bits, and add it, and so on.
...0100101010110101 -> "..." represents more bits
1111111111111111 -> this is 131071 in base 10
0000100101010110101 -> notice how the "..." bits are now 0's

*/

/* One's complement sum */

unsigned long long* ptr;
unsigned long long hdr;
unsigned short sum = 0;
unsigned long mask = 131071;

ptr = (unsigned long long*)iph; // cast structure
hdr = *ptr; // get hdr

for (int i = 0; i < 10; i++) { // 20 bytes -> 160 bits / 16 bits = 10 words

sum += (hdr & mask); // add to sum

hdr >>= 16; // shift the next 16 bits

}

sum = ~sum; // inverse

return sum;

}


TCP Checksum:

unsigned short tcp_checksum(struct tcphdr* tcph,struct iphdr* iph) {

/* Calculate TCP checksum */

struct pseudo_hdr* pseudo_hdr;
u_char* buffer;
u_char* segment;
u_char* pseudo_segment;
unsigned short sum = 0;
unsigned long mask = 131071;
unsigned long long* ptr;
unsigned long long hdr;

pseudo_hdr = malloc(sizeof(struct pseudo_hdr)); // allocate memory
buffer = malloc(32); // allocate for 32 bytes of information

if (pseudo_hdr == NULL || buffer == NULL) { // if memory wasn't allocated properly

err();
if (pseudo_hdr != NULL) free(pseudo_hdr);
if (buffer != NULL) free(buffer);
return 0;

}

pseudo_hdr->saddr = (unsigned long)iph->saddr; // we add the cast because the fields if of type u_int_32
pseudo_hdr->daddr = (unsigned long)iph->daddr; // same reason for adding the cast as above
memset(&pseudo_hdr->reserved,0,8); // set these 8 bits to 0
pseudo_hdr->proto = IPPROTO_TCP; // this will always be 6
pseudo_hdr->len = htons(tcph->doff*4); // length of tcp header

/* Place both headers into a buffer */

segment = (u_char*)tcph;
pseudo_segment = (u_char*)pseudo_hdr;

/* Concactenate */

strncat((char*)buffer,(char*)pseudo_segment,12); // first the pseudo header
strncat((char*)buffer,(char*)segment,20); // then the TCP segment

/* Calculate checksum just like IP checksum */

ptr = (unsigned long long*)buffer; // convert buffer

hdr = *ptr; // dereference for clarity in following clode

for (int i = 0; i < 16; i++) { // 32 bytes -> 256 bits / 16 bits = 16 words

sum += (hdr & mask); // apply mask to header and add to sum

hdr >>= 16; // shift the next 16 bits

}


sum = ~sum; // bitwise NOT operation

return sum;

};


Here's all of the functions I stated above put together:

int tcp_scan(char* ipaddr,unsigned int port,FLAGS f,unsigned int justsend) {

/* Do a TCP port scan */

u_char* buffer;
u_char recvbuf[65535];
u_char* ipbuf;
u_char* tcpbuf;
int s;
size_t bufsize;
size_t size;
struct sockaddr_in sa;
struct sockaddr_in recvstruct;
struct msghdr msg;
struct iovec iv[1];
struct iphdr* iph;
struct tcphdr* tcph;
FLAGS rst = RST;

srand((unsigned int)time(NULL)); // seed random number generator

bufsize = sizeof(struct tcphdr) + sizeof(struct iphdr); // store size in variable

buffer = malloc(bufsize); // allocate memory to buffer
iph = assemble_ip(ipaddr,IPPROTO_TCP); // set the ip address to provided and protocol as TCP
tcph = assemble_tcp(rand() % 65536,port,f); // set flag, source port as rand, and dest port as supplied port num

if (iph == NULL || tcph == NULL || buffer == NULL) { // if error occurs

err();

/* Deallocate memory to variables that still have it */

if (iph != NULL) free(iph);
if (tcph != NULL) free(tcph);
if (buffer != NULL) free(buffer);

return -1;

}

/* Now compute checksum */

iph->check = htons(ip_checksum(iph));
tcph->check = htons(tcp_checksum(tcph,iph));

/* Store headers in buffer */

ipbuf = (u_char*)iph;
tcpbuf = (u_char*)tcph;

/* Concactenate to buffer */

strncat((char*)buffer,(char*)tcpbuf,20); // copy only 20 bytes...this ensures that no extra bytes are catted
strncat((char*)buffer,(char*)ipbuf,20); // do same thing but with ip header

/* Create a socket */

s = create_socket(); // create a raw socket

if (s == -1) return -1; // if the socket wasn't able to be created


/* Clear memory */

bzero(&sa,sizeof(struct sockaddr_in));
bzero(&recvstruct,sizeof(struct sockaddr_in));
bzero(&msg,sizeof(struct msghdr));
bzero(&iv,sizeof(struct iovec));

/* For analyze_packet() */

sa.sin_family = AF_INET; // address family
sa.sin_addr.s_addr = inet_addr(ipaddr); // convert ip address
sa.sin_port = htons(port); // port number

/* For sendmsg() */

iv[0].iov_base = buffer;
iv[0].iov_len = bufsize;

msg.msg_name = &sa; // caller allocated buffer
msg.msg_namelen = sizeof(struct sockaddr_in); // specify size of buffer
msg.msg_iov = iv; // iov structure array
msg.msg_iovlen = 1; // the length of the array
msg.msg_control = NULL; // for ancillary data
msg.msg_controllen = 0; // sizeof ancillary data


if (sendmsg(s,&msg,0) == -1) {

err();
return -1;

}

printf("Sent\n");

if (justsend) return 0; // exit cleanly

bzero(&recvstruct,sizeof(struct sockaddr_in)); // clear structure

size = sizeof(struct sockaddr_in); // acquire size of recv structure

for(int i = 0; i < 100; i++) { // loop until we've received 100 packets

printf("Receiving\n");
bzero(recvbuf,65535); // clear memory

if (recvfrom(s,recvbuf,sizeof(recvbuf),0,(struct sockaddr*)&recvstruct,(socklen_t*)&size) == -1) { // recv

err();
return -1;

}

if (analyze_packet(recvbuf,sa,recvstruct) == 0) { // if packet is what we wanted

printf("\ttcp %d is open\n",port); // print out that port is opened
tcp_scan(ipaddr,port,rst,-1); // abort connection with RST flag
break;

}


}

return 0;

}


Alright, now that you've seen those, here's the 'tshark' command I used to sniff the packets:

sudo tshark -o tcp.check_checksum:TRUE # I also wanted to check the checksum value to make sure it was OK


Now here's the command to run the program:

sudo ./netmap enp0s3 # enp0s3 is the interface I'm sending packets on


After running both of these in separate terminals,
tshark
provides this output:

1 0.000000 10.0.2.15 -> 127.0.0.1 TCP 74 31280->8000 [<None>] Seq=1 Win=0, bogus TCP header length (0, must be 20)


Please note that the declarations for
struct iphdr
and
struct tcphdr
are located in the system header files
<netinet/ip.h>
and
<netinet/tcp.h>
, respectively.

I'm really lost as to how to solve this issue. In fact, I'm not certain what is causing the issue, in the first place. According to my knowledge there's no way to specify the length of the TCP header. Any help would be appreciated.

Answer Source

I think your problem is here

strncat((char*)buffer,(char*)tcpbuf,20); // copy only 20 bytes...this ensures that no extra bytes are catted
strncat((char*)buffer,(char*)ipbuf,20);

The headers aren't strings so you may only be copying part of each header. Try something like this;

memcpy((char*)buffer, (char*)tcpbuf, 20);
memcpy((char*)buffer+20, (char*)ipbuf, 20);
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download