This is a follow-up question to Why is the NDP Neighbor Solicitation message sent to the solicited-node address?.

Summary: say I would send an IPv6 packet and put the unicast address of the host I want to know the L2 address of as the destination address. I then put this packet in an Ethernet frame and put the multicast MAC address 33:33:xx:xx:xx:xx as the destination. The receiving host's NIC will accept the frame and send it up for further processing. The kernel will accept or reject the packet based on the (IPv6 unicast) destination address, without needing to look at the ICMPv6 packet contained within. This seems like it would be more efficient. Why doesn't neighbor discovery work like this?

This question was seemingly resolved in the comments: the IPv6 solicited-node multicast address is the basis for the multicast MAC address, therefore the destination field in the IPv6 packet needs to be the solicited-node multicast address. But did the protocol designers decide ND should work like this, or is there a technical necessity for ND to work like this?

(One problem that I can think of with this approach is that routers may route my packet off the link when I request the global unicast address of a device that is not on the same link, which would be unwanted behavior.)

4 Answers 4


The purpose of IPv6 neighbor discovery is to take a known IPv6 address on an attached subnet and obtain the corresponding MAC address. That enables you to encapsulate a L3 IPv6 packet in a L2 frame and send it on the L2 medium.

This is very similar to IPv4 ARP, where you take a known IPv4 address on an attached subnet, and obtain the corresponding MAC address.

The trick is, you need to get your information by sending an L2 frame. You need to have a valid L2 destination address for that frame. But you don't already have the L2 unicast destination for the frame.

Lets look at the IPv4 ARP example:

Here is an IPv4 ARP for from

For reference: = c0a8 000a = c0a8 0013
My ethernet = 685b 3589 0a04

[iMac:~] droot% sudo tcpdump arp -x
tcpdump: data link type PKTAP
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on pktap, link-type PKTAP (Apple DLT_PKTAP), capture size 262144 bytes
18:58:01.528290 ARP, Request who-has tell, length 28
    0x0000:  ffff ffff ffff 685b 3589 0a04 0806 0001
    0x0010:  0800 0604 0001 685b 3589 0a04 c0a8 000a
    0x0020:  0000 0000 0000 c0a8 0013

In the case above, in order to guarantee that the frame would be decoded by the destination, which has an UNKNOWN L2 MAC address, ARP sends the L2 ethernet frame to destination ff.ff.ff.ff.ff.ff, the ethernet broadcast address.

This ARP protocol, designed in 1982 (RFC 826) is inefficient. Every ARP request results in an interrupt on every host on the subnet.

The designers of IPv6 wanted to do better. They wanted only the intended destination to "listen" for the neighbor discovery packet. They would have loved to make the destination of the ethernet frame the IPv6 destination.

One problem: The destination MAC address is 48 bits. The IPv6 address is 128 bits.

Here's an IPv6 neighbor discovery/solicitation request for link-local address fe80::aaaa:aaaa:aaaa:aaaa from fe80::c0e:acdb:30a3:482c

[iMac:~] droot% sudo tcpdump -x -v icmp6 && ip6 == 135
tcpdump: data link type PKTAP
tcpdump: listening on pktap, link-type PKTAP (Apple DLT_PKTAP), capture size 262144 bytes
19:33:58.748554 IP6 (hlim 255, next-header ICMPv6 (58) payload length: 32) imac.local > ff02::1:ffaa:aaaa: [icmp6 sum ok] ICMP6, neighbor solicitation, length 32, who has fe80::aaaa:aaaa:aaaa:aaaa
      source link-address option (1), length 8 (1): 68:5b:35:89:0a:04
    0x0000:  3333 ffaa aaaa 685b 3589 0a04 86dd 6000
    0x0010:  0000 0020 3aff fe80 0000 0000 0000 0c0e
    0x0020:  acdb 30a3 482c ff02 0000 0000 0000 0000
    0x0030:  0001 ffaa aaaa 8700 4cfc 0000 0000 fe80
    0x0040:  0000 0000 0000 aaaa aaaa aaaa aaaa 0101
    0x0050:  685b 3589 0a04

The first improvement is that this is actually an ICMPv6 packet destined for IPv6 multicast address ff02::1:ffaa:aaaa. We don't need to define a separate ARP-like protocol for each layer-2 technology. We use L3-multicast and define a L3-multicast to L2-multicast protocol. IPv6 neighbor discovery uses the ICMPv6 message type 135.

We cannot use the IPv6 unicast destination as the neighbor discovery destination, because we don't know how to encode the 128-bit IPv6 unicast address as a 48-bit ethernet unicast address.

But we can define a rule to pick a specific L3 multicast desination from our L3 unicast destination. From RFC4291 https://www.rfc-editor.org/rfc/rfc4291#section-2.7.1

   Solicited-Node multicast address are computed as a function of a
   node's unicast and anycast addresses.  A Solicited-Node multicast
   address is formed by taking the low-order 24 bits of an address
   (unicast or anycast) and appending those bits to the prefix
   FF02:0:0:0:0:1:FF00::/104 resulting in a multicast address in the




So our IPv6 unicast request for fe80::aaaa:aaaa:aaaa:aaaa becomes an ICMPv6 neighbor solicitation request for ff02::1:ffaa:aaaa which is an IPv6 multicast address.

We have a rule for IPv6 L3 multicast addresses: they can be transmitted on the wire with an L2 destination of 33:33:xx:xx:xx:xx where the last 32 bits of the L3 address are translated into the last 32 bits of the L2 address!

That results in this destination mac address:

fe80::aaaa:aaaa:aaaa:aaaa -> ff02::1:ffaa:aaaa  -> 3333.ffaa.aaaa

What's cool about this is that this does not result in an interrupt for every host on the net. Only the hosts that are listening for L2 multicasts for 3333.ffaa.aaaa. Assuming the last 24 bits are "random" (not true), that would be 1 out of 16 million (2^24) hosts.

There is some complexity, but only because we have some rules:

  1. A rule to translate an IPv6 L3 unicast address into an IPv6 L3 multicast address for neighbor solicitation
  2. A rule to translate an IPv6 L3 multicast address into an L2 multicast address for transmission.

We cannot do this:

fe80::aaaa:aaaa:aaaa:aaaa (IPv6 unicast)
-> fe80::aaaa:aaaa:aaaa:aaaa (ICMPv6 multicast?)
-> ??

We just don't know how to translate the IPv6 unicast address into an L2 address. Since we are going from 128 bits to 48 bits, at some point we have to use a many->one mapping. In IPv4 ARP we use ethernet broadcast (all->one). In IPv6 we use L3 multicast/L2 multicast (many->one).


NDP will populate a table in the source host so that the source host only needs to discover the destination layer-2 address in the beginning, and can thereafter send unicast layer-2 frames to the destination.

If the destination host does not give the source host its unicast layer-2 address (responding to the NDP request), rather than simply processing the IPv6 packet by sending the payload up the network stack, then the source host would always need to use the multicast layer-2 address to send all frames to the destination host (most traffic between hosts consists of many more than one frame). That would be inefficient because a switch would need to forward all the multicast frames to all the switch interfaces, placing traffic on links where it is not needed or wanted.

The efficiency comes because the source only needs to request the destination layer-2 address (which is sent to all the switch links) once, and the rest of the frames it sends can then use the unicast layer-2 destination address (a switch will then only send the frames to the single interface where the destination can be found).

  • Yes, but what if the destination host does return its layer-2 address? The initial ND message could still be "layer-3 unicast, layer-2 multicast", and the destinations could filter better, because the nodes receiving the traffic could now look only at the IPv6 destination address field, without having to inspect the ICMPv6 packet contained. So the destination for which the ND was meant still processes the ND normally: the destination would return its layer-2 address, and further communication between the two hosts would be unicast traffic. Dec 2, 2019 at 14:38
  • "Yes, but what if the destination host does return its layer-2 address?" No, IPv6 simply passes the packet payload to the process indicated in the Next Header field (the value could indicate ICMP, TCP, UDP, etc.). The reason for ICMP is to handle housekeeping for IP. Remember that ICMP may look and act like a higher-layer protocol, but it is an integral part of IP, and every IP implementation is required to have ICMP.
    – Ron Maupin
    Dec 2, 2019 at 14:43
  • @StefanvandenAkker, are you wanting IPv6 to respond to every packet with NDP? That would be very inefficient. ICMPv6 is IPv6. Somehow, IPv6 needs to know when to respond to such a request, and that is the reason it is done that way. One packet, at the beginning, will deal with NDP, then all the following packets do not need to. That is efficient.
    – Ron Maupin
    Dec 2, 2019 at 14:47
  • Sorry, it seems I have a hard time explaining what I mean. Is it correct to say that the hosts receiving the IPv6 NS packet ignore the destination address field in the IPv6 packet and pass the payload on to the ICMPv6 process regardless of the value of that field? I am trying to figure out why we put ff02:0:0:0:0:1:ffxx:xxxx in the destination field and not just fe80::1234 or ::. Dec 2, 2019 at 14:51
  • 1
    Layer-3 uses a multicast address so that layer-2 will use a multicast address. In IPv4, ARP is a separate process from both the layer-2 and layer-3 protocols (it was bolted on as an afterthought). IPv6 has made NDP part of IP. IPv4 could only (without a lot of difficulty) have one address per interface. IPv6 can (and almost certainly does) have multiple addresses per interface. The original SLAAC would have all the addresses for an interface ending in the same 24 bits, so one NDP request could populate the source NDP table with all the addresses assigned to the interface.
    – Ron Maupin
    Dec 2, 2019 at 14:57

The disconnect here is that layer-3 doesn't specify layer-2 information, nor does it necessarily know what layer-2 is involved. So, while it may be logically possible to build a packet (actually frame since we're delving into L2) with unicast L3 information contained within an ethernet frame (presumably) destined to a multicast address, you'd be stepping over a lot of lines and breaking several rules to do it. Any security policies that enforce those rules would break your "innovative optimization".

(Hint: It's not as much of an optimization as you may think. It's only slightly less processing to look at the L3 header to drop, vs. looking at the NS request. Because it's sent to a layer-2 multicast destination, multiple systems may see it when only one wants it. The point of multicast is reduce the footprint from all -- IPv4 ARP broadcast -- to some -- IPv6 ND.)


You can’t send a NDP packet to a defined MAC address (because NDP is used to know what is the MAC address of a device), so you have to use multicast.

Regarding the request of a global unicast address of a device that is not on the same link, it’s also impossible. If it’s not on the same link you will make a lookup in the FIB and then use the provided next-hop (which is on the same link).

  • Maybe that's what I don't understand: can I send a L3 packet with a unicast address to a L2 multicast address? Or should the L3 address type always match the L2 address type (IP multicast always needs to have a MAC multicast address, IP unicast always needs to have a MAC unicast address)? Dec 2, 2019 at 7:15
  • Yes and no ;) Your host will send a multicast packet to discover other hosts, but the reply will be unicast. You can also take a look at this RIPE video explaining NDP youtube.com/watch?v=A3LFt7CHpgs
    – Alarig
    Dec 2, 2019 at 7:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.