My home firewall router with FreeBSD—A year retrospective

A little over one year ago, I purchased a Seagate Dockstar on woot.com. Everything about it was irresistible. The price, $25. The efficiency, a 7 watt draw. The power, a 1.2ghz ARM. I hoped to turn it into a firewall router to replace my aging, under-powered WRT54G.

One year later, I’ve rolled an operating system and written several times about my little ARM box, and it lived up to every hope I had.

Operating System

The firmware in NAND was a non-starter, incapable of much beyond serving files and connecting to the PogoPlug service.

Plugbox Linux, now known as Arch Linux ARM, already existed, but Arch has never been a favourite environment of mine.

Dissatisfied, I turned toward my go-to OS, FreeBSD. The wiki proclaimed support for Marvell Kirkwood, so I cracked open the Dockstar, added a serial console connection, and set to work. A week later, I released patches for FreeBSD 8.1 to make it play nice on the little white device. I used it lightly for a few months determining what worked what didn’t, fixing things along the way. Those changes paved the way for an extremely solid 8.2 release earlier this year.

As a Router

I don’t demand much of a home router, just 100%1 uptime and reliability along with firewalling, IPv4 NAT, IPv6, NTP, DNS, DHCP, and uPNP.

The first glaring limitation of the Dockstar is its single LAN port, correctable with a USB ethernet controller. I have two 10/100 controllers, one USB 1.1 and one USB 2.0, supported by aue and axe, respectively. USB 1.1 negates 100Mbit operation of the aue controller, because in practice it has trouble reaching 10Mbit.

In this example, ue0 is my USB WAN interface, and mge0 is the gigabit LAN interface. I’ve chosen 172.16.0.0/12 as my private IPv4 address range.

We have cable Internet through Comcast, so the WAN link is configured as DHCP. I use SYNCDHCP, which halts the boot process until ue0 gets an IP. PF will fail to initialize at boot otherwise.

/etc/rc.conf
  1. ifconfig_ue0="SYNCDHCP" ifconfig_mge0="inet 172.16.0.1 netmask 255.255.255.0"

IPv6 connectivity

My ISP doesn’t offer IPv6 service, but it’s possible to get connected through a tunnel broker such as Hurricane Electric or SixXS. Either service provides a tunnel to get IPv6 traffic over an IPv4-only WAN link as well as a subnet of IPv6 addresses for machines on the LAN. I went with SixXS, because their AICCU daemon updates my tunnel endpoint address automatically if my IPv4 WAN IP changes. Just enable IPv6, assign the LAN interface a v6 address from the assigned subnet, create a gif device, set it as UP at boot, and let AICCU do the rest:

/etc/rc.conf
  1. pkg_add -r sixxs-aiccu
/etc/rc.conf
  1. ipv6_enable="YES"
  2. ipv6_gateway_enable="YES"
  3. ipv6_ifconfig_mge0="2001:4830:1632::210:75ff:fe1a:a4f1 prefixlen 64"
  4. gif_interfaces="gif0"
  5. gifconfig_gif0="up"
  6. sixxs_aiccu_enable="YES"
/usr/local/etc/aiccu.conf
  1. # AICCU Configuration
  2. # Login information (defaults: none)
  3. username NAR-SIXXS/T56021
  4. password likeIwouldgivethisout
  5. # Protocol and server to use for setting up the tunnel (defaults: none)
  6. #protocol <tic|tsp|l2tp>
  7. protocol tic
  8. server tic.sixxs.net
  9. # Interface names to use (default: aiccu)
  10. # ipv6_interface is the name of the interface that will be used as a tunnel interface.
  11. # On *BSD the ipv6_interface should be set to gifX (eg gif0) for proto-41 tunnels
  12. # or tunX (eg tun0) for AYIYA tunnels.
  13. ipv6_interface gif0
  14. # The tunnel_id to use (default: none)
  15. # (only required when there are multiple tunnels in the list)
  16. tunnel_id T56021
  17. # Be verbose? (default: false)
  18. verbose false
  19. # Daemonize? (default: true)
  20. # Set to false if you want to see any output
  21. # When true output goes to syslog
  22. #
  23. # WARNING: never run AICCU from DaemonTools or a similar automated
  24. # 'restart' tool/script. When AICCU does not start, it has a reason
  25. # not to start which it gives on either the stdout or in the (sys)log
  26. # file. The TIC server *will* automatically disable accounts which
  27. # are detected to run in this mode.
  28. #
  29. daemonize true
  30. # Automatic Login and Tunnel activation?
  31. automatic true
  32. # Require TLS?
  33. # When set to true, if TLS is not supported on the server
  34. # the TIC transaction will fail.
  35. # When set to false, it will try a starttls, when that is
  36. # not supported it will continue.
  37. # In any case if AICCU is build with TLS support it will
  38. # try to do a 'starttls' to the TIC server to see if that
  39. # is supported.
  40. requiretls false

PF

PF is a stateful packet filter imported from OpenBSD. In my setup, it acts as NAT for IPv4 clients and as firewall for both IPv4 and IPv6 clients.

ftp-proxy

FTP is troublesome. To make it work through PF, enable ftp-proxy (part on the base system, no Port installation needed) and add anchors for it to the rdr and filter sections of pf.conf, as shown below.

/etc/rc.conf
  1. ftpproxy_enable="YES"

UPnP

UPnP allows services inside the LAN to automatically configure port forwards for themselves. It’s a handy way to set up things like P2P applications and games consoles. My home network is a pretty relaxed environment, but don’t enable UPnP if you’re not keen on letting things punch arbitrary holes in your firewall.

Otherwise, grab miniupnpd from Ports or from my pre-built package:

/etc/rc.conf
  1. pkg_add -r miniupnpd

And add anchors for it to the rdr and filter sections of pf.conf, as shown, then edit miniupnpd.conf to suit your environment.

/usr/local/etc/miniupnpd.conf
  1. # WAN network interface
  2. ext_ifname=ue0
  3. # if the WAN interface has several IP addresses, you
  4. # can specify the one to use below
  5. #ext_ip=
  6. # there can be multiple listening ips for receiving SSDP traffic.
  7. # the 1st IP is also used for UPnP Soap traffic.
  8. listening_ip=172.16.0.1
  9. # port for HTTP (descriptions and SOAP) traffic. set 0 for autoselect.
  10. port=5555
  11. # path to the unix socket used to communicate with MiniSSDPd
  12. # If running, MiniSSDPd will manage M-SEARCH answering.
  13. # default is /var/run/minissdpd.sock
  14. #minissdpdsocket=/var/run/minissdpd.sock
  15. # enable NAT-PMP support (default is no)
  16. enable_natpmp=yes
  17. # lease file location
  18. #lease_file=/var/log/upnp.leases
  19. # bitrates reported by daemon in bits per second
  20. bitrate_up=12582912
  21. bitrate_down=3145728
  22. # "secure" mode : when enabled, UPnP client are allowed to add mappings only
  23. # to their IP. (default is yes)
  24. secure_mode=yes
  25. # default presentation url is http address on port 80
  26. #presentation_url=
  27. # report system uptime instead of daemon uptime
  28. system_uptime=yes
  29. # unused rules cleaning.
  30. # never remove any rule before this threshold for the number
  31. # of redirections is exceeded. default to 20
  32. #clean_ruleset_threshold=10
  33. # clean process work interval in seconds. default to 0 (disabled).
  34. # a 600 seconds (10 minutes) interval makes sense
  35. clean_ruleset_interval=600
  36. # notify interval in seconds default is 30 seconds.
  37. #notify_interval=240
  38. # log packets in pf
  39. #packet_log=no
  40. # ALTQ queue in pf
  41. # filter rules must be used for this to be used.
  42. # compile with PF_ENABLE_FILTER_RULES (see config.h file)
  43. queue=upnp
  44. # uuid : generated by the install a new one can be created with
  45. # uuidgen
  46. uuid=1008e7e5-9f48-12df-abef-0010751aa4f1
  47. # UPnP permission rules
  48. # (allow|deny) (external port range) ip/mask (internal port range)
  49. # A port range is <min port>-<max port> or <port> if there is only
  50. # one port in the range.
  51. # ip/mask format must be nn.nn.nn.nn/nn
  52. allow 1024-65535 172.16.0.0/12 1024-65535
  53. deny 0-65535 0.0.0.0/0 0-65535
/etc/rc.conf
  1. miniupnpd_enable="YES"

pf.conf

pf.conf is PF’s configuration file. It holds rules that define how the firewall behaves, such as what ports or services are blocked or allowed and what interfaces get NAT.

Instead of explaining it line-by-line, here’s a well-commented working example from my firewall:

/etc/pf.conf
  1. #Define variables for our interfaces
  2. ext_if="ue0"
  3. int_if="mge0"
  4. lo_if="lo0"
  5. 6_if="gif0"
  6. #Define some IPv4 hosts variables to forward to
  7. #my desktop Emi and my
  8. housemate's NAS wut
  9. Emi = "172.16.0.40"
  10. wut = "172.16.0.103"
  11. #Blocked packets are silently dropped
  12. set block-policy drop
  13. #Enable order-checking in this ruleset
  14. set require-order yes
  15. #Fingerprints file for passive operating system fingerprinting set
  16. fingerprints "/etc/pf.os"
  17. #Log this interface (if using pflog)
  18. set loginterface $ext_if
  19. #Scrub (normalize) incoming packets
  20. scrub in all
  21. #NAT everything except $ext_id. Useful if you have multiple interfaces
  22. #such as LAN and WLAN
  23. #nat on $ext_if inet from ! ($ext_if) to any -> ($ext_if)
  24. # -OR-
  25. #NAT $int_if
  26. nat on $ext_if inet from $int_if:network to any -> ($ext_if)
  27. #FTP-proxy
  28. nat-anchor "ftp-proxy/*"
  29. rdr-anchor "ftp-proxy/*"
  30. rdr pass proto tcp from any to any port ftp -> 127.0.0.1 port 8021
  31. #UPNP
  32. rdr-anchor "miniupnpd"
  33. #httpd on aloe
  34. rdr on $ext_if proto tcp from any to any port http -> $lo_if
  35. #SSH on Emi
  36. rdr on $ext_if proto tcp from any to $ext_if port 220 -> $Emi port 22
  37. #Subsonic on Emi
  38. rdr on $ext_if proto tcp from any to $ext_if port 8180 -> $Emi
  39. #Minecraft on Emi
  40. rdr on $ext_if proto tcp from any to $ext_if port 25565 -> $Emi
  41. #OpenTTD on Emi
  42. rdr on $ext_if proto {tcp, udp} from any to $ext_if port 3978:3979 -> $Emi
  43. #Subsonic on wut
  44. rdr on $ext_if proto tcp from any to $ext_if port 10099 -> $wut port 8180
  45. #SSH on wut
  46. rdr on $ext_if proto tcp from any to $ext_if port 221 -> $wut port 22
  47. # Filtering: the implicit first two rules are
  48. pass in all
  49. pass out all
  50. # block all incoming packets but allow ssh, pass all outgoing tcp and udp
  51. # connections and keep state, logging blocked packets.
  52. block in log all
  53. #nmap
  54. block in log quick on $ext_if inet proto tcp from any to any flags FUP/FUP
  55. #ftp-proxy
  56. anchor "ftp-proxy/*"
  57. #UPNP
  58. anchor "miniupnpd"
  59. #icmp, ping etc
  60. pass in on $ext_if proto icmp all
  61. pass in on $ext_if proto icmp6 all
  62. pass in on $6_if proto icmp6 all
  63. #httpd on aloe
  64. pass in on $ext_if proto tcp from any to any port http
  65. pass in on $6_if proto tcp from any to any port http
  66. #SSH on Emi
  67. pass in on $ext_if proto tcp from any to any port 220
  68. pass in on $6_if proto tcp from any to any port 220
  69. #Subsonic on Emi
  70. pass in on $ext_if proto tcp from any to any port 8180
  71. #Minecraft on Emi
  72. pass in on $ext_if proto tcp from any to any port 25565
  73. #OpenTTD on Emi
  74. pass in on $ext_if proto tcp from any to any port 3978:3979
  75. #Subsonic on wut
  76. pass in on $ext_if proto tcp from any to any port 10099
  77. #SSH on wut
  78. pass in on $ext_if proto tcp from any to any port 221
  79. #allow anything outbound
  80. pass out on $ext_if proto { tcp, udp, icmp } all keep state
  81. #allow anything from LAN
  82. pass in on $int_if all pass on $lo_if all
/etc/rc.conf
  1. pf_enable="YES"
  2. gateway_enable="YES"

IPv4 DHCP

Grab isc-dhcp41-server. It will be used to hand out IPv4 addresses on the network.

/etc/rc.conf
  1. pkg_add -r isc-dhcp41-server

Generate a DNSSEC key

Using the dnssec-keygen utility, generate a key that DHCPD will use to authenticate with named for dynamic DNS. That means when a machine gets an address from DHCPD, the daemon will update named’s zone files with forward and reverse DNS for the new host. You can name the key anything you please. I called mine dhcp-ddns.

/etc/rc.conf
  1. dnssec-keygen -a hmac-md5 -b 512 -n USER dhcp-ddns

It will spit out two key files. Grab the Base64-encoded key from either file. It will look like a random string of characters.

In this example there are two defined DNS zones. One forward, aloe.cooltrainer.org., and one reverse, 0.16.172-in-addr.arpa.

My dhcpd.conf has some defined hosts. These systems can use DHCP for ease of configuration but will always get the same address.

/usr/local/etc/dhcpd.conf
  1. default-lease-time 600;
  2. max-lease-time 7200;
  3. ddns-update-style interim;
  4. update-static-leases on;
  5. authoritative;
  6. option domain-name "aloe.cooltrainer.org";
  7. #Normally, include DNSSEC key to prevent exposing it in git
  8. #include "/usr/local/etc/dhcp-ddns.key";
  9. #For this example, though, a fake key:
  10. key dhcp-ddns {
  11. algorithm HMAC-MD5.SIG-ALG.REG.INT;
  12. secret /YH97F5944576ghgjjkNMSOuu+4NaIlx/ZiJhbhmfgiuy8945ghMmH850xDtSByZtpmVPg6oDfJehG/AnA==;
  13. }
  14. zone aloe.cooltrainer.org.
  15. {
  16. primary 127.0.0.1;
  17. key dhcp-ddns;
  18. }
  19. zone 0.16.172.in-addr.arpa.
  20. {
  21. primary 127.0.0.1;
  22. key dhcp-ddns;
  23. }
  24. subnet 172.16.0.0 netmask 255.255.255.0 {
  25. option domain-name-servers
  26. 172.16.0.1;
  27. range 172.16.0.150 172.16.0.250;
  28. option routers 172.16.0.1;
  29. option time-servers 172.16.0.1;
  30. option subnet-mask 255.255.255.0;
  31. }
  32. group {
  33. option domain-name "aloe.cooltrainer.org";
  34. ddns-domainname "aloe.cooltrainer.org";
  35. host emi {
  36. hardware ethernet 00:1D:7D:AA:FA:3D;
  37. fixed-address 172.16.0.40;
  38. option host-name "emi";
  39. ddns-hostname "emi";
  40. }
  41. host minamo {
  42. hardware ethernet 00:1B:21:3C:FB:C5;
  43. fixed-address 172.16.0.45;
  44. option host-name "minamo";
  45. ddns-hostname "minamo";
  46. }
  47. host wut {
  48. hardware ethernet 00:1B:21:51:AB:97;
  49. fixed-address 172.16.0.103;
  50. option host-name "wut";
  51. ddns-hostname "wut";
  52. }
  53. host jetdirect {
  54. hardware ethernet 00:01:e6:46:ca:ce;
  55. fixed-address 172.16.0.30;
  56. option host-name "jetdirect";
  57. ddns-hostname "jetdirect";
  58. }
  59. host espresso {
  60. hardware ethernet 38:e7:d8:7f:7c:82;
  61. fixed-address 172.16.0.140;
  62. option host-name "espresso";
  63. ddns-hostname "espresso";
  64. }
  65. }
/etc/rc.conf
  1. dhcpd_enable="YES"
  2. dhcpd_ifaces="mge0"

DNS

BIND named is part of the base system, version 9.6 as of FreeBSD 8.2. Most of my configuration file is based on the examples from the FreeBSD handbook with the addition of my DDNS key, my two zones, and some upstream DNS servers (Comcast, Google) to forward and cache.

Named is run jailed by default, so its configuration file can be found in /var/named/etc/namedb/.

/var/named/etc/namedb/named.conf
  1. options {
  2. directory "/etc/namedb/working";
  3. pid-file "/var/run/named/pid";
  4. dump-file "/var/dump/named_dump.db";
  5. statistics-file "/var/stats/named.stats";
  6. listen-on { 127.0.0.1; 172.16.0.1; };
  7. listen-on-v6 { ::1; 2001:4830:1632::210:75ff:fe1a:a4f1; };
  8. // These zones are already covered by the empty zones listed below.
  9. // If you remove the related empty zones below, comment these lines out.
  10. disable-empty-zone "255.255.255.255.IN-ADDR.ARPA";
  11. disable-empty-zone "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA";
  12. disable-empty-zone "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA";
  13. forwarders {
  14. //Comcast Atlanta Primary
  15. 68.87.68.162;
  16. //Comcast Atlanta Secondary
  17. 68.87.74.162;
  18. //Comcast Atlanta v6 Primary
  19. 2001:558:100A:4:68:87:68:162;
  20. //comcast Atlanta v6 Secondary
  21. 2001:558:1012:6:68:87:74:162;
  22. //Google Primary
  23. 8.8.8.8;
  24. //Google Secondary
  25. 8.8.4.4;
  26. //Google v6 Primary
  27. 2001:4860:4860::8888;
  28. //Google v6 Secondary
  29. 2001:4860:4860::8844;
  30. };
  31. forward only;
  32. };
  33. zone "." { type hint; file "/etc/namedb/named.root"; };
  34. /* Serving the following zones locally will prevent any queries
  35. for these zones leaving your network and going to the root
  36. name servers. This has two significant advantages:
  37. 1. Faster local resolution for your users
  38. 2. No spurious traffic will be sent from your network to the roots
  39. */
  40. // RFC 1912 (and BCP 32 for localhost)
  41. zone "localhost" { type master; file "/etc/namedb/master/localhost-forward.db"; };
  42. zone "127.in-addr.arpa" { type master; file "/etc/namedb/master/localhost-reverse.db"; };
  43. zone "255.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  44. // RFC 1912-style zone for IPv6 localhost address
  45. zone "0.ip6.arpa" { type master; file "/etc/namedb/master/localhost-reverse.db"; };
  46. // "This" Network (RFCs 1912 and 3330)
  47. zone "0.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  48. // Private Use Networks (RFC 1918)
  49. zone "10.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  50. zone "16.172.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  51. zone "17.172.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  52. zone "18.172.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  53. zone "19.172.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  54. zone "20.172.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  55. zone "21.172.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  56. zone "22.172.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  57. zone "23.172.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  58. zone "24.172.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  59. zone "25.172.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  60. zone "26.172.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  61. zone "27.172.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  62. zone "28.172.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  63. zone "29.172.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  64. zone "30.172.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  65. zone "31.172.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  66. zone "168.192.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  67. // Link-local/APIPA (RFCs 3330 and 3927)
  68. zone "254.169.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  69. // TEST-NET-[1-3] for Documentation (RFC 5737)
  70. zone "2.0.192.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  71. zone "100.51.198.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  72. zone "113.0.203.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  73. // IPv6 Range for Documentation (RFC 3849)
  74. zone "0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  75. // Domain Names for Documentation and Testing (BCP 32)
  76. zone "test" { type master; file "/etc/namedb/master/empty.db"; };
  77. zone "example" { type master; file "/etc/namedb/master/empty.db"; };
  78. zone "invalid" { type master; file "/etc/namedb/master/empty.db"; };
  79. zone "example.com" { type master; file "/etc/namedb/master/empty.db"; };
  80. zone "example.net" { type master; file "/etc/namedb/master/empty.db"; };
  81. zone "example.org" { type master; file "/etc/namedb/master/empty.db"; };
  82. // Router Benchmark Testing (RFC 3330)
  83. zone "18.198.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  84. zone "19.198.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  85. // IANA Reserved - Old Class E Space
  86. zone "240.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  87. zone "241.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  88. zone "242.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  89. zone "243.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  90. zone "244.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  91. zone "245.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  92. zone "246.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  93. zone "247.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  94. zone "248.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  95. zone "249.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  96. zone "250.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  97. zone "251.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  98. zone "252.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  99. zone "253.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  100. zone "254.in-addr.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  101. // IPv6 Unassigned Addresses (RFC 4291)
  102. zone "1.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  103. zone "3.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  104. zone "4.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  105. zone "5.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  106. zone "6.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  107. zone "7.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  108. zone "8.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  109. zone "9.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  110. zone "a.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  111. zone "b.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  112. zone "c.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  113. zone "d.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  114. zone "e.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  115. zone "0.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  116. zone "1.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  117. zone "2.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  118. zone "3.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  119. zone "4.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  120. zone "5.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  121. zone "6.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  122. zone "7.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  123. zone "8.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  124. zone "9.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  125. zone "a.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  126. zone "b.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  127. zone "0.e.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  128. zone "1.e.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  129. zone "2.e.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  130. zone "3.e.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  131. zone "4.e.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  132. zone "5.e.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  133. zone "6.e.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  134. zone "7.e.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  135. // IPv6 ULA (RFC 4193)
  136. zone "c.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  137. zone "d.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  138. // IPv6 Link Local (RFC 4291)
  139. zone "8.e.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  140. zone "9.e.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  141. zone "a.e.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  142. zone "b.e.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  143. // IPv6 Deprecated Site-Local Addresses (RFC 3879)
  144. zone "c.e.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  145. zone "d.e.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  146. zone "e.e.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  147. zone "f.e.f.ip6.arpa" { type master; file "/etc/namedb/master/empty.db"; };
  148. // IP6.INT is Deprecated (RFC 4159)
  149. zone "ip6.int" { type master; file "/etc/namedb/master/empty.db"; };
  150. # Normally, don't expose our DDNS key in git
  151. include "/etc/namedb/rndc.key";
  152. #include "/etc/namedb/dhcp-ddns.key";
  153. #For this example, though, a fake key:
  154. key dhcp-ddns {
  155. algorithm hmac-md5;
  156. secret "/YH97F5944576ghgjjkNMSOuu+4NaIlx/ZiJhbhmfgiuy8945ghM mH850xDtSByZtpmVPg6oDfJehG/AnA==";
  157. };
  158. controls {
  159. inet * allow { 127.0.0.1; ::1; } keys { "rndc-key"; "dhcp-ddns"; };
  160. };
  161. acl clients {
  162. 172.16.0.0/12;
  163. 2001:4830:1632::/64;
  164. 127.0.0.0/8;
  165. ::1/128;
  166. };
  167. zone "aloe.cooltrainer.org." {
  168. type master;
  169. file "/etc/namedb/dynamic/aloe.cooltrainer.org.conf";
  170. allow-update {
  171. key dhcp-ddns;
  172. };
  173. notify no;
  174. };
  175. zone "0.16.172.in-addr.arpa." {
  176. type master;
  177. file "/etc/namedb/dynamic/0.16.172.in-addr.arpa";
  178. allow-update {
  179. key dhcp-ddns;
  180. };
  181. notify no;
  182. };
  183. #Log to /var/named/var/log/named.log
  184. #logging {
  185. # channel log_file { file "/var/log/named.log" size 10M;
  186. # severity debug 3 ; };
  187. # category queries { log_file; };
  188. # category xfer-in { log_file; };
  189. # category xfer-out { log_file; };
  190. # category default { log_file; };
  191. #};

Once dhcpd and named are both configured, DHCP clients should get forward and reverse DNS records.

/var/named/etc/namedb/named.conf
  1. $ORIGIN 0.16.172.in-addr.arpa.
  2. 1 PTR aloe.cooltrainer.org.
  3. $TTL 300 ; 5 minutes
  4. 140 PTR espresso.aloe.cooltrainer.org.
  5. 146 PTR asholette.aloe.cooltrainer.org.
  6. 166 PTR Eliott-Desktop.aloe.cooltrainer.org.
  7. $TTL 3600 ; 1 hour
  8. 30 PTR jetdirect.aloe.cooltrainer.org.
  9. $TTL 600 ; 10 minutes
  10. 40 PTR emi.aloe.cooltrainer.org.
/var/named/etc/namedb/named.conf
  1. $ORIGIN aloe.cooltrainer.org.
  2. $TTL 300 ; 5 minutes
  3. android-e27f9bc6e9c5fb24 A 172.16.0.140
  4. TXT "006e8238a52a03576df0c055e018bc2e12"
  5. asholette A 172.16.0.146
  6. TXT "319708bac638ca09e7aff3e696ad654c0c"
  7. Eliott-Desktop A 172.16.0.166
  8. TXT "00a2411b8e29027733b2220817956cd8a0"
  9. $TTL 600 ; 10 minutes
  10. emi A 172.16.0.40
  11. $TTL 300 ; 5 minutes
  12. espresso A 172.16.0.140
  13. TXT "006e8238a52a03576df0c055e018bc2e12"
  14. $TTL 3600 ; 1 hour
  15. JetDirect A 172.16.0.30
  16. TXT "003f54fdfc7dd203d6a1573fde3a487cf2"

Using DNS this way simplifies home networking. One can set a mount point for a hostname, for example, so the actual IP address doesn’t matter and can change.

  1. [nicole@emi#etc]host emi
  2. emi.aloe.cooltrainer.org has address 172.16.0.40
  3. [nicole@emi#etc]host 172.16.0.40
  4. 40.0.16.172.in-addr.arpa domain name pointer emi.aloe.cooltrainer.org.
/etc/rc.conf
  1. named_enable="YES"

Stateless IPv6 autoconfiguration (SLAAC)

Now, we need a way to hand out IPv6 addresses from our subnet to clients on the LAN. The easiest method is rtadvd, the router advertisement daemon, inherited in BSD from the KAME project.

rtadvd is very simple to configure. The only piece of required information is your IPv6 subnet, 2001:4830:1632::/64 in my case.

/etc/rtadvd.conf
  1. mge0:\
  2. :addrs#1:addr="2001:4830:1632::":prefixlen#64:
/etc/rc.conf
  1. rtadvd_enable="YES"
  2. rtadvd_interfaces="mge0"

Stateful IPv6 autoconfiguration (DHCPv6)

SLAAC is very easy to set up and use, but it can’t interact with named like DHCP can. Luckily, ISC-DHCP supports DHCPv6 as of version 4.0. I’ve yet to set this up, but I will update this section of this post when I do.

Network Time

I prefer OpenBSD’s OpenNTPD to the ntpd in FreeBSD’s base system. Install it.

/etc/rc.conf
  1. pkg_add -r openntpd

Its configuration, then, is incredibly straightforward. It listens on localhost and on mge0. I use servers from the pool.ntp.org project.

The -s argument allows for a large time correction with openntpd instead of using ntpdate or rdate. This is important on the Dockstar, because it has no real time clock hardware and needs a large time jump at boot.

/usr/local/etc/ntpd.conf
  1. # Addresses to listen on (ntpd does not listen by default)
  2. #listen on *
  3. listen on 127.0.0.1
  4. listen on ::1
  5. listen on 172.16.0.1
  6. listen on 2001:4830:1632::210:75ff:fe1a:a4f1
  7. # sync to a single server
  8. #server ntp.example.org
  9. # use a random selection of NTP Pool Time Servers
  10. # see http://support.ntp.org/bin/view/Servers/NTPPoolServers
  11. servers pool.ntp.org
/etc/rc.conf
  1. openntpd_enable="YES"
  2. openntpd_flags="-s"

Inadyn

I have aloe.cooltrainer.org delegated to afraid.org’s freedns service to chase my dynamic IPv4 WAN address.

  1. ;; AUTHORITY SECTION:
  2. aloe.cooltrainer.org. 86400 IN NS ns3.afraid.org.
  3. aloe.cooltrainer.org. 86400 IN NS ns4.afraid.org.
  4. aloe.cooltrainer.org. 86400 IN NS ns1.afraid.org.
  5. aloe.cooltrainer.org. 86400 IN NS ns2.afraid.org.

Inadyn is a small utility to inform afraid.org when our IP changes.

  1. pkg_add -r inadyn
/usr/local/etc/inadyn.conf
  1. --dyndns_system default@freedns.afraid.org
  2. --alias aloe.cooltrainer.org
  3. --dyndns_server_name freedns.afraid.org
  4. --dyndns_server_url /dynamic/update.php?blahblahuseyourown
  5. --update_period_sec 400
  6. --verbose 0
  7. --background
/etc/rc.conf
  1. inadyn_enable="YES"

Wireless

My old WRT54G was re-purposed as a wireless access point for this project, using a BrainSlayer build of DD-WRT r14929 (dd-wrt.v24_nokaid_generic.bin) that supports IPv6. It’s configured with a static IP for itself and forwards DHCP and SLAAC advertisements to wireless clients with the configuration described here.

Wrap-up

Everything on the LAN should now receive an IPv4 and an IPv6 address, get network time, get cached DNS, and have no trouble opening ports through the IPv4 NAT.

Playstation 3 connection test screen showing results for my router. Xbox 360 connection test screen showing results for my router. Network preferences in Transmission Bittorrent client showing port open via UPNP. Perfect results for my network from test-ipv6.com

Here’s a sample of the new machine’s throughout and packet-pushing performance while running several dozen well-seeded torrents at once, generated with pfstat. Only IPv4 is shown, since there weren’t enough fellow IPv6 hosts to make the test worthwhile. Our connection is rated for 12Mbit down, 3 up, hence the hard cutoff near 12Mbit/s.

pfstat bandwidth utilization graph showing a steady 12MBit. pfstat packet statistics chart.

It didn’t let me down!

My config files for Aloe are stored in Aloe’s GitHub repo. Check them out if you need an example!

  1. This is a joke