How can I capture Gre or Vxlan inner network traffic?

1.6k views Asked by At

I want to capture some network traffic with the filter like "src x.x.x.x and dst x.x.x.x".

Ususally it works, but it doesn't work when the network traffic is encapsulated by protocls like Gre or Vxlan.

For example, the Gre encapsulate a message like this:

Ethernet II, Src: VMware_91:f6:ad (00:0c:29:91:f6:ad), Dst: VMware_dc:c7:71 (00:0c:29:dc:c7:71)
Internet Protocol Version 4, Src: 10.75.2.161, Dst: 10.75.2.140
Generic Routing Encapsulation (Transparent Ethernet bridging)
Ethernet II, Src: Micro-St_e3:51:57 (4c:cc:6a:e3:51:57), Dst: VMware_91:f6:ad (00:0c:29:91:f6:ad)
Internet Protocol Version 4, Src: 10.75.2.11, Dst: 10.75.2.160

So what should I do to capture those inner traffic?

I use "src 10.75.2.160" to capture but it tcpdump captured nothing.

tcpdump -i eth0 "src 10.75.2.11"

It doesn't work.

I use "ip[54:4]" to capture, it works, but my leader tell me it's not accurate.

So what else can I try?

1

There are 1 answers

4
Christopher Maynard On BEST ANSWER

I don't know how accurate your leader wants the filter, but if we can make a few assumptions about the outer IP and GRE headers, then the filter isn't too complicated. So here are the 2 assumptions:

  • The outer IP header is a standard 20 bytes. If this isn't the case or can't be relied upon, then everywhere I use 20 in the filter will need to be replaced with the IP header length calculation, which is (ip[0] & 0x0f) * 4.
  • The GRE header is a standard size of 4 bytes. If options are present and you can guarantee that all GRE headers are exactly the same size, then you can substitute 4 for the actual GRE header length, but if GRE options could vary, then it should still be possible to specify a capture filter that works with any size GRE header, but it will be much more complicated and hard to follow. I leave that as an exercise for the reader.

With those assumptions out of the way, here's a filter that would be the equivalent of "src 10.75.2.11" but for the inner source IP address of a IP/GRE/IP packet:

(ip proto 47) && (ip[20 + 2:2] = 0x0800) && (ip[20 + 4 + 12:4] = 0x0a4b0x0b)

Explanation:

  • ip proto 47: This captures only IP/GRE packets as GRE is assigned protocol number 47.
  • ip[20 + 2:2] = 0x0800: Since 0x0800 is the assigned Ethertype for IPv4, this captures only GRE packets where the Protocol Type field is IP, so only IP/GRE/IP packets. (NOTE: RFC 1701 states, "The Protocol Type field contains the protocol type of the payload packet. In general, the value will be the Ethernet protocol type field for the packet.")
  • ip[20 + 4 + 12:4] = 0x0a4b020b: This captures only the IP/GRE/IP packets where the source IP address field of the inner IP header is 10.75.2.11. (NOTE: To get 0x0a4b020b from 10.75.2.11, you just need to convert each decimal octet to hexadecimal and combine them into a single 4 byte value.)

To verify the resulting BPF code, you can run tcpdump with the -d option to check that the filter meets your expectations, for example:

tcpdump -i eth0 "(ip proto 47) && (ip[20 + 2:2] = 0x0800) && (ip[20 + 4 + 12:4] = 0x0a4b020b)"

You should see output of the following form:

(000) ldh      [12]
(001) jeq      #0x800           jt 2    jf 9
(002) ldb      [23]
(003) jeq      #0x2f            jt 4    jf 9
(004) ldh      [36]
(005) jeq      #0x800           jt 6    jf 9
(006) ld       [50]
(007) jeq      #0xa4b020b       jt 8    jf 9
(008) ret      #262144
(009) ret      #0

If you're not familiar with BPF code, then I would suggest further reading elsewhere, as providing a BPF tutorial here is beyond the scope of this answer.

Finally, if you need to filter for the source IP address whether it's in the outer IP header or the inner IP header, then you can basically just combine the 2 filters, i.e.:

"(ip src 10.75.2.11) || ((ip proto 47) && (ip[20 + 2:2] = 0x0800) && (ip[20 + 4 + 12:4] = 0x0a4b020b))"