Linux, IPv6 and AT&T

If you have a Linux router and AT&T Gigabit fiber, you may want to have all your VLANs addressed with IPv6. This is assuming you set up the AT&T modem to do IP passthrough, so you have an actual IPv4 address in your router.

WAN Setup

First and foremost, your router needs to speak IPv6 to the world. You need to accept AT&Ts router advertisement, so you can set up your IPv6 address using SLAAC.

echo 2 > /proc/sys/net/ipv6/conf/att0/accept_ra

As you want to be a router, you need to enable forwarding for both v4 and v6:

echo 1 > /proc/sys/net/ipv4/ip_forward
echo 1 > /proc/sys/net/ipv6/conf/all/forwarding

I named the WAN interface att0 and the LAN interface home0.

You should see something like this:

$ ip addr show att0
2: att0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:11:22:33:44:55 brd ff:ff:ff:ff:ff:ff
    inet 99.1.2.3/22 brd 99.1.3.255 scope global dynamic att0
       valid_lft 2755sec preferred_lft 2755sec
    inet6 2600:1:2:3::45/128 scope global
       valid_lft forever preferred_lft forever
    inet6 2600:4:5:6:7:8:9:10/64 scope global dynamic mngtmpaddr
       valid_lft 3350sec preferred_lft 3350sec
    inet6 fe80::1:2:3:4/64 scope link
       valid_lft forever preferred_lft forever

With everything working, a ping6 to Google should work:

$ ping6 -n -c 1 google.com
PING google.com(2607:f8b0:4005:802::200e) 56 data bytes
64 bytes from 2607:f8b0:4005:802::200e: icmp_seq=1 ttl=117 time=6.04 ms

--- google.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 6.041/6.041/6.041/0.000 ms

LAN Setup

To request prefixes for your internal VLANs, you will ask a prefix delegation (PD) via DHCPv6. Some ISPs, like Comcast, allow you to request a /60 block, so you can chop it yourself. AT&T doesn’t allow that, but they do allow requesting multiple /64s, which has the same effect at the end.

On Debian/Ubuntu, install the package wide-dhcpv6-client.

Your /etc/wide-dhcpv6/dhcp6c.conf will look like this:

interface att0 {
  send ia-pd 0;
  send ia-pd 1;
  send ia-pd 2;
  send ia-pd 3;
  send ia-na 0;
  send rapid-commit;
};

id-assoc pd 0 {
  prefix ::/64 infinity;

  prefix-interface home0.2 {
    sla-len 0;
    sla-id 0;
  };
};

id-assoc pd 1 {
  prefix ::/64 infinity;

  prefix-interface home0.3 {
    sla-len 0;
    sla-id 0;
  };
};

id-assoc pd 2 {
  prefix ::/64 infinity;

  prefix-interface home0.4 {
    sla-len 0;
    sla-id 0;
  };
};

id-assoc pd 3 {
  prefix ::/64 infinity;

  prefix-interface home0 {
    sla-len 0;
    sla-id 0;
  };
};

id-assoc na {
};

As you can see, I use send ia-pd multiple times with a different ID. Then later I associate each PD to a different interface. The order doesn’t matter.

If everything worked, ip -6 addr should show all interfaces addressed correctly.

Addressing your clients

So far, your internal clients have no idea your network can speak IPv6. You need to advertise your prefixes to them. That’s a job for the package radvd.

Your /etc/radvd.conf will look like this:

interface home0.2
{
   AdvSendAdvert on;
   prefix ::/64
   {
      AdvValidLifetime 900;
      AdvPreferredLifetime 900;
   };
   RDNSS 2001:4860:4860::8888 2001:4860:4860::8844 {};
};

interface home0.3
{
   AdvSendAdvert on;
   prefix ::/64
   {
      AdvValidLifetime 900;
      AdvPreferredLifetime 900;
   };
   RDNSS 2001:4860:4860::8888 2001:4860:4860::8844 {};
};

interface home0.4
{
   AdvSendAdvert on;
   prefix ::/64
   {
      AdvValidLifetime 900;
      AdvPreferredLifetime 900;
   };
   RDNSS 2001:4860:4860::8888 2001:4860:4860::8844 {};
};

interface home0
{
   AdvSendAdvert on;
   prefix ::/64
   {
      AdvValidLifetime 900;
      AdvPreferredLifetime 900;
   };
   RDNSS 2001:4860:4860::8888 2001:4860:4860::8844 {};
};

You pretty much repeat the same thing over and over for each interface. RDNSS in my example is announcing the Google’s DNS servers, which are the IPv6 version of 8.8.8.8 and 8.8.4.4.

Firewall

Do not filter ICMPv6 on your input and forward chains, unless you know what you are doing. ICMP is a crucial part of IPv6.

For DHCPv6 work, you need to accept UDP datagrams on ports 546 and 547.

Security

Once you address all your internal clients, they will all have a valid IPv6 address and are able to receive traffic directly from the Internet. You probably don’t want that, so you need to set up some firewall to prevent undesired incoming traffic.

This is a simple example with NFTables, only showing the forward chain:

table inet filter {
  chain forward {
    type filter hook forward priority 0; policy drop;

    ct state related,established accept
    iifname {home0.2, home0.3} oifname att0 accept

    ip6 nexthdr ipv6-icmp iifname att0 icmpv6 type {1,2,3,4,128,129} accept
  }
}

That’s all!