And here we go again. The problem with the previous configuration is that DNS doesn’t work: you can ping any IP address you like across the open internet, but URLs weren’t being resolved. That is, ping works fine, but ping failed completely.

So this is a variation that focuses on unbound. The rest of the settings and architecture are included, because unbound does not exist in a vaccume - but they’re just a sideshow.

As before, we assume this is a fresh installation of OpenBSD 6.9 on a dedicated physical box which has separate ethernet ports for each subnet and the egress point.

We’re using another source:

Looks like there’s a specific domain I should use for my local network, since I haven’t registered one properly: Guess that’s a holdover from the early days.

Some people recommend that you register a domain name and then use that internally on your LAN, and while that certainly works, it is not necessary at all. According to the RFC 8375 you should use the domain as this is meant to be used inside a small network, such as a home network.

network design

Nonstandard diagram, but might be a little less obtuse than the standard.

| open internet               |
| ISP-assigned router         |
| device: em2                 |
| firewall / router           |
| (OpenBSD)                   |
  |  |
  |  |
  |  |          +------------+-----------------------+
  |  |  +------------------------+   |            |                       |
  |  +--+ (insecure network)     +---+   +--------+---------+  +----------+----------+
  |     | device: em0            |       |wifi access points|  |laptops, servers, etc|
  |     +------------------------+       +------------------+  +---------------------+
  |             +------------+-----------------------+
  |     +------------------------+   |            |                       |
  +-----+ (secure network)       +---+   +--------+---------+  +----------+----------+
        | device: em1            |       |wifi access points|  |laptops, servers, etc|
        +------------------------+       +------------------+  +---------------------+

Note, depending on the OpenBSD firewall/router box configuration, there can also be virtual machines inside that box which are connected to one or more of the subnets.

The router will have an ip address on each of the subnets; this is referred to as the ‘gateway address’, since the router is literally a gateway out of the sub-network. That IP address is usually the first in the available series: i.e., and in the two examples above.

I like having the wireless clients on the same subnet as the wired clients, but that’s my use case. It can be nearly as easy to put all the wifi APs on their own subnet.

router construction


echo 'net.inet.ip.forwarding=1' > /etc/sysctl.conf
echo 'dhcp'                                                                  > /etc/hostname.em2
echo 'inet description   "secure network"' > /etc/hostname.em0
echo 'inet description "insecure network"' > /etc/hostname.em1

DHCP (IP address assignment)

Start the dhcpd daemon, and tell it which network device ports will need the dhcp service available.

rcctl enable dhcpd
rcctl set dhcpd flags em0 em1


option domain-name "";

subnet netmask {
        option routers;
        option domain-name-servers;
subnet netmask {
        option routers;
        option domain-name-servers;

PF (firewall)


secure   = "em0"
insecure = "em1"
external = "em2"

table <martians> {     \

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 $secure $insecure }
block in quick on egress from <martians> to any
block return out quick on egress from any to <martians>
block all

pass out quick inet
# pass out on $external inet from $secure:network   to any nat-to ($external)
# pass out on $external inet from $insecure:network to any nat-to ($external)

pass in on { $secure $insecure } inet


Enable the unbound DNS daemon.

rcctl enable unbound

Create an unbound-specific log file.

mkdir /var/unbound/log
touch /var/unbound/log/unbound.log
chown -R root._unbound /var/unbound/log
chmod -R 774 /var/unbound/log



    # Logging (default is no).
    # Uncomment this section if you want to enable logging.
    # Note enabling logging makes the server (significantly) slower.
    # verbosity: 2
    # log-queries: yes
    # log-replies: yes
    # log-tag-queryreply: yes
    # log-local-actions: yes
    # logfile: "/log/unbound.log"
    # use-syslog: no
    # log-time-ascii: yes


    # Control who has access.
    access-control: refuse
    access-control: ::0/0 refuse
    access-control: ::1 allow

    access-control: allow
    access-control: allow
    access-control: allow

    # "id.server" and "hostname.bind" queries are refused.
    hide-identity: yes

    # "version.server" and "version.bind" queries are refused.
    hide-version: yes

    # Cache elements are prefetched before they expire to keep the cache up to date.
    prefetch: yes

    # Our LAN segments.
    # local domain

    # otherwise unbound tries to get DNS info from IPV6 addresses:
    # "info: error sending query to auth server"
    do-ip6: no

    # save cached lookups for way longer than I should
    #   3600 = 1 hour
    #  86400 = 1 day
    # 604800 = 1 week
    cache-min-ttl: 604800
    serve-expired: yes

    # use an updated root hints file
    root-hints: "/var/unbound/etc/named.cache"

    # We want DNSSEC validation.
    # auto-trust-anchor-file: "/var/unbound/db/root.key"

# Enable the usage of the unbound-control command.
    control-enable: yes
    control-interface: /var/run/unbound.sock


We want the router to use its own unbound DNS cache.


# nameserver

echo "nameserver" > /etc/resolv.conf

Problem is, this gets overwritten every time dhcp runs, because part of the dhcp protocol is a record of the server which provided the IP address. We have our own such server, and we want to use it instead.


interface "em2" { 
    ignore domain-name-servers;