When I built my OpenBSD router, I separated my wifi and Ethernet networks, because the bridge solution was too slow.

I came back to it, because of OpenBSD. Actually, one of my laptops runs OpenBSD isotop, which uses trunk, which is a failover system : if my cable connection fails (which happens quite often with a laptop), the connection passes automatically on wifi.

Here is the thing.

I needed the laptop's two network interfaces, wifi and Ethernet, to be in the same logical network (apparently). So it did not go so well as they were really separated.

So I tried again to put both router's lan interface on a bridge. And there it worked.

Interfaces’ configuration

As a general rule, I did like the wifi network was absorbed in the Ethernet one. The wifi network had only mobile connections, no fixed hosts. Opposite, the Ethernet network has fixed hosts, first and foremost, the server itself. So better use the original eth interface configuration on the bridge to avoid unnecessary renumbering and debugging.

The re0 interface facing the $internet does not change.

/etc/hostname.bridge0

Just setting the bridge's members.

add vether0
add re2
add athn0
up

/etc/hostname.vether0

We setup the lan virtual interface, vether0. The dhcpv6 client shall give it a public ipv6 address later. This is truly a copy of the original /etc/hostname.re2.

inet6           fd00:22:dec:e2::1               64
inet            10.0.0.1     255.255.0.0     10.0.255.255
up

/etc/hostname.athn0

The two physical interfaces have no more network config elements.

mediaopt        hostap mode 11n
chan            6
nwid            ...
wpaakms         psk
wpaprotos       wpa2
wpakey          ...
up

/etc/hostname.re2

up

DHCPv6 client

I still use Dhcpcd. Its job is mainly to get the ipv6 prefix from my ISP and add the public address to vether0.

# Persist interface configuration when dhcpcd exits.
persistent

interface re0
        nooption dhcp6_vivco
        ...
        ia_na 1

    # 226 = e2 en hexa; 160 = a0 en hexa
    #ia_pd 1 re2/226/64 athn0/160/64

    ia_pd 1 vether0/226/64

fqdn ptr

PF

PF has to use both virtual and physical interfaces :

eth="re2"
wifi="athn0"
lan="vether0"
internet="re0"

## General

set block-policy drop
set skip on lo
...
antispoof       for ($lan)
...
pass in quick on { $lan, $eth,$wifi }   proto udp   to any    port bootps
...
pass        from { (self), ($lan:network) }

Macros

First, let's have a look at the macros in the first lines which shall help make it easier to read.

eth="re2"
wifi="athn0"
lan="vether0"
internet="re0"

Antispoof

The antispoof line requests to block all connections pretending to have origin addresses inside the $lan network, but which actually don't come from this very same network and thus are actually fraudulent.

antispoof       for ($lan)

DHCP requests authorizations

NB: this explanation is really my best guest. I cannot pretend to understand it all.

This line let pass all DHCP requests coming in on Ethernet and wifi interfaces. DHCP requests actually arrive on those interfaces first before being assigned to vether0 and have not yet any actual address with $lan network. Which make sense as its the DHCP protocol's job.

pass in quick on { $lan, $eth,$wifi }   proto udp   to any  port bootps

It also accept connections on $lan interfaces because when renewing leases, it already has an established address.

I believe it's the reason you need to let pass on both virtual and physical interfaces. Without this line I cannot run the network.

Last line

pass        from { (self), ($lan:network) }

Finally the last line let pass all packets from the router itself, with the well-named self keyword, or $lan network.

Dnsmasq

I still use Dnsmasq as DHCP server and local DNS.

# If you want dnsmasq to listen for DHCP and DNS requests only on
# specified interfaces (and the loopback) give the name of the
# interface (eg eth0) here.
# Repeat the line for more than one interface.

interface=lo0
interface=vether0

################ eth
ra-param=vether0,low,1000,3600
dhcp-range=::100,::1000,constructor:vether0,ra-names,slaac,ra-stateless
dhcp-range=vether0,10.0.0.50,10.0.255.250,255.255.0.0,12h

Once again I just took the original eth configuration, changed the name for vether0 and commented wifi interface options.