In the previous parts we built a packet sniffer that can capture and display information about packets. Today we are going to add filtering capabilities so we can capture specific packets.
Filter expressions consists of qualifiers followed by primitives, For example :
host www.duckduckgo.com
, port 1337
, src port 80
To use filters we first compile them using pcap_compile()
, this function takes a capture device, a pointer to a bpf_program
struct, filters string, an integer to enable/disable optimization and a network mask variable of type bpf_u_in32
. The result of the compilation is a filter program which will be stored on the bpf_program
struct. This filter program can then be applied to the capture device using pcap_setfilter()
.
pcap_compile()
retruns 0 on success and PCAP_ERROR if anything went wrong, and you can call pcap_geterr()
on the capture device to get the error.
struct bpf_program bpf;
bpf_u_int32 netmask;
char *filters = "host www.duckduckgo.com";
if (pcap_compile(dev, &bpf, filters, 0, netmask) == PCAP_ERROR) {
printf("ERR: pcap_compile() %s", pcap_geterr(dev));
}
if (pcap_setfilter(dev, &bpf)) {
printf("ERR: pcap_setfilter() %s", pcap_geterr(dev));
}
Add the previous code to you nsniff.c
file before the pcap_loop()
call, then compile and run the program. You will notice that no results are being shown in the terminal, but if you pinged www.duckduckgo.com
you will start seeing results, and that is because we filtered every packet not related to the duckduckgo host.
$ cc -o nsniff nsniff.c -lpcap
$ sudo ./nsniff
**************************************************************************
ID: 35344 | SRC: 40.114.177.156 | DST: 40.114.177.156 | TOS: 0x0 | TTL: 64
PROTO: ICMP | TYPE: 8 | CODE: 0 |
**************************************************************************
ID: 0 | SRC: 192.168.8.4 | DST: 192.168.8.4 | TOS: 0x0 | TTL: 109
PROTO: ICMP | TYPE: 0 | CODE: 0 |
**************************************************************************
ID: 14704 | SRC: 40.114.177.156 | DST: 40.114.177.156 | TOS: 0x0 | TTL: 1
PROTO: TCP | FLAGS: S/-/- | SPORT: 41684 | DPORT: 60180 |
**************************************************************************
ID: 35616 | SRC: 40.114.177.156 | DST: 40.114.177.156 | TOS: 0x0 | TTL: 64
PROTO: ICMP | TYPE: 8 | CODE: 0 |
**************************************************************************
ID: 0 | SRC: 192.168.8.4 | DST: 192.168.8.4 | TOS: 0x0 | TTL: 109
PROTO: ICMP | TYPE: 0 | CODE: 0 |
**************************************************************************
ID: 19066 | SRC: 40.114.177.156 | DST: 40.114.177.156 | TOS: 0x0 | TTL: 1
PROTO: TCP | FLAGS: S/-/- | SPORT: 43220 | DPORT: 60180 |
**************************************************************************
ID: 35728 | SRC: 40.114.177.156 | DST: 40.114.177.156 | TOS: 0x0 | TTL: 64
PROTO: ICMP | TYPE: 8 | CODE: 0 |
**************************************************************************
ID: 0 | SRC: 192.168.8.4 | DST: 192.168.8.4 | TOS: 0x0 | TTL: 109
PROTO: ICMP | TYPE: 0 | CODE: 0 |
**************************************************************************
ID: 58529 | SRC: 40.114.177.156 | DST: 40.114.177.156 | TOS: 0x0 | TTL: 1
PROTO: TCP | FLAGS: S/-/- | SPORT: 43732 | DPORT: 60180 |
**************************************************************************
ID: 36282 | SRC: 40.114.177.156 | DST: 40.114.177.156 | TOS: 0x0 | TTL: 64
PROTO: ICMP | TYPE: 8 | CODE: 0 |
On another terminal run ping -c4 www.duckduckgo.com
to start seeing the results. This isn’t the only command that will generate packets matching the filter, you can use netcat as another example :
nc www.duckduckgo.com 80
And we are done, our sniffer can sniff is now a useful network sniffer, it can display information about headers and set filters, but, no one will uses it the way it as it is right now, because it uses fixed values for device name, filters and packet count. Its impractical to compile every time we wanted to use the program with different filters or a different device.
I will leave these improvements to you (i can’t take all the fun for myself 🙂). You can :
- Refactor, somethings could have been done in a better way.
- Add command line arguments for any value that would change such as the device name, so when running the program it will be something like
./nsniff -i enp0s3 -n 10 -f host www.duckduckgo.com
- Display packets information in a nicer way.
libpcap is an amazing library make sure to read more about it.
nsniff repository on github : https://github.com/talalio/nsniff