Skip to content

Linux ZeroTier does not use UPnP-mapped ports in surface addresses, causing permanent RELAY - works correctly on Windows #2593

Description

@Kamilkime

(AI generated description after quite some time spent debugging)

Environment

  • OS: Arch Linux (kernel 7.0.10-arch1-1)
  • ZeroTier version: 1.16.1
  • Network: Behind CGNAT (Orange ISP, Poland) with a consumer router (Orange Funbox 3.0)
  • Firewall: firewalld (nftables backend)

Problem

On the same machine and network, ZeroTier on Windows established DIRECT connections to peers with no issues. After switching to Linux, the same peer is permanently on RELAY and direct connections cannot be established.

Root cause identified

The surfaceAddresses field in zerotier-cli info -j on Linux shows only CGNAT-mangled random ports:

"surfaceAddresses": [
  "2a01:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/9995",
  "79.184.236.195/35445",
  "79.184.236.195/35424",
  "79.184.236.195/36174"
]

These random ports (35445, 35424, 36174) are assigned by CGNAT for outgoing connections and have no corresponding port forwarding — making it impossible for peers to reach this node directly.

On Windows, ZeroTier correctly mapped port 9993 via UPnP and advertised <external_ip>:9993 as a surface address, which peers could successfully reach. On Linux, despite UPnP mappings existing on the router, the mapped ports never appear in IPv4 surface addresses.

Key observation

ZeroTier on Linux successfully creates UPnP mappings on the router (e.g., port 9995 for the tertiary port), but 79.184.236.195:9995 does not appear in surface addresses. Linux ZeroTier appears to discover surface addresses exclusively from what PLANET servers reflect (outgoing NAT-mangled ports), while Windows ZeroTier additionally uses the UPnP-reported external IP + mapped port to construct surface addresses.

Steps taken on Linux to try to fix this

  1. Opened firewalld ports for ZeroTier: 9993/udp, 9994/udp, 9995/udp
  2. Confirmed UPnP works — router IGD is discoverable via upnpc -m eno1 -l
  3. Added fixed secondary and tertiary ports in /var/lib/zerotier-one/local.conf:
{
  "settings": {
    "secondaryPort": 9994,
    "tertiaryPort": 9995,
    "portMappingEnabled": true
  }
}
  1. Manually added UPnP mappings for ports 9993 and 9994 via upnpc (since ZeroTier only maps secondary/tertiary itself)
  2. Created a systemd service to re-add UPnP mappings for 9993/9994 on every boot
  3. Confirmed UPnP entries exist on router for 9993, 9994, 9995
  4. Confirmed firewalld allows traffic from router IP (required for UPnP discovery)

Despite all of the above, surface addresses still show only CGNAT-mangled random ports, and the peer remains on RELAY.

Expected behavior

Same as Windows — ZeroTier should use UPnP-mapped port + external IP from the UPnP IGD to construct IPv4 surface addresses, allowing peers to reach this node directly through the UPnP-forwarded port.

Peer status

<ztaddr>   <ver>  <role> <lat> <link>
28bd3f4a10 1.16.1 LEAF     -1  RELAY

Peer API response (/peer/<addr>)

{"address":"28bd3f4a10","paths":[],"role":"LEAF","tunneled":false}

No paths are ever discovered even when the peer is actively sending traffic.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions