Skip to content

Commit 5c38730

Browse files
feat(iroh-net): Nat-PMP probes and mappings (#1283)
1 parent f671aa5 commit 5c38730

8 files changed

Lines changed: 705 additions & 29 deletions

File tree

iroh-net/src/portmapper.rs

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use current_mapping::CurrentMapping;
2020
mod current_mapping;
2121
mod mapping;
2222
mod metrics;
23+
mod nat_pmp;
2324
mod pcp;
2425
mod upnp;
2526

@@ -38,20 +39,20 @@ const UNAVAILABILITY_TRUST_DURATION: Duration = Duration::from_secs(5);
3839

3940
/// Output of a port mapping probe.
4041
#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)]
41-
#[display("portmap={{ UPnP: {upnp}, PMP: {pmp}, PCP: {pcp} }}")]
42+
#[display("portmap={{ UPnP: {upnp}, PMP: {nat_pmp}, PCP: {pcp} }}")]
4243
pub struct ProbeOutput {
4344
/// If UPnP can be considered available.
4445
pub upnp: bool,
4546
/// If PCP can be considered available.
4647
pub pcp: bool,
4748
/// If PMP can be considered available.
48-
pub pmp: bool,
49+
pub nat_pmp: bool,
4950
}
5051

5152
impl ProbeOutput {
5253
/// Indicates if all port mapping protocols are available.
5354
pub fn all_available(&self) -> bool {
54-
self.upnp && self.pcp && self.pmp
55+
self.upnp && self.pcp && self.nat_pmp
5556
}
5657
}
5758

@@ -214,8 +215,8 @@ struct Probe {
214215
last_upnp_gateway_addr: Option<(upnp::Gateway, Instant)>,
215216
/// Last time PCP was seen.
216217
last_pcp: Option<Instant>,
217-
// TODO(@divma): PMP placeholder.
218-
last_pmp: Option<Instant>,
218+
/// Last time NAT-PMP was seen.
219+
last_nat_pmp: Option<Instant>,
219220
}
220221

221222
impl Default for Probe {
@@ -224,7 +225,7 @@ impl Default for Probe {
224225
last_probe: Instant::now() - AVAILABILITY_TRUST_DURATION,
225226
last_upnp_gateway_addr: None,
226227
last_pcp: None,
227-
last_pmp: None,
228+
last_nat_pmp: None,
228229
}
229230
}
230231
}
@@ -237,11 +238,11 @@ impl Probe {
237238
local_ip: Ipv4Addr,
238239
gateway: Ipv4Addr,
239240
) -> Probe {
240-
let ProbeOutput { upnp, pcp, pmp: _ } = output;
241+
let ProbeOutput { upnp, pcp, nat_pmp } = output;
241242
let Config {
242243
enable_upnp,
243244
enable_pcp,
244-
enable_nat_pmp: _,
245+
enable_nat_pmp,
245246
} = config;
246247
let mut upnp_probing_task = util::MaybeFuture {
247248
inner: (enable_upnp && !upnp).then(|| {
@@ -264,31 +265,37 @@ impl Probe {
264265
}),
265266
};
266267

267-
let pmp_probing_task = async { None };
268+
let mut nat_pmp_probing_task = util::MaybeFuture {
269+
inner: (enable_nat_pmp && !nat_pmp).then(|| {
270+
Box::pin(async {
271+
nat_pmp::probe_available(local_ip, gateway)
272+
.await
273+
.then(Instant::now)
274+
})
275+
}),
276+
};
268277

269278
if upnp_probing_task.inner.is_some() {
270279
inc!(Metrics, upnp_probes);
271280
}
272281

273282
let mut upnp_done = upnp_probing_task.inner.is_none();
274283
let mut pcp_done = pcp_probing_task.inner.is_none();
275-
let mut pmp_done = true;
276-
277-
tokio::pin!(pmp_probing_task);
284+
let mut nat_pmp_done = nat_pmp_probing_task.inner.is_none();
278285

279286
let mut probe = Probe::default();
280287

281-
while !upnp_done || !pcp_done || !pmp_done {
288+
while !upnp_done || !pcp_done || !nat_pmp_done {
282289
tokio::select! {
283290
last_upnp_gateway_addr = &mut upnp_probing_task, if !upnp_done => {
284291
trace!("tick: upnp probe ready");
285292
probe.last_upnp_gateway_addr = last_upnp_gateway_addr;
286293
upnp_done = true;
287294
},
288-
last_pmp = &mut pmp_probing_task, if !pmp_done => {
289-
trace!("tick: pmp probe ready");
290-
probe.last_pmp = last_pmp;
291-
pmp_done = true;
295+
last_nat_pmp = &mut nat_pmp_probing_task, if !nat_pmp_done => {
296+
trace!("tick: nat_pmp probe ready");
297+
probe.last_nat_pmp = last_nat_pmp;
298+
nat_pmp_done = true;
292299
},
293300
last_pcp = &mut pcp_probing_task, if !pcp_done => {
294301
trace!("tick: pcp probe ready");
@@ -318,10 +325,13 @@ impl Probe {
318325
.map(|last_probed| *last_probed + AVAILABILITY_TRUST_DURATION > now)
319326
.unwrap_or_default();
320327

321-
// not probing for now
322-
let pmp = false;
328+
let nat_pmp = self
329+
.last_nat_pmp
330+
.as_ref()
331+
.map(|last_probed| *last_probed + AVAILABILITY_TRUST_DURATION > now)
332+
.unwrap_or_default();
323333

324-
ProbeOutput { upnp, pcp, pmp }
334+
ProbeOutput { upnp, pcp, nat_pmp }
325335
}
326336

327337
/// Updates a probe with the `Some` values of another probe that is _assumed_ newer.
@@ -330,7 +340,7 @@ impl Probe {
330340
last_probe,
331341
last_upnp_gateway_addr,
332342
last_pcp,
333-
last_pmp,
343+
last_nat_pmp,
334344
} = probe;
335345
if last_upnp_gateway_addr.is_some() {
336346
inc!(Metrics, upnp_available);
@@ -359,8 +369,8 @@ impl Probe {
359369
inc!(Metrics, pcp_available);
360370
self.last_pcp = last_pcp;
361371
}
362-
if last_pmp.is_some() {
363-
self.last_pmp = last_pmp;
372+
if last_nat_pmp.is_some() {
373+
self.last_nat_pmp = last_nat_pmp;
364374
}
365375

366376
self.last_probe = last_probe;
@@ -571,7 +581,7 @@ impl Service {
571581
Err(e) => return debug!("can't get mapping: {e}"),
572582
};
573583

574-
let ProbeOutput { upnp, pcp, pmp: _ } = self.full_probe.output();
584+
let ProbeOutput { upnp, pcp, nat_pmp } = self.full_probe.output();
575585

576586
debug!("getting a port mapping for {local_ip}:{local_port} -> {external_addr:?}");
577587
let recently_probed =
@@ -583,6 +593,10 @@ impl Service {
583593
self.mapping_task = if pcp || (!recently_probed && self.config.enable_pcp) {
584594
let task = mapping::Mapping::new_pcp(local_ip, local_port, gateway, external_addr);
585595
Some(tokio::spawn(task).into())
596+
} else if nat_pmp || (!recently_probed && self.config.enable_nat_pmp) {
597+
let task =
598+
mapping::Mapping::new_nat_pmp(local_ip, local_port, gateway, external_addr);
599+
Some(tokio::spawn(task).into())
586600
} else if upnp || self.config.enable_upnp {
587601
let external_port = external_addr.map(|(_addr, port)| port);
588602
let gateway = self

iroh-net/src/portmapper/mapping.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::{net::Ipv4Addr, num::NonZeroU16, time::Duration};
44

55
use anyhow::Result;
66

7-
use super::{pcp, upnp};
7+
use super::{nat_pmp, pcp, upnp};
88

99
pub(super) trait PortMapped: std::fmt::Debug + Unpin {
1010
fn external(&self) -> (Ipv4Addr, NonZeroU16);
@@ -21,6 +21,9 @@ pub enum Mapping {
2121
/// A PCP mapping.
2222
#[debug(transparent)]
2323
Pcp(pcp::Mapping),
24+
/// A NAT-PMP mapping.
25+
#[debug(transparent)]
26+
NatPmp(nat_pmp::Mapping),
2427
}
2528

2629
impl Mapping {
@@ -36,6 +39,23 @@ impl Mapping {
3639
.map(Self::Pcp)
3740
}
3841

42+
/// Create a new NAT-PMP mapping.
43+
pub(crate) async fn new_nat_pmp(
44+
local_ip: Ipv4Addr,
45+
local_port: NonZeroU16,
46+
gateway: Ipv4Addr,
47+
external_addr: Option<(Ipv4Addr, NonZeroU16)>,
48+
) -> Result<Self> {
49+
nat_pmp::Mapping::new(
50+
local_ip,
51+
local_port,
52+
gateway,
53+
external_addr.map(|(_addr, port)| port),
54+
)
55+
.await
56+
.map(Self::NatPmp)
57+
}
58+
3959
/// Create a new UPnP mapping.
4060
pub(crate) async fn new_upnp(
4161
local_ip: Ipv4Addr,
@@ -53,6 +73,7 @@ impl Mapping {
5373
match self {
5474
Mapping::Upnp(m) => m.release().await,
5575
Mapping::Pcp(m) => m.release().await,
76+
Mapping::NatPmp(m) => m.release().await,
5677
}
5778
}
5879
}
@@ -62,13 +83,15 @@ impl PortMapped for Mapping {
6283
match self {
6384
Mapping::Upnp(m) => m.external(),
6485
Mapping::Pcp(m) => m.external(),
86+
Mapping::NatPmp(m) => m.external(),
6587
}
6688
}
6789

6890
fn half_lifetime(&self) -> Duration {
6991
match self {
7092
Mapping::Upnp(m) => m.half_lifetime(),
7193
Mapping::Pcp(m) => m.half_lifetime(),
94+
Mapping::NatPmp(m) => m.half_lifetime(),
7295
}
7396
}
7497
}

0 commit comments

Comments
 (0)