This article details how to configure an IPv4 and IPv6 dual-stack Internet gateway router secured with PF firewall.
It is a prerequisite that your Internet Service Provider is providing native IPv6 Internet connectivity and IP addressing via DHCPv6.
net/dhcpcd
will be used instead of FreeBSD's base dhclient. dhcpcd will manage acquiring DHCP address leases for both IPv4 and IPv6. dhcpcd will be configured to request a /64 PD prefix delegation, and assign the first address of the offered PD to the internal interface.
Already installed with base system rtadvd
will be enabled and configured to send router advertisements on the internal interface, providing default gateway and temporary IPv6 addresses from the acquired /64 prefix delegation to all connected LAN clients that support IPv6.
Install and Configure dhcpcd
Install net/dhcpcd using your preferred method.
Edit and add the following to /usr/local/etc/dhcpcd.conf
:
denyinterfaces re1 # Don't touch re1 at all interface re0 ipv6rs # enable routing solicitation for re0 ia_na 1 # request a NA network address ia_pd 2 re1/0 # request a PD /64 prefix delegation and assign it to re1
Configure System Startup
Configure the network interfaces, enable IP forwarding, PF firewall, and rtadvd by adding the following to /etc/rc.conf
:
dhclient_program="/usr/local/sbin/dhcpcd"
ipv6_cpe_wanif="re0"
ifconfig_re0="SYNCDHCP"
ifconfig_re1="inet 192.0.2.1 netmask 255.255.255.0"
gateway_enable="YES"
ipv6_gateway_enable="YES"
# pf firewall
pf_enable="YES"
pf_rules="/etc/pf.conf"
pflog_enable="YES"
pflog_logfile="/var/log/pflog"
# rtadvd
rtadvd_enable="YES"
rtadvd_interfaces="re1"
rtadvd_flags="-s"
Configure the PF Firewall Rules
Edit /etc/pf.conf
# Macros
ext_if = re0
int_if = re1
# ICMP types
icmp_types = "{ echoreq }"
icmp6_types = "{ echoreq, neighbradv, neighbrsol, routeradv, routersol }"
# Private and documentation example networks
priv_nets = "{ 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8, 255.255.255.255/32 }"
doc_example_nets = "{ 2001:DB8::/32, 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24 }"
# Options
set block-policy drop
set skip on lo0
# Scrub
scrub on $ext_if all no-df random-id reassemble tcp
# IPv4 NAT
nat on $ext_if from 192.0.2.0/24 to any -> ($ext_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 interface(s)
pass out on $ext_if flags any
# ICMP
pass in on $ext_if inet proto icmp all icmp-type $icmp_types
pass in on $ext_if inet6 proto icmp6 all icmp6-type $icmp6_types allow-opts
# Allow DHCP v6 Client
pass in log on $ext_if inet6 proto udp from fe80::201:5cff:fe9c:e445 to fe80::213:3bff:fe0e:2e4 port dhcpv6-client
pfctl -f /etc/pf.conf
to load the new ruleset.
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 for the required server/service. This ensures that unwanted ingress IPv6 traffic is not allowed to reach other destinations on your internal network.
Testing the Setup
Before dhcpcd can be run we must stop any running dhclient processes. It may be necessary to be logged into FreeBSD at the local console to prevent loss of connectivity if initially connected via SSH.
killall dhclient
dhcpcd
Observe the output as dhcpcd requests IP address leases and adds routes for addresses offered.
Watch specifically for a line output similar to:
re0: REPLY6 received from fe80::201:5cff:fe9c:e445
This is the link-local address of the responding DHCPv6 server and should be copy/pasted into the "from" address of the Allow DHCP v6 Client PF firewall rule.
Use ifconfig
to confirm that the system received both an IPv4 address as well as an IPv6 network address. Also verify that a prefix delegation was offered and the first address of the offered /64 address block is assigned to the internal interface.
Test and verify both v4 and v6 Internet connectivity. ping
and ping6 google.com
When both IPv4 and IPv6 connectivity are confirmed working, you can start the ratdvd service to begin assigning IPv6 addresses from your assigned /64 prefix delegation. By default no configuration is required, simply specifying the internal interface with rtadvd_interfaces="re1" in /etc/rc.conf
is typically sufficient for most cases. Start the service by executing:
service rtadvd start
After verifying the setup and configuration is all working normally, reboot the system and reverify.
To install DHCP servers for stateful IPv4 and IPv6 address assignments, refer to: IPv4 & IPv6 ISC DHCP Server on a Dual-Stack Network