try to get public address via igd#2166
Conversation
There was a problem hiding this comment.
Pull request overview
This PR changes UDP public address resolution to prefer deriving the public IP via UPnP/IGD (and combine it with the mapped external port) to improve P2P connectivity in certain multi-router / same-public-network scenarios.
Changes:
- Added
try_get_public_addr_via_upnpto obtain external IP from an IGD gateway and build a publicSocketAddr. - Updated
resolve_udp_public_addrto attempt IGD-based public address resolution and fall back to STUN on failure.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| match try_get_public_addr_via_upnp(&global_ctx, local_listener, &port_mapping).await { | ||
| Ok(public_addr) => { | ||
| tracing::info!(%local_listener, %public_addr, "got public addr via upnp/igd"); | ||
| mapped_addr = public_addr; | ||
| } | ||
| Err(err) => { | ||
| tracing::debug!(?err, "upnp/igd failed, fallback to stun"); | ||
| } | ||
| } |
| let (gateway, _local_addr) = ActiveUdpPortMapping::discover_igd_gateway(global_ctx, local_listener).await?; | ||
|
|
||
| let public_ip = gateway.get_external_ip().await | ||
| .context("failed to get external ip from gateway")?; | ||
|
|
||
| let lease_ref = mapping.as_ref(); | ||
|
|
||
| let public_addr = if let Some(lease) = lease_ref { | ||
| SocketAddr::new(public_ip, lease.gateway_external_port()) | ||
| } else { | ||
| bail!("port mapping lease is required to get external port") | ||
| }; | ||
| Ok(public_addr) |
| let (gateway, _local_addr) = ActiveUdpPortMapping::discover_igd_gateway(global_ctx, local_listener).await?; | ||
|
|
||
| let public_ip = gateway.get_external_ip().await | ||
| .context("failed to get external ip from gateway")?; | ||
|
|
||
| let lease_ref = mapping.as_ref(); | ||
|
|
||
| let public_addr = if let Some(lease) = lease_ref { | ||
| SocketAddr::new(public_ip, lease.gateway_external_port()) | ||
| } else { | ||
| bail!("port mapping lease is required to get external port") | ||
| }; |
| let (gateway, _local_addr) = ActiveUdpPortMapping::discover_igd_gateway(global_ctx, local_listener).await?; | ||
|
|
||
| let public_ip = gateway.get_external_ip().await | ||
| .context("failed to get external ip from gateway")?; | ||
|
|
||
| let lease_ref = mapping.as_ref(); | ||
|
|
||
| let public_addr = if let Some(lease) = lease_ref { | ||
| SocketAddr::new(public_ip, lease.gateway_external_port()) | ||
| } else { | ||
| bail!("port mapping lease is required to get external port") | ||
| }; | ||
| Ok(public_addr) |
| match try_get_public_addr_via_upnp(&global_ctx, local_listener, &port_mapping).await { | ||
| Ok(public_addr) => { | ||
| tracing::info!(%local_listener, %public_addr, "got public addr via upnp/igd"); | ||
| mapped_addr = public_addr; | ||
| } | ||
| Err(err) => { | ||
| tracing::debug!(?err, "upnp/igd failed, fallback to stun"); | ||
| } |
| match try_get_public_addr_via_upnp(&global_ctx, local_listener, &port_mapping).await { | ||
| Ok(public_addr) => { | ||
| tracing::info!(%local_listener, %public_addr, "got public addr via upnp/igd"); | ||
| mapped_addr = public_addr; | ||
| } |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
我感觉 stun 取 public ip 已经非常成熟稳定了,改用 igd 好像收益有限 |
感谢大佬。如果easytier所在主机的网关(简称网关A)具有公网ip地址,那么使用stun或者igd两种方法一致,应该获得相同的“公网地址”。然而,如果网关A不具有公网地址,此时stun获得的应该是网关A上层网关(可能有多层nat)的公网地址,但igd方法获得的是网关A的地址(可能是私网地址)。因此,两种实现获得的ip地址是由一定区别的。 这个pr主要是提供一个方案。供同一内网(比如典型的校园网,有同一公网地址)下的两个网关(网关A,网关B)下的两台主机(主机C,主机D)能直接建立p2p连接。拓扑关系描述如下:主机C在网关A下,主机D在网关B下,网关A和网关B可以通过各自的ip地址通信。由于网关A和B均进行了nat,此时主机C和D没有办法直接p2p。启用此pr后,主机C上的easytier使用igd协议建立端口映射,主机D上的easytier通过网关A的ip地址可直接与主机C上的easytier进行p2p。 |
尝试优先通过igd获得easytier所在主机的网关地址用于建立p2p连接。此pr后,可以支持同一公网内两台路由器(这两台路由器需要可以互访)下的两个easytier主机通过两个网关地址建立p2p连接。此pr会影响公网地址获得,影响原来的功能,可能需要加一个选项来设置是否开启。由于对rust并不熟悉,仅抛砖引玉,看大佬是否可以帮忙完善。