Skip to main content

pb_mapper_client/diagnostics/
port_mapping.rs

1//! What a tunnel's gateway port mapping is doing, as [`TunnelDiagnostics`]
2//! and [`TunnelEventKind::PortMappingChanged`] report it.
3//!
4//! [`TunnelDiagnostics`]: super::TunnelDiagnostics
5//! [`TunnelEventKind::PortMappingChanged`]: super::TunnelEventKind::PortMappingChanged
6
7use serde::Serialize;
8use std::net::{Ipv4Addr, SocketAddr};
9
10/// Where a gateway mapping stands. A lease outlives some stages: whether one
11/// is usable right now is [`PortMappingStatus::active`].
12#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
13#[serde(rename_all = "snake_case")]
14pub enum PortMappingStage {
15    /// Asked for when needed (`PortMapping::Auto`): starts if a direct
16    /// attempt fails to punch through, which none has.
17    Standby,
18    /// Requested; starts with the tunnel's first direct session, when its UDP
19    /// endpoint opens.
20    WaitingForDirect,
21    /// Looking for the default gateway and the protocols it speaks.
22    Discovering,
23    /// Found the gateway.
24    GatewayFound,
25    /// Learned which of PCP, NAT-PMP and UPnP the gateway speaks.
26    Discovered,
27    /// Asking the gateway for a mapping.
28    Requesting,
29    /// The gateway granted a lease.
30    Active,
31    /// Extending the lease, which stays usable meanwhile.
32    Renewing,
33    /// The lease ran out before it was renewed.
34    Expired,
35    /// No lease yet; asking again, 30 s after the first time and then less
36    /// often, up to every 10 min.
37    Retrying,
38    /// The local network changed; the gateway is looked for again.
39    NetworkChanged,
40    /// Asking the gateway to delete the lease.
41    Releasing,
42    /// The gateway confirmed the deletion (UPnP).
43    Released,
44    /// The deletion was sent; PCP and NAT-PMP do not acknowledge it.
45    ReleaseSent,
46    /// Discovery or a request failed; see [`PortMappingStatus::error`].
47    Failed,
48    /// The lease could not be deleted and may stay until it expires.
49    ReleaseFailed,
50    /// An operation was abandoned before it finished.
51    Cancelled,
52    /// The mapping stopped with its tunnel.
53    Stopped,
54}
55
56impl PortMappingStage {
57    /// Stable diagnostic label, matching the serialized representation.
58    pub fn as_str(self) -> &'static str {
59        match self {
60            Self::Standby => "standby",
61            Self::WaitingForDirect => "waiting_for_direct",
62            Self::Discovering => "discovering",
63            Self::GatewayFound => "gateway_found",
64            Self::Discovered => "discovered",
65            Self::Requesting => "requesting",
66            Self::Active => "active",
67            Self::Renewing => "renewing",
68            Self::Expired => "expired",
69            Self::Retrying => "retrying",
70            Self::NetworkChanged => "network_changed",
71            Self::Releasing => "releasing",
72            Self::Released => "released",
73            Self::ReleaseSent => "release_sent",
74            Self::Failed => "failed",
75            Self::ReleaseFailed => "release_failed",
76            Self::Cancelled => "cancelled",
77            Self::Stopped => "stopped",
78        }
79    }
80}
81
82/// How a gateway is asked for a mapping.
83#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
84#[serde(rename_all = "snake_case")]
85pub enum PortMappingProtocol {
86    Pcp,
87    NatPmp,
88    Upnp,
89}
90
91impl PortMappingProtocol {
92    /// Stable diagnostic label, matching the serialized representation.
93    pub fn as_str(self) -> &'static str {
94        match self {
95            Self::Pcp => "pcp",
96            Self::NatPmp => "nat_pmp",
97            Self::Upnp => "upnp",
98        }
99    }
100}
101
102/// A gateway mapping as it stands after its latest change. Credential-free;
103/// what the gateway said about a failure is cut to 256 characters.
104#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
105pub struct PortMappingStatus {
106    pub stage: PortMappingStage,
107    /// The local network this mapping belongs to; a change starts over.
108    pub network_generation: u64,
109    /// The direct endpoint's UDP port, once it is open.
110    pub local_port: Option<u16>,
111    pub gateway: Option<Ipv4Addr>,
112    /// The protocol of the latest request or lease.
113    pub protocol: Option<PortMappingProtocol>,
114    /// What the gateway speaks, once discovery finished.
115    pub available_protocols: Vec<PortMappingProtocol>,
116    /// The public address the latest lease mapped to.
117    pub external_addr: Option<SocketAddr>,
118    /// Whether a lease is usable now: still true while a renewal is pending
119    /// or has failed, until the lease expires or is released.
120    pub active: bool,
121    pub lease_lifetime_ms: Option<u64>,
122    pub error: Option<String>,
123}
124
125impl PortMappingStatus {
126    pub(crate) fn new(stage: PortMappingStage, network_generation: u64) -> Self {
127        Self {
128            stage,
129            network_generation,
130            local_port: None,
131            gateway: None,
132            protocol: None,
133            available_protocols: Vec::new(),
134            external_addr: None,
135            active: false,
136            lease_lifetime_ms: None,
137            error: None,
138        }
139    }
140}
141
142/// A tunnel's port mapping: where it stands, and what it took to get there.
143#[derive(Clone, Debug, Serialize)]
144pub struct PortMappingDiagnostics {
145    pub current: PortMappingStatus,
146    /// Requests for a new lease.
147    pub attempts: u64,
148    pub renewals: u64,
149    /// Failed discoveries, requests and releases.
150    pub failures: u64,
151}