Thursday, December 04, 2008

BPF for IP or VLAN Traffic

Four years ago I did a second post on Understanding Tcpdump's -d Option, showing how you can using the -d option to understand how Berkeley Packet Filter syntax works.

Recently my colleagues and I encountered a problem where we were monitoring traffic on a tap, but the traffic contained traffic with and without 802.1q VLAN tags. We wanted to create a BPF that would catch traffic whether or not it had VLAN tags. It turns out there is a difference between these two BPFs:

ip or vlan

is not the same as

vlan or ip

The first accomplishes our goal, but the second does not.

To understand why, I used Tcpdump's -d option.

$ tcpdump -d -n -r sample.pcap ip or vlan
reading from file sample.pcap, link-type EN10MB (Ethernet)
(000) ldh [12]
(001) jeq #0x800 jt 3 jf 2
(002) jeq #0x8100 jt 3 jf 4
(003) ret #65535
(004) ret #0

That looks right. Load the half word at offset 12. If it's the IP Ethertype, you get the whole packet. If it's not IP, go to the next instruction. If it's a 802.1Q VLAN tag, again you get the whole packet. Otherwise, return nothing.

This is the other option.

$ tcpdump -d -n -r sample.pcap vlan or ip
reading from file sample.pcap, link-type EN10MB (Ethernet)
(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

That doesn't work. Load the half word at offset 12. If it's a 802.1Q VLAN tag, you get the whole packet. If it's not a 802.1Q VLAN tag, load the half word at offset 16. If that half word is an IP Ethertype (which it won't be), you get the whole packet. Otherwise, return nothing.

For an example of how you would combine a host and port filter with this syntax, see the following:

tcpdump -n -r ip.pcap \(ip and host 1.2.3.4 and port 80\) or \(vlan and host 1.2.3.4 and port 80\)

You might see this new option appear in Sguil CVS soon.


Richard Bejtlich is teaching new classes in DC and Europe in 2009. Register by 1 Jan and 1 Feb, respectively, for the best rates.

3 comments:

JaBbA said...

But why does tcpdump insert the ldh [16] command? There's no explanation of the mechanism that causes that to happen - how can we avoid problems like this?

SCORPIO said...
This comment has been removed by a blog administrator.
John Thacker said...

"But why does tcpdump insert the ldh [16] command? "

If you read the tcpdump man page, you'll see that any vlan primitive changes all offsets by 4 bytes to account for the vlan tag. This makes future offsets work correctly, including multiple vlans.

However, it only really makes sense with "and" not with "or."

vlan 2 or vlan 4

Doesn't match if the first vlan tag is 2 or if the first vlan tag is 4. What it actually matches is if the first vlan tag is 2.... or if a second (nested, offset by 4) vlan tag is 4. Counterintuitive.