OpenBSD Home Router : IPv6

This guide is an unofficial sequel to the OpenBSD PF FAQ: router example.

If you have a home router configured based on that example, and your ISP has just announced IPv6 availability: read on.

Concepts

Requirements

RFC 7084, Basic Requirements for IPv6 Customer Edge Routers, specifies how an IPv6 router should function in a residential environment. With the OpenBSD PF example as the starting point, the changes required are:

  1. On the external interface: act as a DHCPv6 client to receive a Prefix Delegation

  2. On the internal interfaces: serve Router Advertisements for local hosts, which is necessary for SLAAC

  3. Adjust the firewall ruleset to allow the Neighbor Discovery Protocol and DHCPv6 to function

Firewall

Enable IPv6 routing:

# echo 'net.inet6.ip.forwarding=1' >> /etc/sysctl.conf

Then update your ruleset to:

/etc/pf.conf
wired = "em1"
wifi = "athn0"
table <martians> {
  0.0.0.0/8 10.0.0.0/8 100.64.0.0/10            \
  127.0.0.0/8 169.254.0.0/16 172.16.0.0/12      \
  192.0.0.0/24 192.0.2.0/24 192.88.99.0/24      \
  192.168.0.0/16 198.18.0.0/15 198.51.100.0/24  \
  203.0.113.0/24 224.0.0.0/3 255.255.255.255/32 \
  ::/128 ::/96 ::1/128 ::ffff:0:0/96 100::/64   \
  2001:10::/28 2001:2::/48 2001:db8::/32        \
  3ffe::/16 fec0::/10 fc00::/7 }
set block-policy drop
set loginterface egress
set skip on lo0
match in all scrub (no-df random-id max-mss 1440)
match out on egress inet from !(egress:network) to any nat-to (egress:0)
antispoof quick for { egress $wired $wifi }
block in quick on egress from <martians> to any
block return out quick on egress from any to <martians>
block all
pass out quick
pass in on { $wired $wifi }
pass in on egress inet proto tcp from any to (egress) port { 80 443 } rdr-to 192.168.1.2
pass in on egress inet6 proto icmp6 all icmp6-type { routeradv neighbrsol neighbradv }
pass in on egress inet6 proto udp from fe80::/10 port dhcpv6-server to fe80::/10 port dhcpv6-client no state

ICMP

You should allow echo requests (aka ping) for diagnostics.

pass in on egress inet6 proto icmp6 all icmp6-type echoreq
pass in on egress inet proto icmp all icmp-type echoreq

For everything else, read RFC 4890, Recommendations for Filtering ICMPv6 Messages in Firewalls, and decide if any other packet types have a place in your network.

Other Considerations

DHCPv6

The OpenBSD base system does not have a DHCPv6 client at present.

Install and configure dhcpcd to do the prefix delegation request to obtain a chunk of public addresses for the local network.

# pkg_add dhcpcd
# rcctl enable dhcpcd

Configure dhcpcd to request a prefix delegation and assign prefixes to all the internal interfaces.

/etc/dhcpcd.conf
ipv6only
noipv6rs
duid
persistent
option rapid_commit
require dhcp_server_identifier

script ""

allowinterfaces em0 em1 athn0
interface em0
  ipv6rs
  ia_pd 1 em1/1 athn0/2

See dhcpcd.conf(5) and the pkg-readme for details

SLAAC

The rad daemon serves Router Advertisements. Hosts on the local network use these to assign themselves IPv6 addresses, which means that you often don’t need to run a DHCPv6 server.

# echo 'interface em1' >> rad.conf
# echo 'interface athn0' >> rad.conf
# rcctl enable rad

Other nodes on your network (eg a wireless access point in bridge mode) might also be sending router advertisements. Check the output of ndp -an for nodes with the R (Router) flag set. Any misbehaving nodes might need to have forwarding sysctl’s turned off to stop them generating unnecessary traffic.

DNS

The unbound DNS resolver will function well with the same configuration as shown in the FAQ example. It will run over IPv4, but that doesn’t matter—the DNS responses will have the appropriate AAAA records which contain IPv6 addresses.

There are a few knobs you can tweak—unrelated to IPv6—that can improve your network.

DNS Filtering (Blocking Ads)

Choose which type of hosts you wish to block, such as from one of the lists maintained by StevenBlack.

Automate fetching your chosen blocklist, and converting it into a form that is suitable for unbound. Adapt this dnsblock.sh script and let it run regularly (eg by calling it from /etc/weekly.local). Use the include server option to insert the blocklist rules in the main unbound config file.

Protection from DNS Rebinding

DNS rebinding attacks subvert the same-origin policy and convert browsers into open network proxies. These attacks can circumvent firewalls to access internal documents and services.

Networks can be protected against firewall circumvention by forbidding external host names from resolving to internal IP addresses.

— Protecting Browsers from DNS Rebinding Attacks

Use the private-address server option in unbound.conf to protect against these attacks.

/var/unbound/etc/unbound.conf
server:
  interface: 192.168.1.1
  interface: 192.168.2.1
  interface: 127.0.0.1
  access-control: 192.168.1.0/24 allow
  access-control: 192.168.2.0/24 allow
  do-not-query-localhost: no
  hide-identity: yes
  hide-version: yes

  private-address: 10.0.0.0/8
  private-address: 127.0.0.0/8
  private-address: 169.254.0.0/16
  private-address: 172.16.0.0/12
  private-address: 192.168.0.0/16
  private-address: ::1/128
  private-address: ::ffff:0:0/96
  private-address: fd00::/8
  private-address: fe80::/10

  include: /usr/local/etc/blocklist.conf

forward-zone:
  name: "."
  forward-addr: 1.2.3.4  # IP of the upstream resolver

Done

Reboot and enjoy.