# IPv6 NDP proxy

Preface

I moved to Seattle, WA from Ireland in January. Once I got settled I looked for some decent internet provider. I found Wave G really good, especially if you compare to the ISPs in Dublin.

I was really excited to get IPv6 address from ISP rather than tunneling through hurricane electrics. After connecting my new shiny pcengines router I hit a problem where LAN nodes couldn’t properly configure IPv6 address.

Since I found very little resource about this issue in the internet I decided to cover it here. It’s going to primarily be about the simple router configuration with the use of NDP proxy daemon or ndp proxy kernel feature.

IPv6 neighbor discovery protocol

In IPv4 address resolution to MAC would happen via ARP (Address Resolution Protocol) where a host sends the broadcast message (destination ff:ff:ff:ff), host with matching IP would send ARP reply message with it’s hardware address.

In short, IPv4 ARP has some inefficiencies so with the IPv6 we have NDP (neighbor discovery protocol) which instead of broadcast uses the multicast groups. In short, the destination IPv6 address is converted to multicast IPv6 address (ff02::1…) where neighbor solicitation packet is going to be sent, in response the host should get the reply with the MAC address.

This short visual explanation should give you some more insight in solicited node multicast.

Wave G IPv6 configuration via SLAAC

Wave G configures IPv6 clients via SLAAC which I find nice since you don’t need to run dhcpv6 etc. In short the SLAAC IPv6 address is based on your hw addr and the network prefix sent via RA (router advertisement) packet.

More info: https://en.wikipedia.org/wiki/IPv6#Stateless_addressautoconfiguration(SLAAC)

router configuration

My home router configuration is pretty dumb and straightforward:

  • 1x WAN interface
  • 3x LAN interfaces
  • 2x WLAN pci cards

I bridged the the LAN and WLAN interfaces so the configuration is effortless.

$ brctl show
bridge name bridge id       STP enabled interfaces
br0     8000.9e08cd2a9436   no      enp2s0
                            enp3s0
                                                        enp4s0
                            wlp1s0
                            wlp5s0

radvd & ndppd & ndp proxy

Let try to understand the problem. Once I enabled SLAAC on WAN interface I have IPv6 address (!=fe80::) assigned to the interface. This happens after the host receives the RA packet with network prefix and bunch of other information.

Now we need to "forward" (it’s not a right term) the RA downstream to configure (W)LAN interfaces. In order to do it we need to spawn the radvd daemon with example configuration:

# PRO TIP: you can use radvdump to turn RA packet to radvd configuration.

interface br0
{
    AdvSendAdvert on;
    AdvManagedFlag off;
    AdvOtherConfigFlag off;

    prefix 2001:db8:1:2::/64
    {
        AdvOnLink on;
        AdvAutonomous on;
        AdvRouterAddr off;
    };
};

Once radvd is running we should see clients with IPv6 SLAAC IP address. Since the Wave G is not truly routing the subnet to the host we have some troubles. To illustrate this you could run:

# double check we see the router
[client host with slaac] $ ip -6 neigh
fe80::7c08:abff:fe2a:8721 dev wlp4s0 lladdr 7e:08:ab:2a:87:21 router REACHABLE

[client host with slaac] $ ping6 2001:4860:4860::8888
it's going to fail

[router] $ tcpdump -n -i < wan_interface > dst 2001:4860:4860::8888
you see some packets going out 

We see packets going out of interface, yay! But no response?
Right… We configured the IPv6 address but it’s not on the WAN interface which means no-one will answer to the NDP solicitation packets, therefore from ISP perspective the destination IPv6 address is not reachable, so the response gets dropped on the floor.

What if we could proxy the solicit request downstream so that the clients could answer to NDP solicit request?

You can do it in two ways:

  • with kernel proxy ndp option (per IP), example: ip -6 neigh add proxy 2001:db8:1:2:ffff::1 dev eth0
  • with ndp proxy, the benefit is that we don’t need to specify every single IP address by hand

The diagram explains how the setup is going to work.

+-------------------+
|        ISP        |
|  radvd announces  |
| 2001:db8:1:2::/64 |
+---------+---------+
          |
          |
          | eth0
          | addr 2001:db8:1:2::42
          | default route via link-local address
          | ndppd answers to NDP solicitations, so ISP thinks that the whole 2001:db8:1:2::/64 is here
   +------+------+
   |  My router  |
   +------+------+
          | br0
          | radvd announces 2001:db8:1:2::/64
          | route to 2001:db8:1:2::/64
          |
          |
       +--+--+
       | LAN |  3x LAN + 2x WLAN
       +-----+

Example ndppd configuration:

proxy < wan_interface > {
  autowire yes
  rule 2001:db8:1:2::/64  {
    iface br0
  }
}

systemd network

I used systemd network to configure the interfaces. The network setup is very simple so I think it does the job.

$ cat systemd/network/10-wan.network
[Match]
Name=enp4s0

[Network]
DHCP=yes
IPForward=yes
IPv6AcceptRA=yes

$ cat systemd/network/50-br0.network
[Match]
Name=br0

[Network]
Address=192.168.5.1/24
IPForward=yes

$ cat /etc/systemd/network/90-wireless.network
[Match]
Name=w*

[Network]
DHCP=yes
Bridge=br0

Summary

The issue was not so obvious to me at the first glance. With a few commands and a bit of IPv6 knowledge we identified the issue and found the solution to it (NDP proxy).
Now you can blame the network 😛

I am wondering if anyone else hit similar issue?