Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected behaviour when VLAN is used in BPF filter #606

Closed
EduardoMiravalls opened this issue May 31, 2017 · 8 comments
Closed

Unexpected behaviour when VLAN is used in BPF filter #606

EduardoMiravalls opened this issue May 31, 2017 · 8 comments

Comments

@EduardoMiravalls
Copy link

I was testing a BPF filter that accepted both VLAN tagged and not tagged traffic of a subnet, when I noticed that the filter didn't work as expected depending on how I wrote it.

Investigating why the filter didn't work, I noticed that tcpdump generates different compiled code depending on what side of the "or" I place the "vlan" keyword, even if I use parenthesis.

Here is the output of the compiled code for both filters. Notice that the first code is shorter than the second one, which is the one that works. The first one doesn't seem to accept any packet.

# ./tcpdump -d "(vlan and net 192.168.0.0/16) or net 192.168.0.0/16" -vvv -i eth0
(000) ldh      [12]
(001) jeq      #0x8100          jt 3    jf 2
(002) jeq      #0x9100          jt 3    jf 3
(003) ldh      [16]
(004) jeq      #0x800           jt 5    jf 11
(005) ld       [30]
(006) and      #0xffff0000
(007) jeq      #0xc0a80000      jt 19   jf 8
(008) ld       [34]
(009) and      #0xffff0000
(010) jeq      #0xc0a80000      jt 19   jf 20
(011) jeq      #0x806           jt 13   jf 12
(012) jeq      #0x8035          jt 13   jf 20
(013) ld       [32]
(014) and      #0xffff0000
(015) jeq      #0xc0a80000      jt 19   jf 16
(016) ld       [42]
(017) and      #0xffff0000
(018) jeq      #0xc0a80000      jt 19   jf 20
(019) ret      #65535
(020) ret      #0

# ./tcpdump -d "net 192.168.0.0/16 or (vlan and net 192.168.0.0/16)" -vvv -i eth0
(000) ldh      [12]
(001) jeq      #0x800           jt 2    jf 8
(002) ld       [26]
(003) and      #0xffff0000
(004) jeq      #0xc0a80000      jt 34   jf 5
(005) ld       [30]
(006) and      #0xffff0000
(007) jeq      #0xc0a80000      jt 34   jf 35
(008) jeq      #0x806           jt 10   jf 9
(009) jeq      #0x8035          jt 10   jf 16
(010) ld       [28]
(011) and      #0xffff0000
(012) jeq      #0xc0a80000      jt 34   jf 13
(013) ld       [38]
(014) and      #0xffff0000
(015) jeq      #0xc0a80000      jt 34   jf 35
(016) jeq      #0x8100          jt 18   jf 17
(017) jeq      #0x9100          jt 18   jf 35
(018) ldh      [16]
(019) jeq      #0x800           jt 20   jf 26
(020) ld       [30]
(021) and      #0xffff0000
(022) jeq      #0xc0a80000      jt 34   jf 23
(023) ld       [34]
(024) and      #0xffff0000
(025) jeq      #0xc0a80000      jt 34   jf 35
(026) jeq      #0x806           jt 28   jf 27
(027) jeq      #0x8035          jt 28   jf 35
(028) ld       [32]
(029) and      #0xffff0000
(030) jeq      #0xc0a80000      jt 34   jf 31
(031) ld       [42]
(032) and      #0xffff0000
(033) jeq      #0xc0a80000      jt 34   jf 35
(034) ret      #65535
(035) ret      #0

I've reproduced this behaviour in Ubuntu 14.04 with kernel

# uname -a
Linux HOST1 4.4.0-78-generic #99~14.04.2-Ubuntu SMP Thu Apr 27 18:49:46 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

and the tcpdump version installed from the official repos

# tcpdump --version
tcpdump version 4.9.0
libpcap version 1.5.3
OpenSSL 1.0.1f 6 Jan 2014

also in a Centos 7 with kernel

# uname -a
Linux HOST2 3.10.0-229.el7.x86_64 #1 SMP Fri Mar 6 11:36:42 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

and tcpdump's version

# tcpdump --version
tcpdump version 4.5.1
libpcap version 1.5.3

I've also tested a compiled version from source, downloaded from the git repository:

# ./tcpdump --version
tcpdump version 4.10.0-PRE-GIT_2017_05_31
libpcap version 1.5.3
OpenSSL 1.0.1f 6 Jan 2014

and I still see the same output.

Is this the expected behaviour?
Or have not I used the vlan keyword correctly?
Thank you.

@guyharris
Copy link
Member

vlan is a bit of a weird keyword - it has a side-effect on the BPF compiler, adjusting the offset used for testing other fields.

(vlan and net 192.168.0.0/16) or net 192.168.0.0/16

will force the net test after the or to check with offsets that assume the presence of a VLAN header, but

net 192.168.0.0/16 or (vlan and net 192.168.0.0/16)

will force only the net test after vlan and to do that.

@guyharris
Copy link
Member

This, BTW, is a libpcap issue, not a tcpdump issue.

@guyharris
Copy link
Member

The first one doesn't seem to accept any packet.

It should accept VLAN-encapsulated packets to or from addresses on 192.168.0.0/16. It won't accept any non-VLAN-encapsulated packets.

@EduardoMiravalls
Copy link
Author

Ok, my bad. I was testing with various filters for nets that had both tagged and untagged traffic, and the net that puzzled me must have had no tagged traffic.
Thank you for the explanation.

Is it a known issue, or do I have to report it?

@guyharris
Copy link
Member

guyharris commented May 31, 2017

It's known, and even mentioned in the pcap-filter man page:

vlan [vlan_id]
True if the packet is an IEEE 802.1Q VLAN packet. If [vlan_id] is specified, only true if the packet has the specified vlan_id. Note that the first vlan keyword encountered in expression changes the decoding offsets for the remainder of expression on the assumption that the packet is a VLAN packet. The vlan [vlan_id] expression may be used more than once, to filter on VLAN hierarchies. Each use of that expression increments the filter offsets by 4.
For example:
vlan 100 && vlan 200
filters on VLAN 200 encapsulated within VLAN 100, and
vlan && vlan 300 && ip
filters IPv4 protocols encapsulated in VLAN 300 encapsulated within any higher order VLAN.

@EduardoMiravalls
Copy link
Author

Ok, but I though the vlan keyword would apply only within the parenthesis. Granted, it does say "for the remainder of expression", but it's a little counter-intuitive that parenthesis don't work for this case.
Anyways, thank you.

@guyharris
Copy link
Member

it's a little counter-intuitive that parenthesis don't work for this case

It's a consequence of the implementation.

If, for example, the compiler had a pass that produced a syntax tree, and a pass that walked the syntax tree and generated code, it might be able to note which particular subtree had vlan in it, and have that apply only to the tests in that subtree.

@infrastation
Copy link
Member

For posterity, there is now a FAQ entry about this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

3 participants