Score:1

Simple OpenBSD NAT router not working

me flag

I'm toying around with using OpenBSD as a NAT router behind Starlink but efforts with the NAT are failing even in the simplest cases. I've probably simply been staring at it too long and need an extra set of eyes.

Here's the essential setup.

[Starlink 192.168.1.1] <=> <igc0: 192.168.1.189> [OpenBSD Router "cerby"] igc3:172.16.16.1 <=> {LAN}

ifconfig igcN

cerby$ ifconfig igc0
igc0: flags=808843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,AUTOCONF4> mtu 1500
    lladdr 7c:2b:e1:13:2f:35
    index 1 priority 0 llprio 3
    groups: egress
    media: Ethernet autoselect (1000baseT full-duplex)
    status: active
    inet 192.168.1.189 netmask 0xffffff00 broadcast 192.168.1.255
cerby$ ifconfig igc3
igc3: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    lladdr 7c:2b:e1:13:2f:38
    index 4 priority 0 llprio 3
    media: Ethernet autoselect (1000baseT full-duplex)
    status: active
    inet 172.16.16.1 netmask 0xffffff00 broadcast 172.16.16.255

sysctl

cerby$ sysctl | grep -i forwarding
net.inet.ip.forwarding=1
net.inet.ip.mforwarding=0
net.inet6.ip6.forwarding=0
net.inet6.ip6.mforwarding=0

pf.conf

This is one of dozens of configs I've experimented with, just trying to get the NAT forwarding working:

ext = "igc0"
int  = "igc3"
table <martians> { 0.0.0.0/8 10.0.0.0/8 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 224.0.0.0/3 \
                   198.18.0.0/15 198.51.100.0/24        \
                   203.0.113.0/24 }
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 $ext $int }
# 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 in on { $ext $int } inet
pass in on egress inet proto tcp from any to (egress) port { 80 443 } rdr-to 172.16.16.1

I've tried disabling every variant of the blocking, etc., to no avail

pfctl -sr

cerby$ doas pfctl -sr
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) round-robin
block drop in quick on ! egress inet from 192.168.1.0/24 to any
block drop in quick inet from 192.168.1.189 to any
block drop in quick on ! igc0 inet from 192.168.1.0/24 to any
block drop in quick on ! igc3 inet from 172.16.16.0/24 to any
block drop in quick inet from 172.16.16.1 to any
block drop all
pass out quick inet all flags S/SA
pass in on igc0 inet all flags S/SA
pass in on igc3 inet all flags S/SA
pass in on egress inet proto tcp from any to (egress) port = 80 flags S/SA rdr-to 172.16.16.1
pass in on egress inet proto tcp from any to (egress) port = 443 flags S/SA rdr-to 172.16.16.1

pf2.conf

Just to eliminate the possibility that some sort of blocking is happening:

extIF = "igc0"
lanIF  = "igc3"
table <martians> { 0.0.0.0/8 10.0.0.0/8 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 224.0.0.0/3 \
           198.18.0.0/15 198.51.100.0/24        \
           203.0.113.0/24 }
lan = $lanIF:network
extIP = 192.168.1.189
set block-policy drop
set loginterface egress
set skip on lo0
pass from { lo0 $lan } to any keep state
match out on $extIF from $lan to any nat-to $extIP
pass out on $extIF from $lan to any nat-to ($extIF)
pass out quick inet
pass in on { $extIF $lanIF } inet
pass in on egress inet proto tcp from any to (egress) port { 80 443 } rdr-to 172.16.16.1

pfctl -sr

cerby$ doas pfctl -sr
pass inet6 from ::1 to any flags S/SA
pass on lo0 inet6 from fe80::1 to any flags S/SA
pass inet from 127.0.0.1 to any flags S/SA
pass inet from 172.16.16.0/24 to any flags S/SA
match out on igc0 inet from 172.16.16.0/24 to any nat-to 192.168.1.189
pass out on igc0 inet from 172.16.16.0/24 to any flags S/SA nat-to (igc0) round-robin
pass out quick inet all flags S/SA
pass in on igc0 inet all flags S/SA
pass in on igc3 inet all flags S/SA
pass in on egress inet proto tcp from any to (egress) port = 80 flags S/SA rdr-to 172.16.16.1
pass in on egress inet proto tcp from any to (egress) port = 443 flags S/SA rdr-to 172.16.16.1

route show

cerby$ doas route show
Routing tables

Internet:
Destination        Gateway            Flags   Refs      Use   Mtu  Prio Iface
default            192.168.1.1        UGS        5  1107992     -     8 igc0
base-address.mcast localhost          URS        0     2730 32768     8 lo0
127/8              localhost          UGRS       0        0 32768     8 lo0
localhost          localhost          UHhl       1      425 32768     1 lo0
172.16.16/24       172.16.16.1        UCn        1        0     -     4 igc3
172.16.16.1        7c:2b:e1:13:2f:38  UHLl       0    10738     -     1 igc3
172.16.16.100      a8:60:b6:2f:f6:0a  UHLc       2       69     -     3 igc3
172.16.16.255      172.16.16.1        UHb        0       39     -     1 igc3
192.168.1/24       192.168.1.189      UCn        2    42319     -     4 igc0
192.168.1.1        74:24:9f:c0:80:26  UHLch      2    53063     -     3 igc0
192.168.1.35       80:ea:96:e5:c2:dc  UHLc       2    42272     -     3 igc0
192.168.1.189      7c:2b:e1:13:2f:35  UHLl       0   117169     -     1 igc0
192.168.1.255      192.168.1.189      UHb        0      543     -     1 igc0

dhcpd.conf

cerby$ cat /etc/dhcpd.conf
subnet 172.16.16.0 netmask 255.255.255.0 {
  option domain-name-servers 172.16.16.1;
  option routers 172.16.16.1;
  range 172.16.16.100 172.16.16.200;
}

Observations

  • A host on the LAN has no problem connecting to the router at 172.16.16.1
  • DNS lookups work via unbound on 172.16.16.1,
  • pinging 172.16.16.1 works fine,
  • the default route on the LAN machine is 172.16.16.1.
  • pinging 192.168.1.189 works
  • pinging 192.168.1.1 does not work

Everything seems to be as expected however packets just aren't being sent through i.e. the stateful part isn't working.

It feels like I'm surely missing something obvious.

Score:0
me flag

The answer seemed to be to switch to a different network e.g. 192.168.2.x:

hostname.igc3

cerby$ cat /etc/hostname.igc3
inet 192.168.2.1 255.255.255.0 NONE

pf.conf

cerby$ cat /etc/pf-2022-10-27.conf
extIF = "igc0"
lanIF  = "igc3"
table <martians> { 0.0.0.0/8 10.0.0.0/8 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 224.0.0.0/3 \
           198.18.0.0/15 198.51.100.0/24        \
           203.0.113.0/24 }
lan = $lanIF:network
extIP = 192.168.1.189
set block-policy return
set loginterface egress
set skip on lo0
pass from { lo0 $lan } to any keep state
match out on $extIF from $lan to any nat-to $extIP
pass out on $extIF from $lan to any nat-to ($extIF)
pass out quick inet
pass in on { $extIF $lanIF } inet

pfctl -sr

cerby$ doas pfctl -sr
pass inet6 from ::1 to any flags S/SA
pass on lo0 inet6 from fe80::1 to any flags S/SA
pass inet from 127.0.0.1 to any flags S/SA
pass inet from 192.168.2.0/24 to any flags S/SA
match out on igc0 inet from 192.168.2.0/24 to any nat-to 192.168.1.189
pass out on igc0 inet from 192.168.2.0/24 to any flags S/SA nat-to (igc0) round-robin
pass out quick inet all flags S/SA
pass in on igc0 inet all flags S/SA
pass in on igc3 inet all flags S/SA

Obviously these are rules are likely too lax for any real-world situation, but it does resolve the incapacity to NAT.

I now vaguely recall hitting a problem with the 172.16 address space over 20 years ago, too. Will diarize for 2040. In the mean time, I hope the config here gives a bit of illumination for anyone driving by with similar issues/setups.

I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.