OpenVPN getting NAT type strict on two of three servers

Hi everyone,

I have three vServers running KVM with identical OpenVPN and firewall configurations on each of them. On two of those three servers I get NAT type strict when testing on my clients, however on one I get NAT type moderate (which is what I want). My aim is to bypass my ISP’s CGNAT for games that have trouble with NAT type strict.

Those are the servers, all of them are with the same server hosting provider (netcup):

  • Server 1 (strict NAT): Debian 10
  • Server 2 (strict NAT): Debian 10
  • Server 3 (moderate NAT): Debian 9

OpenVPN version: (2.4.7-1)

Full tunneling works on all of them as well as port forwarding via DNAT to individual clients.

When using the Xbox companion app to check the NAT type on the clients traffic with TCPdump looks exatly the same when monitoring the tun0 iface.

I want to understand what I am doing wrong. Why is my client reporting nat type strict on the first two servers but not the third one? Is my configuration wrong? Is there a setting on Debian 10 that I am unaware of?

For testing I’ve put the policy of my iptables filter input chain to accept.

I want to avoid forwarding game ports to individual clients except when they’re hosting a gameserver, this is why I need nat type moderate to work.

Here’s my OpenVPN server configuration for all three:

port 51515
proto udp

keepalive 10 120
max-clients 3

dev tun0

ca /etc/openvpn/easy-rsa/keys/ca.crt
cert /etc/openvpn/easy-rsa/keys/server.crt
key /etc/openvpn/easy-rsa/keys/server.key

dh /etc/openvpn/easy-rsa/keys/dh2048.pem

mode server
tls-server
ifconfig 10.11.12.1 255.255.255.0
ifconfig-pool 10.11.12.127 10.11.12.254 255.255.255.0
push "route-gateway 10.11.12.1"
ifconfig-pool-persist ipp.txt

client-config-dir ccd
client-to-client

tls-auth /etc/openvpn/easy-rsa/keys/ta.key 0 # This file is secret
cipher AES-256-CBC

user nobody
group nogroup

persist-key
persist-tun

status openvpn-status.log
verb 4
explicit-exit-notify 1

Here’s my client configuration for all three (with different ips of course and certs omitted):

client

auth-nocache

redirect-gateway def1
route 0.0.0.0 0.0.0.0 vpn_gateway 50
dev tun

script-security 2
proto udp
remote ${SERVERIP} 51515
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
cipher AES-256-CBC
verb 3
;mute 20

key-direction 1

Here’s my filter table: I’m filtering netbios traffic and allow forwarding of tun to eth0 and vice versa.

Chain INPUT (policy ACCEPT 4116K packets, 7774M bytes)
 pkts bytes target     prot opt in     out     source               destination
2543M 1832G ACCEPT     udp  --  any    any     anywhere             anywhere             udp dpt:51515
 186K   32M ACCEPT     all  --  tun+   any     anywhere             anywhere
    0     0 ACCEPT     all  --  tap+   any     anywhere             anywhere
 2124 88856 DROP       tcp  --  eth0   any     anywhere             anywhere             tcp dpt:5555

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 DROP       tcp  --  any    eth0    anywhere             anywhere             tcp spts:netbios-ns:netbios-ssn
 1473  117K DROP       udp  --  any    eth0    anywhere             anywhere             udp spts:netbios-ns:netbios-ssn
2490M 1669G ACCEPT     all  --  tun+   any     anywhere             anywhere
    0     0 ACCEPT     all  --  tap+   any     anywhere             anywhere
1892M 4796G ACCEPT     all  --  eth0   tun+    anywhere             anywhere
    0     0 ACCEPT     all  --  eth0   tap+    anywhere             anywhere

Chain OUTPUT (policy ACCEPT 3929M packets, 5001G bytes)
 pkts bytes target     prot opt in     out     source               destination
 1261 50440 DROP       tcp  --  any    eth0    anywhere             anywhere             tcp spts:netbios-ns:netbios-ssn
    0     0 DROP       udp  --  any    eth0    anywhere             anywhere             udp spts:netbios-ns:netbios-ssn

This is the NAT table: I’m forwarding two ports to a game server which works fine. And of course there’s masquerading for tunneling all traffic if desired by the user (redirect gateway).

Chain PREROUTING (policy ACCEPT 8463K packets, 629M bytes)
 pkts bytes target     prot opt in     out     source               destination
   21  1389 DNAT       udp  --  any    any     anywhere             anywhere             udp dpt:27102 to:10.11.12.5:27102
 376K   17M DNAT       udp  --  any    any     anywhere             anywhere             udp dpt:27131 to:10.11.12.5:27131

Chain INPUT (policy ACCEPT 842K packets, 56M bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain POSTROUTING (policy ACCEPT 1112K packets, 60M bytes)
 pkts bytes target     prot opt in     out     source               destination
7409K  536M MASQUERADE  all  --  any    eth0    10.11.12.0/24        anywhere

Chain OUTPUT (policy ACCEPT 3414 packets, 230K bytes)
 pkts bytes target     prot opt in     out     source               destination

Any clues?

You’re doing masquerade in postrouting, usually people do it on output

Why aren’t you doing other ports or TCP in dnat?

MASQUERADE is only a valid target in the POSTROUTING chain of the nat table. You are probably thinking of the DNAT target which is valid in the PREROUTING and OUTPUT chains of the nat table.

One networking difference between the Debian 9 and 10 defaults is the rp_filter value. The default in 9 is 1 and the default in 10 is 0. If that were your problem though, it would seem like debian 10 would be the one that gave you a “moderate” type. Though it may be worth the 5 seconds to change the Debian 10 values to 2 to see if that helps.

1 Like

Thanks for the suggestion!

rp_filter is 0 on both the Debian 10 servers and the Debian 9 one.

net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.eth0.rp_filter = 0
net.ipv4.conf.lo.rp_filter = 0
net.ipv4.conf.tun0.rp_filter = 0

Changing the values to 2 unfortunately had no effect.

This is what TCPdump on tun0 is reporting while I’m doing the NAT type check.
Windows Terminal

11:15:37.248209 IP 10.11.12.2.56165 > 65.55.42.21.3074: UDP, length 31
11:15:37.265446 IP 65.55.42.21.3074 > 10.11.12.2.56165: UDP, length 59
11:15:37.266094 IP 10.11.12.2.56165 > 65.55.42.21.3074: UDP, length 31
11:15:37.284143 IP 65.55.42.21.3074 > 10.11.12.2.56165: UDP, length 59
11:15:37.286249 IP 10.11.12.2.56165 > 65.55.42.21.3074: UDP, length 31
11:15:37.293551 IP 65.55.42.21.3074 > 10.11.12.2.56165: UDP, length 59
11:15:37.295500 IP 10.11.12.2.56165 > 65.55.42.21.3074: UDP, length 31
11:15:37.312391 IP 65.55.42.21.3074 > 10.11.12.2.56165: UDP, length 59
11:15:37.315382 IP 10.11.12.2.56165 > 65.55.42.21.3074: UDP, length 31
11:15:37.331448 IP 65.55.42.21.3074 > 10.11.12.2.56165: UDP, length 59
11:15:37.333172 IP 10.11.12.2.56165 > 65.55.42.21.3074: UDP, length 31
11:15:37.341008 IP 65.55.42.21.3074 > 10.11.12.2.56165: UDP, length 59
11:15:37.342869 IP 10.11.12.2.56165 > 65.55.42.21.3074: UDP, length 31
11:15:37.359475 IP 65.55.42.21.3074 > 10.11.12.2.56165: UDP, length 59
11:15:37.361602 IP 10.11.12.2.56165 > 65.55.42.21.3074: UDP, length 31

I solved my issue.
Applying the solution to the 2nd server worked, so it’s unlikely that this is a fluke.
I don’t understand why but it eh.

This assumes that Teredo is working fine, so to anyone who encounters this problem make sure that Teredo tunneling works properly without the VPN.

What I did:

  • Put INPUT chain policy back to DROP.
  • Create rule in INPUT to allow state RELATED,ESTABLISHED for all traffic
  • Forward Teredo 60209 udp - which is the Teredo default port - to one machine that is connected via the PREROUTING chain.
  • Wait for at least 5 minutes. Check NAT type in Windows settings.
  • Should now say Moderate and Connected.

Running netsh int Teredo show state should show connected like this:

Teredo Parameters
---------------------------------------------
Type                    : natawareclient
Server Name             : win1901.ipv6.microsoft.com.
Client Refresh Interval : 20 seconds
Client Port             : unspecified
State                   : qualified
Client Type             : teredo client
Network                 : managed
NAT                     : restricted (port)
NAT Special Behaviour   : UPNP: No, PortPreserving: Yes
Local Mapping           : 10.11.12.128:60996
External NAT Mapping    : ${EXTERNAL_IP}:60996

Afterwards delete the rule that you added to PREROUTING. For all machines on the network, even when you don’t forward ports to any of them it will now state NAT type Moderate for Teredo and even Open for other services.

Again, this does not make any sense whatsoever to me, but hey, I thought this might help someone.
Also if anyone has an explanation feel free to chime in.

1 Like