EQ_ALL_THE_WAY EQ_ALL_THE_WAY - 23 days ago 7
C Question

C - Negative payload length using pcap library

I was writing a packet sniffer in C, using the pcap library and I have noticed that some of my packets have negative payload length.

The IPs and MACs and checksums and all of that seem to be alright, but some packets (not all) seem to have negative payload.

I do it like this: I have a pointer that points to the start of the packet (which is u_char). I move the pointer to the right (to get past the L2,3,4 headers). In this process, I also print the headers (Source MAC, Destination MAC, Source IP and so on). The headers seem to be in order, but I don't know why I get negative payload sometimes.

If needed, I can provide the code.

Thank you!

void got_packet(u_char* args, struct pcap_pkthdr* hdr, const u_char* packet) {

FILE* f = fopen("/home/bogdan/C_Stuff/log.txt", "w+");
static int packet_counter = 1;
printf("\n\n");
printf("PACKET NUMBER %i\n", packet_counter);

int ethernet_header_length = 14;
int ip_header_length;
int tcp_header_length;
int payload_length;

const u_char* ip_header;
const u_char* tcp_header;
const u_char* payload;

ip_header = packet + ethernet_header_length;

/* The second part of the IP Header contains
the length of the IP Header */
ip_header_length = ((*ip_header) & 0x0F);
ip_header_length = ip_header_length * 4;

printf("IP Header Length = %i\n", ip_header_length);
/* At the 10th byte we find info about TCP/UDP stuff */
if(*(ip_header + 9) == IPPROTO_TCP) {
printf("TCP Packet\n");
}

tcp_header = ip_header + ip_header_length;
tcp_header_length = ((*(tcp_header) + 12) & 0xF0) >> 4;
tcp_header_length *= 4;
printf("TCP Header Length = %i\n", tcp_header_length);

// Total header size
int total_header_size = ethernet_header_length + ip_header_length + tcp_header_length;
payload = packet + total_header_size;
payload_length = hdr->caplen - total_header_size;
printf("Payload Length = %i\n", payload_length);

if(payload_length > 0) {
int i;
printf("\n**************************************************");
printf("\nPAYLOAD:\n");
for(i = 0; i < payload_length; i++) {
printf("%i ", payload[i]);
if(i % 10 == 0) {
printf("\n");
}
}
printf;
printf("\n");
}


struct ether_header* eptr;
eptr = (struct ether_header*)packet;

if(ntohs(eptr->ether_type) == ETHERTYPE_IP) {
printf("IP PACKET\n");
} else if(ntohs(eptr->ether_type) == ETHERTYPE_ARP) {
printf("ARP PACKET\n");
}

struct ip* ip;
ip = (struct ip*)(packet + sizeof(struct ether_header));

printf("Source MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", eptr->ether_shost[0], eptr->ether_shost[1], eptr->ether_shost[2], eptr->ether_shost[3], eptr->ether_shost[4], eptr->ether_shost[5]);

printf("Destination MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", eptr->ether_dhost[0], eptr->ether_dhost[1], eptr->ether_dhost[2], eptr->ether_dhost[3], eptr->ether_dhost[4], eptr->ether_dhost[5]);

printf("Source IP: %s (IPv%i) | Total length = %i |\nTTL = %i | Checksum = %i\n", inet_ntoa(ip->ip_src), ip->ip_v, ip->ip_len, ip->ip_ttl, ip->ip_sum);

printf("Destination IP: %s (IPv%i) | Total length = %i |\nTTL= %i | Checksum = %i\n", inet_ntoa(ip->ip_dst), ip->ip_v, ip->ip_len, ip->ip_ttl, ip->ip_sum);

printf("\n====================================================\n");

fprintf(f, "%s", inet_ntoa(ip->ip_dst));

fflush(stdout);
packet_counter++;

fclose(f);
}


And here is a sample capture (everything seems alright, except for the payload length):


PACKET NUMBER 9 IP Header Length = 20 TCP Packet TCP Header Length =
52 Payload Length = -20 IP PACKET Source MAC:

24:0a:64:22:a2:39 Destination MAC: 78:d7:52:29:06:db Source IP:
192.168.100.10 (IPv4) | Total length = 13312 | TTL = 64 | Checksum = 11794 Destination IP: 216.58.209.165 (IPv4) | Total length = 13312 |
TTL= 64 | Checksum = 11794

Answer

TCP Header Length = 52 in your output looks to me weird. TCP headers are usually with no option and a TCP header is 20 bytes length without options.

I've been not writing C for a long time, so I'm not really sure but probably tcp_header_length = ((*(tcp_header) + 12) & 0xF0) >> 4; is wrong. It should be:

tcp_header_length = (*(tcp_header + 12) & 0xF0) >> 4;

And, you should use hdr->len instead of hdr->caplen to always get the actual length of a captured packet as pointed in a comment.

Comments