1use std::{
4 net::{Ipv4Addr, SocketAddrV4},
5 num::NonZeroU16,
6 sync::Arc,
7 time::{Duration, Instant},
8};
9
10use current_mapping::CurrentMapping;
11use n0_error::{e, stack_error};
12use n0_future::StreamExt;
13use netwatch::interfaces::HomeRouter;
14use tokio::sync::{mpsc, oneshot, watch};
15use tokio_util::task::AbortOnDropHandle;
16use tracing::{Instrument, debug, info_span, trace};
17
18mod current_mapping;
19mod mapping;
20mod metrics;
21mod observer;
22#[cfg(test)]
23mod observer_tests;
24pub use observer::{Event, MappingProtocol, Observer};
25mod nat_pmp;
26mod pcp;
27mod upnp;
28mod util;
29mod defaults {
30 use std::time::Duration;
31
32 pub(crate) const UPNP_SEARCH_TIMEOUT: Duration = Duration::from_secs(1);
34
35 pub(crate) const PCP_RECV_TIMEOUT: Duration = Duration::from_millis(500);
37
38 pub(crate) const NAT_PMP_RECV_TIMEOUT: Duration = Duration::from_millis(500);
40}
41
42pub use metrics::Metrics;
43
44const AVAILABILITY_TRUST_DURATION: Duration = Duration::from_secs(60 * 10); const SERVICE_CHANNEL_CAPACITY: usize = 32; const UNAVAILABILITY_TRUST_DURATION: Duration = Duration::from_secs(5);
54
55#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)]
57#[display("portmap={{ UPnP: {upnp}, PMP: {nat_pmp}, PCP: {pcp} }}")]
58pub struct ProbeOutput {
59 pub upnp: bool,
61 pub pcp: bool,
63 pub nat_pmp: bool,
65}
66
67impl ProbeOutput {
68 pub fn all_available(&self) -> bool {
70 self.upnp && self.pcp && self.nat_pmp
71 }
72}
73
74#[allow(missing_docs)]
75#[stack_error(derive, add_meta)]
76#[derive(Clone)]
77#[non_exhaustive]
78pub enum ProbeError {
79 #[error("Mapping channel is full")]
80 ChannelFull,
81 #[error("Mapping channel is closed")]
82 ChannelClosed,
83 #[error("No gateway found for probe")]
84 NoGateway,
85 #[error("gateway found is ipv6, ignoring")]
86 Ipv6Gateway,
87 #[error("Probe task stopped. is_panic: {is_panic}, is_cancelled: {is_cancelled}")]
88 Join { is_panic: bool, is_cancelled: bool },
89}
90
91#[derive(derive_more::Debug)]
92enum Message {
93 Deactivate {
94 done: oneshot::Sender<()>,
95 },
96 ProcureMapping,
98 UpdateLocalPort {
104 local_port: Option<NonZeroU16>,
105 },
106 Probe {
111 #[debug("_")]
113 result_tx: oneshot::Sender<Result<ProbeOutput, ProbeError>>,
114 },
115}
116
117#[derive(Debug, Clone, Copy)]
119pub enum Protocol {
120 Udp,
122 Tcp,
124}
125
126#[derive(Debug, Clone)]
128pub struct Config {
129 pub enable_upnp: bool,
131 pub enable_pcp: bool,
133 pub enable_nat_pmp: bool,
135 pub protocol: Protocol,
137}
138
139impl Default for Config {
140 fn default() -> Self {
142 Config {
143 enable_upnp: true,
144 enable_pcp: true,
145 enable_nat_pmp: true,
146 protocol: Protocol::Udp,
147 }
148 }
149}
150
151#[derive(Debug, Clone)]
153pub struct Client {
154 port_mapping: watch::Receiver<Option<SocketAddrV4>>,
158 service_tx: mpsc::Sender<Message>,
160 metrics: Arc<Metrics>,
162 _service_handle: std::sync::Arc<AbortOnDropHandle<()>>,
164}
165
166impl Default for Client {
167 fn default() -> Self {
168 Self::new(Config::default())
169 }
170}
171
172impl Client {
173 pub fn new(config: Config) -> Self {
175 Self::with_metrics(config, Default::default())
176 }
177
178 pub fn with_metrics(config: Config, metrics: Arc<Metrics>) -> Self {
180 Self::with_observer(config, metrics, Observer::default())
181 }
182
183 pub fn with_observer(config: Config, metrics: Arc<Metrics>, observer: Observer) -> Self {
185 let (service_tx, service_rx) = mpsc::channel(SERVICE_CHANNEL_CAPACITY);
186
187 let (mut service, watcher) = Service::new(config, service_rx, metrics.clone());
188 service.observer = observer;
189
190 let handle = AbortOnDropHandle::new(tokio::spawn(
191 async move { service.run().await }.instrument(info_span!("portmapper.service")),
192 ));
193
194 Client {
195 port_mapping: watcher,
196 service_tx,
197 metrics,
198 _service_handle: std::sync::Arc::new(handle),
199 }
200 }
201
202 pub fn probe(&self) -> oneshot::Receiver<Result<ProbeOutput, ProbeError>> {
206 let (result_tx, result_rx) = oneshot::channel();
207
208 if let Err(e) = self.service_tx.try_send(Message::Probe { result_tx }) {
209 use mpsc::error::TrySendError::*;
210
211 let (result_tx, e) = match e {
213 Full(Message::Probe { result_tx }) => (result_tx, e!(ProbeError::ChannelFull)),
214 Closed(Message::Probe { result_tx }) => (result_tx, e!(ProbeError::ChannelClosed)),
215 Full(_) | Closed(_) => unreachable!("Sent value is a probe."),
216 };
217
218 if let Err(Err(e)) = result_tx.send(Err(e)) {
222 trace!("Failed to request probe: {e}")
223 }
224 }
225 result_rx
226 }
227
228 pub fn procure_mapping(&self) {
230 if let Err(e) = self.service_tx.try_send(Message::ProcureMapping) {
232 trace!("Failed to request mapping {e}")
233 }
234 }
235
236 pub fn update_local_port(&self, local_port: NonZeroU16) {
240 let local_port = Some(local_port);
241 if let Err(e) = self
243 .service_tx
244 .try_send(Message::UpdateLocalPort { local_port })
245 {
246 trace!("Failed to update local port {e}")
247 }
248 }
249
250 pub fn deactivate(&self) {
252 if let Err(e) = self
254 .service_tx
255 .try_send(Message::UpdateLocalPort { local_port: None })
256 {
257 trace!("Failed to deactivate port mapping {e}")
258 }
259 }
260
261 pub async fn deactivate_and_wait(&self) {
264 let (done, waiting) = oneshot::channel();
265 if self
266 .service_tx
267 .send(Message::Deactivate { done })
268 .await
269 .is_ok()
270 {
271 let _ = waiting.await;
272 }
273 }
274
275 pub fn watch_external_address(&self) -> watch::Receiver<Option<SocketAddrV4>> {
277 self.port_mapping.clone()
278 }
279
280 pub fn metrics(&self) -> &Arc<Metrics> {
282 &self.metrics
283 }
284}
285
286#[derive(Debug)]
288struct Probe {
289 last_probe: Instant,
291 last_upnp_gateway_addr: Option<(upnp::Gateway, Instant)>,
293 last_pcp: Option<Instant>,
295 last_nat_pmp: Option<Instant>,
297}
298
299impl Probe {
300 fn empty() -> Self {
302 Self {
303 last_probe: Instant::now(),
304 last_upnp_gateway_addr: None,
305 last_pcp: None,
306 last_nat_pmp: None,
307 }
308 }
309 async fn from_output(
311 config: Config,
312 output: ProbeOutput,
313 local_ip: Ipv4Addr,
314 gateway: Ipv4Addr,
315 metrics: Arc<Metrics>,
316 ) -> Probe {
317 let ProbeOutput { upnp, pcp, nat_pmp } = output;
318 let Config {
319 enable_upnp,
320 enable_pcp,
321 enable_nat_pmp,
322 protocol: _,
323 } = config;
324 let mut upnp_probing_task = util::MaybeFuture {
325 inner: (enable_upnp && !upnp).then(|| {
326 let metrics = metrics.clone();
327 Box::pin(async move {
328 upnp::probe_available(&metrics)
329 .await
330 .map(|addr| (addr, Instant::now()))
331 })
332 }),
333 };
334
335 let mut pcp_probing_task = util::MaybeFuture {
336 inner: (enable_pcp && !pcp).then(|| {
337 let metrics = metrics.clone();
338 Box::pin(async move {
339 metrics.pcp_probes.inc();
340 pcp::probe_available(local_ip, gateway)
341 .await
342 .then(Instant::now)
343 })
344 }),
345 };
346
347 let mut nat_pmp_probing_task = util::MaybeFuture {
348 inner: (enable_nat_pmp && !nat_pmp).then(|| {
349 Box::pin(async {
350 nat_pmp::probe_available(local_ip, gateway)
351 .await
352 .then(Instant::now)
353 })
354 }),
355 };
356
357 if upnp_probing_task.inner.is_some() {
358 metrics.upnp_probes.inc();
359 }
360
361 let mut upnp_done = upnp_probing_task.inner.is_none();
362 let mut pcp_done = pcp_probing_task.inner.is_none();
363 let mut nat_pmp_done = nat_pmp_probing_task.inner.is_none();
364
365 let mut probe = Probe::empty();
366
367 while !upnp_done || !pcp_done || !nat_pmp_done {
368 tokio::select! {
369 last_upnp_gateway_addr = &mut upnp_probing_task, if !upnp_done => {
370 trace!("tick: upnp probe ready");
371 probe.last_upnp_gateway_addr = last_upnp_gateway_addr;
372 upnp_done = true;
373 },
374 last_nat_pmp = &mut nat_pmp_probing_task, if !nat_pmp_done => {
375 trace!("tick: nat_pmp probe ready");
376 probe.last_nat_pmp = last_nat_pmp;
377 nat_pmp_done = true;
378 },
379 last_pcp = &mut pcp_probing_task, if !pcp_done => {
380 trace!("tick: pcp probe ready");
381 probe.last_pcp = last_pcp;
382 pcp_done = true;
383 },
384 }
385 }
386
387 probe
388 }
389
390 fn output(&self) -> ProbeOutput {
392 let now = Instant::now();
393
394 let upnp = self
396 .last_upnp_gateway_addr
397 .as_ref()
398 .map(|(_gateway_addr, last_probed)| *last_probed + AVAILABILITY_TRUST_DURATION > now)
399 .unwrap_or_default();
400
401 let pcp = self
402 .last_pcp
403 .as_ref()
404 .map(|last_probed| *last_probed + AVAILABILITY_TRUST_DURATION > now)
405 .unwrap_or_default();
406
407 let nat_pmp = self
408 .last_nat_pmp
409 .as_ref()
410 .map(|last_probed| *last_probed + AVAILABILITY_TRUST_DURATION > now)
411 .unwrap_or_default();
412
413 ProbeOutput { upnp, pcp, nat_pmp }
414 }
415
416 fn update(&mut self, probe: Probe, metrics: &Arc<Metrics>) {
418 let Probe {
419 last_probe,
420 last_upnp_gateway_addr,
421 last_pcp,
422 last_nat_pmp,
423 } = probe;
424 if last_upnp_gateway_addr.is_some() {
425 metrics.upnp_available.inc();
426 let new_gateway = last_upnp_gateway_addr
427 .as_ref()
428 .map(|(addr, _last_seen)| addr);
429 let old_gateway = self
430 .last_upnp_gateway_addr
431 .as_ref()
432 .map(|(addr, _last_seen)| addr);
433 if new_gateway != old_gateway {
434 metrics.upnp_gateway_updated.inc();
435 debug!(
436 "upnp gateway changed {:?} -> {:?}",
437 old_gateway
438 .map(|gw| gw.to_string())
439 .unwrap_or("None".into()),
440 new_gateway
441 .map(|gw| gw.to_string())
442 .unwrap_or("None".into())
443 )
444 };
445 self.last_upnp_gateway_addr = last_upnp_gateway_addr;
446 }
447 if last_pcp.is_some() {
448 metrics.pcp_available.inc();
449 self.last_pcp = last_pcp;
450 }
451 if last_nat_pmp.is_some() {
452 self.last_nat_pmp = last_nat_pmp;
453 }
454
455 self.last_probe = last_probe;
456 }
457}
458
459type ProbeResult = Result<ProbeOutput, ProbeError>;
461
462#[derive(Debug)]
464pub struct Service {
465 config: Config,
466 observer: Observer,
467 local_port: Option<NonZeroU16>,
469 rx: mpsc::Receiver<Message>,
473 current_mapping: CurrentMapping,
475 full_probe: Probe,
477 mapping_task: Option<AbortOnDropHandle<Result<mapping::Mapping, mapping::Error>>>,
482 probing_task: Option<(AbortOnDropHandle<Probe>, Vec<oneshot::Sender<ProbeResult>>)>,
487 metrics: Arc<Metrics>,
488}
489
490impl Service {
491 fn new(
492 config: Config,
493 rx: mpsc::Receiver<Message>,
494 metrics: Arc<Metrics>,
495 ) -> (Self, watch::Receiver<Option<SocketAddrV4>>) {
496 let (current_mapping, watcher) = CurrentMapping::new(metrics.clone());
497 let mut full_probe = Probe::empty();
498 if let Some(in_the_past) = full_probe
499 .last_probe
500 .checked_sub(AVAILABILITY_TRUST_DURATION)
501 {
502 full_probe.last_probe = in_the_past;
504 }
505 let service = Service {
506 config,
507 observer: Observer::default(),
508 local_port: None,
509 rx,
510 current_mapping,
511 full_probe,
512 mapping_task: None,
513 probing_task: None,
514 metrics,
515 };
516
517 (service, watcher)
518 }
519
520 async fn invalidate_mapping(&mut self) {
522 if let Some(old_mapping) = self.current_mapping.update(None) {
523 use mapping::PortMapped;
524 let (ip, port) = old_mapping.external();
525 let external = SocketAddrV4::new(ip, port.get());
526 let confirmed = matches!(&old_mapping, mapping::Mapping::Upnp(_));
527 self.observer.emit(Event::Releasing(external));
528 match old_mapping.release().await {
529 Ok(()) => self.observer.emit(Event::Released {
530 external,
531 confirmed,
532 }),
533 Err(e) => {
534 debug!("failed to release mapping {e}");
535 self.observer.emit(Event::ReleaseFailed {
536 external,
537 error: observer::detail(e),
538 });
539 }
540 }
541 }
542 }
543
544 async fn run(mut self) {
545 debug!("portmap starting");
546 loop {
547 tokio::select! {
548 msg = self.rx.recv() => {
549 trace!("tick: msg {msg:?}");
550 match msg {
551 Some(msg) => {
552 self.handle_msg(msg).await;
553 },
554 None => {
555 debug!("portmap service channel dropped. Likely shutting down.");
556 break;
557 }
558 }
559 }
560 mapping_result = util::MaybeFuture{ inner: self.mapping_task.as_mut() } => {
561 trace!("tick: mapping ready");
562 self.mapping_task = None;
564 self.on_mapping_result(mapping_result);
567 }
568 probe_result = util::MaybeFuture{ inner: self.probing_task.as_mut().map(|(fut, _rec)| fut) } => {
569 trace!("tick: probe ready");
570 let receivers = self.probing_task.take().expect("is some").1;
572 let probe_result = probe_result.map_err(|e| e!(ProbeError::Join { is_panic: e.is_panic(), is_cancelled: e.is_cancelled() }));
573 self.on_probe_result(probe_result, receivers);
574 }
575 Some(event) = self.current_mapping.next() => {
576 trace!("tick: mapping event {event:?}");
577 match event {
578 current_mapping::Event::Renew { external_ip, external_port } => {
579 self.get_mapping(Some((external_ip, external_port)));
580 },
581 current_mapping::Event::Expired { external_ip, external_port } => {
582 self.observer.emit(Event::Expired(SocketAddrV4::new(external_ip, external_port.get())));
583 self.get_mapping(Some((external_ip, external_port)));
584 },
585 }
586
587 }
588 }
589 }
590 }
591
592 fn on_probe_result(
593 &mut self,
594 result: Result<Probe, ProbeError>,
595 receivers: Vec<oneshot::Sender<ProbeResult>>,
596 ) {
597 let result = result.map(|probe| {
598 self.full_probe.update(probe, &self.metrics);
599 let output = self.full_probe.output();
603 trace!(?output, "probe output");
604 output
605 });
606 match &result {
607 Ok(output) => self.observer.emit(Event::Discovered(output.clone())),
608 Err(error) => self
609 .observer
610 .emit(Event::DiscoveryFailed(observer::detail(error))),
611 }
612 for tx in receivers {
613 let _ = tx.send(result.clone());
615 }
616 }
617
618 fn on_mapping_result(
619 &mut self,
620 result: Result<Result<mapping::Mapping, mapping::Error>, tokio::task::JoinError>,
621 ) {
622 match result {
623 Ok(Ok(mapping)) => {
624 use mapping::PortMapped;
625 let protocol = match &mapping {
626 mapping::Mapping::Pcp(_) => MappingProtocol::Pcp,
627 mapping::Mapping::NatPmp(_) => MappingProtocol::NatPmp,
628 mapping::Mapping::Upnp(_) => MappingProtocol::Upnp,
629 };
630 let (ip, port) = mapping.external();
631 self.observer.emit(Event::Active {
632 protocol,
633 external: SocketAddrV4::new(ip, port.get()),
634 lifetime: mapping.half_lifetime().saturating_mul(2),
635 });
636 self.current_mapping.update(Some(mapping));
637 }
638 Ok(Err(e)) => {
639 debug!("failed to get a port mapping {e}");
640 self.metrics.mapping_failures.inc();
641 self.observer
642 .emit(Event::MappingFailed(observer::detail(e)));
643 }
644 Err(e) => {
645 debug!("failed to get a port mapping {e}");
646 self.metrics.mapping_failures.inc();
647 self.observer
648 .emit(Event::MappingFailed(observer::detail(e)));
649 }
650 }
651 }
652
653 async fn handle_msg(&mut self, msg: Message) {
654 match msg {
655 Message::Deactivate { done } => {
656 self.update_local_port(None).await;
657 self.probing_task = None;
658 let _ = done.send(());
659 }
660 Message::ProcureMapping => self.update_local_port(self.local_port).await,
661 Message::UpdateLocalPort { local_port } => self.update_local_port(local_port).await,
662 Message::Probe { result_tx } => self.probe_request(result_tx),
663 }
664 }
665
666 async fn update_local_port(&mut self, local_port: Option<NonZeroU16>) {
671 if local_port != self.local_port {
673 self.metrics.local_port_updates.inc();
674 let old_port = std::mem::replace(&mut self.local_port, local_port);
675
676 let dropped_task = self.mapping_task.take();
679 let did_cancel = dropped_task
681 .map(|task| !task.is_finished())
682 .unwrap_or_default();
683
684 if did_cancel {
685 self.observer.emit(Event::Cancelled);
686 debug!(
687 "canceled mapping task due to local port update. Old: {:?} New: {:?}",
688 old_port, self.local_port
689 )
690 }
691
692 let external_addr = self.current_mapping.external();
694
695 if external_addr.is_some() {
699 self.invalidate_mapping().await;
700 }
701
702 self.get_mapping(external_addr);
704 } else if self.current_mapping.external().is_none() {
705 self.get_mapping(None)
707 }
708 }
709
710 fn get_mapping(&mut self, external_addr: Option<(Ipv4Addr, NonZeroU16)>) {
711 if let Some(local_port) = self.local_port {
712 self.metrics.mapping_attempts.inc();
713
714 let (local_ip, gateway) = match ip_and_gateway() {
715 Ok(ip_and_gw) => ip_and_gw,
716 Err(e) => {
717 self.observer
718 .emit(Event::MappingFailed(observer::detail(&e)));
719 return debug!("can't get mapping: {e}");
720 }
721 };
722
723 let ProbeOutput { upnp, pcp, nat_pmp } = self.full_probe.output();
724
725 debug!("getting a port mapping for {local_ip}:{local_port} -> {external_addr:?}");
726 let recently_probed =
727 self.full_probe.last_probe + UNAVAILABILITY_TRUST_DURATION > Instant::now();
728 let protocol = self.config.protocol;
729 let selected = if pcp {
732 MappingProtocol::Pcp
733 } else if nat_pmp {
734 MappingProtocol::NatPmp
735 } else if upnp || self.config.enable_upnp {
736 MappingProtocol::Upnp
737 } else if !recently_probed && self.config.enable_pcp {
738 MappingProtocol::Pcp
739 } else if !recently_probed && self.config.enable_nat_pmp {
740 MappingProtocol::NatPmp
741 } else {
742 self.observer
743 .emit(Event::MappingFailed("no supported mapping protocol".into()));
744 return;
745 };
746 self.observer.emit(Event::Requesting {
747 protocol: selected,
748 gateway,
749 renewal: self.current_mapping.external().is_some(),
750 });
751 let upnp_gateway = self
752 .full_probe
753 .last_upnp_gateway_addr
754 .as_ref()
755 .map(|(gateway, _)| gateway.clone());
756 self.mapping_task = Some(AbortOnDropHandle::new(tokio::spawn(
757 async move {
758 match selected {
759 MappingProtocol::Pcp => {
760 mapping::Mapping::new_pcp(
761 protocol,
762 local_ip,
763 local_port,
764 gateway,
765 external_addr,
766 )
767 .await
768 }
769 MappingProtocol::NatPmp => {
770 mapping::Mapping::new_nat_pmp(
771 protocol,
772 local_ip,
773 local_port,
774 gateway,
775 external_addr,
776 )
777 .await
778 }
779 MappingProtocol::Upnp => {
780 mapping::Mapping::new_upnp(
781 protocol,
782 local_ip,
783 local_port,
784 upnp_gateway,
785 external_addr.map(|(_, port)| port),
786 )
787 .await
788 }
789 }
790 }
791 .instrument(info_span!("mapping", ?selected)),
792 )));
793 }
794 }
795
796 fn probe_request(&mut self, result_tx: oneshot::Sender<Result<ProbeOutput, ProbeError>>) {
802 match self.probing_task.as_mut() {
803 Some((_task_handle, receivers)) => receivers.push(result_tx),
804 None => {
805 let probe_output = self.full_probe.output();
806 if probe_output.all_available() {
807 self.observer.emit(Event::Discovered(probe_output.clone()));
808 let _ = result_tx.send(Ok(probe_output));
810 } else {
811 self.metrics.probes_started.inc();
812 self.observer.emit(Event::Discovering);
813
814 let (local_ip, gateway) = match ip_and_gateway() {
815 Ok(ip_and_gw) => ip_and_gw,
816 Err(e) => {
817 debug!("could not start probe: {e}");
819 self.observer
820 .emit(Event::DiscoveryFailed(observer::detail(&e)));
821 let _ = result_tx.send(Err(e));
822 return;
823 }
824 };
825
826 self.observer.emit(Event::Gateway { local_ip, gateway });
827
828 let config = self.config.clone();
829 let metrics = self.metrics.clone();
830 let handle = tokio::spawn(
831 async move {
832 Probe::from_output(config, probe_output, local_ip, gateway, metrics)
833 .await
834 }
835 .instrument(info_span!("portmapper.probe")),
836 );
837 let receivers = vec![result_tx];
838 self.probing_task = Some((AbortOnDropHandle::new(handle), receivers));
839 }
840 }
841 }
842 }
843}
844
845fn ip_and_gateway() -> Result<(Ipv4Addr, Ipv4Addr), ProbeError> {
847 let Some(HomeRouter { gateway, my_ip }) = HomeRouter::new() else {
848 return Err(e!(ProbeError::NoGateway));
849 };
850
851 let local_ip = match my_ip {
852 Some(std::net::IpAddr::V4(ip))
853 if !ip.is_unspecified() && !ip.is_loopback() && !ip.is_multicast() =>
854 {
855 ip
856 }
857 other => {
858 debug!("no address suitable for port mapping found ({other:?}), using localhost");
859 Ipv4Addr::LOCALHOST
860 }
861 };
862
863 let std::net::IpAddr::V4(gateway) = gateway else {
864 return Err(e!(ProbeError::Ipv6Gateway));
865 };
866
867 Ok((local_ip, gateway))
868}