After several years with my pretty stable and reliable home router Linksys E3000 running DD-WRT time came to have something new and faster. I do backups of my servers through OpenVPN and was fairly disappointed about the speed. Making backups of 80GB with about 600kB/s isn't really funny.

I decided to check a PC Engines apu.1d4 running my favorite UNIX©-alike FreeBSD. Installing FreeBSD via serial console and USB Drive is straight forward. Don't use ZFS with 4GB RAM or less.

Some words of warning: Configuring FreeBSD as a NAT-Router is pretty complicated and rarely intuitive. Don't give up, I didn't and I'm happy I didn't.

Some requirements I needed:

  • Firewall throughput performance for my 200 MBit/s cable
  • 6to4 tunnel by tunnelbroker.net
  • Router Advertisement
  • OpenVPN using TAP and IPs from my DHCP server
  • at least 802.11n Wifi
  • bridging WiFi, TAP and the remaining ethernet ports

I also bought a Compex WLE200NX a/b/g/n miniPCI express radio card (also a WLE600NX 802.11ac but failed to notice that FreeBSD does not support this yet).

$ pkg install bind99 isc-dhcp43-server openvpn

/usr/local/etc/namedb/named.conf:

zone "cerberus.us.to" {
        type master;
        allow-update {
                127.0.0.1;
                192.168.2.1;
                ::1;
        };
        file "/usr/local/etc/namedb/dynamic/cerberus.us.to.org";
};

zone "2.168.192.in-addr.arpa" {
        type master;
        allow-update {
                127.0.0.1;
                192.168.2.1;
                ::1;
        };
        file "/usr/local/etc/namedb/dynamic/2.168.192.in-addr.arpa";
};

/usr/local/etc/dhcpd.conf:

# option definitions common to all supported networks
option domain-name "cerberus.us.to";
option domain-name-servers 8.8.8.8, 8.8.4.4;
default-lease-time 86400;
max-lease-time 90000;

# Use this to enble / disable dynamic dns updates globally.
#ddns-update-style none;

# If this DHCP server is the official DHCP server for the local
# network, the authoritative directive should be uncommented.
authoritative;

ddns-updates on;
ddns-update-style interim;
do-forward-updates on;
allow client-updates;

# Use this to send dhcp log messages to a different log file (you also
# have to hack syslog.conf to complete the redirection).
log-facility local7;

# Home Network

zone cerberus.us.to {
  primary 127.0.0.1;
}
zone 2.168.192.in-addr.arpa {
  primary 127.0.0.1;
}

subnet 192.168.2.0 netmask 255.255.255.0 {
range 192.168.2.100 192.168.2.254;
interface bridge0;
option domain-name-servers 192.168.2.1;
option broadcast-address 192.168.2.255;
option routers 192.168.2.1;
option ntp-servers 192.168.2.1;
default-lease-time 86400;
max-lease-time 90000;
ddns-domainname "cerberus.us.to.";
ddns-rev-domainname "in-addr.arpa.";

/etc/rc.conf:

hostname="gateway"
keymap="german.iso.kbd"
ifconfig_re0_name="wan0"
ifconfig_wan0="DHCP"
ifconfig_wan0_ipv6="inet6 accept_rtadv"
rtsold_enable="YES"
sshd_enable="YES"
ntpd_enable="YES"
named_enable="YES"
# Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable
dumpdev="NO"
gateway_enable="YES"
ipv6_gateway_enable="YES"
cloned_interfaces="bridge0 tap0 gif0"
ifconfig_tap0="inet 192.168.2.1 netmask 255.255.255.0 broadcast 192.168.2.255 up"
ifconfig_bridge0="inet 192.168.2.1 netmask 255.255.255.0 addm re1 addm re2 addm tap0 addm wlan0"
ifconfig_bridge0_ipv6="inet6 2001:xxx:xx:xxx::x prefixlen 64"
ifconfig_bridge0_alias0="inet6 fe80::0c7:21ff:fe84:3000 prefixlen 64"
ifconfig_re1="up"
ifconfig_re2="up"
ifconfig_bridge0_ipv6="up"

gifconfig_gif0="xx.xx.xxx.xx xxx.xx.xx.xxx"
ifconfig_gif0="inet6 2001:xxx:xx:xxx::x 2001:xxx:xx:xxx::x prefixlen 128"
ipv6_defaultrouter="2001:xxx:xx:xxx::x"
rtadvd_enable="YES"
rtadvd_interfaces="bridge0"

wlans_ath0="wlan0"
create_args_wlan0="wlanmode hostap country DE"
ifconfig_wlan0="channel 6:ht/40+ -bgscan up"
hostapd_enable="YES"

firewall_enable="YES"
firewall_script="/etc/ipfw.rules"
firewall_logging="YES"
firewall_nat_enable="YES"

dhcpd_enable="YES"
dhdpd_ifaces="bridge0 wlan0 tap0 re1 re2"

openvpn_enable="YES"
openvpn_configfile="/usr/local/etc/openvpn/openvpn.conf"

apache24_enable="yes"
postgresql_enable="YES"
vnstat_enable="YES"
vnstat_additional_ifaces="bridge0 wlan0 tap0 gif0"
ejabberd_enable="YES"

After reboots I noticed that the GIF tunnel will not be established for some reason. For any suggestions drop me a line.

/etc/ipfw.rules:

#!/bin/sh
cmd="/sbin/ipfw -q"

# external interface
wan_if="wan0"

# internal interface
lan_if="bridge0"

lan_net="192.168.2.0/24"

# flush rules
$cmd flush
#$cmd disable one_pass

$cmd nat 1 config log if $wan_if unreg_only reset same_ports

# Create the NAT redirect rules
######################################
#ipfw -q nat 1 config if $pif unreg_only reset \
#    redirect_port tcp <lan ip>:<lan port> <remote port> \
#    redirect_port udp <lan ip>:<lan port> <remote port>

$cmd add 10 allow ipv4 from any to any via $lan_if
#$cmd add 11 allow ipv6 from any to any via $lan_if
$cmd add 11 allow ip6 from any to any out proto tcp via $lan_if setup keep-state
$cmd add 12 allow ip6 from any to any out proto udp via $lan_if keep-state
$cmd add 13 allow all from any to any via lo0
#$cmd add 13 deny log ip from any to any not antispoof in

$cmd add 20 allow udp from any to me openvpn in keep-state
$cmd add 21 allow tcp from any to me ssh in keep-state
$cmd add 22 allow tcp from any to me http in keep-state
$cmd add 23 allow tcp from any to me https in keep-state
$cmd add 20 allow udp from any to me6 openvpn in keep-state
$cmd add 21 allow tcp from any to me6 ssh in keep-state
$cmd add 22 allow tcp from any to me6 http in keep-state
$cmd add 23 allow tcp from any to me6 https in keep-state
$cmd add 25 allow all from me to any out keep-state
#$cmd add 26 allow tcp from 192.168.2.0/24 to any out via $wan_if setup keep-state
#$cmd add 27 allow udp from 192.168.2.0/24 to any out via $wan_if keep-state
#$cmd add 28 allow ip6 proto tcp from any to any out via

$cmd add 30 deny log all from 192.168.0.0/16 to any in via $wan_if   #RFC 1918 private IP
$cmd add 31 deny log all from 172.16.0.0/12 to any in via $wan_if    #RFC 1918 private IP
$cmd add 32 deny log all from 10.0.0.0/8 to any in via $wan_if       #RFC 1918 private IP
$cmd add 33 deny log all from 127.0.0.0/8 to any in via $wan_if      #loopback
$cmd add 34 deny log all from 0.0.0.0/8 to any in via $wan_if        #loopback
$cmd add 35 deny log all from 169.254.0.0/16 to any in via $wan_if   #DHCP auto-config
$cmd add 36 deny log all from 192.0.2.0/24 to any in via $wan_if     #reserved for doc's
$cmd add 37 deny log all from 204.152.64.0/23 to any in via $wan_if  #Sun cluster interconnect
$cmd add 38 deny log all from 224.0.0.0/3 to any in via $wan_if      #Class D & E multicast

$cmd add 49 nat 1 ip4 from any to any in via $wan_if
$cmd add 50 check-state
$cmd add 51 skipto 600 tcp from any to any out via $wan_if setup keep-state
$cmd add 52 skipto 600 udp from any to any out via $wan_if keep-state
$cmd add 53 allow icmp from any to any in
$cmd add 54 allow icmp6 from any to any in
$cmd add 55 allow icmp from any to any out
$cmd add 56 allow icmp6 from any to any out

$cmd add 60 deny log all from any to any in via $wan_if

$cmd add 600 nat 1 ip4 from any to any out via $wan_if
$cmd add 610 allow ip from any to any

I'm not pretty sure about the firewall rules but it seems to be secure.