Disclosure up front: I run IPYE, a company that leases IP space. It gets one short paragraph at the end; the rest is plain networking.
When an IPv6 problem is not obvious from logs, a packet capture usually settles it: is the Router Advertisement arriving, is the Neighbor Solicitation answered, does the handshake complete, is something sending ICMPv6 errors? The IPv6 filter syntax has a few differences from IPv4 that cause silent mistakes. This article gives recipes for tcpdump (which uses the libpcap filter language) and for Wireshark display filters, and explains the limitation that catches most people. Examples use 2001:db8:100::25, 2001:db8:100::/48, and eth0.
Two different filter languages
Both tools have filters, and they are not the same:
- A capture filter (tcpdump's expression, or Wireshark's capture filter box) uses BPF syntax and decides what is recorded at all.
- A display filter (Wireshark) works on dissected packets and decides what is shown from a capture.
Capture filters are efficient but crude. Display filters are expressive but need the packets to have been captured first. A typical workflow is a broad capture filter, then a display filter in Wireshark to narrow things down. Quote tcpdump expressions in the shell, because (, ), and ! have meaning there.
Basic tcpdump options
tcpdump -n -i eth0 -vv 'ip6'
-
-nstops name resolution, which avoids delays and extra DNS queries that pollute the capture. -
-i anycaptures on all interfaces; it uses a different link-layer header, butip6filters still work. -
-vvprints more detail, including hop limit. -
-w file.pcapwrites a capture file for Wireshark.
Addresses and prefixes
host 2001:db8:100::25
src host 2001:db8:100::25
net 2001:db8:100::/48
ip6 and not host 2001:db8:100::25
host matches either direction, net takes a prefix, and the ip6 primitive restricts the filter to IPv6.
Protocols and ports
tcp port 443, udp port 53, and icmp6 all work for IPv6. icmp6 is a shorthand for IPv6 packets with next header 58. Port filters apply to both IPv4 and IPv6 unless you add ip6 or ip, so write ip6 and tcp port 443 when you want IPv6 only.
The extension header caveat
This is the limitation that matters. In libpcap's filter language, ip6 proto 6 and similar expressions look only at the Next Header field in the fixed IPv6 header. If a packet has a Hop-by-Hop, Routing, or Fragment extension header, the next header is that extension, so a filter for TCP will not match it. Similarly, filters for ports or ICMPv6 types read at fixed offsets and assume the upper-layer header follows the 40-byte fixed header immediately. For ordinary traffic this works. For packets with extension headers it can silently miss. When a capture seems to be missing traffic you know exists, widen the filter, or capture everything for a short period and filter in Wireshark, whose dissector follows the extension header chain.
There is also a primitive for chains: ip6 protochain 44 matches packets that have a Fragment header anywhere in the chain.
ICMPv6 and neighbor discovery
The ICMPv6 type is the first byte after the 40-byte header, so with the no-extension-header assumption you can match it at offset 40:
| Goal | Filter |
|---|---|
| Router Solicitation (type 133) | icmp6 and ip6[40] == 133 |
| Router Advertisement (134) | icmp6 and ip6[40] == 134 |
| Neighbor Solicitation (135) | icmp6 and ip6[40] == 135 |
| Neighbor Advertisement (136) | icmp6 and ip6[40] == 136 |
| All neighbor discovery (133 to 136) | icmp6 and ip6[40] >= 133 and ip6[40] <= 136 |
| Packet Too Big (type 2) | icmp6 and ip6[40] == 2 |
| Echo request and reply (128, 129) | icmp6 and (ip6[40] == 128 or ip6[40] == 129) |
Neighbor discovery messages must be sent with a hop limit of 255, and receivers reject anything else, which stops off-link senders from spoofing them. The hop limit is the byte at offset 7, so icmp6 and ip6[7] == 255 and ip6[40] >= 133 and ip6[40] <= 136 shows only well-formed discovery packets, and a capture with ip6[7] != 255 on the same types shows suspicious ones. For multicast, ip6 multicast matches any IPv6 multicast destination.
Useful checks with these:
- Is SLAAC working? Capture type 134 on a client interface. If none arrive, the problem is upstream of the host: router, switch, or MLD snooping.
- Is a neighbour answering? Capture types 135 and 136 for the target address and look for the advertisement.
- Path MTU trouble? Capture type 2 to see whether Packet Too Big reaches the sender. Absence in the face of black-holed large packets suggests filtering, as described in the earlier MTU and ICMPv6 articles.
TCP handshake and flags
For a SYN on IPv6 without extension headers, the TCP flags byte is at offset 53 (40 plus 13 in the TCP header):
ip6 and tcp and (ip6[53] & 0x02 != 0)
Behaviour of protocol-relative expressions such as tcp[13] with IPv6 has varied between libpcap versions, so the explicit offset is more predictable, and you should verify any filter against known traffic first.
Fragments
ip6 protochain 44 matches fragmented packets. IPv6 fragmentation is done only by the source, so fragments you see came from the endpoint or from a tunnel entry point, and a flow of fragments often points to an MTU problem.
Wireshark display filters
Display filters use field names:
-
ipv6.addr == 2001:db8:100::25,ipv6.src, andipv6.dstfor addresses. -
icmpv6for all ICMPv6, andicmpv6.type == 135for a specific type. -
ipv6.hlim == 255oripv6.hlim < 64for hop limit. -
ipv6.nxt == 44for packets whose fixed header points to a fragment header. -
tcp.flags.syn == 1 && tcp.flags.ack == 0for new connections, regardless of address family. -
ipv6 && !icmpv6to remove ICMPv6 noise while looking at application traffic.
Summary
Use capture filters to limit what you record and display filters to explore it. Remember that libpcap IPv6 offsets assume no extension headers, check the hop limit of 255 for neighbor discovery, and test a new filter against traffic you know exists before trusting an empty result.
About IPYE
IPYE (https://my.ipye.com) leases IPv4, IPv6, and ASN space; a /48 IPv6 block is free for the first year. LOA, ROA, rDNS, and WHOIS are handled by IPYE. Locations: Los Angeles, New York, Ashburn, Salt Lake City, London, Amsterdam, and Frankfurt. Questions: ipye@turnel.com.
Disclosure: I run IPYE
Top comments (0)