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.