Discussion:
NAT on same interface as vlan on OpenBSD 6.8
Dirk Coetzee
2021-05-10 09:19:12 UTC
Permalink
Hi All,

I am hoping to create a pf.conf configuration that has VLAN’s and NAT via the same (physical) interface. The hardware is only capable of having a single Ethernet interface.

vlan100 is an isolated network to setup servers and is connected to a switch that is setup for vlan100 and vlan 1. The same interface has the main internal IP address 192.168.10.241 ("vlan 1" / no vlan). Devices behind vlan100 network need to be NAT’ed behind the internal / corporate interface 192.168.10.241, so that they can still have internet access - without affecting the main / corporate network.

When attempting to do this with either:
match out on $int_if inet from $vl100_net to any nat-to $int_ip source-hash
-or-
pass out quick log on $int_if inet from $vl100_net to any nat-to $int_ip

I see the traffic exiting re0 (default vlan1) interface with the same IP address as before on vlan100 (i.e. no NAT happened).

When troubleshooting I see the below error in dmesg:
arp: attempt to overwrite entry for 192.168.10.1 on re0 by 7c:5a:1c:74:4f:46 on vlan100

Although I am not super sure that is related. There are thousands of them though.

Is there a way to have both vlan100 and NAT all on the same interface as my usual default network interface?

To make sure I am testing the pf configuration correctly on the proxy/firewall, I execute:
doas ping -I 192.168.100.1 9.9.9.9
and also execute tcpdump at the same time (output below). No NAT is being observed for 192.168.100.1 (no ICMP replies). Interestingly replies are seen at 192.168.10.241 - but not passed to 192.168.100.1.


Kind Regards
dirk

========================================================================================================
tetraodontidae# ifconfig
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 32768
index 3 priority 0 llprio 3
groups: lo
inet6 ::1 prefixlen 128
inet6 fe80::1%lo0 prefixlen 64 scopeid 0x3
inet 127.0.0.1 netmask 0xff000000
re0: flags=8b43<UP,BROADCAST,RUNNING,PROMISC,ALLMULTI,SIMPLEX,MULTICAST> mtu 1500
lladdr 8c:89:a5:ed:5c:0e
index 1 priority 0 llprio 3
groups: if_host egress
media: Ethernet autoselect (1000baseT full-duplex,rxpause,txpause)
status: active
inet 192.168.10.241 netmask 0xffffff00 broadcast 192.168.10.255
enc0: flags=0<>
index 2 priority 0 llprio 3
groups: enc
status: active
vlan100: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 8c:89:a5:ed:5c:0e
index 5 priority 0 llprio 3
encap: vnetid 100 parent re0 txprio packet rxprio outer
groups: vlan igrp_vlanif
media: Ethernet autoselect (1000baseT full-duplex,rxpause,txpause)
status: active
inet 192.168.100.1 netmask 0xffffff00 broadcast 192.168.100.255
========================================================================================================
tetraodontidae# cat hostname.re0
inet 192.168.10.241 255.255.255.0 192.168.10.255
========================================================================================================
tetraodontidae# cat hostname.vlan100
vnetid 100 parent re0
inet 192.168.100.1 255.255.255.0 192.168.100.255 vlan 100 vlandev re0
up

### Description
### VLAN100 = setup firewalls
========================================================================================================
tetraodontidae# cat pf.conf
# $OpenBSD: pf.conf,v 1.55 2017/12/03 20:40:04 sthen Exp $
#
# See pf.conf(5) and /etc/examples/pf.conf

set skip on { lo0 pflog0 }
set reassemble yes
set ruleset-optimization none
set block-policy drop
set state-policy if-bound

#block return # block stateless traffic
#block drop log # block stateless traffic


# ---=== Macros ===---
squidclient_ips = "{ 192.168.10.0/24 }"
syslogclient_ips = "{ 192.168.10.0/24, 192.168.20.0/24, 192.168.12.0/24 }"
table <blockedhostileips> persist file "/etc/pf.files/blockedhostileips.txt"
table <bruteforcers> persist file "/etc/pf.files/bruteforcers.txt"
int_if = "{ re0 }"
int_ip = "{ re0:0 }"
int_net = re0:network
vl100_if = "{ vlan100 }"
vl100_ip = "{ vlan100:0 }"
vl100_net = vlan100:network


# By default, do not permit remote connections to X11
block return in on ! lo0 proto tcp to port 6000:6010

# Port build user does not need network
block return out log proto {tcp udp} user _pbuild

# ---=== Outbound from proxy ===---
antispoof quick for lo0
antispoof quick for re0
antispoof quick for vlan100
pass out quick log on $int_if
pass out quick log on $vl100_if

# ---=== NAT from VLAN 100 Outbound ===---
# match out on re0 inet proto { tcp udp icmp } from $vl100_net to any nat-to $re0_ip source-hash
match out on $int_if inet from $vl100_net to any nat-to $int_ip source-hash
match in all scrub (no-df random-id)

# ---=== Inbound from re0 ===---
block in quick log from <blockedhostileips> label "Blocked_Hostile_IPs"
block out quick log to <blockedhostileips> label "Blocked_Hostile_IPs"
pass in quick log on $vl100_if inet from $vl100_net to any
## pass out quick log on $int_if inet from $vl100_net to any nat-to $int_ip
pass in quick on any inet proto tcp from any to 192.168.10.241 port 22 synproxy state (max-src-conn 40, max-src-conn-rate 10/10, overload <bruteforcers> flush global)
pass in quick on any inet proto tcp from any to 192.168.10.241 port 22 synproxy state (max-src-conn 40, max-src-conn-rate 10/10, overload <bruteforcers> flush global)
pass in quick on any inet proto udp from any to 192.168.10.241 port 53
pass in quick on any inet proto tcp from any to 192.168.10.241 port 53
pass in quick log on any inet proto tcp from any to 192.168.10.241 port 22 user root synproxy state
pass in quick log on any inet proto tcp from any to 192.168.10.241 port 25 synproxy state
pass in quick log on any inet proto tcp from any to 192.168.10.241 port 587 synproxy state
pass in quick on any inet proto tcp from any to 192.168.10.241 port 3128
pass in quick log on any inet proto tcp from any to 192.168.10.241 port {80,443}
pass in quick log on any inet proto tcp from 192.168.10.0/24 to 192.168.10.241 port 10051
pass in quick on any inet proto udp from any to 192.168.10.241 port 123
pass in quick on any inet proto udp from $syslogclient_ips to 192.168.10.241 port 514
pass in quick on any inet proto udp from any to any port 67:68
pass in quick on any inet proto icmp all
block in quick inet from any
block in quick inet6 from any
tetraodontidae#
========================================================================================================
tetraodontidae# tcpdump -Npn -i re0 host 9.9.9.9
tcpdump: listening on re0, link-type EN10MB
15:47:03.837292 192.168.100.1 > 9.9.9.9: icmp: echo request
15:47:03.993645 192.168.10.241 > 9.9.9.9: icmp: echo request (DF)
15:47:04.071316 9.9.9.9 > 192.168.10.241: icmp: echo reply
15:47:04.837312 192.168.100.1 > 9.9.9.9: icmp: echo request
15:47:05.017633 192.168.10.241 > 9.9.9.9: icmp: echo request (DF)
15:47:05.095584 9.9.9.9 > 192.168.10.241: icmp: echo reply
15:47:05.837333 192.168.100.1 > 9.9.9.9: icmp: echo request
15:47:06.041633 192.168.10.241 > 9.9.9.9: icmp: echo request (DF)
15:47:06.119628 9.9.9.9 > 192.168.10.241: icmp: echo reply
15:47:06.837353 192.168.100.1 > 9.9.9.9: icmp: echo request
15:47:07.065623 192.168.10.241 > 9.9.9.9: icmp: echo request (DF)
15:47:07.143899 9.9.9.9 > 192.168.10.241: icmp: echo reply
15:47:07.837375 192.168.100.1 > 9.9.9.9: icmp: echo request
15:47:08.089670 192.168.10.241 > 9.9.9.9: icmp: echo request (DF)
15:47:08.167401 9.9.9.9 > 192.168.10.241: icmp: echo reply
^C
1106 packets received by filter
0 packets dropped by kernel
========================================================================================================
Kenneth Gober
2021-05-10 15:49:13 UTC
Permalink
Post by Dirk Coetzee
I am hoping to create a pf.conf configuration that has VLAN’s and NAT via
the same (physical) interface. The hardware is only capable of having a
single Ethernet interface.
vlan100 is an isolated network to setup servers and is connected to a
switch that is setup for vlan100 and vlan 1. The same interface has the
main internal IP address 192.168.10.241 ("vlan 1" / no vlan). Devices
behind vlan100 network need to be NAT’ed behind the internal / corporate
interface 192.168.10.241, so that they can still have internet access -
without affecting the main / corporate network.
pass out quick log on $int_if
match out on $int_if inet from $vl100_net to any nat-to $int_ip source-hash
I believe your issue is that the "pass out quick" takes effect immediately
and rules that come later aren't checked. Either remove the 'quick' or
move the 'match out' NAT rule so that it's above the 'pass out quick'.

Some people don't like to use 'quick' because it makes "last match"
semantics hard to follow (it's an exception to keep track of if most of
your ruleset is structured for 'last match wins'). I prefer to use 'quick'
on almost all of my rules to get "first match" semantics because I find
'first match wins' easier to work with (it means that the remainder of the
ruleset after a matching 'pass' is irrelevant and need not be examined).
It is purely personal preference on whether you prefer to read your rules
from the bottom up or the top down. If you want to continue to use 'quick'
the most expedient fix is to move the 'match out' so that it comes before
your first 'pass out' rule.

-ken
Dirk Coetzee
2021-05-10 23:56:12 UTC
Permalink
Hi Kenneth,

It was staring me in the face and I did not see it. I shot myself in the foot and didn’t realize it.

Thanks heaps for your support.



From: Kenneth Gober <***@gmail.com>
Sent: Monday, 10 May 2021 11:49 PM
To: Dirk Coetzee <***@best-it.tech>
Cc: ***@openbsd.org
Subject: Re: NAT on same interface as vlan on OpenBSD 6.8

On Mon, May 10, 2021 at 5:26 AM Dirk Coetzee <***@best-it.tech<mailto:***@best-it.tech>> wrote:
I am hoping to create a pf.conf configuration that has VLAN’s and NAT via the same (physical) interface. The hardware is only capable of having a single Ethernet interface.

vlan100 is an isolated network to setup servers and is connected to a switch that is setup for vlan100 and vlan 1. The same interface has the main internal IP address 192.168.10.241 ("vlan 1" / no vlan). Devices behind vlan100 network need to be NAT’ed behind the internal / corporate interface 192.168.10.241, so that they can still have internet access - without affecting the main / corporate network.

pass out quick log on $int_if
match out on $int_if inet from $vl100_net to any nat-to $int_ip source-hash

I believe your issue is that the "pass out quick" takes effect immediately and rules that come later aren't checked. Either remove the 'quick' or move the 'match out' NAT rule so that it's above the 'pass out quick'.

Some people don't like to use 'quick' because it makes "last match" semantics hard to follow (it's an exception to keep track of if most of your ruleset is structured for 'last match wins'). I prefer to use 'quick' on almost all of my rules to get "first match" semantics because I find 'first match wins' easier to work with (it means that the remainder of the ruleset after a matching 'pass' is irrelevant and need not be examined). It is purely personal preference on whether you prefer to read your rules from the bottom up or the top down. If you want to continue to use 'quick' the most expedient fix is to move the 'match out' so that it comes before your first 'pass out' rule.

-ken

Loading...