Question

I am trying to work with pcap but I have some troubles reading a sequence number.

I use the code below to listen for an incomming packet, but when I try to read the sequence number the program halts without an error. Just stops the execution silently.

This code largely comes from the pcap tutorial from the tcpdump site.

bpf_u_int32 net=0, mask=0;
pcap_t *descr = NULL;
struct bpf_program filter;
struct ip *iphdr = NULL;
struct tcphdr *tcphdr = NULL;
struct pcap_pkthdr pkthdr;
const unsigned char *packet = NULL;
char pcap_errbuf[PCAP_ERRBUF_SIZE];
char filter_exp[] = "tcp port 111 dst host 10.0.0.10";
char * dev;

// Define the device
dev = pcap_lookupdev(pcap_errbuf);
if (dev == NULL) {
    printf( "Couldn't find default device: %s\n", pcap_errbuf);  fflush(stdout);
    exit(1);
}

// Find the properties for the device 
if( pcap_lookupnet(dev, &net, &mask, pcap_errbuf) == -1 ){
    printf("Couldn't get netmask for device %s: %s\n", dev, pcap_errbuf);  fflush(stdout);
    net = 0;
    mask = 0;
}
// Open the session in non-promiscuous mode 
descr = pcap_open_live(dev, BUFSIZ, 0, 1000, pcap_errbuf);
if (descr == NULL) {
    printf("Couldn't open device %s: %s\n", dev, pcap_errbuf); fflush(stdout);
    exit(1);
}
// Compile and apply the filter 
if( pcap_compile(descr, &filter, filter_exp, 0, net) == -1) {
    printf("Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(descr)); fflush(stdout);
    exit(1);
}
if (pcap_setfilter(descr, &filter) == -1) {
    printf( "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(descr)); fflush(stdout);
    exit(1);
}

packet = pcap_next(descr, &pkthdr );
iphdr = (struct ip *)(packet+14);
tcphdr = (struct tcphdr *)(packet+14+20);

printf("test1\n"); fflush( stdout );
printf("SEQ: %d\n", ntohl(tcphdr->th_seq) ); fflush( stdout );
    printf("test2\n");
pcap_close(descr);

The "test1" is printed, but the "test2" and the "SEQ: %d" isn't. Its hard to debug if there's no error at all.

Anyone seen this before?

Thanks

Was it helpful?

Solution

Nikolai Fetissov is correct - you must check whether pcap_next() returns NULL or not. It might return NULL if, for example, the timeout expires and no packets have arrived. In that case, you should keep looping until it returns a non-null value.

However, it could also return NULL if there's an error, and that error might mean that you won't get any more packets. A better routine to use is pcap_next_ex(), which returns returns 1 if the packet was read without problems, 0 if packets are being read from a live capture and the timeout expired, -1 if an error occurred while reading the packet, and -2 if packets are being read from a savefile and there are no more packets to read from the savefile.

In your case, you're doing a live capture, so you should use pcap_next_ex(), and loop until it returns either 1, in which case you print the packet information, or -1, in which case you report an error and exit:

int status;

while ((status = pcap_next_ex(descr, &pkthdr, &packet)) == 0)
    ;

if (status == -1) {
    fprintf(stderr, "pcap_next_ex failed: %s\n", pcap_geterr(descr));
    exit(1);
}

iphdr = (struct ip *)(packet+14);
tcphdr = (struct tcphdr *)(packet+14+20);

printf("test1\n"); fflush( stdout );
printf("SEQ: %d\n", ntohl(tcphdr->th_seq) ); fflush( stdout );
printf("test2\n");
pcap_close(descr);

Note also that there's no guarantee that the IPv4 header is 20 bytes long - it could be longer, so you need to extract the header length from the first byte of the IPv4 header (the header length/version field), multiply it by 4 (as it's in units of 4-byte words), and use that when calculating the address of the TCP header, rather than using a hard-coded 20.

In addition, you should also make sure that the link-layer header type of the device, as returned by pcap_datalink(descr), is DLT_EN10MB, to make sure the packets have Ethernet headers rather than some other type of header.

In addition, I just copied your printf code, as I was concentrating on the capture problem. Somebody else added an ntohl() call, which is necessary when printing the sequence number - multi-byte numerical fields in IP and TCP headers are in "network byte order", i.e. big-endian, but you might be running on a little-endian machine, so the sequence number has to be converted to the host byte order before printing it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top