IPv4 & IPv6 Router and PF Firewall

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.

Both router interfaces will be protected from inbound connections directly to the router itself, while allowing all outbound Internet routed traffic to pass through.

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
either_if = "{ re0, re1 }"
neither_if = "{ ! re0, ! 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 fragment reassemble # IPv4 NAT nat on $ext_if from 192.0.2.0/24 to any -> ($ext_if) # 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 ### block log all

# Block Specific ICMP 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 proto-unr
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 routed traffic in on internal interface pass in on $int_if to $neither_if flags any # Allow all outbound traffic on both interfaces pass out flags any # ICMP
pass in on $int_if inet proto icmp pass in on $int_if inet6 proto icmp6 allow-opts
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:2e4c port dhcpv6-client


### Inbound Services ###
# SSH
pass in on $int_if proto tcp to $int_if port ssh

 

Start dhcpcd

Before dhcpcd can be run we must stop the pf firewall, and 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.

service pf stop
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.
Start the pf firewall service.
service pf start

 

Testing the Setup

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: Kea DHCP Server