September 27, 2015
The next topic that we’ll be covering is fairly straightforward (allowing for a quick article and providing some forward momentum on this blog series). Neighbor discovery is a crucial element of host communication on a local network. While it’s not a particularly complex topic, it is a fundamental IPv6 networking concept that should be understood by any administrator of an IPv6 network. This article will build on our previous discussion of Stateless Autoconfiguration in some ways, and it is recommended that you read that article first.
Neighbor Discovery Overview
When a host needs to communicate with another host on the local network, it must resolve the known layer 3 address to the corresponding layer 2 address. In IPv4, this is accomplished with the Address Resolution Protocol (ARP), as defined in RFC 826. IPv6 makes use of the Neighbor Discovery Protocol, as defined in RFC 4861.
Note: Since we are dealing primarily with RFC 4861 in this article, I will omit phrases such as “Section 3 of RFC 4861.” It’s safe to say that we will always be referring to RFC 4861, unless otherwise noted.
Neighbor Discovery offers multiple improvements, discussed in Section 3.1, over the legacy IPv4 protocols that were necessary to accomplish the same functions. Previously, we saw the usefulness of a standardized method for router and prefix discovery using Router Advertisements when we discussed Stateless Autoconfiguration. During this article, we’ll take a look at the application of Neighbor Discovery for communication between two hosts on the same local network.
Our topology is simple: two CentOS 6.7 hosts connected to a switch, forming a single broadcast domain. We allow the hosts to autoconfigure addresses, as previously discussed. We are only using the FE80::/10 link-local address space, as it is sufficient to demonstrate the neighbor discovery features that we will be examining in this article.
Diving into the packets
Now that we understand the topology, let’s take a look at the traffic generated when we execute a ping from Host B (FE80::A00:27FF:FE4D:CD1B) to Host A (FE80::A00:27FF:FE4D:CD1A).
The address resolution process is described in Section 7.2. We can see that this is a fairly simple exchange. Host B sends an ICMPv6 Neighbor Solicitation, and Host A replies with a Neighbor Advertisement. Once layer 3 addresses have been mapped to layer 2 addresses, traffic can flow freely between the two hosts on the LAN. We can see the exchange of ICMPv6 Echo Request and Reply after the Neighbor Solicitation. Let’s take a closer look at this neighbor discovery process at the packet level.
Section 7.2.2 specifies that when a host wishes to send traffic to another host on the local network, it must create a neighbor cache entry in the INCOMPLETE state and transmit a Neighbor Solicitation. Above, we can see the INCOMPLETE state entry created when Host B first attempts to send traffic to Host A. We see the IPv6 address for Host A, but we do not yet see a link-layer address for LAN communication.
The first packet that we see is the Neighbor Solicitation. At layer 2, we can see that the solicitation is sourced from the Ethernet address of Host B. The frame is destined to the solicited-node multicast address for address that we are trying to resolve.
The IPv6 header is similar. The packet is sourced from the IPv6 address of Host B, and destined to the solicited-node multicast address of the IPv6 address that we are trying to resolve. Remember that the solicited-node multicast address does not correspond exactly to the target IPv6 address. The formation of this special address is described in Section 2.7.1 of RFC 4291. The hop limit field is set to 255, which is mandated by Section 7.1.1. A node must discard a Neighbor Solicitation with a hop count less than 255. Doing so guarantees that Neighbor Discovery traffic remains on the local area network, and is not routed.
Next, we come to the actual ICMPv6 header. The message is of type 135, which indicates Neighbor Solicitation as specified by Section 4.3. The target address is the layer 3 address that the host is trying to resolve, which is Host A in our example. There is only one ICMPv6 Option allowed for a Neighbor Solicitation: the source link-layer address. We can see that this has been populated with the MAC address of Host B.
According to Section 7.2.3, the receiving host of a Neighbor Solicitation SHOULD create a STALE entry in the neighbor cache for the sender of the solicitation. I was unable to recreate this behavior, as seen in the screenshots above. I continuously polled the neighbor table on Host A for a STALE entry while executing a ping from Host B. I was never able to see a STALE entry. However, Host A did create a DELAY entry for Host B until traffic was successfully sent, as described in Section 7.3.3.
Upon receipt of the Neighbor Solicitation, Host A responds with an ICMPv6 Neighbor Advertisement. At the Ethernet layer, we can see that it’s a unicast frame from the Host A to Host B. The same is true of layer 3. We can see this is a unicast IPv6 packet sent from Host A to Host B. Notice that this contrasts with the Neighbor Advertisement, which was sent to the solicited-node multicast address for the target.
Next we come to the ICMPv6 header. The behavior for a Neighbor Advertisement is specified in Section 7.2.4. We can see that the header is of type 136, which is used for Neighbor Advertisements. There are two flags set: the solicited flag and the override flag. The solicited flag is set because the source of the Neighbor Solicitation was not the unspecified address. Rather, the source of the solicitation was Host B, and we know that Host B is trying to resolve the address of Host A. The override flag should set because the target address is not an anycast address or a unicast address for which the target node is performing proxy service. We can also see that the target address is the same as the target address in the Neighbor Solicitation: the address of Host A.
Finally, we see that the target link-layer address option is also provided as the MAC address of Host A. The target link-layer address must be included in the Neighbor Advertisement when the Neighbor Solicitation was sent to a multicast address. This allows for address resolution: if the Neighbor Solicitation was sent to a multicast address (such as the solicited-node multicast address), then the soliciting host must not know the link-layer address of the neighbor and will need it explicitly provided. This is the entire purpose of the Neighbor Discovery exchange.
Above, we can see that the address resolution process was successful. In addition to the successful ICMPv6 Echo traffic seen earlier in the complete trace, we can see that both Host B and Host A now have neighbor cache entries for the link-layer address of each other.
That’s it! These two hosts have successfully completed the neighbor discovery process, and LAN traffic can now flow freely between them.
The address resolution process in IPv6 is definitely a bit foreign to those who have become used to the Address Resolution Protocol (ARP) of IPv4. However, even a brief analysis of the new protocol indicates similarities between its IPv4 counterpart. While these similarities allow us to develop a quick understanding of the fairly basic task of address resolution, the use of ICMPv6 allows for a more robust protocol that keeps to IPv6 design goals: no broadcast traffic, a more clearly defined protocol, and room for future protocol expansion via additional ICMPv6 options. While address resolution is certainly a fairly simple task, having a packet-level understanding can be helpful information for any administrator, especially when the need to troubleshoot problems on the LAN arises.