Search code examples
clibpcaparpsniffing

How to set libpcap filters to get arp packets


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 ?


Solution

  • 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