Before I go further, let me be clear: these were IPv6 addresses, not IPv4. I only have 1 IPv4 address, and that’s all that Comcast is going to give me. But on the IPv6 side, I am rolling in addresses.
This blog post describes how to modify a FreeBSD-based firewall to allow internal machines to acquire IPv6 addresses and communicate with the Internet over IPv6.
First, a Network Diagram
Pretty pictures are worth a thousand words, and here’s a pretty picture of my network layout (green boxes, e.g. the MacBook Air, are behind the firewall. The green-and-red box is the firewall):
Pre-requisites
You’ll need to make sure your firewall itself can acquire an IPv6 address. This post describes how to setup DHCPv6 on your FreeBSD firewall.
You’ll need to make sure you have a solid set of firewall rules to protect your inside machines. This post is a good start.
A Pinch of Hackery
This is the step I’m most embarrassed about. You’ll need to determine your IPv6 your default route and hard-code it into /etc/rc.conf.
$ netstat -nr | grep default
default 24.23.190.1 UGS 0 9633402 em3
default fe80::201:5cff:fe50:c041%em3 UGS em3
Now take that default route (i.e. fe80::201:5cff:fe50:c041%em3
) and place it in your /etc/rc.conf
like this:
ipv6_defaultrouter="fe80::201:5cff:fe50:c041%em3" # ugly hack
Others have run into this problem. It [hard-coding the route] wasn’t necessary when the firewall was strictly a DHCPv6 client, but once the firewall enabled IPv6 packet forwarding and ran the rtadvd daemon, it would periodically lose its default IPv6 route.
The downside of hard-coding the default route is that if/when your ISP re-configures the upstream router, your IPv6 configuration will break. [Within two months of publishing this post, Comcast re-configured their upstream router (at approx 01:40 2013-11-12). I’ve added a section “When Comcast changes its configuration” at the bottom to recover from cases like this.]
A quick note about IPv6 addresses: an IPv6 address that begins with “fe80” is a link-local address—it can only be reached by machines on the local subnet, it cannot be reached by machines outside that subnet. Often it includes components of the ethernet MAC’s address (IPv6 uses Modified EUI-64, which is derived from the MAC address). There is nothing quite analogous to this in the IPv4 protocol.
IPv6 Gateway and rtadvd Daemon
We are now ready to turn our firewall into an IPv6 gateway and enable the rtadvd (router advertisement daemon). Modify /etc/rc.conf to include the following lines:
ipv6_gateway_enable="YES"
rtadvd_enable="YES" # Set to YES to enable an IPv6 router
rtadvd_interfaces="em0"
Note that you should enable rtadvd only on your inside interface (in this example, em0). You should not enable it on your outside interface. rtadvd is the daemon that manages IPv6’s router advertisement protocol. Router advertisement is similar to IPv4’s DHCP protocol in that it provides IP addresses to clients.
You can view my full rc.conf on github.
Reboot your firewall so that the changes take effect: sudo shutdown -r now
Testing
After your firewall has rebooted, your internal machines should automatically acquire IPv6 addresses—you shouldn’t need to do any additional configuration. But how to be sure? Let’s test.
The easiest way to test is to point a browser from one of your inside machines to http://test-ipv6.com/. You should score 10/10. Don’t worry if the page displays, “Your browser has real working IPv6 address – but is avoiding using it. We’re concerned about this.”
Another test is available from Google.
Finally, for those command-line aficionados, you can ping Google’s IPv6 DNS server:
$ ping6 -c 2 2001:4860:4860::8888
PING6(56=40+8+8 bytes) 2601:9:7980:1c2:6dd0:bef4:d215:4dfe --> 2001:4860:4860::8888
16 bytes from 2001:4860:4860::8888, icmp_seq=0 hlim=52 time=32.934 ms
16 bytes from 2001:4860:4860::8888, icmp_seq=1 hlim=52 time=37.299 ms
--- 2001:4860:4860::8888 ping6 statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 32.934/35.117/37.299/2.182 ms
Observations
The client machines didn’t need any configuration—they were IPv6-ready out-of-the-box. Kudos to Apple and Microsoft. Even my printer worked (Go HP!). Sadly, my HTC One Google Edition Android Version 4.3 did not acquire an IPv6 address, but that appears to be a limitation of the HTC One and not the Android OS. Also, my Raspberry Pi (Linux running Raspbian) required slight re-configuration to enable IPv6, but almost every other Linux distro “just works”.
18 Quintillion Addresses
To clarify, I didn’t use all 18 quintillion addresses. In fact, I only used 11 of them. Comcast assigned me 2601:9:7980:1c2/64 (“/64” is IPv6’s prefixlen, which is similar to IPv4’s CIDR notation, i.e. subnet-mask). That leaves 64 remaining bits (IPv6 addresses have 128 bits) for me to assign to my machines, which works out to 2^64 addresses, or 18 quintillion.
When Comcast changes its Configuration
Comcast can change its configuration; specifically, it might re-configure/swap-out its upstream router, which in turn may cause your IPv6 default route to change. Your first clue that something is wrong is that your IPv6 configuration will break. (e.g. ping6 ipv6.google.com
will fail) Additionally, you may see messages similar to the following the following in /var/log/messages:
Nov 12 01:40:35 lana kernel: em3: link state changed to DOWN
Nov 12 01:40:53 lana kernel: em3: link state changed to UP
...
Nov 12 02:27:05 lana dhclient: New IP Address (em3): 24.23.190.188
First, you’ll need to check your upstream by using a combination of arp
and ndp
:
$ arp -an
? (24.23.190.188) at 00:00:24:ce:7b:fb on em3 permanent [ethernet]
? (24.23.190.1) at 00:01:5c:63:ec:46 on em3 expires in 1192 seconds [ethernet]
...
$ ndp -an
Neighbor Linklayer Address Netif Expire S Flags
fe80::200:24ff:fece:7bfb%em3 00:00:24:ce:7b:fb em3 permanent R
2001:558:6045:109:30b8:46f0:7e52:77ca 00:00:24:ce:7b:fb em3 permanent R
fe80::201:5cff:fe63:ec46%em3 00:01:5c:63:ec:46 em3 4s R R
Notice that our default route’s MAC address is 00:01:5c:63:ec:46? The IPv6 counterpart is fe80::201:5cff:fe63:ec46%em3. That is what you need to set your default route to in /etc/rc.conf
, i.e.
ipv6_defaultrouter="fe80::201:5cff:fe63:ec46%em3" # ugly hack
Reboot your router, and IPv6 should work again (note that it may take a couple of hours for things to shake out. For example, connectivity to my DHCPv6-acquired external IPv6 address was intermittent for ~8 hours after reconfiguration). Curiously, my internal machines who acquired their addresses via router solicitation worked right away.
Kyle Manna has written an excellent post on how to configure a Linux firewall as a native IPv6 Comcast client.