Routing IPv6

I am in the process of building a Linux router on top of Alpine Linux.
Routing (and NAT) of IPv4 works flawlessly, but I cannot get IPv6 routing to work.

The router has two network interfaces:

  • eth1 is connected to my cable modem.
  • eth0 is connected to the LAN.

On eth1 I get a /64 IPv6 prefix from my ISP:

3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.240/24 brd 192.168.0.255 scope global eth1
       valid_lft forever preferred_lft forever
    inet6 2a02:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/64 scope global dynamic mngtmpaddr 
       valid_lft 4526sec preferred_lft 1826sec
    inet6 fe80::xxxx:xxxx:xxxx:xxxx/64 scope link 
       valid_lft forever preferred_lft forever

On eth0 initially I don’t have an IPv6 address (apart from the link-local one), but I set done manually using the prefix from eth1:

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.1.1/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 2a02:xxxx:xxxx:xxxx::1/64 scope global 
       valid_lft 86395sec preferred_lft 14395sec
    inet6 fe80::xxxx:xxxx:xxxx:xxxx/64 scope link 
       valid_lft forever preferred_lft forever

I also installed radvd to send router advertisement to the LAN using the following configuration:

interface eth0
{
    AdvSendAdvert on;
    MaxRtrAdvInterval 10;
    prefix ::/64
    {
        AdvOnLink on;
        AdvAutonomous on;
        AdvRouterAddr on;
    };
};

And set net.ipv6.conf.eth1.accept_ra=2 using sysctl so I don’t loose the IPv6 prefix I get from my ISP.

Using this configuration the computers on the LAN get an IPv6 address through stateless autoconfig, e.g. on my PC:

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.205/24 brd 192.168.1.255 scope global br0
       valid_lft forever preferred_lft forever
    inet6 2a02:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/64 scope global temporary dynamic 
       valid_lft 86393sec preferred_lft 14393sec
    inet6 2a02:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/64 scope global dynamic mngtmpaddr 
       valid_lft 86393sec preferred_lft 14393sec
    inet6 fe80::xxxx:xxxx:xxxx:xxxx/64 scope link 
       valid_lft forever preferred_lft forever

(This one even has two.)

But I still cannot use IPv6. Neither can I access any IPv6 enabled websites nor can I ping any IPv6 enabled servers. Using tshark on the router I can see that the ping is being transmitted through eth1, but instead of getting a ping reply, I receive a neighbor solicitation (prtobably from my ISP’s router), which isn’t forwarted to my PC:

9988 48.040945071 2a02:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:d821 → 2a00:1450:4001:81d::200e ICMPv6 118 Echo (ping) request id=0xc7dc, seq=1, hop limit=63
9989 48.063201702 fe80::de53:7cff:fe0e:4812 → ff02::1:xxxx:d821 ICMPv6 86 Neighbor Solicitation for 2a02:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:d821 from dc:53:7c:0e:48:12 

My routes are looking like this (I didn’t change anything manually):

2a02:xxxx:xxxxx:xxxx::/64 dev eth0 proto kernel metric 256 expires 86391sec pref medium
2a02:xxxx:xxxx:xxxx::/64 dev eth1 proto kernel metric 256 expires 4173sec pref medium
fe80::/64 dev eth0 proto kernel metric 256 pref medium
fe80::/64 dev eth1 proto kernel metric 256 pref medium
default via fe80::xxxx:xxxx:xxxx:xxxx dev eth1 proto ra metric 1024 expires 1529sec hoplimit 64 pref high

What am I missing?

1 Like

There are at least a couple things.

First, if you have forwarding enabled (/proc/sys/net/ipv6/conf/…/forwarding = 1), and you should since you want to use this host as a router, then you want to set accept_ra = 2 on your WAN interface so your WAN interface can get its default route from your ISP.

Second, you don’t use radvd to broadcast router advertisements unless you used DHCPv6 / PD to get an ipv6 subnet from your ISP. If you just have 1 subnet on your LAN, as long as your firewall doesn’t block your ISP’s router advertisements, hosts on your LAN will autoconfigure from your ISP’s router advertisements.

If you want to broadcast your own advertisements, you need to use DHCPv6 to request at least a /64 subnet from your ISP.

The latest version ISC’s dhclient has a command line flag that allows you to request a subnet of /64 /60 or /56.

1 Like

Thanks for your advice!

I have removed radvd and set forwarding=1 and accept_ra=2 on my WAN interface (eth1). Unfortunately, the router neither forwards the Router Advertisements from the WAN to the LAN nor the Router Solicitations from the LAN to the WAN.

Setting both options on both interfaces also doesn’t change anything.

Unfortunately, I can only get the /64 subnet via SLAAC from my cable modem.

Maybe the /64 prefix is only a “link prefix” instead of a “routed prefix”. Would there be a way to work around that? I mean there must be a way to use all of those 2⁶⁴ addresses. :wink:

Maybe the /64 prefix is only a “link prefix” instead of a “routed prefix”.

That’s correct, the /64 in your eth0 address is just the netmask on your address. You have not been delegated that entire subnet. (That part is identical to ipv4 just with more bits. When you get an IPv4 address on your WAN that probably has the netmask 255.255.255.0 or /24, that doesn’t mean you have been delegated the remaing X.X.X.1 - 255 addresses) In order for your ISP to route a subnet to you, regardless of its size, you need to use prefix delegation.

Unfortunately, I can only get the /64 subnet via SLAAC from my cable modem.

I don’t understand that. What ISP do you use that doesn’t respond to prefix delegation requests?

Would there be a way to work around that?

Of course, you do have 1 ipv6 address (Assuming you have 1 working address. You may want to try ping6 -c 2 -I eth1 google.com to make sure that is working). You just need to SNAT all ipv6 requests from your LAN to your WAN interface’s 1 ipv6 address.

In that case just advertise an internal address, something like fd00:8000::/64 on your lan interface (If you manually add the internal address to eth0 with ip -6 address add fd00:8000::1/64 dev eth0 it should add the correct route in your routing table).

And ip6tables -t nat -A POSTROUTING -s fd00:8000::/64 -o eth1 -j SNAT --to-source (your eth1 ipv6 address).

You could continue to use your ISP’s global /64 subnet as long as you are SNATing everything before it leaves your router, but using link-local address will make it obvious at a later time that there needs to be an SNAT rule on your router.

Technically your hosts can work with a single IPv6 (/128) prefix or a shorter prefix than /64 each, … but that doesn’t work well, or seamlessly with various clients. You’re supposed to give each host a /64, and have a /56 (or a /48) on your router that you can partition into approx 256 (or 64k) prefixes for each lan host.

If your ISP doesn’t give you a prefix bigger than /64, you can still probably ask for multiple /64 while keeping your router/firewall, using a bunch of techniques called ND proxying, or RD proxying, or ND relaying. Openwrt, for example, supports this using odhcpcd, which should be something you can easily build for Alpine if you wanted, here’s a doc in their git repo.

See: https://git.openwrt.org/?p=project/odhcpd.git;a=blob_plain;f=README;hb=HEAD

Who is your ISP?