mirror of
https://github.com/fosrl/olm.git
synced 2026-05-06 02:37:55 -05:00
[GH-ISSUE #108] iOS/OLM client: UDP hole punch fails on IPv6-only/NAT64 mobile networks #59
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @SystemFuchs on GitHub (Mar 25, 2026).
Original GitHub issue: https://github.com/fosrl/olm/issues/108
Originally assigned to: @oschwartz10612 on GitHub.
Describe the Bug
The Pangolin iOS client (OLM) fails to register on IPv6-only mobile networks (e.g., T-Mobile Germany 5G with NAT64/DNS64). The WebSocket connection (TCP) establishes correctly over IPv6, but the UDP hole punch never sends a single packet, causing the client to be stuck in "Registering" state indefinitely.
The root cause appears to be that the hole punch code resolves
base_endpointto an IPv4 address and opens anAF_INETUDP socket, which cannot function on an IPv6-only network. On NAT64 networks, there is no native IPv4 stack available — only TCP connections benefit from the system's Happy Eyeballs / NAT64 translation.Actual Behavior
The iOS client connects the WebSocket (TCP) successfully over IPv6 but gets stuck at "Registering" because the UDP hole punch never completes.
Diagnostic Evidence
1. WebSocket connects, but server receives no hole punch
Pangolin server logs show the WebSocket is established, but the hole punch timestamp is never updated:
2. tcpdump confirms zero UDP packets from iPhone
Full packet capture on the server's
eth0interface withtcpdump -i eth0 -n 'udp'shows no UDP packets from any T-Mobile IPv6 prefix or NAT64 address while the iPhone is attempting to connect. All captured UDP traffic belongs to other (IPv4) sites that are functioning correctly.A separate capture filtered for IPv6 UDP (
tcpdump -i eth0 -n 'ip6 and udp') shows 0 packets from the iPhone, while UDP from other IPv6 sources (e.g., another Hetzner VPS) arrives correctly.3. Server infrastructure is correctly configured
ip6tables: default policy ACCEPT, no DROP rules ✅docker-proxy: listening on both0.0.0.0:51820and[::]:51820✅4. tcpdump of the WebSocket (TCP) over IPv6
The TCP connection from the iPhone's IPv6 address works perfectly — full TLS handshake, HTTP 101 upgrade, and the client sends 732-byte keepalive frames every 2 seconds. The server ACKs on TCP level but sends no application data back (because registration is blocked by missing hole punch).
5. WiFi (IPv4) works perfectly
Same iPhone, same app, same Pangolin server — switching from 5G to WiFi (which provides IPv4) immediately connects all sites.
Analysis
The sequence on an IPv6-only/NAT64 network:
pangolin.mydomain→ gets both A and AAAA recordsbase_endpointto the A record (IPv4) and opens anAF_INETUDP socket → ❌ fails silently on IPv6-only network (no native IPv4 stack available)On NAT64 networks, the correct approach is to use
getaddrinfo()withAF_UNSPECand let the OS synthesize a NAT64 IPv6 address (e.g.,64:ff9b::4d2a:147a) for IPv4-only destinations. Alternatively, the code should attempt IPv6 UDP first when an AAAA record is available.Note on AAAA records making it worse: With an AAAA record present, DNS64 synthesis does not activate (DNS64 only synthesizes when there is no AAAA record). This means the TCP stack connects via real IPv6, but the UDP stack gets a real IPv4 address it cannot use. Without the AAAA record, DNS64 would at least synthesize a NAT64 address, which might work if the UDP code uses
getaddrinfo()properly.Suggested Fix
In the iOS OLM client's hole punch implementation:
getaddrinfo()withAF_UNSPECinstead of resolving to IPv4 onlyAF_INET6) when available, fall back to IPv4Network.framework'sNWConnectionwith UDP, which handles Happy Eyeballs and NAT64 transparentlyThis would also fix the issue for all other NAT64/DS-Lite networks, which are extremely common in Germany (Vodafone Kabel, 1&1, Telekom mobile).
Impact
This affects all users on IPv6-only/NAT64 mobile networks, which includes:
This is the majority of German mobile and cable internet users. The issue likely also affects other countries where carriers have deployed IPv6-only with NAT64.
Workaround
Currently none — the only option is to use WiFi with IPv4 connectivity. Removing the AAAA record from DNS does not help because the hole punch code appears to force
AF_INETregardless.Environment
fosrl/gerbilimage)To Reproduce
Expected Behavior
The iOS client should complete registration and connect to all sites regardless of whether the client is on an IPv4 or IPv6-only network.
@SystemFuchs commented on GitHub (Mar 30, 2026):
As an additional information:
I'm facing the same issue if I'm using my the Pangolin client on my Macbook & a NAT64 (5G/LTE) based connection via a mobile router.
If I change the APN of the router to an IPV4 based APN (which is sadly not possible at anytime) the client is connecting fine.