I'm configuring an OpenVPN gateway to allow a LAN access to the internet through the tunnel. The gateway is running OpenBSD 5.5-stable amd64 on the PC Engines APU platform.

The LAN contains re1, re2, and ral0 interfaces. It also contains vether0 which hosts the local network. These interfaces are linked under bridge0 to provide a common gateway, subnet, and DHCP via dhcpd.

The VPN is established and tun0 is opened. The gateway itself can access the VPN just fine.

The problem is that, by default, the hosts access the VPN with their native addresses. NAT is needed to translate the local network to the VPN network at

I have tried the following pf.conf configurations:

# pf.conf -- is my tun0 gateway
set skip on lo
block return
pass in quick on { vether0 re1 re2 ral0 } from to ! nat-to

I had similar results with these rules:

# pf.conf
pass in from any nat-to
pass out from tun0 to any

This allows LAN traffic to pass through tun0 with a source address of, and return traffic is passed back to the respective host. The new problem is that return traffic still does not appear to be routing correctly.

For example, I can ping and google.com from any LAN host, however the first reply is ALWAYS dropped between tun0 and the return interface. Tools like dig, nslookup, traceroute, and ping are generally sluggish and take far longer than they should. Despite some traffic still passing, browsers and other applications are unusable.

tcpdump demonstrating the loss:

# is a LAN host
# is google.com

# on ral0
20:59:35.668251 > icmp: echo request <-- no reply
20:59:40.651184 > icmp: echo request
20:59:40.736748 > icmp: echo reply
20:59:41.656101 > icmp: echo request
20:59:41.741251 > icmp: echo reply
20:59:42.661071 > icmp: echo request
20:59:42.802410 > icmp: echo reply

# on tun0
20:59:35.668359 > icmp: echo request
20:59:35.764052 > icmp: echo reply <-- here's the missing reply, it didn't get to ral0
20:59:40.651221 > icmp: echo request
20:59:40.736721 > icmp: echo reply <-- but the of the replies rest did
20:59:41.656138 > icmp: echo request
20:59:41.741226 > icmp: echo reply
20:59:42.661107 > icmp: echo request
20:59:42.802372 > icmp: echo reply

I know that this is almost certainly a NAT issue in pf.conf but after so many configuration attempts I'm having trouble finding the correct way to go about passing traffic.

When I was using DD-WRT and iptables, this was my configuration:

iptables -D FORWARD -i tun1 -j logaccept
iptables -D FORWARD -o tun1 -j logaccept

I'm not sure how to "port" this to pf, though. Any suggestions would be greatly appreciated!


This turned out to be a pf.conf issue. Some extra time spent studying the OpenBSD PF NAT page lead me to the following rule which allowed traffic to pass correctly through the tun0 interface:

# /etc/pf.conf
pass out on tun0 inet from to any flags S/SA nat-to (tun0) round-robin

This essentially reads: pass traffic from the local network destined for any address out on tun0, IPv4 specifically, only look at the syn and ack flags, and perform outbound NAT using tun0. The parentheses around (tun0) tell pf to automatically update the rule when the interface changes its address. This may occur if your VPN supports multiple peers and you failover, and thus manually reloading the ruleset becomes unnecessary.

Some time on the OpenBSD PF Filtering page helped me refine the rule:

# /etc/pf.conf
pass out on $vpn_if inet proto { $protos } from $lan_net to any flags S/SA modulate state nat-to ($vpn_if) round-robin
pass in  on $vpn_if inet proto { $protos } from $vpn_gw  to any flags S/SA modulate state

The modulate state flag allows pf to substitute a stronger Initial Sequence Number which may help protect some operating systems on the network.

The gateway is working great now and I'm onto more complex pf.conf configurations.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.