How to Configure UPnP for NAT Traversal in iroh
iroh enables UPnP-based NAT traversal automatically via the portmapper service, which you can configure using PortmapperConfig::Enabled or PortmapperConfig::Disabled when building an Endpoint.
The n0-computer/iroh repository provides a robust networking stack that handles NAT traversal through an internal port-mapper service supporting UPnP, PCP, and NAT-PMP. By default, iroh automatically probes your local gateway to request external port mappings, but you can explicitly control this behavior during Endpoint construction. Understanding how to configure UPnP properly ensures optimal connectivity while avoiding potential firewall dialogs on restrictive platforms like macOS.
How the iroh Portmapper Handles UPnP
According to the iroh source code, NAT traversal is managed by a port-mapper client created inside the socket layer. This client communicates with routers using multiple protocols, with UPnP being the most common for consumer-grade hardware. The behavior is governed by the PortmapperConfig enum defined in iroh/src/portmapper.rs (lines 10-32), which offers two variants:
- PortmapperConfig::Enabled {}: Activates the port-mapper service. This is the default behavior and automatically initiates SSDP multicast probes to discover gateways and request external port mappings.
- PortmapperConfig::Disabled: Skips gateway probing entirely. This prevents SSDP multicast traffic that may trigger firewall permission dialogs, though direct connectivity may degrade behind certain NAT configurations.
PortmapperConfig Variants Explained
The Enabled variant starts a background task that attempts to map internal ports to external-facing ports using UPnP IGD, PCP, or NAT-PMP, depending on router capabilities. When set to Disabled, iroh operates without attempting to punch holes in the firewall via these protocols, relying solely on other NAT traversal techniques like QUIC's path validation and hole punching via relays.
Prerequisites: The portmapper Feature Flag
Before configuring UPnP, ensure the portmapper feature is enabled in your Cargo.toml. As defined in iroh/Cargo.toml (lines 78-149), this feature is included in the default feature set:
[dependencies]
iroh = { version = "0.x", features = ["portmapper"] } # Or use default-features = true
If you disabled default features, you must explicitly include "portmapper" to access the PortmapperConfig enum and the Builder::portmapper_config method.
Configuring UPnP When Building an Endpoint
You control UPnP behavior through the Builder::portmapper_config method in iroh/src/endpoint.rs (lines 781-789). This method accepts a PortmapperConfig variant and applies it during the Endpoint binding process.
Enabling UPnP (Default)
While iroh defaults to enabling the port-mapper, you can explicitly declare this to make the configuration obvious in your codebase:
use iroh::endpoint::presets::N0;
use iroh::portmapper::PortmapperConfig;
let endpoint = iroh::Endpoint::builder(N0)
.portmapper_config(PortmapperConfig::Enabled {})
.bind()
.await
.expect("failed to bind endpoint");
This configuration triggers the automatic SSDP multicast discovery defined in iroh/src/socket.rs (lines 892-904), where the port-mapper client is instantiated during socket binding.
Disabling UPnP to Avoid Firewall Prompts
On macOS and some restrictive networks, the SSDP multicast probe can trigger system firewall dialogs. To prevent this, explicitly disable the port-mapper:
use iroh::endpoint::presets::N0;
use iroh::portmapper::PortmapperConfig;
let endpoint = iroh::Endpoint::builder(N0)
.portmapper_config(PortmapperConfig::Disabled)
.bind()
.await
.expect("failed to bind endpoint");
Combining UPnP with Static External Addresses
When you know your public address but still want UPnP for additional port mapping, combine configurations:
use iroh::endpoint::presets::N0;
use iroh::portmapper::PortmapperConfig;
use std::net::SocketAddr;
let external: SocketAddr = "203.0.113.42:4000".parse().unwrap();
let endpoint = iroh::Endpoint::builder(N0)
.portmapper_config(PortmapperConfig::Enabled {})
.external_addr(external)
.bind()
.await
.expect("failed to bind endpoint");
How the Configuration Is Applied Internally
When you call bind(), iroh processes the PortmapperConfig in iroh/src/socket.rs (lines 892-904). If Enabled, it spawns a port-mapper client that runs independently of the main Endpoint lifecycle, periodically refreshing port mappings as needed. If Disabled, the socket layer skips client creation entirely, eliminating UPnP-related network traffic.
Summary
- PortmapperConfig::Enabled is the default state and activates UPnP, PCP, and NAT-PMP discovery via SSDP multicast.
- PortmapperConfig::Disabled prevents gateway probing, avoiding firewall dialogs but potentially reducing connectivity behind symmetric NATs.
- Configure UPnP explicitly using
Builder::portmapper_config()iniroh/src/endpoint.rsbefore callingbind(). - Ensure the
portmapperfeature is enabled in yourCargo.tomlto access these configuration options. - The port-mapper client is instantiated during socket binding in
iroh/src/socket.rs.
Frequently Asked Questions
Is UPnP enabled by default in iroh?
Yes. According to the source in iroh/src/endpoint.rs, the Endpoint builder defaults to PortmapperConfig::Enabled when the portmapper feature is active. This allows automatic NAT traversal without explicit configuration.
Why would I disable UPnP in iroh?
You should disable UPnP using PortmapperConfig::Disabled if you are running on macOS or restrictive networks where SSDP multicast probes trigger firewall permission dialogs. Disabling prevents these prompts while maintaining functionality through other NAT traversal methods.
Does disabling UPnP prevent all NAT traversal?
No. Disabling the port-mapper only stops UPnP, PCP, and NAT-PMP gateway requests. iroh still employs techniques such as QUIC path validation, hole punching via relays, and direct external address advertisement to establish connections.
What protocols does the iroh portmapper support besides UPnP?
The portmapper client supports three protocols: UPnP IGD, PCP (Port Control Protocol), and NAT-PMP (NAT Port Mapping Protocol). The implementation automatically selects the appropriate protocol based on gateway capabilities during the probing phase.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →