major.io words of wisdom from a systems engineer

Time Warner Road Runner, Linux, and large IPv6 subnets

I’ve written about how to get larger IPv6 subnets from Time Warner Cable’s Road Runner service on a Mikrotik router before, but I’ve converted to using a Linux server as my router for my home. Getting the larger /56 IPv6 subnet is a little tricky and it’s not terribly well documented.

My network

My Linux router has two bridges, br0 and br1, that handle WAN and LAN traffic respectively. This is a fairly simple configuration.

                +-------------------+
                |                   |
+-----------+   |                   |     +----------+
|Cable modem+---+ br0          br1  +-----+LAN switch|
+-----------+   |                   |     +----------+
                |    Linux router   |
                +-------------------+

Ideally, I’d like to have a single address assigned to br0 so that my Linux router can reach IPv6 destinations. I’d also like a /64 assigned to the br1 interface so that I can distribute addresses from that subnet to devices on my LAN.

Getting DHCPv6 working

The wide-dhcpv6 package provides a DHCPv6 client and also takes care of assigning some addresses for you. Installing it is easy with dnf:

dnf install wide-dhcpv6

We will create a new configuration file at /etc/wide-dhcpv6/dhcp6c.conf:

interface br0 {
 send ia-pd 1;
 send ia-na 1;
};

id-assoc na 1 {
};

id-assoc pd 1 {
 prefix ::/56 infinity;
 prefix-interface br0 {
  sla-id 1;
  sla-len 8;
 };
 prefix-interface br1 {
  sla-id 2;
  sla-len 8;
 };
 prefix-interface vlan1 {
  sla-id 3;
  sla-len 8;
 };
};

If this configuration file makes sense to you without explanation, I’m impressed. Let’s break it up into pieces to understand it.

The first section with interface br0 specifies that we want to do our DHCPv6 requests on the br0 interface. The configuration lines inside the curly braces says we want to specify a prefix delegation (the _IAPD DHCPv6 option) and we also want a stateful (SLAAC) address assigned on br0 (the _IANA DHCPv6 option). These are just simple flags that tell the upstream DHCPv6 server that we want to specify a particular prefix size and that we also want a single address (via SLAAC) for our external interface.

The id-assoc na 1 section specifies that we want to accept the default SLAAC address provided by the upstream network device.

The id-assoc pd 1 section gives the upstream DHCPv6 server a hint that we really want a /56 block of IPv6 addresses. The next three sections give our DHCPv6 client an idea of how we want addresses configured on our internal network devices. The three interfaces in each prefix-interface section will receive a different block (noted by the sla-id increasing by one each time). Also, the block size we intend to assign is a /64 (sla-len is 8, which means we knock 8 bits off a /56 and end up with a /64). Don’t change your sla-id after you set it. That will cause the DHCPv6 client to move your /64 address blocks around to a different interface.

Still with me? This stuff is really confusing and documentation is sparse.

Start the DHCPv6 client and ensure it comes up at boot:

systemctl enable dhcp6c
systemctl start dhcp6c

Run ip addr and look for IPv6 blocks configured on each interface. In my case, br0 got a single address, and the other interfaces received unique /64’s.

Telling the LAN about IPv6

The router is working now, but we need to tell our devices on the LAN that we have some IPv6 addresses available. You have different options for this, such as dnsmasq or radvd, but we will use radvd here:

dnf -y install radvd

If you open /etc/radvd.conf, you’ll notice a helpful comment block at the top with a great example configuration. I only want to announce IPv6 on my br1 interface, so I’ll add this configuration block:

interface br1
{
  AdvSendAdvert on;
  MaxRtrAdvInterval 30;

  prefix ::/64
  {
    AdvOnLink on;
    AdvAutonomous on;
    AdvRouterAddr off;
  };
};

You don’t actually need to specify the IPv6 prefix since radvd is smart enough to examine your interface and discover the IPv6 subnet assigned to it. This configuration says we will send router advertisements, let systems on the network choose their own addresses, and we will advertise those addresses as soon as the link comes up.

Let’s start radvd and ensure it comes up at boot:

systemctl enable radvd
systemctl start radvd

Connect a machine to your LAN and you should receive an IPv6 address shortly after the link comes up!

Troubleshooting

If you’re having trouble getting an IPv6 address, double-check your iptables rules. You will need to ensure you’re allowing UDP 546 into your external interface. Here are some examples you can use:

# If you're using firewalld
firewall-cmd --add-port=546/udp
firewall-cmd --add-port=546/udp --permanent
# If you're using bare ip6tables
ip6tables -A INPUT -p udp -m udp --dport 546 -j ACCEPT