Huge thanks to our Platinum Members Endace and LiveAction,
and our Silver Member Veeam, for supporting the Wireshark Foundation and project.

Wireshark-users: Re: [Wireshark-users] Filtering on fields in tunnel headers

From: Sake Blok <sake@xxxxxxxxxx>
Date: Wed, 12 Sep 2012 10:42:55 +0200
On 12 sep 2012, at 09:01, Jaap Keuter wrote:
> On 09/11/2012 11:30 PM, Martin Isaksson wrote:
>> If I have a packet with protocols like eth:vlan:ip:udp:gtp:ip:tcp, is there a
>> way to filter in one of the IP headers only?
>> I know I can do frame[22:2] == D4:DD (here IP ID of first IP header), but it's
>> not very dynamic, so if for some reason the bytes are in different places, this
>> would fail.

Well, for the IPid in the first IP header, you can just use ip.id ;-)
But yes, for the inner IP layer, it gets complicated...

> Currently there's no way to filter on ip{inner}/ip{outer} in a packet. If it's ip it's ip it's ip; s/ip/<your proto>/g. That can be a strength (like catching ICMP) and a weakness (like in tunnels). This would require some fundamental dissection and display filter work.

>> Another work-around I've tried is to list one of the IP IDs with tshark and grep.

Capture filters can come to the rescue, while they are not very good for higher layer protocols and protocols that involve reassembly, they can do an amazing job at looking at offsets and calculating offsets into the packet.

Here is an example of finding SIP packets (tcp or udp) on port 5060 inside an IP-IP tunnel:

ip proto 4 and (ip[((ip[0]&0x0f)<<2)+9]=17 or ip[((ip[0]&0x0f)<<2)+9]=6) and (ip[((ip[0]&0x0f)<<2)+((ip[((ip[0]&0x0f)<<2)]&0x0f)<<2)+0:2]=5060 or ip[((ip[0]&0x0f)<<2)+((ip[((ip[0]&0x0f)<<2)]&0x0f)<<2)+2:2]=5060)

OK, it's not as complicated as it looks, here we go:

We only look at IP-IP tunneled packets, so:

ip proto 4

To get to the start of the inner IP layer, we need to calculate the ip header length of the outer IP layer. This can be done with:

(ip[0]&0x0f)<<2

So, the IP protocol field at offset 9 of the inner IP header is at offset ((ip[0]&0x0f)<<2)+9 in the outer IP header. So to check on UDP or TCP, we need:

ip[((ip[0]&0x0f)<<2)+9]=17 or ip[((ip[0]&0x0f)<<2)+9]=6

Then we need to check the ports, which are at offset 0 (src) and 2 (dst) in the UDP/TCP header, so we need to skip both IP headers and incorporate flexible IP header lengths. For the inner Ip layer, the length is:

ip[((ip[0]&0x0f)<<2)]&0x0f)<<2

Adding the offset of the inner IP layer into the outer IP layer:

(ip[0]&0x0f)<<2)+((ip[((ip[0]&0x0f)<<2)]&0x0f)<<2

So the src port can be found at:

ip[((ip[0]&0x0f)<<2)+((ip[((ip[0]&0x0f)<<2)]&0x0f)<<2)+0:2]

So, checking for port 5060 becomes:

ip[((ip[0]&0x0f)<<2)+((ip[((ip[0]&0x0f)<<2)]&0x0f)<<2)+0:2]=5060 or ip[((ip[0]&0x0f)<<2)+((ip[((ip[0]&0x0f)<<2)]&0x0f)<<2)+2:2]=5060



Putting it all together:

ip proto 4 and (ip[((ip[0]&0x0f)<<2)+9]=17 or ip[((ip[0]&0x0f)<<2)+9]=6) and (ip[((ip[0]&0x0f)<<2)+((ip[((ip[0]&0x0f)<<2)]&0x0f)<<2)+0:2]=5060 or ip[((ip[0]&0x0f)<<2)+((ip[((ip[0]&0x0f)<<2)]&0x0f)<<2)+2:2]=5060)

You can follow the same process for your packets and use tcpdump to extract your traffic into a new capture file.

If you need any help, just post a tracefile with a couple of packets and I see if I can assist you...

Cheers,
Sake