The challenge was: “Write a tcpdump/windump filter that will capture ICMPv6 Multicast Listener packets.” I have an extensive write up on what makes the answer so complex. If you know IPv6 and just want the answer, skip to the end.
First, Some Background
Steinar made some comments to the previous posts and was 100% on track. If you read them and thought “Wow, that sounds really messy”, you understand the scope of the problem as well.
Protocol Vs. Next Header Field
With IPv4 we had the options field. This could cause the IP header to grow from 20 bytes to as large as 60 bytes in size. With IPv6, there is no longer an options field and the header is fixed at 40 bytes in size. When options are required, we use extension headers to identify them. This throws an interesting curve ball at us because with IPv4 the protocol field (byte 9) would (almost) always identify the upper level transport (TCP, UDP, etc.). With IPv6 the next header field (byte 6) might identify the upper layer transport, or it might identify an extension header which will include some number of options.
Here’s a list of some IPv6 extension headers you might run into, as well as the RFCs that define them:
| Option # | Option Description | RFC |
| 0 | Hop-by-Hop | 2460 |
| 6 | TCP | 793 |
| 17 | UDP | 768 |
| 43 | Routing | 5095 |
| 44 | Fragmentation | 2460 |
| 50 | ESP | 4303 |
| 51 | AH | 4302 |
| 58 | ICMPv6 | 4443 |
| 59 | No next header | 2460 |
| 60 | Destination options | 2460 |
| 135 | Mobility | 3775 |
IPv6 does not limit the number of extension headers you can use in a single packet.There is however a published “recommended order” as to how the headers should be laid out. The order is:
- IPv6 Header
- Hop-by-Hop Options
- Routing Header
- Fragment Header
- AH
- ESP
- Destination Options
- Mobility Header
- TCP/UDP/ICMPv6
Note this list is “recommended” but not mandatory. An IPv6 host must be able to process the headers in what ever order they were received. This means you will probably find vendors following this list but not attackers. I’ve personally seen devices start acting really odd when you mess with the header order. In fact I’ve run across quite a bit of “IPv6 compatible code” which can’t deal if the preferred order is not used.
Chasing The Protocol Header
So with IPv6 we can have multiple headers behind the IPv6 header. If this sounds like a new concept, it is actually not. In fact you’ve probably worked with it already. When you deploy IPSec the two possible security protocols are ESP and AH. These were actually borrowed from IPv6 and massaged to work on IPv4. Both AH and ESP include a next header field to identify what type of packet they are protecting.This is referred to as protocol chaining, as you effectively have multiple headers sitting behind the layer 3 protocol header.
So to figure out what upper level transport (TCP, UDP, etc.) is being used, you may have to search through multiple headers before you find the answer. This is referred to as “chasing the header“, and tcpdump/Windump give us a filter option to perform this task. You may be fimiliar with the proto filter. In the IPv4 world, if I say:
ip proto tcp
That filter reads “check byte 9 of the IPv4 header and if the value is equal to 6 (protocol value for TCP), match on the packet”. This filter is not as effective in the IPv6 world of course because byte 6 of the IPv6 header might identify the upper layer transport, or it might just identify an optional extension header that is being used. To solve this problem, the protochain filter was introduced. Writing:
ip6 protochain tcp
Reads as “Check byte 6 of the IPv6 header to see if the value is equal to 6. If instead you find a value which identifies an optional extension header, check the extension header’s next header field for a value of 6. If you find more optional extension headers, keep repeating the last test till you find the last extension header”.
Pretty simple to write in English, but this is a nightmare to implement in code. Most optional extension headers are variable in length which just adds to the complexity. I’ve done some testing with Scapy and you can actually see the difference in performance when protochain gets invoked. In fact you could probably do a pretty good job of DoSing an IDS/IPS by forcing it to process a lot of useless extension headers.
Writing our filter
So our first problem in writing the challenge filter is that the ICMPv6 header may not appear right after the IPv6 header. We have to watch out for optional extension headers. In fact RFC 2710 states: “All MLD messages described in this document are sent with a link-local IPv6 Source Address, an IPv6 Hop Limit of 1, and an IPv6 Router Alert option [RTR-ALERT] in a Hop-by-Hop Options header.” This means our multicast listener packet is required to have a Hop-by-Hop extension header with the Router Alert option set. With this in mind, our first check should be:
ip6 protochain icmp6
To ensure we are only looking at ICMPv6 packets. Now it is just a matter of checking to see if the type field (byte 0) is set to 130 (Multicast Listener Query) or 131 (Multicast Listener Report).This brings us to our second problem however. In the IPv4 world I can do a:
icmp[0]= <type value of interest>
If I try this with icmp6 I get:
[root@fubar ~]# tcpdump -nn icmp6[0]=130
tcpdump: IPv6 upper-layer protocol is not supported by proto[x]
In other words, I can’t use offsets with icmp6 to search for specific values. Windump and tcpdump are advertised as IPv6 compatible, but don’t expect to get all the same functionality you have with IPv4. YUCK!
So what do we do? We’ll have to fall back on referencing the value from an ip6 offset. In other words, we’ll have to measure through the IPv6 header, through the required Hop-by-Hop header, and into the ICMPv6 header to see if the type field is set to 130 or 131. Some things to take into consideration:
- IPv6 header is fixed at 40 bytes in size
- Hop-by-Hop header is variable, but 4 bytes with Router Alert set
- The type field is byte 0 within the ICMPv6 header
So here is what we end up with:
ip6 protochain icmp6 and (ip6[44]=130 or ip6[44]=131)
Whew! We finally got it! Or did we?
Q: What happens if the packet has additional extension headers?
A: Our filter will not work.
Q: What if the Hop-by-Hop header has more options set than Router Alert?
A: Our filter will not work.
Q: Can we fix the above two problems?
A: Not till tcpdump/Windump filtering adds IF/THEN loop support.
So if we want to capture normal ML traffic, the above filter will work fine. If however we want to insure we catch attacker trickiness as well, the filter is not going to fly.
What if we try something like this:
tcpdump -nn -s 1500 -x ip6 protochain icmp6 | grep -i multicast > multicast.txt
and then just use Wireshark’s text2cap tool to convert it to Libpcap format? The problem here is we will only get the header info. Grep will match the summary line which contains the word “Multicast” but then skip all the Hex below it which is the actual contents of the packet.
So the final answer is: “Can’t get theya from haya”
So what if you really need to be able to see this traffic? Until support for IPv6 matures, the only 100% method is to grab all ICMPv6 traffic and then manually sort through it.
At least that’s my view on this. If anyone can actually come up with a 100% working solution, I would love to hear it.



