Reputation: 1307
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
Upvotes: 0
Views: 622
Reputation:
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.
Upvotes: 2