0

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?

Linksman
  • 3
  • 2

1 Answers1

0

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))"
Christopher Maynard
  • 5,702
  • 2
  • 17
  • 23
  • Thank you for your reply. The "accurate“ meaning is the length of the protocl header is variable length. Meanwhile, sometimes the gre inner message maybe not include link layer protocol. So I'm confused to determine the length of the offset. – Linksman Jan 31 '23 at 07:27
  • *the length of the protocl header is variable length.* If you're referring to the IP header length, then I already showed how to compute that. If you're referring to the GRE header length, then yes, there could be optional header fields as I mentioned, so if you **really** want a generic filter to handle any optional GRE header fields, then you're going to have to check the `CRKSs` flags as described in [RFC1701](https://www.rfc-editor.org/rfc/rfc1701) and adjust the offsets accordingly. – Christopher Maynard Jan 31 '23 at 14:01
  • *sometimes the gre inner message maybe not include link layer protocol* I don't know what you mean here. You need to check the GRE Protocol Type field for 0x0800, i.e., for IPv4, as I've shown above. IPv4 is a network layer protocol, not a link layer protocol. Maybe have a look at RFC1701 if you're still confused? – Christopher Maynard Jan 31 '23 at 14:03
  • As long as the GRE Routing bit isn't set, then the following **untested** filter *should* work for any variable-length IPv4 header length and any number of other GRE optional header fields: `"(ip proto 47) && (ip[((ip[0] & 0x0f) * 4) + 2:2] = 0x0800) && (ip[((ip[0] & 0x0f) * 4) + 4 + ((((ip[(ip[0] & 0x0f) * 4] & 0x80) >> 7) | ((ip[(ip[0] & 0x0f) * 4] & 0x40) >> 6)) * 4) + (((ip[(ip[0] & 0x0f) * 4] & 0x20) >> 5) * 4) + (((ip[(ip[0] & 0x0f) * 4] & 0x10) >> 4) * 4) + 12:4] = 0x0a4b020b)"`. But if the Routing bit is set, then I don't think there's a way to write any such generic filter. – Christopher Maynard Jan 31 '23 at 22:26