IPv4 & IPv6 Router and PF Firewall (tunnelbroker.net)

This article details how to configure a IPv4/IPv6 dual-stack Internet gateway router and secure it with PF firewall.

IPv6 Internet connectivity and prefix delegation address block is provided by IPv6 tunnel broker: https://tunnelbroker.net
Refer to this article: IPv6 via Hurricane Electric's Free IPv6 Tunnel Broker Service for additional information and how to sign up for and create this tunnel.

If you Internet Service Provider does provide you with IPv6 addresses via router advertisements or other mechanism, then refer to the configuration in this article: IPv4 & IPv6 Router and PF Firewall.


Configure System Startup

Add the following (below) to /etc/rc.conf:

Substitute IP addresses for your internal network as needed and modify the gif0 tunnel addresses to reflect the ones provided to you by tunnelbroker.net.

Optionally you can assign an additional IPv6 address to the system from the 'routed /64' block assigned to your tunnel. To accomplish this include and edit line: ifconfig_gif0_alias0="inet6 Routed /64 IP Address prefixlen 64" substituting Routed /64 IP Address with an IPv6 address from the 'routed /64' block assigned to your tunnel.

ifconfig_re1="inet netmask"
ifconfig_re1_ipv6="inet6 Routed /64 IP Address prefixlen 64"
cloned_interfaces="gif0" ifconfig_gif0="tunnel Client IPv4 Address Server IPv4 Address" ifconfig_gif0_ipv6="inet6 Client IPv6 Address Server IPv6 Address prefixlen 128" ipv6_defaultrouter="Server IPv6 Address" gateway_enable="YES" ipv6_gateway_enable="YES" # PF pf_enable="YES" pf_rules="/etc/pf.conf" pflog_enable="YES" pflog_logfile="/var/log/pflog"


Configure the Firewall Rules

NOTE: It is important to understand in this configuration that the network interface re0 is the IPv4 gateway, and the gif0 tunnel interface is the IPv6 gateway for Internet traffic. IPv4 and IPv6 interfaces will need to be secured separately with some IP address family specific firewall rules for the respective interfaces.

Edit /ect/pf.conf

# Macros #
int_if = re1
ext_if = "{ re0, gif0 }"
extv4_if = re0
extv6_if = gif0

# ICMP types #
icmp_types = "{ echoreq }"
icmp6_types = "{ echoreq, neighbradv, neighbrsol, routeradv, routersol }"

# Private and documentation example networks #
priv_nets = "{,,,, }"
doc_example_nets = "{ 2001:DB8::/32,,, }"

# Options #
set block-policy drop
set skip on lo0

# Scrub #
scrub on $ext_if all no-df random-id reassemble tcp

# NAT #
nat on $extv4_if from -> ($extv4_if)

### Block ###
block log all

# Antispoof #
antispoof quick log for $ext_if
block in log on $ext_if from { urpf-failed, no-route }
block in quick log on $ext_if from { $priv_nets, $doc_example_nets }

# Block ICMP Port Unreachable Messages to Prevent and Mitigate DDOS and Other Attacks i.e. SAD DNS
block out quick log on $ext_if inet proto icmp icmp-type unreach code port-unr
block out quick log on $ext_if inet6 proto icmp6 icmp6-type unreach code port-unr

### Allow ###
# Allow all traffic on internal interface #
pass quick on $int_if flags any

# Allow all traffic out on external interfaces #
pass out on $ext_if flags any

# IPv6 over IPv4 tunnel #
pass in on $extv4_if proto 41 from Server IPv4 Address
pass out on $extv4_if proto 41 to Server IPv4 Address 

# ICMP #
pass in on $extv4_if inet proto icmp all icmp-type $icmp_types
pass in on $extv6_if inet6 proto icmp6 all icmp6-type $icmp6_types allow-opts

### Inbound Services ###
# pass in on $extv4_if proto tcp to port http
# pass in on $extv6_if proto tcp to 2001:db8::2 port http

NOTE: When specifying ingress allow rules it is important to create separate rules for IPv4 and IPv6 as well as specifying the exact destination addresses to the required server/service. This ensures that unwanted ingress IPv6 traffic is not inadvertently allowed to reach other destinations on your internal network.


After rebooting the system, check that all IP addresses were assigned and ensure that you have network connectivity to both IPv4 and IPv6 external Internet hosts.

tcpdump -netttti pflog0 can be used to monitor the firewall activity in real-time.


When both IPv4 and IPv6 are confirmed working you can begin assigning IPv6 addresses to LAN clients from the routed /64 block assigned to your tunnel.
rtadvd(8) - router advertisement daemon is a quick and easy way to automatically configure IPv6 addresses for your LAN systems and devices.

To install dual-stack DHCP servers for stateful address assignments, refer to this article: IPv4 & IPv6 ISC DHCP Server on a Dual-Stack Network