I'm trying to write a simple packet sniffer that will only need to work with certain types of traffic. I'm coding it up in C++ and using libpcap on a Centos 7 box. It works fairly well but was hoping to improve the performance by using the filtering options of the pcap library so that only the packet types I am interested in are presented for processing.
A sample of my code:
struct bpf_program filter;
if (pcap_compile(pcap, &filter,
"(ip && (tcp || udp)) || (vlan && (ip && (tcp || udp)))",
1 /*OPTIMIZE*/, PCAP_NETMASK_UNKNOWN) == PCAP_ERROR) {
std::cout << "Failed to compile filter: " << pcap_geterr(pcap);
} else {
// Load filter into packet capture device
if (pcap_setfilter(pcap, &filter) == PCAP_ERROR) {
std::cout << "Failed to set filter: " << pcap_geterr(pcap);
}
}
Essentially, I want IP packets that either contain TCP or UDP and also accept vlan tagged packets of the same type. I believe the filter syntax is correct but need to now support double vlan packets (802.1ad) and not sure how I should rewrite the filter string. I don't have any double vlan packets to test with so can't just experiment until I get it right. Does anyone have any suggestions of how the filter string should look for accepting double vlan packets (as well as the single vlan and no vlan)?
I also noticed that when I run live, the pcap_compile
function fails with the error no VLAN support for data link type 113
. Strangely, when I put the same filter code into wireshark it doesn't complain. Any idea what the issue is here?
Update 1:
I managed to find some double vlan packets online to test with. Using wireshark, the filter ieee8021ad works ok but produces a syntax error when used as an argument with pcap_compile
I found the correct filter string for allowing 802.1ad tagged packets through. Here is the updated code:
if (pcap_compile(pcap, &filter,
"(ip && (tcp || udp)) ||"
"(vlan && (ip && (tcp || udp))) ||"
"((ether[12:2] == 34984) && (vlan && (ip && (tcp || udp))))"
"|| vlan",
1 /*OPTIMIZE*/, PCAP_NETMASK_UNKNOWN) == PCAP_ERROR) {
LOG(ERROR) << "Failed to compile filter: " << pcap_geterr(pcap);
} else {
// Load filter into packet capture device
if (pcap_setfilter(pcap, &filter) == PCAP_ERROR) {
LOG(ERROR) << "Failed to set filter: " << pcap_geterr(pcap);
}
}
Basically, it allows tcp/udp(IP), tcp/udp(IP) in VLAN, tcp/udp(IP) in double VLAN and then if there is 3 or more levels of VLAN, everything is allowed in. From what I can tell, there is no wildcard option for allowing all nested VLANS when you want to specify other options so for now, this will do.
The second part of the problem is due to the fact that with cooked captured ('any' interface), not all filters are supported e.g. vlan, eth addr etc. We can get around this by opening and capturing on each interface (own threads) so that we have the proper link type which allows us more filter options.