Reputation: 1687
I'm trying to write a simple application to output dst and src TCP port numbers. To test I've tried to apply a pcap filter to only sniff packets to or from port 80. However, I keep getting port numbers of 0, despite everything looking correct to me. About 10% of the time I get a port number in the 5 digits. Can anyone give me any hints as to what I might be doing wrong?
#include<pcap.h>
#include<stdio.h>
#include<net/ethernet.h>
#include<netinet/ip.h>
#include<netinet/tcp.h>
#include<arpa/inet.h>
void handle_packet(u_char* args, const struct pcap_pkthdr* pkthdr, const u_char* p)
{
struct iphdr* ip_hdr;
struct tcphdr* tcp_hdr;
ip_hdr = (struct iphdr*) (p+sizeof(struct ether_header));
tcp_hdr = (struct tcphdr*) (ip_hdr+sizeof(struct iphdr));
printf("src:%d\n", ntohs(tcp_hdr->source));
printf("dst:%d\n", ntohs(tcp_hdr->dest));
}
int main(int argc, char** argv)
{
pcap_t *handle; /* Session handle */
char *dev; /* The device to sniff on */
char errbuf[PCAP_ERRBUF_SIZE]; /* Error string */
struct bpf_program filter; /* The compiled filter */
char filter_app[] = "tcp port 80"; /* The filter expression */
bpf_u_int32 mask; /* Our netmask */
bpf_u_int32 net; /* Our IP */
/* Define the device */
dev = pcap_lookupdev(errbuf);
/* Find the properties for the device */
pcap_lookupnet(dev, &net, &mask, errbuf);
/* Open the session in promiscuous mode */
handle = pcap_open_live(dev, BUFSIZ, 0, 0, errbuf);
/* Compile and apply the filter */
pcap_compile(handle, &filter, filter_app, 0, net);
pcap_setfilter(handle, &filter);
pcap_loop(handle, 10, handle_packet, NULL);
pcap_close(handle);
return(0);
}
Upvotes: 2
Views: 554
Reputation: 54551
Two problems here. For one, keep in mind that pointer arithmetic in C is scaled. So, when you say something like:
tcp_hdr = (struct tcphdr*) (ip_hdr+sizeof(struct iphdr));
You are in fact advancing far more bytes than you intended (sizeof(struct iphdr) * sizeof(struct iphdr)
to be exact). To achieve what you'd want you could say:
tcp_hdr = (struct tcphdr*) &ip_hdr[1];
But this doesn't work either. IP headers don't have a fixed length. Instead you should check the header's ihl
field and the calculation should look more like this:
tcp_hdr = (struct tcphdr*) ((char*)ip_hdr + 4*ip_hdr->ihl); /* ihl is the number of 32-bit words in the header */
Caveat: I'm not sure about ethernet frames and if their headers have a fixed length. You'll need to verify that as well.
Upvotes: 4