Wednesday, January 1, 2020

Pinging all IPv6 nodes on a link

A series of blog articles on troubleshooting tips and tricks using IPv6 ping (ICMPv6 Echo Request/Echo Reply).

Note that I'm using Linux (specifically Fedora 31), however the ping command syntax for other operating systems should hopefully be quite similar.

Pinging all IPv6 nodes on a link


The first and most simple tip is to ping all IPv6 nodes on a link.

IPv6 only uses multicast addresses for all types of one to many communication. There is no IPv6 address that is designated as a "broadcast" address. This is unlike IPv4, where there are a number of types of broadcast addresses, such as the "limited broadcast" address of 255.255.255.255 [RFC1122].

However there is an "all-nodes" IPv6 multicast address, so we'll use that to ping all IPv6 nodes on the link. (A "broadcast" address is really just a specially named multicast address that happens to be a multicast group that includes all nodes. For example, notice how the "group" or multicast address bit is switched on in Ethernet link-layer broadcast addresses.)

The all-nodes IPv6 multicast address, for a link, is ff02::1. The ff designates an IPv6 multicast address. The following 0 is a flag nibble, with no flag bits set.

The following 2 is specifying a link multicast "scope". Unlike IPv4 multicast addresses, IPv6 multicast addresses have a scope. This scope value indicates the portion of the network over which the multicast packet is allowed to be forwarded. Once the packet reaches the boundary of the specified scope, the packet is to be discarded, regardless of if its Hop Count field is non-zero. Of course, if the Hop Count reaches zero before reaching the indicated multicast scope boundary, it is also discarded immediately. Here's the full list of IPv6 multicast scopes.

Finally, the ::1 is specifying the all-nodes multicast group.

One thing to notice about the ff02::1 address is that it is ambiguous. On an IPv6 node with multiple interfaces, such as a router or multi-homed host, there is nothing in the ff02::1 address to specify which interface to send the ICMPv6 Echo Requests out, or to expect to receive ICMPv6 Echo Responses when they come in. ff02::1 is valid and can be used on any of the interfaces and links attached to the multi-interface node.

So when we ping all IPv6 nodes on a link, we need to somehow also tell the IPv6 ping utility which interface to use.


Specifying Interfaces - Command Line Parameter


As we've seen, the all-nodes multicast address we want to useff02::1 - does not provide any information as to which interface to send and receive the ICMPv6 Echo Request and Reply packets on.

So how to do we specify an interface to use when using either link scope multicast addresses, or Link-Local unicast addresses?

The first and most obvious way would be to provide it as a parameter to the application we're using. 

For the ping utility, we provide it via the -I option.

[mark@opy ~]$ ping -w 1 -I enp3s2 ff02::1
ping: Warning: source address might be selected on device other than: enp3s2
PING ff02::1(ff02::1) from :: enp3s2: 56 data bytes
64 bytes from fe80::1d36:1fff:fefd:82be%enp3s2: icmp_seq=1 ttl=64 time=0.438 ms
64 bytes from fe80::f31c:ccff:fe26:a6d9%enp3s2: icmp_seq=1 ttl=64 time=0.589 ms (DUP!)
64 bytes from fe80::7e31:f5ff:fe1b:9fdb%enp3s2: icmp_seq=1 ttl=64 time=5.15 ms (DUP!)
64 bytes from fe80::f7f8:15ff:fe6f:be6e%enp3s2: icmp_seq=1 ttl=64 time=58.0 ms (DUP!)
64 bytes from fe80::877d:4ff:fe1a:b881%enp3s2: icmp_seq=1 ttl=64 time=62.3 ms (DUP!)
64 bytes from fe80::877d:4ff:fe1a:ad79%enp3s2: icmp_seq=1 ttl=64 time=62.8 ms (DUP!)

--- ff02::1 ping statistics ---
1 packets transmitted, 1 received, +5 duplicates, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.438/31.544/62.786/29.566 ms
[mark@opy ~]$

From this all-nodes multicast ping, we've received responses from a total of 6 IPv6 nodes. The responses have come from the nodes' Link-Local IPv6 addresses, starting with the fe80::/10 prefix.

To prevent ping continuing to endlessly send ICMPv6 Echo Requests until we interrupt it, we would normally specify a number of packets to send via the -c option. However, this also stops ping from accepting and displaying more than one ICMPv6 Echo Response when sending a multicast ICMPv6 Echo Request. Instead, we've used the -w parameter to specify that ping should terminate after 1 second, regardless of how many ICMPv6 Echo Requests or Echo Responses have been sent or received.

One other thing to notice is the (DUP!) output on the second and subsequent responses. These packets are identified as duplicates of the response because they have the same ICMP sequence value as the single ICMPv6 Echo Requests that was sent in the first place. They're occurring because the multicast ICMPv6 Echo Request results in multiple individual unicast responses. The number of duplicates is also reported in the statistics summary.

Specifying Interfaces - Zone ID


The other way we can provide the interface to use is as part of the IPv6 address parameter.

We can see an example of this in the ping output, where the addresses of the responding IPv6 nodes also has a %enp3s2 suffix e.g.:

64 bytes from fe80::1d36:1fff:fefd:82be%enp3s2: icmp_seq=1 ttl=64 time=0.438 ms

This way of specifying interfaces is formally described in [RFC4007], "IPv6 Scoped Address Architecture". Although they're commonly the operating system's name for an interface, they're actually specifying something more general - a "zone" or "scope zone".

The reason for having more general zones or scope zones is that as [RFC4007] mentions, an IPv6 node could have multiple different IPv6 interfaces connected to the same link. These interfaces are members of the same zone.

It should be possible to group multiple interfaces within a zone under the operating system; I don't currently know if that is possible under Linux or how to do so.

Using the %<zone_id> suffix, we can drop the -I ping command line parameter.

[mark@opy ~]$ ping -w 1 ff02::1%enp3s2
PING ff02::1%enp3s2(ff02::1%enp3s2) 56 data bytes
64 bytes from fe80::2392:6213:a15b:66ff%enp3s2: icmp_seq=1 ttl=64 time=0.106 ms
64 bytes from fe80::1d36:1fff:fefd:82be%enp3s2: icmp_seq=1 ttl=64 time=0.453 ms (DUP!)
64 bytes from fe80::f31c:ccff:fe26:a6d9%enp3s2: icmp_seq=1 ttl=64 time=0.606 ms (DUP!)
64 bytes from fe80::7e31:f5ff:fe1b:9fdb%enp3s2: icmp_seq=1 ttl=64 time=6.23 ms (DUP!)
64 bytes from fe80::f7f8:15ff:fe6f:be6e%enp3s2: icmp_seq=1 ttl=64 time=157 ms (DUP!)
64 bytes from fe80::877d:4ff:fe1a:ad79%enp3s2: icmp_seq=1 ttl=64 time=159 ms (DUP!)
64 bytes from fe80::877d:4ff:fe1a:b881%enp3s2: icmp_seq=1 ttl=64 time=161 ms (DUP!)
64 bytes from fe80::23d:e8ff:feec:958c%enp3s2: icmp_seq=1 ttl=64 time=179 ms (DUP!)

--- ff02::1%enp3s2 ping statistics ---
1 packets transmitted, 1 received, +7 duplicates, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.106/82.858/179.216/81.281 ms

[mark@opy ~]$


Link-Local Address Responses


From this all-nodes multicast ping we've received a total of 6 unique responses.

These responses have come from the IPv6 nodes' Link-Local unicast addresses. For example, here is the first response:

64 bytes from fe80::2392:6213:a15b:66ff%enp3s2: icmp_seq=1 ttl=64 time=0.106 ms

IPv6 unicast Link-Local addresses are required on all IPv6 enabled interfaces [RFC4291], "IP Version 6 Addressing Architecture". The reason for this is so that an IPv6 node always and automatically has an IPv6 unicast address that it can use to at least communicate with other nodes on its directly attached links. This includes communicating with other hosts' applications over the hosts' Link-Local addresses.

This simplifies design and implementation of protocols such as IPv6 Neighbor Discovery and OSPFv3. It also allows end-user applications on hosts to communicate on a link without requiring any other supporting IPv6 infrastructure on the link. There is no need for an IPv6 router or an DHCPv6 server on the link for attached IPv6 hosts to directly communicate.

Link-Local addresses start with a 10 bit prefix of fe80, followed by 54 zero bits, and then the 64 bit Interface Identifier (IID). In the above first response, 2392:6213:a15b:66ff is the 64 bit IID.


Looped Multicast


By default, multicast packets are looped back internally to the host that is sending them. This occurs for both IPv6 and IPv4 multicasts.

The reason for this default is that when sending multicast packets, there may also be a local multicast listening application running on the sending host itself, as well as somewhere on the network. This local application should also receive the multicast packets.

We can see this multicast local looping in our ping output:

[mark@opy ~]$ ping -w 1 ff02::1%enp3s2
PING ff02::1%enp3s2(ff02::1%enp3s2) 56 data bytes
64 bytes from fe80::2392:6213:a15b:66ff%enp3s2: icmp_seq=1 ttl=64 time=0.106 ms
64 bytes from fe80::1d36:1fff:fefd:82be%enp3s2: icmp_seq=1 ttl=64 time=0.453 ms (DUP!)
...

The first response and the fastest one (0.106 ms compared to 0.453 ms) is from the Link-Local address configured on the enp3s2 interface itself.

[mark@opy ~]$ ip addr show dev enp3s2 | grep fe80
    inet6 fe80::2392:6213:a15b:66ff/64 scope link noprefixroute 
[mark@opy ~]$

The ping utility provides a way to suppress local loopback of multicast, using the -L parameter. If we send an all-nodes multicast ping with this flag, then the responses are limited to remote nodes. We don't get a response from the sending interface's Link-Local address.

[mark@opy ~]$ ping -L -w 1 ff02::1%enp3s2
PING ff02::1%enp3s2(ff02::1%enp3s2) 56 data bytes
64 bytes from fe80::1d36:1fff:fefd:82be%enp3s2: icmp_seq=1 ttl=64 time=0.383 ms

64 bytes from fe80::f31c:ccff:fe26:a6d9%enp3s2: icmp_seq=1 ttl=64 time=0.467 ms (DUP!)
...


Pinging Link-Local Addresses


As you might guess, Link-Local unicast addresses by themselves also don't provide enough information to indicate which interface to use to reach them. As with our all-nodes multicast ping, we also need to supply either an interface as a ping command line parameter, or a zone ID with the address when pinging Link-Local addresses.

This time around we can use -c to limit the number of packets and responses ping sends and receives, since we're doing a unicast ping.

[mark@opy ~]$ ping -c 1 fe80::f31c:ccff:fe26:a6d9%enp3s2

PING fe80::f31c:ccff:fe26:a6d9%enp3s2(fe80::fad1:11ff:feb7:3704%enp3s2) 56 data bytes
64 bytes from fe80::f31c:ccff:fe26:a6d9%enp3s2: icmp_seq=1 ttl=64 time=0.395 ms

--- fe80::f31c:ccff:fe26:a6d9%enp3s2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.395/0.395/0.395/0.000 ms
[mark@opy ~]$

Pinging (all) other IPv6 Addresses?


In this article we've seen how to ping all IPv6 nodes on a link, using the all-nodes IPv6 multicast address of ff02::1. We've also seen how to specify which interface to use with the all-nodes IPv6 multicast address, as the address by itself cannot provide that information. We used either a ping command line parameter, or specified the interface via the %<zone_id> suffix.

We then learned about Link-Local unicast addresses, which are the addresses being used for the responses to the all-nodes multicast ICMPv6 Echo Requests.

We also saw how multicast packets are looped back into the sending node by default, and how to switch that off for the ping utility.

Finally, we pinged a single Link-Local address, using the %<zone_id> suffix, as Link-Local addresses by themselves also don't provide outgoing interface information.

So what about ping all other nodes and receiving their Global Unicast Addresses (GUAs) (i.e. their public Internet addresses) or their Unique Local Unicast Addresses (ULAs)? We'll cover that in the next blog article.

2 comments:

  1. Hey Mark,

    This is pretty cool, I'll keep it handy for future reference.

    I've had a quick look and on OSX there are a few differences, I can't find a way to limit by time directly, but you can set a long interval then just ctrl-C to exit. You also have to specify ping6.

    The command to ping all for OSX that I found works well (insert your own interface ID of course, mine is en7) is:
    ping6 -i 10 -I en7 ff02::1

    This will wait 10 seconds before sending the second, giving you lots of time to get the responses and still cancel it.

    The following link local pings work fine with the %interface/group on them, as long as you also use ping6 there :)

    ReplyDelete
  2. Hi Mark,

    Also found this to be really nifty and will be very useful going forward. Digging into the Windows side here is a little more tricky than *nix/macOS though, because the ping command there is a bit limited and from what I can tell cannot seem to differentiate between multiple responses to a single echo request. As a result I could only ever get one ping response.

    After trying Windows Subsystem for Linux, I was able to use a Linux WSL shell to execute a ping command and get the expected results.

    GOTCHA ALERT: Windows Firewall will block echo reply packets unless they are from the exact destination they were sent to. So regardless if it is an IPv4 broadcast or IPv6 Multicast echo request, no reply will be received as the packets will be dropped by the firewall unless an exception is created on the inbound rules. I created temporary exceptions, one each for IPv4 and IPv6. To make these, a new exception needs to be created with the protocol type being ICMPv[4/6], and if you are using the GUI you can Customize Internet Control Message Protocol (ICMP) Settings. Choose Specific ICMP Types, and as echo reply is not listed as an option, you will need to choose 0 for ICMPv4, or 129 for ICMPv6, and the code for both is 0. Probably a good idea to disable the rules when you are done, but that should let more than just stateful echo replys back in, and allow the above to work.

    ReplyDelete

The Stateful DHCPv6 Myth. No, it doesn't record IPv6 addresses in use. Neither does DHCPv4.

A false sense of security is worse than no security at all; at least with no security you know you don't have any. For many years, peopl...