IPv6 on a FreeBSD router

Since one and a half years I am using a FreeBSD machine as my home router. I am very happy with the system as it is very fast, easily configurable and rock solid.
After studying the technical details of IPv6 some time ago I had the wish to play around with it but unfortunately my provider has a very stupid model for connectivity. If your contract is older (like mine) you get just IPv4 connectivity and no IPv6. If you have a newer contract you get IPv6 connectivity but your cable modem just gives you a fake NATed IPv4 address.
Luckily there are tunnel providers that give you IPv6 subnets and tunnels for free. My tunnel broker of choice was SixXS and for now I’m really happy with this choice. During the setup process on my router I ran into some troubles so I want do describe a nice and working setup here.

Components

  • aiccu to connect the tunnel and keep it alive using heartbeat
  • rtadvd to announce the prefix on the local net
  • unbound as the local DNS server
  • pf as the firewall

Assumptions

These are configurations specific to my environment and should be adapted to your own:

  • The interface to the internal network is em0
  • The provided prefix is 2001:6f8:452:8f32::/64
  • The endpoint address (our side) is 2001:6f8:452:f32::2
  • The endpoint address (their side) is 2001:6f8:452:f32::1
  • The IPv4 endpoint address on their side is 212.224.0.188
  • FreeBSD 10 is used

Basic setup

First all the required packets should be installed. Most of the needed tools are already installed by default – only sixxs-aiccu is missing.

Afterwards configuration in /etc/rc.conf is necessary. Here we make sure that IPv6 is enabled for all interfaces and afterwards create a tunnel interface gif0. Afterwards we manually set the addresses of both the local network interface. I think it should somehow be possible to have the em0 address set automatically but I did not manage to do so. The address of the tunnel interface will be set by aiccu but we have to force a IPv6 link local address on the interface because otherwise IPv6 traffic will get disabled.
Then we can set address of the tunnel interface as the default router and enable IPv6 gateway functionality.
Afterwards rtadvd, aiccu and unbound can be started. To make sure that DNS resolving and address advertisement are only local they have to be limited to the internal interface.

# [...]
ipv6_interfaces="auto"

gif_interfaces="gif0"
gifconfig_gif0="UP"
ipv6_ifconfig_em0="add 2001:6f8:452:8f32:6a05:caff:fe0f:fba prefixlen 64"
ipv6_ifconfig_gif0="auto_linklocal"
ipv6_defaultrouter="2001:6f8:452:f32::2"
ipv6_gateway_enable="YES"

rtadvd_enable="YES"
rtadvd_interfaces="em0"

sixxs_aiccu_enable="YES"

dhcpd_ifaces="em0"
dhcpd_enable="YES"
# [...]

pf

Now we have to tell pf to let traffic through. As my /etc/pc.conf starts with a block all we have to add a few lines to allow IPv6 traffic.

# [...]

int_if = "em0"
ext_if = "re0"
tun_if = "gif0"

mytunnelstart = 2001:6f8:452:f32::1
mytunnelendpoint = 2001:6f8:452:f32::2

pass out log proto 41 from ($ext_if) to 212.224.0.188 keep state
pass in log proto 41 from 212.224.0.188 to ($ext_if) keep state

# block in/out on $tun_if
block in log on $tun_if inet6
block out log on $tun_if inet6

# allow heartbeat ping
pass in log quick on $tun_if inet6 proto { ipv6-icmp } from $mytunnelstart to $mytunnelendpoint keep state

# pass tcp, udp, and icmp6 out on the ipv6 tunnel interface.
pass out log quick on $tun_if inet6 proto { tcp udp ipv6-icmp} keep state

# allow internal v6 traffic
pass on $int_if inet6 all

aiccu

The aiccu configuration in /usr/local/etc/aiccu.conf is pretty straightforward. Just enter username and password for your tunnel, configure the tunnel interface, enable automatic tunnel creation and that’s it.

username youruser
password yourpassword

ipv6_interface gif0
daemonize true
automatic true

unbound

I run unbound as a resolving nameserver. To allow DNS requests over IPv6 a few lines have to be added to /etc/unbound/unbound.conf. This enables listening on the v6 address and allows all machines from the local net to query the server:

interface: 2001:6f8:452:8f32:6a05:caff:fe0f:fba
access-control: 2001:6f8:452:8f32::/64 allow

rtadvd

Now everything for local IPv6 connection is set up. We just have to tell all other clients in the network the prefix and the DNS server so they can configure themselves. This is done by the rtadvd which sends cyclic router advertisement. The content of the advertisement can be configured using a somewhat strange syntax in /etc/rtadvd.conf. You have to set the prefix, the prefix length and specify the address of the DNS server.

em0:\
    :addrs#1:addr="2001:6f8:452:8f32::"\
    :prefixlen#64\
    :tc=default\
    :rdnss="2001:6f8:452:8f32:6a05:caff:fe0f:fba":

Summary

After all this the router should be restarted. And hopefully everything regarding IPv6 connectivity comes up. All clients in the network should receive the router advertisement and magically configure themselves. This is at least the case for Apple products. Both Mac OS and iOS have IPv6 support enabled by default and take prefixes and DNS servers from the advertisement. The network-manager on the laptop of a friend under Archlinux did the same after manually enabling IPv6. Even my crappy Windows work laptop managed to get IPv6 connectivity without any configuration.

Sources

[0]
[1]
[2]
[3]
[4]
[5]
[6]