IPv6 router with 6to4 and pf

From TykWiki
Jump to navigationJump to search

This page is about IPv6 enabling an existing pf gateway. I will show the changes I have done to the setup to v6 enable the firewall and the network behind it. The current setup has worked fine for a while, and I decided to v6-enable the network mostly for the learning experience.

The firewall in question is one in a pair of redundant pf/pfsync/carp firewalls, but I will leave out the redundancy stuff to keep this article about IPv6 and not confuse things. I will be assigning IPv6 addresses to the carp interfaces rather than to the physical interfaces though.

I decided to use 6to4 even though a tunnel provider like SixXS is preferred by most people.

The disadvantages with 6to4 are:

  • No promises of uptime and stability
  • There appears to be routing issues with 6to4 in some places/some providers, which (among other things) means that I can't see the dancing turtle on the KAME website because the connection falls back to v4.
  • There are several security issues with 6to4 since it is an open-ended tunneling method. In practice this means that even though you send all your traffic to the non-6to4 part of the IPv6 internet to the anycast gateway at, reply traffic can come from the normal v4 address of the anycast server instead of Also, IPv6 traffic from other 6to4 networks will come directly from the IPv4 address of the 6to4 network, and not from the anycast server. These two facts effectively prevent any filtering of the encapsulated traffic.

The advantages are:

  • No registration is required, and it is free like the tunnel providers.
  • I only need a public (preferrably static) IP address.
  • I get automatic gateway failover since 6to4 uses an anycast gateway.

If you are located close (bgp-wise) to an anycast 6to4 gateway, and you are ok with the security issues, 6to4 is worth considering. You can check your pingtimes and route to your "local" 6to4 tunnel server by pinging or tracerouting the IP

[tykling@fw2 ~]$ ping
PING ( 56 data bytes
64 bytes from icmp_seq=0 ttl=254 time=1.801 ms
64 bytes from icmp_seq=1 ttl=254 time=1.751 ms
64 bytes from icmp_seq=2 ttl=254 time=1.825 ms
--- ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 1.751/1.792/1.825/0.031 ms
[tykling@fw2 ~]$

If your pingtimes are relatively low, like under 50ms, you should be able to get decent v6 connectivity using 6to4. The main selling point for 6to4 is how easy it is to configure. The main problem is security (spoofed IPv6 packets), but since I am playing around and not running a bank here I will take my chances.

Getting the firewall IPv6 connected

The first step is getting the firewall connected with a tunnel to the 6to4 tunnel server. I added the following lines to /etc/rc.conf and rebooted the firewall. The steps to apply the same settings without rebooting are included below:

#ipv6 6to4 stuff

Line-by-line, the first option enables IPv6 and sets the sysctl values needed for basic operation. The next line enables the stf tunneling device, which takes care of encapsulating IPv6 packets in IPv4 and sending them off to the default gateway, specified in the third line. Due to the anycast address, the ipv6_defaultrouter will always be 2002:c058:6301::, which translates to the v4 address

To enable this configuration without rebooting, I need to manually calculate my 6to4 prefix from my public IP address. It is pretty simple, I just stick the hex form of the public v4 address on the end of the 6to4 prefix 2002:: and that is it. In this case, the v4 address is, the individual octets of the IP converted to hex are 59 e9 2b 42, so the 6to4 IPv6 prefix for is 2002:59e9:2b42::/48. First I create the stf0 interface, then I give it an address in the prefix, then I add the default inet6 gateway. Note that I need to specify the prefix length for the stf0 interface as /16 as I want this to be an IPv6 gateway (eventually), according to this bit from the stf man page:

     If you would like the node to behave as a relay router, the prefix length
     for the IPv6 interface address needs to be 16 so that the node would con-
     sider any 6to4 destination as ``on-link''. 

So, on to the commands:

[tykling@fw2 ~]$ sudo ifconfig stf0 create up
[tykling@fw2 ~]$ sudo ifconfig stf0 inet6 2002:59e9:2b42::1 prefixlen 16
[tykling@fw2 ~]$ ifconfig stf0
stf0: flags=1<UP> metric 0 mtu 1280
        inet6 2002:59e9:2b42::1 prefixlen 16
[tykling@fw2 ~]$ sudo route add -inet6 default 2002:c058:6301::
add net default: gateway 2002:c058:6301::
[tykling@fw2 ~]$ sudo sysctl net.inet6.ip6.accept_rtadv=1
net.inet6.ip6.accept_rtadv: 0 -> 1
[tykling@fw2 ~]$ sudo sysctl net.inet6.ip6.auto_linklocal=1
net.inet6.ip6.auto_linklocal: 0 -> 1
[tykling@fw2 ~]$

The stf interface is up and running. I need to add a few things to pf.conf to allow:

  • Traffic from 2002:c058:6301:: needs to be out on stf0
  • IPv4 traffic using protocol 41 needs to and from all destinations. The nature of 6to4 and anycast prevents any filtering here, this is the main security concern with IPv6.

I added the following lines to the existing pf.conf:

[tykling@fw1 ~]$ grep -E "(stf|proto 41)" /etc/pf.conf
pass out quick on ext_phys_if proto 41 from $carp_gateway_address to any
pass in quick on ext_phys_if proto 41 from any to $carp_gateway_address
pass out on $stf_if inet6 from $stf_prefix to any

After reloading pf.conf I tested basic IPv6 connectivity:

[tykling@fw1 ~]$ ping6 ipv6.google.com
PING6(56=40+8+8 bytes) 2002:59e9:2b42::1 --> 2001:4860:a005::68
16 bytes from 2001:4860:a005::68, icmp_seq=0 hlim=54 time=33.483 ms
16 bytes from 2001:4860:a005::68, icmp_seq=1 hlim=54 time=33.621 ms
16 bytes from 2001:4860:a005::68, icmp_seq=2 hlim=54 time=33.445 ms
--- ipv6.l.google.com ping6 statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 33.445/33.516/33.621/0.076 ms

So far so good. The next step is changing the setup to get auto configuration working for the network behind the firewall, and route IPv6 traffic to and from the network.

Configuring the firewall to route IPv6 traffic

First things first: For the firewall to be able to speak to the network using IPv6 it needs to have an IP address on the inside interface.

Assigning IPv6 addresses from the 6to4 prefix

I choose a /64 subnet for the inside and the outside interfaces and then assign the firewall an IP within them:

[tykling@fw1 ~]$ grep ipv6_ifco /etc/rc.conf

Note: The outside interface is carp1 and the inside interface is carp0. Given this, the subnet that the network behind this firewall will be using is 2002:59e9:2b42:1:1::/64.

Enabling rtadvd for stateless autoconfiguration

I also enable rtadvd in rc.conf, and specify the interface I want to advertise the prefix on. rtadvd is the FreeBSD routing advertisement deamon. It works out of the box with no configuration other than telling it which interface to use.

Enabling rtadvd will enable the network behind this firewall to use stateless auto configuration, which basically means that the firewall will periodically broadcast router advertisements using ICMPv6. Clients can also request a router advertisement by using their auto configured link-local IPv6 address. The firewall will respond with a router advertisement, which contains the prefix in use on the network. The clients will then stick their mac/hardware address in something called modified EUI-64 format on the end of the prefix, to generate a valid globally unique IPv6 address. I add the following to /etc/rc.conf:

[tykling@fw1 ~]$ grep rtadvd /etc/rc.conf

I reboot the firewall again (to avoid rebooting, set the IP addresses on the interfaces and then start rtadvd manually), and I can see with tcpdump that rtadvd is doing its job of transmitting router advertisements to the network:

... to be continued...