Configuring native IPv6 in pfSense firewall

Today, we’re going to talk about pfSense. A software stateful-firewall based on the excellent pf firewall in FreeBSD. It’s an easy to install from-ISO appliance.
From the pfSense website:
pfSense is a free, open source customized distribution of FreeBSD tailored for use as a firewall and router. In addition to being a powerful, flexible firewalling and routing platform, it includes a long list of related features and a package system allowing further expandability without adding bloat and potential security vulnerabilities to the base distribution. pfSense is a popular project with more than 1 million downloads since its inception, and proven in countless installations ranging from small home networks protecting a PC and an Xbox to large corporations, universities and other organizations protecting thousands of network devices.
pfSense is a nice piece of software, but the developers don’t seem to be very interested in integrating IPv6 support in the interface. Too bad, because IPv6 is hot and will replace IPv4 within the next few years. I’m not going to integrate IPv6 in the GUI of pfsense with this tutorial, but after following the instructions you will have a working IPv6 router/firewall with support for stateless autoconfiguration. The configuration is built from my own needs, so if it doesn’t match your expectations please add your features.
For this setup i use pfSense 1.2.3-RC1 which is out for quite a while and pretty stable in it’s use.
I’m not going to discuss the installation of pfSense. If you can’t install the pfSense ISO, you shouldn’t be doing IPv6 on it anyway
. First of all, make sure you enable SSH in pfSense. You can find the feature at “System” > “Advanced”
After enabling, connect (via SSH) to the pfSense box. Ofcourse, if you’re sitting behind the box you can do it on the console also
.
You will be presented a nice text menu:
*** Welcome to pfSense 1.2.3-RC1-pfSense on myFirewall ***
WAN* -> bce0 -> 123.123.123.1
LAN* -> bce1 -> 192.168.0.254pfSense console setup
***********************
0) Logout (SSH only)
1) Assign Interfaces
2) Set LAN IP address
3) Reset webConfigurator password
4) Reset to factory defaults
5) Reboot system
6) Halt system
7) Ping host
8) Shell
9) PFtop
10) Filter Logs
11) Restart webConfigurator
12) pfSense PHP shell
13) Upgrade from console
14) Disable Secure Shell (sshd)
We want to go to the CLI shell. Select 8.
On my box, i’m using Broadcom network interfaces. On FreeBSD these are named ‘bce0′ and ‘bce1′. You can find the respective names with the ‘ifconfig’ command. On my setup, bce0 is the outside interface and bce1 is the inside interface.
My setup is fully native-IPv6, which means that i’m not doing any tunnelling at all. On the outside interface, i have an IPv6 address from my provider’s /64 block he used for my connection. On the inside, i have a /64 of IPv6 addresses which are publically reachable (global-unicast). Ofcourse i’m using fake addresses to prevent my firewall being bombed all-over
.
Let’s say, these are my network variables :
- The WAN IPv6 network is : 2001:4cb8:a95:1::/64
- The WAN IPv6 address is : 2001:4cb8:a95:1::2
- The WAN IPv6 default gateway is : 2001:4cb8:a95:1::1
- The LAN IPv6 network is : 2001:4cb8:b95:1::/64
- The LAN IPv6 address is : 2001:4cb8:b95:1::1
With this information, we’re going to create our boot-script to configure the interfaces and routing.
cd /usr/local/etc/rc.d vi 00_config-ipv6-if.sh #!/bin/sh # # IFOUT = outside interface # IFIN = inside interface # DFGW = default gateway IFOUT="bce0" IFIN="bce1" DFGW="2001:4cb8:a95:1::1" ####### Configure the stuff # Configure the interfaces ifconfig $IFOUT inet6 alias 2001:4cb8:a95:1::2 prefixlen 64 ifconfig $IFIN inet6 alias 2001:4cb8:b95:1::1 prefixlen 64 # Set the default route route -n add -inet6 default $DFGW # Configure IPv6 forwarding sysctl net.inet6.ip6.forwarding=1 # My /etc/rtadvd.conf looks like this # # bce1:\ # :addrs#1:addr="2001:4cb8:b95:1::":prefixlen#64:tc=ether: # # Startup rtadvd /usr/sbin/rtadvd -d -D -c /etc/rtadvd.conf $IFIN
Ok, that’s pretty much all there is to enable IPv6 and configure the static routing to the ISP.
Next, we need to change permissions on this file :
chmod 755 /usr/local/etc/rc.d/00_config-ipv6-if.sh
After bootup, IPv6 will be running on the pfSense box, but it won’t do a thing. This is because we need to change the filter (PF) also. This is going to be our next script.
cd /usr/local/etc/rc.d
vi 10_config-ipv6-pf.sh
#!/bin/sh
#
# IFOUT = outside interface
# IFIN = inside interface
# DFGW = default gateway
IFOUT="bce0"
IFIN="bce1"
####### Configure the stuff
# Configure PF
# pfSense puts it's rules in /tmp/rules.debug for debugging purposes after boot
# We will use these rules, add IPv6 additions, read the config with pfctl and
# disable and enable PF
cat /tmp/rules.debug | sed "/User-defined rules follow/{
p;s/.*/\
pass in quick on $IFIN inet6 from any to any\\
pass out quick on $IFIN inet6 from any to any\\
pass out quick on $IFOUT inet6 from any to any\\
pass quick proto ipv6-icmp from any to any\\
pass in on $IFOUT inet6 proto tcp from any to any port 22\\
/;}" > /tmp/rules.config-ipv6.txt
# Read the new PF configuration file
pfctl -f /tmp/rules.config-ipv6.txt
pfctl -d; pfctl -e
And change the permissions also:
chmod 755 /usr/local/etc/rc.d/10_config-ipv6-pf.sh
Finally, we need to configure the router advertisement daemon (rtadvd) to get stateful autoconfiguration to work.
vi /etc/rtadvd.conf bce1:\ :addrs#1:addr="2001:4cb8:b95:1::":prefixlen#64:tc=ether:
After rebooting the pfSense firewall (or run script 00 and 10) IPv6 will work on your box.
But.. when you change filter rules (or anything actually) in the GUI, the filter settings are overwritten and your IPv6 connectivity will break.
After some searching on the box, i noticed that after changing things in the GUI the function filter_configure_sync() is called and the rules will be flushed.
This function can be found in /etc/inc/filter.inc (line 78). In the function, there’s a hook to a plugin directory. When the function filter_configure_sync() is called, the function will look in the /usr/local/pkg/pf directory for scripts, which will be executed. This only happens if scripts end with “.sh” as the extension.
We will symlink the 10_config-ipv6-pf.sh script to this location to make it work.
ln -s /usr/local/etc/rc.d/10_config-ipv6-pf.sh /usr/local/pkg/pf/
Congratulation! You got yourself a working IPv6 setup.
If you want to know more ins and outs about IPv6, i suggest reading the book “Running IPv6″ by Iljitsch van Beijnum. You can find more information at http://runningipv6.net/

