I’ve set up a server on two different IPs that returns the NAT IP:port to a connecting client. One can use nat-check.py to connect to these and get a classification of their NAT. The code for this, mostly written by an LLM, can be found in GitHub - 0xB10C/tcp-nat-check: A tool to check the NAT type of your router, which tells you if TCP hole punching would work on your router. · GitHub. Making requests to my hosts leaks your IP address to me, so you might chose to run your own servers. IP addresses are masked in output by default.
For me, at home and also using a phone hotspot indicates APDM for IPv4 and “no NAT” for IPv6:
$ python3 nat-check.py http://b10c.me:7770 http://b10c.me:7771 http://bnoc.xyz:7770 http://bnoc.xyz:7771
nat-check TCP NAT mapping classifier
── IPv4 ──
local source port: 53321
destination external addr
────────────────────────────────────────────────────────────────
b10c.me:7770 (x.x.x.1) x.x.x.2:61314
b10c.me:7771 (x.x.x.1) x.x.x.2:59431
bnoc.xyz:7770 (x.x.x.3) x.x.x.2:63172
bnoc.xyz:7771 (x.x.x.3) x.x.x.2:64569
classification: ADDRESS+PORT-DEPENDENT MAPPING (APDM, 'symmetric')
External port varies per destination (IP, port). TCP hole punching is very
unlikely to work for you: the coordinator cannot predict the external port
your NAT will assign for any given peer. This pattern is typical of CGNAT,
mobile carriers, and restrictive enterprise networks.
── IPv6 ──
local source port: 53322
destination external addr
────────────────────────────────────────────────────────────────
b10c.me:7770 (x::x:4) x::x:5:53322
b10c.me:7771 (x::x:4) x::x:5:53322
bnoc.xyz:7770 (x::x:6) x::x:5:53322
bnoc.xyz:7771 (x::x:6) x::x:5:53322
classification: NO NAT
External address (x::x:5:53322) equals the local address. There is no NAT;
hole punching is unnecessary. However, your home router may still have a
stateful firewall that blocks unsolicited inbound connections. You may need to
open the port on your router to be reachable.
Via Obscura VPN it’s EIM for both IPv4 and IPv6:
python3 nat-check.py http://b10c.me:7770 http://b10c.me:7771 http://bnoc.xyz:7770 http://bnoc.xyz:7771
── IPv4 ──
local source port: 53486
destination external addr
────────────────────────────────────────────────────────────────
b10c.me:7770 (x.x.x.1) x.x.x.2:53486
b10c.me:7771 (x.x.x.1) x.x.x.2:53486
bnoc.xyz:7770 (x.x.x.3) x.x.x.2:53486
bnoc.xyz:7771 (x.x.x.3) x.x.x.2:53486
classification: ENDPOINT-INDEPENDENT MAPPING (EIM)
All four destinations saw the same external port. Your NAT maps (internal IP,
internal port) to a single external port regardless of destination. TCP hole
punching has a strong chance of working: a coordinator can reliably tell a
peer which external port to send to.
── IPv6 ──
local source port: 53487
destination external addr
────────────────────────────────────────────────────────────────
b10c.me:7770 (x::x:4) x::x:5:53487
b10c.me:7771 (x::x:4) x::x:5:53487
bnoc.xyz:7770 (x::x:6) x::x:5:53487
bnoc.xyz:7771 (x::x:6) x::x:5:53487
classification: ENDPOINT-INDEPENDENT MAPPING (EIM)
All four destinations saw the same external port. Your NAT maps (internal IP,
internal port) to a single external port regardless of destination. TCP hole
punching has a strong chance of working: a coordinator can reliably tell a
peer which external port to send to.
So IPv4 TCP hole punching would not work at home nor via phone hotspot due to being APDM and IPv6 likely requiring opening the firewall, but would work when using Obscura VPN on both IPv4 and IPv6 as it’s EIM.
I would be interested in seeing results from others.
| Who and what | IPv4 NAT | IPv6 NAT |
|---|---|---|
| b10c at home & mobile hotspot | APDM | no NAT |
| Obscura VPN | EIM | EIM |
| @sipa at home | EIM | no NAT |
| @sipa using conference wifi | EIM | no IPv6 |
| @sipa using hotel wifi | EIM | no IPv6 |
| @sipa using airport wifi | APDM | no IPv6 |
| @sipa using plane |
EIM | no IPv6 |
| @willcl-ark via starlink (business local priority) | EIM | no NAT |
| @dunxen at home | EIM | no NAT |
| @cedarctic at university campus | APDM | - |
| anon using ProtonVPN (default NAT) | APDM | - |
| @m3dwards using office internet on Mac | EIM | no IPv6 |
| @m3dwards using office internet on Linux | EIM | no IPv6 |
| @m3dwards using Docker Desktop on Mac | APDM | no IPv6 |
| @m3dwards using Docker on Linux | EIM | no IPv6 |
| @m3dwards using T-Mobile US hotspot | EIM | no IPv6 |
| @m3dwards using Home router (OPNSense) | APDM | no IPv6 |
| @Crypt-iQ using home internet | EIM | no NAT |
| @b10c via eduroam at University | APDM | - |
As mentioned in this comment by sipa, it seems we could hole punch (through the firewall; not through NAT) for no NAT IPv6 too.