Losing NAT when starting VPN on a FreeBSD router

Whenever I start my OpenVPN (mullvad) my NAT stops working, i.e. no traffic gets through from my client to the internet. I can ping both sides from theFreeBSD router. I can ping the inside from my client machine i.e. the interface its connected to on the FreeBSD router. I can also ping my ISP's default gateway from my client machine. I have tried with pf rules allowing all traffic and logging but that doesn't seem to be the issue.

So the topology looks like this:

My machine (it's only a single machine at the moment but more hardware will be coming) <----> FreeBSD router <----> OpenVPN endpoint or ISP gateway.

Here's netstat -rn without VPN running:

I have obfuscated the IPs so 'Myispnetwork' is equivalent to the first three octects in a routeable IP. These are the same three octets in this case:

Code:
Internet:
Destination        Gateway            Flags    Refs      Use  Netif Expire
default            Myispsnetwork.193     UGS         0 167566097    em0
Myispsnetwork.192/27  link#1             U           0     5919    em0
Myispsnetwork.194     link#1             UHS         0        0    lo0
127.0.0.1          link#4             UH          0   993647    lo0
172.16.0.0/24      link#2             U           0 154928084   igb0
172.16.0.3         link#2             UHS         0        0    lo0
This is netstat -rn with my VPN running:

Code:
Destination        Gateway            Flags    Refs      Use  Netif Expire
0.0.0.0/1          10.8.0.73          UGS         0       85   tun0 =>
default            Myispnetwork.193     UGS         0 167566549    em0
10.8.0.1/32        10.8.0.73          UGS         0        0   tun0
10.8.0.73          link#6             UH          0        0   tun0
10.8.0.74          link#6             UHS         0        0    lo0
Myispnetwork.192/27  link#1             U           0     5930    em0
Myispnetwork.194     link#1             UHS         0        0    lo0
95.211.136.21/32   94.137.110.193     UGS         0     2272    em0
127.0.0.1          link#4             UH          0   998624    lo0
128.0.0.0/1        10.8.0.73          UGS         0        9   tun0
172.16.0.0/24      link#2             U           0 154930115   igb0
172.16.0.3         link#2             UHS         0        0    lo0
Here's my mullvad.conf (i.e. openvpn.conf):

Code:
client
dev tun
proto udp
remote nl.mullvad.net 1194
resolv-retry infinite
nobind
persist-key
persist-tun
comp-lzo
verb 3
remote-cert-tls server
ping-exit 60
script-security 2
up /usr/local/etc/openvpn/update-resolv-conf
down /usr/local/etc/openvpn/update-resolv-conf
ping 10
ca ca.crt
cert mullvad.crt
key mullvad.key
crl-verify crl.pem

The up and down script (same script):

Code:
#!/usr/local/bin/bash


[ -x /sbin/resolvconf ] || exit 0

case $script_type in
  up)
    for optionname in ${!foreign_option_*} ; do
      option="${!optionname}"
      echo $option
      part1=$(echo "$option" | cut -d " " -f 1)
      if [ "$part1" == "dhcp-option" ] ; then
        part2=$(echo "$option" | cut -d " " -f 2)
        part3=$(echo "$option" | cut -d " " -f 3)
        if [ "$part2" == "DNS" ] ; then
          IF_DNS_NAMESERVERS="$IF_DNS_NAMESERVERS $part3"
        fi
        if [ "$part2" == "DOMAIN" ] ; then
          IF_DNS_SEARCH="$part3"
        fi
      fi
    done
    R=""
    if [ "$IF_DNS_SEARCH" ] ; then
      R="${R}search $IF_DNS_SEARCH"
    fi
    for NS in $IF_DNS_NAMESERVERS ; do
      R="${R}nameserver $NS"
    done
    echo -n "$R" | /sbin/resolvconf -a "${dev}.inet"
    ;;
  down)
    /sbin/resolvconf -d "${dev}.inet"
    cp /usr/local/etc/openvpn/resolv.bak /etc/resolv.conf
    ;;
esac

IP=$(/sbin/ifconfig  | grep -E 'inet.[0-9]' | grep -v 'tun0' | awk '{ print $4}' | tail -1);/usr/sbin/setfib 1 /sbin/route add default $IP
 
When the VPN becomes active the tun0 interface is created, when the VPN is inactive the interface gets destroyed. PF doesn't dynamically load rules and will have to be reloaded when this happens. Adding pfctl -f /etc/pf.conf to your up/down script should do the trick.
 
This issue here.
Code:
Destination        Gateway            Flags    Refs      Use  Netif Expire
0.0.0.0/1          10.8.0.73          UGS         0       85   tun0 =>
default            Myispnetwork.193     UGS         0 167566549    em0
128.0.0.0/1        10.8.0.73          UGS         0        9   tun0

Your remote end uses the following configuration lines:
Code:
push "redirect-gateway def1"

This puts in two /1 routes that effectively override the default route. It's not that NAT isn't working. I'm sure your ISP is seeing your NAT'd address when you ping from inside your LAN to the ISP default gateway. It's just that anything other than that tries to use the VPN routes because they have priority. This setting probably makes sense in most cases. I use it on my laptop's OpenVPN client because I might be in a public place or hotel where I just want all my traffic to use my OpenVPN back home and go out from there. It probably doesn't make as much sense on a router however.
 
Thank you very much the both of you. When I delete those 2 routes you mention, @junovitch, my NAT does indeed work in the sense I can now reach the internet from my client machine. Thank you very much both of you for your information and time. I know that the post I made was quite lengthy and I really appreciate your assistance.
 
Last edited by a moderator:
So I didn't want to start a new thread since all the relevant info is here. The two open ports don't work for some reason.

That´s the rules:
Code:
pass in quick on $ext_if proto { tcp udp } from any to any port 29981 keep state
and
Code:
pass in quick on $ext_vpn inet proto { tcp udp } from any to $ext_vpn port 22579 keep state

The port that is being redirected to the LAN is actually another openvpn client. I can understand why that doesn't work. But why the port on the router ie pass in quick on $ext_vpn inet proto { tcp udp } from any to $ext_vpn port 22579 keep state doesn't work really has me stumped. When I tcpdump -n -e -ttt -i pflog0|grep 22579 I don't get any hits for that rule over hours.

Here's the whole pf.conf:
Code:
# Macros
int_if = "igb0"
ext_dmz = "igb1"
ext_if = "em0"
ext_vpn = "tun0"
Golden="172.16.0.20"

#Settings
set block-policy return
set skip on lo
set skip on $int_if

#scrub
#scrub in all no-df

#Nat
nat on $ext_if inet from !($ext_if) -> ($ext_if:0)

#Allow this
rdr on $ext_if proto { tcp udp } from any to any port 29981 -> $Golden
rdr on $ext_vpn proto { tcp udp } from any to any port 29981 -> $Golden
pass in quick on $ext_if proto { tcp udp } from any to any port 29981 keep state
pass in log quick on $ext_vpn inet proto { tcp udp } from any to $ext_vpn port 22579 keep state
#pass out quick on $ext_vpn from any to any

#Default deny. Blocks all traffic not specifically allowed.
block in log

#Pass out all traffic from this machine
pass out quick

#Spoofprotection
antispoof for igb0 inet
#antispoof for igb1 inet
#antispoof for em0 inet
#antispoof for tun0 inet
antispoof for lo0 inet

tcpdump -n -e -ttt -i pflog0|grep 22579
 
You rdr port 29981 in both rules and pass 29981 in one rule and 22579 in the other. Why not just combine them into one to make things less confusing?

Code:
rdr pass on $ext_if proto { tcp udp } from any to any port 29981 -> $Golden
rdr pass on $ext_vpn proto { tcp udp } from any to any port 22579 -> $Golden

Check pf.conf(5) for more details and examples.
 
Thanks for posting junovitch. You are right of course. My focus has however been to make this stuff work first and foremost.
 
Back
Top