Reputation: 3595
I had filter="ip or vlan" and I pass it to libpcap like below and it is working in my application for a long time without any problem.
pcap_set_filter(pdesc->pd, filter, 0, netp);
Now I have been asked to parse arp trafic also . So I set my filter
"ip or vlan or arp or rarp"
. But my callback function is not being called for arp packets even for ip packets my function still being called.
In short my question is how to set libpcap filter correctly to get arp packets from system ?
Upvotes: 1
Views: 1277
Reputation: 28688
I examined the BPF instructions in WireShark to figure out what happens. Here are the BPF filter programs for various filters:
1. Simple cases:
Filter: vlan
if the frame is VLAN-tagged then
return true
else
return false
Filter: ip
if the frame is not VLAN-tagged and the protocol is IP then
return true
else
return false
Filter: arp
if the frame is not VLAN-tagged and the protocol is ARP then
return true
else
return false
Filter: rarp
if the frame is not VLAN-tagged and the protocol is RARP then
return true
else
return false
Filter: ip or arp or rarp
if the frame is not VLAN-tagged and the protocol is either IP, ARP or RARP then
return true
else
return false
2. Combining ip
with vlan
reveals that the order of search tags is important:
Your first filter was ip or vlan
. Its pseudocode is as follows:
if either the frame is not VLAN-tagged and the protocol is IP
or the frame is VLAN-tagged
then
return true
else
return false
For filter vlan or ip
, we would like to see this:
if either the frame is VLAN-tagged
or the frame is not VLAN-tagged and the protocol is IP
then
return true
else
return false
This would mean the same, which is OK, because A or B
should mean the same as B or A
, shouldn't it. But we get this:
(000) ldh [12]
(001) jeq #0x8100 jt 4 jf 2
(002) ldh [16]
(003) jeq #0x800 jt 4 jf 5
(004) ret #65535
(005) ret #0
This means something like the following pseudocode:
if either the frame is VLAN-tagged
or the frame is not VLAN-tagged but it has an EtherType field shifted 4 bytes right, which says the protocol is IP
then
return true
else
return false
This doesn't make sense. Line (002) is unnecessary. The instructions should look like this:
(000) ldh [12]
(001) jeq #0x8100 jt 3 jf 2
(002) jeq #0x800 jt 3 jf 4
(003) ret #65535
(004) ret #0
Maybe I will be killed for saying this, but I think this is bug in libpcap. Where does the above line (002) ldh [16]
come from? If the filter was vlan and ip
, then checking the bytes at offset 16 would make sense: now we want to find VLAN-tagged frames which contain an IP packet. In such frames, there are two EtherType fields: the first (at offset 12) contains the VLAN EtherType value (0x8100), an the second (at offset 16) contains the EtherType of the IP protocol (#0x800):
(000) ldh [12]
(001) jeq #0x8100 jt 2 jf 5
(002) ldh [16]
(003) jeq #0x800 jt 4 jf 5
(004) ret #65535
(005) ret #0
3. Why didn't your filter find ARP and RARP packets?
Your filter was ip or vlan or arp or rarp
. This compiles to:
(000) ldh [12]
(001) jeq #0x800 jt 6 jf 2
(002) jeq #0x8100 jt 6 jf 3
(003) ldh [16]
(004) jeq #0x806 jt 6 jf 5
(005) jeq #0x8035 jt 6 jf 7
(006) ret #65535
(007) ret #0
This code has the above bug: libpcap tries to find the ARP and RARP EtherTypes at offset 16.
4. The solution to your problem
You can avoid the bug by adding them at the beginning of the filter: arp or rarp or ip or vlan
. This compiles to:
(000) ldh [12]
(001) jeq #0x806 jt 5 jf 2
(002) jeq #0x8035 jt 5 jf 3
(003) jeq #0x800 jt 5 jf 4
(004) jeq #0x8100 jt 5 jf 6
(005) ret #65535
(006) ret #0
Which means:
if either the frame is not VLAN-tagged and the protocol is either IP, ARP or RARP,
or the frame is VLAN-tagged
then
return true
else
return false
Upvotes: 2