libpcap icon indicating copy to clipboard operation
libpcap copied to clipboard

Possible filter Bug when using MPLS

Open edgar-costa opened this issue 2 years ago • 5 comments

Version:

tcpdump version 4.9.3 libpcap version 1.8.1 OpenSSL 1.1.1 11 Sep 2018

As indicated in this issue https://github.com/the-tcpdump-group/libpcap/issues/1070 I am doing some packet filtering on ip.tos field, however, the expected packets that might come can be normal ethernet+ip or can have mpls headers in between. As shown in that previous issue I ended up using filters of the following nature:

tcpdump -n -i <intf> "(ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14)"

This is the only way I found to parse packets with mpls headers (in this case, up to 9 stacked labels). Now, this seemed to work well, until today which I started to see some specific TCP ACK packets (not sure if other packets could match too) started to be captured as well. After some investigation, I found out that, this only happens when I parse more than 8 MPLS labels, thus I deduced this had to be related to the depth of the parsing.

Now, I was thinking, why is this even matching the filter at all? I looked at all the packets that are matching this filter, and they all have the byte 51 equal to 0x0e (which is 14 and its the tos field I am matching).

For example, the following packet, matches the filter (this is a normal tcp ack, with tos field equal 0, but with a byte equal 0x0e... in a very specific place which I guess triggers this weird behaviour.

0e8ee077695a1ac57a389fb5080045000034150840003e0604bb0a0007010a0008010139013110075f69d5828e0c801097624b0e00000101080ad317debb605f89ae

Is this a bug? How can it even match this packet, when the ether type is not mpls, and thus it should not even parse that deep? And even if it did it, it should not match?

edgar-costa avatar Dec 02 '21 15:12 edgar-costa

Thank you for providing this analysis. This might be a bug, but to tell it would be necessary to reproduce this behaviour. Could you add a .pcap file with at least one packet that triggers the false positive?

infrastation avatar Dec 04 '21 21:12 infrastation

Do you see the same behavior if you disable the optimizer?

       --no-optimize
              Do not run the packet-matching code optimizer.  This is useful only if you suspect a bug in the optimizer.

I see some curious output in the optimized code (run tcpdump -d with your filter), but haven't taken the time to diagram exactly what it's looking at in the packet.

fenner avatar Dec 07 '21 23:12 fenner

Hi @infrastation

Here I upload a pcap where you can see this behaviour. This is a pcap that was captured with this exact filter, and there is one packet that should not be there, its a TCP packet for which I added the hex in my original post.

If you run the filter, the TCP packet passes the filter (due to the matching 0x0e by chance...)

tcpdump -r PAR-eth10.pcap "(ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14)"

If yo remove 1 ((mpls && ip[1]==14)), then only the MPLS/UDP packets match.

tcpdump -r PAR-eth10.pcap "(ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14) or (mpls && ip[1]==14)"

@fenner Same happens with --no-optimize flag.

PAR-eth10.pcap.zip

edgar-costa avatar Dec 07 '21 23:12 edgar-costa

The bug exists in the program whether there are 8 or 9 copies, it's just that once you get to 9, you happen to get to offset 51 looking for the 0xe, and there's a 0xe in the tcp checksum of that packet.

The bug is early on:

(000) ldh      [12]
(001) jeq      #0x800           jt 2    jf 4
(002) ldb      [15]
(003) jeq      #0xe             jt 93   jf 13
(004) jeq      #0x8847          jt 5    jf 13
(005) ldb      [16]
(006) and      #0x1
(007) jeq      #0x1             jt 8    jf 13
(008) ldb      [18]
(009) and      #0xf0
(010) jeq      #0x40            jt 11   jf 13
(011) ldb      [19]
(012) jeq      #0xe             jt 93   jf 13
(013) ldb      [16]
(014) jset     #0x1             jt 23   jf 15

In instruction 1, we check for ether proto IP; then in instruction 3 we check if ip[1] == 14. That's correctly implementing your first (ip[1]==14) clause. But, if ip[1] != 14, we jump over the check for ether proto MPLS at instruction 4, and head straight to the "does the first label have bos set" at 13/14.

Just adding information for now, no insight as to what the expression generator is doing wrong.

fenner avatar Dec 08 '21 15:12 fenner

I converted the tcp packet's ethertype to MPLS, and that makes it clear that the filter is even more buggy - it moves past the BOS bit on (what would be) the 3rd label and selects the packet based on the 0x470e (guessing based on 0x47 & 0xf0 == 0x40 that it's IP, then the 0x0e that you're looking for) that happens to appear what would be 6 labels further in. demo.zip

fenner avatar Dec 08 '21 15:12 fenner