From fad3e24b3f2698ce6c1fa3fdad54201bec668298 Mon Sep 17 00:00:00 2001
From: Divma <26765164+divagant-martian@users.noreply.github.com>
Date: Tue, 29 Oct 2024 11:19:01 -0500
Subject: [PATCH] refactor(iroh-net): portmapper and network monitor are crates
(#2855)
## Description
The non-controversial part of the big network-utils refactor as
described in the title.
## Breaking Changes
Full cargo public-api
diff
report
```
Removed items from the public API
=================================
-pub struct iroh_net::metrics::PortmapMetrics
-pub iroh_net::metrics::PortmapMetrics::external_address_updated: iroh_metrics::core::Counter
-pub iroh_net::metrics::PortmapMetrics::local_port_updates: iroh_metrics::core::Counter
-pub iroh_net::metrics::PortmapMetrics::mapping_attempts: iroh_metrics::core::Counter
-pub iroh_net::metrics::PortmapMetrics::mapping_failures: iroh_metrics::core::Counter
-pub iroh_net::metrics::PortmapMetrics::pcp_available: iroh_metrics::core::Counter
-pub iroh_net::metrics::PortmapMetrics::pcp_probes: iroh_metrics::core::Counter
-pub iroh_net::metrics::PortmapMetrics::probes_started: iroh_metrics::core::Counter
-pub iroh_net::metrics::PortmapMetrics::upnp_available: iroh_metrics::core::Counter
-pub iroh_net::metrics::PortmapMetrics::upnp_gateway_updated: iroh_metrics::core::Counter
-pub iroh_net::metrics::PortmapMetrics::upnp_probes: iroh_metrics::core::Counter
-pub iroh_net::metrics::PortmapMetrics::upnp_probes_failed: iroh_metrics::core::Counter
-impl core::default::Default for iroh_net::metrics::PortmapMetrics
-impl core::default::Default for iroh_net::metrics::PortmapMetrics
-pub fn iroh_net::metrics::PortmapMetrics::default() -> Self
-pub fn iroh_net::metrics::PortmapMetrics::default() -> Self
-impl iroh_metrics::core::Metric for iroh_net::metrics::PortmapMetrics
-impl iroh_metrics::core::Metric for iroh_net::metrics::PortmapMetrics
-pub fn iroh_net::metrics::PortmapMetrics::name() -> &'static str
-pub fn iroh_net::metrics::PortmapMetrics::name() -> &'static str
-impl struct_iterable_internal::Iterable for iroh_net::metrics::PortmapMetrics
-impl struct_iterable_internal::Iterable for iroh_net::metrics::PortmapMetrics
-pub fn iroh_net::metrics::PortmapMetrics::iter<'a>(&'a self) -> alloc::vec::into_iter::IntoIter<(&'static str, &'a dyn core::any::Any)>
-pub fn iroh_net::metrics::PortmapMetrics::iter<'a>(&'a self) -> alloc::vec::into_iter::IntoIter<(&'static str, &'a dyn core::any::Any)>
-pub mod iroh_net::net
-pub mod iroh_net::net::ip
-pub struct iroh_net::net::ip::LocalAddresses
-pub iroh_net::net::ip::LocalAddresses::loopback: alloc::vec::Vec
-pub iroh_net::net::ip::LocalAddresses::regular: alloc::vec::Vec
-impl iroh_net::net::ip::LocalAddresses
-pub fn iroh_net::net::ip::LocalAddresses::new() -> Self
-impl core::default::Default for iroh_net::net::ip::LocalAddresses
-pub fn iroh_net::net::ip::LocalAddresses::default() -> Self
-pub const fn iroh_net::net::ip::is_unicast_link_local(addr: core::net::ip_addr::Ipv6Addr) -> bool
-pub mod iroh_net::net::netmon
-pub struct iroh_net::net::netmon::CallbackToken(_)
-pub struct iroh_net::net::netmon::Monitor
-impl iroh_net::net::netmon::Monitor
-pub async fn iroh_net::net::netmon::Monitor::network_change(&self) -> anyhow::Result<()>
-pub async fn iroh_net::net::netmon::Monitor::new() -> anyhow::Result
-pub async fn iroh_net::net::netmon::Monitor::subscribe(&self, callback: F) -> anyhow::Result where F: core::ops::function::Fn(bool) -> futures_lite::future::Boxed<()> + 'static + core::marker::Sync + core::marker::Send
-pub async fn iroh_net::net::netmon::Monitor::unsubscribe(&self, token: iroh_net::net::netmon::CallbackToken) -> anyhow::Result<()>
-impl core::ops::drop::Drop for iroh_net::net::netmon::Monitor
-pub fn iroh_net::net::netmon::Monitor::drop(&mut self)
-pub enum iroh_net::net::IpFamily
-pub iroh_net::net::IpFamily::V4
-pub iroh_net::net::IpFamily::V6
-impl iroh_net::net::IpFamily
-pub fn iroh_net::net::IpFamily::local_addr(&self) -> core::net::ip_addr::IpAddr
-pub fn iroh_net::net::IpFamily::unspecified_addr(&self) -> core::net::ip_addr::IpAddr
-impl core::convert::From for iroh_net::net::IpFamily
-pub fn iroh_net::net::IpFamily::from(value: core::net::ip_addr::IpAddr) -> Self
-impl core::convert::From for socket2::Domain
-pub fn socket2::Domain::from(value: iroh_net::net::IpFamily) -> Self
-pub struct iroh_net::net::UdpSocket(_)
-impl iroh_net::net::UdpSocket
-pub fn iroh_net::net::UdpSocket::bind(network: iroh_net::net::IpFamily, port: u16) -> anyhow::Result
-pub fn iroh_net::net::UdpSocket::bind_full(addr: impl core::convert::Into) -> anyhow::Result
-pub fn iroh_net::net::UdpSocket::bind_local(network: iroh_net::net::IpFamily, port: u16) -> anyhow::Result
-pub fn iroh_net::net::UdpSocket::bind_local_v4(port: u16) -> anyhow::Result
-pub fn iroh_net::net::UdpSocket::bind_local_v6(port: u16) -> anyhow::Result
-pub fn iroh_net::net::UdpSocket::bind_v4(port: u16) -> anyhow::Result
-pub fn iroh_net::net::UdpSocket::bind_v6(port: u16) -> anyhow::Result
-impl core::convert::From for iroh_net::net::UdpSocket
-pub fn iroh_net::net::UdpSocket::from(socket: tokio::net::udp::UdpSocket) -> Self
-impl core::ops::deref::Deref for iroh_net::net::UdpSocket
-pub type iroh_net::net::UdpSocket::Target = tokio::net::udp::UdpSocket
-pub fn iroh_net::net::UdpSocket::deref(&self) -> &Self::Target
-impl core::ops::drop::Drop for iroh_net::net::UdpSocket
-pub fn iroh_net::net::UdpSocket::drop(&mut self)
-impl std::os::fd::owned::AsFd for iroh_net::net::UdpSocket
-pub fn iroh_net::net::UdpSocket::as_fd(&self) -> std::os::fd::owned::BorrowedFd<'_>
-pub mod iroh_net::portmapper
-pub struct iroh_net::portmapper::Client
-impl iroh_net::portmapper::Client
-pub fn iroh_net::portmapper::Client::deactivate(&self)
-pub fn iroh_net::portmapper::Client::new(config: iroh_net::portmapper::Config) -> Self
-pub fn iroh_net::portmapper::Client::probe(&self) -> tokio::sync::oneshot::Receiver>
-pub fn iroh_net::portmapper::Client::procure_mapping(&self)
-pub fn iroh_net::portmapper::Client::update_local_port(&self, local_port: core::num::nonzero::NonZeroU16)
-pub fn iroh_net::portmapper::Client::watch_external_address(&self) -> tokio::sync::watch::Receiver>
-impl core::default::Default for iroh_net::portmapper::Client
-pub fn iroh_net::portmapper::Client::default() -> Self
-pub struct iroh_net::portmapper::Config
-pub iroh_net::portmapper::Config::enable_nat_pmp: bool
-pub iroh_net::portmapper::Config::enable_pcp: bool
-pub iroh_net::portmapper::Config::enable_upnp: bool
-impl core::default::Default for iroh_net::portmapper::Config
-pub fn iroh_net::portmapper::Config::default() -> Self
-pub struct iroh_net::portmapper::Metrics
-pub iroh_net::portmapper::Metrics::external_address_updated: iroh_metrics::core::Counter
-pub iroh_net::portmapper::Metrics::local_port_updates: iroh_metrics::core::Counter
-pub iroh_net::portmapper::Metrics::mapping_attempts: iroh_metrics::core::Counter
-pub iroh_net::portmapper::Metrics::mapping_failures: iroh_metrics::core::Counter
-pub iroh_net::portmapper::Metrics::pcp_available: iroh_metrics::core::Counter
-pub iroh_net::portmapper::Metrics::pcp_probes: iroh_metrics::core::Counter
-pub iroh_net::portmapper::Metrics::probes_started: iroh_metrics::core::Counter
-pub iroh_net::portmapper::Metrics::upnp_available: iroh_metrics::core::Counter
-pub iroh_net::portmapper::Metrics::upnp_gateway_updated: iroh_metrics::core::Counter
-pub iroh_net::portmapper::Metrics::upnp_probes: iroh_metrics::core::Counter
-pub iroh_net::portmapper::Metrics::upnp_probes_failed: iroh_metrics::core::Counter
-pub struct iroh_net::portmapper::ProbeOutput
-pub iroh_net::portmapper::ProbeOutput::nat_pmp: bool
-pub iroh_net::portmapper::ProbeOutput::pcp: bool
-pub iroh_net::portmapper::ProbeOutput::upnp: bool
-impl iroh_net::portmapper::ProbeOutput
-pub fn iroh_net::portmapper::ProbeOutput::all_available(&self) -> bool
-pub struct iroh_net::portmapper::Service
Changed items in the public API
===============================
-pub async fn iroh_net::netcheck::Client::get_report(&mut self, dm: iroh_net::relay::RelayMap, stun_conn4: core::option::Option>, stun_conn6: core::option::Option>) -> anyhow::Result>
+pub async fn iroh_net::netcheck::Client::get_report(&mut self, dm: iroh_net::relay::RelayMap, stun_conn4: core::option::Option>, stun_conn6: core::option::Option>) -> anyhow::Result>
-pub async fn iroh_net::netcheck::Client::get_report_channel(&mut self, dm: iroh_net::relay::RelayMap, stun_conn4: core::option::Option>, stun_conn6: core::option::Option>) -> anyhow::Result>>>
+pub async fn iroh_net::netcheck::Client::get_report_channel(&mut self, dm: iroh_net::relay::RelayMap, stun_conn4: core::option::Option>, stun_conn6: core::option::Option>) -> anyhow::Result>>>
-pub fn iroh_net::netcheck::Client::new(port_mapper: core::option::Option, dns_resolver: iroh_net::dns::DnsResolver) -> anyhow::Result
+pub fn iroh_net::netcheck::Client::new(port_mapper: core::option::Option, dns_resolver: hickory_resolver::async_resolver::TokioAsyncResolver) -> anyhow::Result
-pub iroh_net::netcheck::Report::portmap_probe: core::option::Option
+pub iroh_net::netcheck::Report::portmap_probe: core::option::Option
Added items to the public API
=============================
+pub use iroh_net::metrics::PortmapMetrics
```
- `iroh_net::net` is removed. The unchanged functionality can be found
in the published crate `netwatch` which the `n0 team` will keep
maintaining. This has been moved to allow serving a more general public.
- `iroh_net::portmapper` is removed. The unchanged functionality can be
found in the published crate `portmapper` which the `n0 team` will keep
maintaining. This has been moved to allow serving a more general public.
- The origin of `iroh_net::metrics::PortmapMetrics` is changed. The type
is no longer internal to `iroh-net` but re-exported from `portmapper`
- Some `netcheck` functions now require `netwatcher` and `portmapper`
parameters that are _not_ currently re-exported. `netcheck` will be
moved to it's own crate as well so this intermediate state is acceptable
for the time being
## Notes & open questions
- **what used to be called net**
has a very small API that might not be as useful to the general public.
It only gives a `subscribe` to changes that only reports if the change
"is major" without any description of what being major is or the origin
of the change. Providing a more detailed API and better docs is
something we can do later, but it's worth noticing.
- **portmapper**
might be good to start doing the move to `thiserror` here since it's
very clearly isolated. Maybe turn on/off as features specific protocols.
All this, again, to make it more attractive/useful to a wider audience.
- there is `netwatcher`, which I found by looking for what I thought was
a natural name to this (`netwatch`) and it has the API I'd imagine this
crate should provide. https://crates.io/crates/netwatcher
## Change checklist
- [x] Self-review.
- [x] Documentation updates following the [style
guide](https://rust-lang.github.io/rfcs/1574-more-api-documentation-conventions.html#appendix-a-full-conventions-text),
if relevant.
- [x] Tests if relevant.
- [x] All breaking changes documented.
---
.github/workflows/ci.yml | 2 +-
.github/workflows/tests.yaml | 2 +-
Cargo.lock | 62 ++++++++++++++++++-
Cargo.toml | 2 +
iroh-cli/Cargo.toml | 1 +
iroh-cli/src/commands/doctor.rs | 2 +-
iroh-net/Cargo.toml | 9 +--
iroh-net/src/defaults.rs | 9 ---
iroh-net/src/lib.rs | 2 -
iroh-net/src/magicsock.rs | 4 +-
iroh-net/src/magicsock/node_map/node_state.rs | 2 +-
iroh-net/src/magicsock/udp_conn.rs | 6 +-
iroh-net/src/metrics.rs | 7 +--
iroh-net/src/netcheck.rs | 10 ++-
iroh-net/src/netcheck/reportgen.rs | 3 +-
iroh-net/src/netcheck/reportgen/hairpin.rs | 2 +-
iroh-net/src/netcheck/reportgen/probes.rs | 2 +-
net-tools/netwatch/Cargo.toml | 49 +++++++++++++++
net-tools/netwatch/README.md | 24 +++++++
.../netwatch/src}/interfaces.rs | 40 ++++--------
.../netwatch/src}/interfaces/bsd.rs | 0
.../netwatch/src}/interfaces/bsd/freebsd.rs | 0
.../netwatch/src}/interfaces/bsd/macos.rs | 0
.../netwatch/src}/interfaces/bsd/netbsd.rs | 0
.../netwatch/src}/interfaces/bsd/openbsd.rs | 0
.../netwatch/src}/interfaces/linux.rs | 0
.../netwatch/src}/interfaces/windows.rs | 0
.../src/net => net-tools/netwatch/src}/ip.rs | 0
.../netwatch/src}/ip_family.rs | 0
.../net.rs => net-tools/netwatch/src/lib.rs | 2 +-
.../net => net-tools/netwatch/src}/netmon.rs | 0
.../netwatch/src}/netmon/actor.rs | 2 +-
.../netwatch/src}/netmon/android.rs | 0
.../netwatch/src}/netmon/bsd.rs | 4 +-
.../netwatch/src}/netmon/linux.rs | 2 +-
.../netwatch/src}/netmon/windows.rs | 0
.../src/net => net-tools/netwatch/src}/udp.rs | 0
net-tools/portmapper/Cargo.toml | 49 +++++++++++++++
net-tools/portmapper/README.md | 24 +++++++
.../portmapper/src}/current_mapping.rs | 0
.../portmapper/src/lib.rs | 16 ++++-
.../portmapper/src}/mapping.rs | 0
.../portmapper/src}/metrics.rs | 0
.../portmapper/src}/nat_pmp.rs | 3 +-
.../portmapper/src}/nat_pmp/protocol.rs | 0
.../src}/nat_pmp/protocol/request.rs | 0
.../src}/nat_pmp/protocol/response.rs | 0
.../portmapper/src}/pcp.rs | 3 +-
.../portmapper/src}/pcp/protocol.rs | 0
.../src}/pcp/protocol/opcode_data.rs | 0
.../portmapper/src}/pcp/protocol/request.rs | 0
.../portmapper/src}/pcp/protocol/response.rs | 0
.../portmapper/src}/upnp.rs | 2 +-
net-tools/portmapper/src/util.rs | 30 +++++++++
54 files changed, 301 insertions(+), 76 deletions(-)
create mode 100644 net-tools/netwatch/Cargo.toml
create mode 100644 net-tools/netwatch/README.md
rename {iroh-net/src/net => net-tools/netwatch/src}/interfaces.rs (90%)
rename {iroh-net/src/net => net-tools/netwatch/src}/interfaces/bsd.rs (100%)
rename {iroh-net/src/net => net-tools/netwatch/src}/interfaces/bsd/freebsd.rs (100%)
rename {iroh-net/src/net => net-tools/netwatch/src}/interfaces/bsd/macos.rs (100%)
rename {iroh-net/src/net => net-tools/netwatch/src}/interfaces/bsd/netbsd.rs (100%)
rename {iroh-net/src/net => net-tools/netwatch/src}/interfaces/bsd/openbsd.rs (100%)
rename {iroh-net/src/net => net-tools/netwatch/src}/interfaces/linux.rs (100%)
rename {iroh-net/src/net => net-tools/netwatch/src}/interfaces/windows.rs (100%)
rename {iroh-net/src/net => net-tools/netwatch/src}/ip.rs (100%)
rename {iroh-net/src/net => net-tools/netwatch/src}/ip_family.rs (100%)
rename iroh-net/src/net.rs => net-tools/netwatch/src/lib.rs (83%)
rename {iroh-net/src/net => net-tools/netwatch/src}/netmon.rs (100%)
rename {iroh-net/src/net => net-tools/netwatch/src}/netmon/actor.rs (99%)
rename {iroh-net/src/net => net-tools/netwatch/src}/netmon/android.rs (100%)
rename {iroh-net/src/net => net-tools/netwatch/src}/netmon/bsd.rs (96%)
rename {iroh-net/src/net => net-tools/netwatch/src}/netmon/linux.rs (99%)
rename {iroh-net/src/net => net-tools/netwatch/src}/netmon/windows.rs (100%)
rename {iroh-net/src/net => net-tools/netwatch/src}/udp.rs (100%)
create mode 100644 net-tools/portmapper/Cargo.toml
create mode 100644 net-tools/portmapper/README.md
rename {iroh-net/src/portmapper => net-tools/portmapper/src}/current_mapping.rs (100%)
rename iroh-net/src/portmapper.rs => net-tools/portmapper/src/lib.rs (98%)
rename {iroh-net/src/portmapper => net-tools/portmapper/src}/mapping.rs (100%)
rename {iroh-net/src/portmapper => net-tools/portmapper/src}/metrics.rs (100%)
rename {iroh-net/src/portmapper => net-tools/portmapper/src}/nat_pmp.rs (98%)
rename {iroh-net/src/portmapper => net-tools/portmapper/src}/nat_pmp/protocol.rs (100%)
rename {iroh-net/src/portmapper => net-tools/portmapper/src}/nat_pmp/protocol/request.rs (100%)
rename {iroh-net/src/portmapper => net-tools/portmapper/src}/nat_pmp/protocol/response.rs (100%)
rename {iroh-net/src/portmapper => net-tools/portmapper/src}/pcp.rs (98%)
rename {iroh-net/src/portmapper => net-tools/portmapper/src}/pcp/protocol.rs (100%)
rename {iroh-net/src/portmapper => net-tools/portmapper/src}/pcp/protocol/opcode_data.rs (100%)
rename {iroh-net/src/portmapper => net-tools/portmapper/src}/pcp/protocol/request.rs (100%)
rename {iroh-net/src/portmapper => net-tools/portmapper/src}/pcp/protocol/response.rs (100%)
rename {iroh-net/src/portmapper => net-tools/portmapper/src}/upnp.rs (98%)
create mode 100644 net-tools/portmapper/src/util.rs
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index dafda9cb78..ca06beeb06 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -190,7 +190,7 @@ jobs:
# uses: obi1kenobi/cargo-semver-checks-action@v2
uses: n0-computer/cargo-semver-checks-action@feat-baseline
with:
- package: iroh, iroh-base, iroh-cli, iroh-dns-server, iroh-metrics, iroh-net, iroh-net-bench, iroh-router
+ package: iroh, iroh-base, iroh-cli, iroh-dns-server, iroh-metrics, iroh-net, iroh-net-bench, iroh-router, netwatch, portmapper
baseline-rev: ${{ env.HEAD_COMMIT_SHA }}
use-cache: false
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
index aa52c43b06..7ebd2ab2ce 100644
--- a/.github/workflows/tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -23,7 +23,7 @@ env:
RUSTFLAGS: -Dwarnings
RUSTDOCFLAGS: -Dwarnings
SCCACHE_CACHE_SIZE: "50G"
- CRATES_LIST: "iroh,iroh-metrics,iroh-net,iroh-net-bench,iroh-test,iroh-cli,iroh-dns-server,iroh-router"
+ CRATES_LIST: "iroh,iroh-metrics,iroh-net,iroh-net-bench,iroh-test,iroh-cli,iroh-dns-server,iroh-router,netwatch,portmapper"
IROH_FORCE_STAGING_RELAYS: "1"
jobs:
diff --git a/Cargo.lock b/Cargo.lock
index 318b8ce138..66342bd5e3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2618,6 +2618,7 @@ dependencies = [
"parking_lot",
"pkarr",
"portable-atomic",
+ "portmapper",
"postcard",
"quic-rpc",
"rand",
@@ -2837,12 +2838,14 @@ dependencies = [
"netlink-packet-core",
"netlink-packet-route",
"netlink-sys",
+ "netwatch",
"ntest",
"num_enum",
"once_cell",
"parking_lot",
"pin-project",
"pkarr",
+ "portmapper",
"postcard",
"pretty_assertions",
"proptest",
@@ -2864,7 +2867,6 @@ dependencies = [
"stun-rs",
"surge-ping",
"swarm-discovery",
- "testdir",
"testresult",
"thiserror",
"time",
@@ -3365,6 +3367,35 @@ dependencies = [
"tokio",
]
+[[package]]
+name = "netwatch"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "bytes",
+ "derive_more",
+ "futures-lite 2.3.0",
+ "futures-sink",
+ "futures-util",
+ "iroh-test",
+ "libc",
+ "netdev",
+ "netlink-packet-core",
+ "netlink-packet-route",
+ "netlink-sys",
+ "once_cell",
+ "rtnetlink",
+ "serde",
+ "socket2",
+ "thiserror",
+ "time",
+ "tokio",
+ "tracing",
+ "tracing-subscriber",
+ "windows 0.51.1",
+ "wmi",
+]
+
[[package]]
name = "nibble_vec"
version = "0.1.0"
@@ -3971,6 +4002,35 @@ version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265"
+[[package]]
+name = "portmapper"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "base64 0.22.1",
+ "bytes",
+ "derive_more",
+ "futures-lite 2.3.0",
+ "futures-util",
+ "igd-next",
+ "iroh-metrics",
+ "libc",
+ "netwatch",
+ "ntest",
+ "num_enum",
+ "rand",
+ "rand_chacha",
+ "serde",
+ "smallvec",
+ "socket2",
+ "thiserror",
+ "time",
+ "tokio",
+ "tokio-util",
+ "tracing",
+ "url",
+]
+
[[package]]
name = "positioned-io"
version = "0.3.3"
diff --git a/Cargo.toml b/Cargo.toml
index 1cb098e60b..c069d76c1a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,6 +9,8 @@ members = [
"iroh-net/bench",
"iroh-cli",
"iroh-router",
+ "net-tools/netwatch",
+ "net-tools/portmapper",
]
resolver = "2"
diff --git a/iroh-cli/Cargo.toml b/iroh-cli/Cargo.toml
index 2fe907240f..71071696fc 100644
--- a/iroh-cli/Cargo.toml
+++ b/iroh-cli/Cargo.toml
@@ -46,6 +46,7 @@ iroh-metrics = { version = "0.27.0" }
parking_lot = "0.12.1"
pkarr = { version = "2.2.0", default-features = false }
portable-atomic = "1"
+portmapper = { version = "0.1.0", path = "../net-tools/portmapper" }
postcard = "1.0.8"
quic-rpc = { version = "0.12", features = ["flume-transport", "quinn-transport"] }
rand = "0.8.5"
diff --git a/iroh-cli/src/commands/doctor.rs b/iroh-cli/src/commands/doctor.rs
index 27a2279d58..c01529c553 100644
--- a/iroh-cli/src/commands/doctor.rs
+++ b/iroh-cli/src/commands/doctor.rs
@@ -37,7 +37,7 @@ use iroh::{
endpoint::{self, Connection, ConnectionTypeStream, RecvStream, RemoteInfo, SendStream},
key::{PublicKey, SecretKey},
metrics::MagicsockMetrics,
- netcheck, portmapper,
+ netcheck,
relay::{RelayMap, RelayMode, RelayUrl},
ticket::NodeTicket,
Endpoint, NodeAddr, NodeId,
diff --git a/iroh-net/Cargo.toml b/iroh-net/Cargo.toml
index 1e88697de1..af992ab9b8 100644
--- a/iroh-net/Cargo.toml
+++ b/iroh-net/Cargo.toml
@@ -17,10 +17,9 @@ workspace = true
[dependencies]
anyhow = { version = "1" }
-base64 = "0.22.1"
backoff = "0.4.0"
+base64 = "0.22.1"
bytes = "1.7"
-netdev = "0.30.0"
der = { version = "0.7", features = ["alloc", "derive"] }
derive_more = { version = "1.0.0", features = ["debug", "display", "from", "try_into", "deref"] }
futures-buffered = "0.2.8"
@@ -40,11 +39,14 @@ hyper-util = "0.1.1"
igd-next = { version = "0.15.1", features = ["aio_tokio"] }
iroh-base = { version = "0.27.0", features = ["key"] }
libc = "0.2.139"
+netdev = "0.30.0"
+netwatch = { version = "0.1.0", path = "../net-tools/netwatch" }
num_enum = "0.7"
once_cell = "1.18.0"
parking_lot = "0.12.1"
pin-project = "1"
pkarr = { version = "2", default-features = false, features = ["async", "relay"] }
+portmapper = { path = "../net-tools/portmapper" }
postcard = { version = "1", default-features = false, features = ["alloc", "use-std", "experimental-derive"] }
quinn = { package = "iroh-quinn", version = "0.11" }
quinn-proto = { package = "iroh-quinn-proto", version = "0.11" }
@@ -63,10 +65,10 @@ thiserror = "1"
time = "0.3.20"
tokio = { version = "1", features = ["io-util", "macros", "sync", "rt", "net", "fs", "io-std", "signal", "process"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] }
+tokio-stream = { version = "0.1.15" }
tokio-tungstenite = "0.21"
tokio-tungstenite-wasm = "0.3"
tokio-util = { version = "0.7.12", features = ["io-util", "io", "codec", "rt"] }
-tokio-stream = { version = "0.1.15" }
tracing = "0.1"
tungstenite = "0.21"
url = { version = "2.4", features = ["serde"] }
@@ -114,7 +116,6 @@ ntest = "0.9"
pretty_assertions = "1.4"
proptest = "1.2.0"
rand_chacha = "0.3.1"
-testdir = "0.9.1"
tokio = { version = "1", features = ["io-util", "sync", "rt", "net", "fs", "macros", "time", "test-util"] }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
iroh-test = "0.27.0"
diff --git a/iroh-net/src/defaults.rs b/iroh-net/src/defaults.rs
index 95ab102953..fee45ea305 100644
--- a/iroh-net/src/defaults.rs
+++ b/iroh-net/src/defaults.rs
@@ -162,18 +162,9 @@ pub(crate) mod timeouts {
/// The amount of time we wait for a hairpinned packet to come back.
pub(crate) const HAIRPIN_CHECK_TIMEOUT: Duration = Duration::from_millis(100);
- /// Maximum duration a UPnP search can take before timing out.
- pub(crate) const UPNP_SEARCH_TIMEOUT: Duration = Duration::from_secs(1);
-
- /// Timeout to receive a response from a PCP server.
- pub(crate) const PCP_RECV_TIMEOUT: Duration = Duration::from_millis(500);
-
/// Default Pinger timeout
pub(crate) const DEFAULT_PINGER_TIMEOUT: Duration = Duration::from_secs(5);
- /// Timeout to receive a response from a NAT-PMP server.
- pub(crate) const NAT_PMP_RECV_TIMEOUT: Duration = Duration::from_millis(500);
-
/// Timeouts specifically used in the iroh-relay
pub(crate) mod relay {
use super::*;
diff --git a/iroh-net/src/lib.rs b/iroh-net/src/lib.rs
index 759eaefa65..bda876ccad 100644
--- a/iroh-net/src/lib.rs
+++ b/iroh-net/src/lib.rs
@@ -241,10 +241,8 @@ pub mod dns;
pub mod endpoint;
mod magicsock;
pub mod metrics;
-pub mod net;
pub mod netcheck;
pub mod ping;
-pub mod portmapper;
pub mod relay;
pub mod stun;
pub mod ticket;
diff --git a/iroh-net/src/magicsock.rs b/iroh-net/src/magicsock.rs
index 1a9341e18f..55c009e0e4 100644
--- a/iroh-net/src/magicsock.rs
+++ b/iroh-net/src/magicsock.rs
@@ -35,6 +35,7 @@ use futures_lite::{FutureExt, Stream, StreamExt};
use futures_util::stream::BoxStream;
use iroh_base::key::NodeId;
use iroh_metrics::{inc, inc_by};
+use netwatch::{interfaces, ip::LocalAddresses, netmon};
use quinn::AsyncUdpSocket;
use rand::{seq::SliceRandom, Rng, SeedableRng};
use smallvec::{smallvec, SmallVec};
@@ -64,8 +65,7 @@ use crate::{
dns::DnsResolver,
endpoint::NodeAddr,
key::{PublicKey, SecretKey, SharedSecret},
- net::{interfaces, ip::LocalAddresses, netmon},
- netcheck, portmapper,
+ netcheck,
relay::{RelayMap, RelayUrl},
stun, AddrInfo,
};
diff --git a/iroh-net/src/magicsock/node_map/node_state.rs b/iroh-net/src/magicsock/node_map/node_state.rs
index 3a96e49ff1..5d72f47e5c 100644
--- a/iroh-net/src/magicsock/node_map/node_state.rs
+++ b/iroh-net/src/magicsock/node_map/node_state.rs
@@ -6,6 +6,7 @@ use std::{
};
use iroh_metrics::inc;
+use netwatch::ip::is_unicast_link_local;
use serde::{Deserialize, Serialize};
use tokio::sync::mpsc;
use tracing::{debug, event, info, instrument, trace, warn, Level};
@@ -22,7 +23,6 @@ use crate::{
endpoint::AddrInfo,
key::PublicKey,
magicsock::{ActorMessage, MagicsockMetrics, QuicMappedAddr, Timer, HEARTBEAT_INTERVAL},
- net::ip::is_unicast_link_local,
relay::RelayUrl,
stun,
util::relay_only_mode,
diff --git a/iroh-net/src/magicsock/udp_conn.rs b/iroh-net/src/magicsock/udp_conn.rs
index db376521c5..c176ae8144 100644
--- a/iroh-net/src/magicsock/udp_conn.rs
+++ b/iroh-net/src/magicsock/udp_conn.rs
@@ -9,13 +9,12 @@ use std::{
};
use anyhow::{bail, Context as _};
+use netwatch::UdpSocket;
use quinn::AsyncUdpSocket;
use quinn_udp::{Transmit, UdpSockRef};
use tokio::io::Interest;
use tracing::{debug, trace};
-use crate::net::UdpSocket;
-
/// A UDP socket implementing Quinn's [`AsyncUdpSocket`].
#[derive(Clone, Debug)]
pub struct UdpConn {
@@ -197,11 +196,12 @@ where
#[cfg(test)]
mod tests {
use anyhow::Result;
+ use netwatch::IpFamily;
use tokio::sync::mpsc;
use tracing::{info_span, Instrument};
use super::*;
- use crate::{key, net::IpFamily, tls};
+ use crate::{key, tls};
const ALPN: &[u8] = b"n0/test/1";
diff --git a/iroh-net/src/metrics.rs b/iroh-net/src/metrics.rs
index 90ebbae09e..db9e77c015 100644
--- a/iroh-net/src/metrics.rs
+++ b/iroh-net/src/metrics.rs
@@ -1,8 +1,7 @@
//! Co-locating all of the iroh-net metrics structs
+pub use portmapper::Metrics as PortmapMetrics;
+
#[cfg(feature = "iroh-relay")]
#[cfg_attr(iroh_docsrs, doc(cfg(feature = "iroh-relay")))]
pub use crate::relay::server::Metrics as RelayMetrics;
-pub use crate::{
- magicsock::Metrics as MagicsockMetrics, netcheck::Metrics as NetcheckMetrics,
- portmapper::Metrics as PortmapMetrics,
-};
+pub use crate::{magicsock::Metrics as MagicsockMetrics, netcheck::Metrics as NetcheckMetrics};
diff --git a/iroh-net/src/netcheck.rs b/iroh-net/src/netcheck.rs
index 5c5f904a5e..5e8d54deeb 100644
--- a/iroh-net/src/netcheck.rs
+++ b/iroh-net/src/netcheck.rs
@@ -15,7 +15,9 @@ use std::{
use anyhow::{anyhow, Context as _, Result};
use bytes::Bytes;
+use hickory_resolver::TokioAsyncResolver as DnsResolver;
use iroh_metrics::inc;
+use netwatch::{IpFamily, UdpSocket};
use tokio::{
sync::{self, mpsc, oneshot},
time::{Duration, Instant},
@@ -23,12 +25,8 @@ use tokio::{
use tokio_util::{sync::CancellationToken, task::AbortOnDropHandle};
use tracing::{debug, error, info_span, trace, warn, Instrument};
-use super::{portmapper, relay::RelayMap, stun};
-use crate::{
- dns::DnsResolver,
- net::{IpFamily, UdpSocket},
- relay::RelayUrl,
-};
+use super::{relay::RelayMap, stun};
+use crate::relay::RelayUrl;
mod metrics;
mod reportgen;
diff --git a/iroh-net/src/netcheck/reportgen.rs b/iroh-net/src/netcheck/reportgen.rs
index 007a04a410..c9cbb10610 100644
--- a/iroh-net/src/netcheck/reportgen.rs
+++ b/iroh-net/src/netcheck/reportgen.rs
@@ -26,6 +26,7 @@ use std::{
use anyhow::{anyhow, bail, Context, Result};
use iroh_metrics::inc;
+use netwatch::{interfaces, UdpSocket};
use rand::seq::IteratorRandom;
use tokio::{
sync::{mpsc, oneshot},
@@ -39,10 +40,8 @@ use super::NetcheckMetrics;
use crate::{
defaults::DEFAULT_STUN_PORT,
dns::{DnsResolver, ResolverExt},
- net::{interfaces, UdpSocket},
netcheck::{self, Report},
ping::{PingError, Pinger},
- portmapper,
relay::{RelayMap, RelayNode, RelayUrl},
stun,
util::MaybeFuture,
diff --git a/iroh-net/src/netcheck/reportgen/hairpin.rs b/iroh-net/src/netcheck/reportgen/hairpin.rs
index eba5b202eb..47f23e1a88 100644
--- a/iroh-net/src/netcheck/reportgen/hairpin.rs
+++ b/iroh-net/src/netcheck/reportgen/hairpin.rs
@@ -15,13 +15,13 @@
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use anyhow::{bail, Context, Result};
+use netwatch::UdpSocket;
use tokio::{sync::oneshot, time::Instant};
use tokio_util::task::AbortOnDropHandle;
use tracing::{debug, error, info_span, trace, warn, Instrument};
use crate::{
defaults::timeouts::HAIRPIN_CHECK_TIMEOUT,
- net::UdpSocket,
netcheck::{self, reportgen, Inflight},
stun,
};
diff --git a/iroh-net/src/netcheck/reportgen/probes.rs b/iroh-net/src/netcheck/reportgen/probes.rs
index 5bf62deec9..c8bfca845a 100644
--- a/iroh-net/src/netcheck/reportgen/probes.rs
+++ b/iroh-net/src/netcheck/reportgen/probes.rs
@@ -7,10 +7,10 @@
use std::{collections::BTreeSet, fmt, sync::Arc};
use anyhow::{ensure, Result};
+use netwatch::interfaces;
use tokio::time::Duration;
use crate::{
- net::interfaces,
netcheck::Report,
relay::{RelayMap, RelayNode, RelayUrl},
};
diff --git a/net-tools/netwatch/Cargo.toml b/net-tools/netwatch/Cargo.toml
new file mode 100644
index 0000000000..3628883cfa
--- /dev/null
+++ b/net-tools/netwatch/Cargo.toml
@@ -0,0 +1,49 @@
+[package]
+name = "netwatch"
+version = "0.1.0"
+readme = "README.md"
+description = "Cross-platform monitoring for network interface changes"
+license = "MIT OR Apache-2.0"
+authors = ["n0 team"]
+repository = "https://github.com/n0-computer/iroh"
+keywords = ["networking", "interfaces"]
+edition = "2021"
+
+[lints]
+workspace = true
+
+[dependencies]
+anyhow = { version = "1" }
+bytes = "1.7"
+futures-lite = "2.3"
+futures-sink = "0.3.25"
+futures-util = "0.3.25"
+libc = "0.2.139"
+netdev = "0.30.0"
+once_cell = "1.18.0"
+socket2 = "0.5.3"
+thiserror = "1"
+time = "0.3.20"
+tokio = { version = "1", features = ["io-util", "macros", "sync", "rt", "net", "fs", "io-std", "signal", "process", "time"] }
+tracing = "0.1"
+
+[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
+netlink-packet-core = "0.7.0"
+netlink-packet-route = "0.17.0"
+netlink-sys = "0.8.5"
+rtnetlink = "0.13.0"
+
+[target.'cfg(target_os = "windows")'.dependencies]
+wmi = "0.13"
+windows = { version = "0.51", features = ["Win32_NetworkManagement_IpHelper", "Win32_Foundation", "Win32_NetworkManagement_Ndis", "Win32_Networking_WinSock"] }
+serde = { version = "1", features = ["derive"] }
+derive_more = { version = "1.0.0", features = ["debug"] }
+
+[dev-dependencies]
+tokio = { version = "1", features = ["io-util", "sync", "rt", "net", "fs", "macros", "time", "test-util"] }
+tracing-subscriber = { version = "0.3", features = ["env-filter"] }
+iroh-test = "0.27.0"
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "iroh_docsrs"]
diff --git a/net-tools/netwatch/README.md b/net-tools/netwatch/README.md
new file mode 100644
index 0000000000..e0c8f39b05
--- /dev/null
+++ b/net-tools/netwatch/README.md
@@ -0,0 +1,24 @@
+# Netwatch
+
+`netwatch` is a cross-platform library for monitoring of networking interfaces
+and route changes.
+
+Used in [iroh](https://github.com/n0-computer/iroh), created with love by the
+[n0 team](https://n0.computer/).
+
+# License
+
+This project is licensed under either of
+
+ * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
+ http://www.apache.org/licenses/LICENSE-2.0)
+ * MIT license ([LICENSE-MIT](LICENSE-MIT) or
+ http://opensource.org/licenses/MIT)
+
+at your option.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in this project by you, as defined in the Apache-2.0 license,
+shall be dual licensed as above, without any additional terms or conditions.
diff --git a/iroh-net/src/net/interfaces.rs b/net-tools/netwatch/src/interfaces.rs
similarity index 90%
rename from iroh-net/src/net/interfaces.rs
rename to net-tools/netwatch/src/interfaces.rs
index 759b0925f7..f9f511d879 100644
--- a/iroh-net/src/net/interfaces.rs
+++ b/net-tools/netwatch/src/interfaces.rs
@@ -29,11 +29,11 @@ use self::bsd::default_route;
use self::linux::default_route;
#[cfg(target_os = "windows")]
use self::windows::default_route;
-use crate::net::ip::{is_private_v6, is_up};
+use crate::ip::{is_private_v6, is_up};
/// Represents a network interface.
#[derive(Debug)]
-pub(crate) struct Interface {
+pub struct Interface {
iface: netdev::interface::Interface,
}
@@ -71,7 +71,7 @@ impl Interface {
}
/// A list of all ip addresses of this interface.
- pub(crate) fn addrs(&self) -> impl Iterator- + '_ {
+ pub fn addrs(&self) -> impl Iterator
- + '_ {
self.iface
.ipv4
.iter()
@@ -82,14 +82,7 @@ impl Interface {
/// Creates a fake interface for usage in tests.
///
- /// Sometimes tests want to be deterministic, e.g. [`ProbePlan`] tests rely on the
- /// interface state. This allows tests to be independent of the host interfaces.
- ///
- /// It is rather possible that we'll want more variations of this in the future, feel
- /// free to add parameters or different alternative constructors.
- ///
- /// [`ProbePlan`]: crate::netcheck::reportgen::probes::ProbePlan
- #[cfg(test)]
+ /// This allows tests to be independent of the host interfaces.
pub(crate) fn fake() -> Self {
use std::net::Ipv4Addr;
@@ -126,7 +119,7 @@ impl Interface {
/// Structure of an IP network, either IPv4 or IPv6.
#[derive(Clone, Debug)]
-pub(crate) enum IpNet {
+pub enum IpNet {
/// Structure of IPv4 Network.
V4(Ipv4Net),
/// Structure of IPv6 Network.
@@ -161,16 +154,16 @@ impl IpNet {
/// Intended to store the state of the machine's network interfaces, routing table, and
/// other network configuration. For now it's pretty basic.
#[derive(Debug, PartialEq, Eq)]
-pub(crate) struct State {
+pub struct State {
/// Maps from an interface name interface.
- pub(crate) interfaces: HashMap,
+ pub interfaces: HashMap,
/// Whether this machine has an IPv6 Global or Unique Local Address
/// which might provide connectivity.
- pub(crate) have_v6: bool,
+ pub have_v6: bool,
/// Whether the machine has some non-localhost, non-link-local IPv4 address.
- pub(crate) have_v4: bool,
+ pub have_v4: bool,
//// Whether the current network interface is considered "expensive", which currently means LTE/etc
/// instead of Wifi. This field is not populated by `get_state`.
@@ -255,15 +248,8 @@ impl State {
/// Creates a fake interface state for usage in tests.
///
- /// Sometimes tests want to be deterministic, e.g. [`ProbePlan`] tests rely on the
- /// interface state. This allows tests to be independent of the host interfaces.
- ///
- /// It is rather possible that we'll want more variations of this in the future, feel
- /// free to add parameters or different alternative constructors.
- ///
- /// [`ProbePlan`]: crate::netcheck::reportgen::probes::ProbePlan
- #[cfg(test)]
- pub(crate) fn fake() -> Self {
+ /// This allows tests to be independent of the host interfaces.
+ pub fn fake() -> Self {
let fake = Interface::fake();
let ifname = fake.iface.name.clone();
Self {
@@ -341,7 +327,7 @@ pub async fn default_route_interface() -> Option {
/// Likely IPs of the residentla router, and the ip address of the current
/// machine using it.
#[derive(Debug, Clone)]
-pub(crate) struct HomeRouter {
+pub struct HomeRouter {
/// Ip of the router.
pub gateway: IpAddr,
/// Our local Ip if known.
@@ -354,7 +340,7 @@ impl HomeRouter {
/// In addition, it returns the IP address of the current machine on
/// the LAN using that gateway.
/// This is used as the destination for UPnP, NAT-PMP, PCP, etc queries.
- pub(crate) fn new() -> Option {
+ pub fn new() -> Option {
let gateway = Self::get_default_gateway()?;
let my_ip = netdev::interface::get_local_ipaddr();
diff --git a/iroh-net/src/net/interfaces/bsd.rs b/net-tools/netwatch/src/interfaces/bsd.rs
similarity index 100%
rename from iroh-net/src/net/interfaces/bsd.rs
rename to net-tools/netwatch/src/interfaces/bsd.rs
diff --git a/iroh-net/src/net/interfaces/bsd/freebsd.rs b/net-tools/netwatch/src/interfaces/bsd/freebsd.rs
similarity index 100%
rename from iroh-net/src/net/interfaces/bsd/freebsd.rs
rename to net-tools/netwatch/src/interfaces/bsd/freebsd.rs
diff --git a/iroh-net/src/net/interfaces/bsd/macos.rs b/net-tools/netwatch/src/interfaces/bsd/macos.rs
similarity index 100%
rename from iroh-net/src/net/interfaces/bsd/macos.rs
rename to net-tools/netwatch/src/interfaces/bsd/macos.rs
diff --git a/iroh-net/src/net/interfaces/bsd/netbsd.rs b/net-tools/netwatch/src/interfaces/bsd/netbsd.rs
similarity index 100%
rename from iroh-net/src/net/interfaces/bsd/netbsd.rs
rename to net-tools/netwatch/src/interfaces/bsd/netbsd.rs
diff --git a/iroh-net/src/net/interfaces/bsd/openbsd.rs b/net-tools/netwatch/src/interfaces/bsd/openbsd.rs
similarity index 100%
rename from iroh-net/src/net/interfaces/bsd/openbsd.rs
rename to net-tools/netwatch/src/interfaces/bsd/openbsd.rs
diff --git a/iroh-net/src/net/interfaces/linux.rs b/net-tools/netwatch/src/interfaces/linux.rs
similarity index 100%
rename from iroh-net/src/net/interfaces/linux.rs
rename to net-tools/netwatch/src/interfaces/linux.rs
diff --git a/iroh-net/src/net/interfaces/windows.rs b/net-tools/netwatch/src/interfaces/windows.rs
similarity index 100%
rename from iroh-net/src/net/interfaces/windows.rs
rename to net-tools/netwatch/src/interfaces/windows.rs
diff --git a/iroh-net/src/net/ip.rs b/net-tools/netwatch/src/ip.rs
similarity index 100%
rename from iroh-net/src/net/ip.rs
rename to net-tools/netwatch/src/ip.rs
diff --git a/iroh-net/src/net/ip_family.rs b/net-tools/netwatch/src/ip_family.rs
similarity index 100%
rename from iroh-net/src/net/ip_family.rs
rename to net-tools/netwatch/src/ip_family.rs
diff --git a/iroh-net/src/net.rs b/net-tools/netwatch/src/lib.rs
similarity index 83%
rename from iroh-net/src/net.rs
rename to net-tools/netwatch/src/lib.rs
index a010dc235c..213fe78e00 100644
--- a/iroh-net/src/net.rs
+++ b/net-tools/netwatch/src/lib.rs
@@ -1,6 +1,6 @@
//! Networking related utilities
-pub(crate) mod interfaces;
+pub mod interfaces;
pub mod ip;
mod ip_family;
pub mod netmon;
diff --git a/iroh-net/src/net/netmon.rs b/net-tools/netwatch/src/netmon.rs
similarity index 100%
rename from iroh-net/src/net/netmon.rs
rename to net-tools/netwatch/src/netmon.rs
diff --git a/iroh-net/src/net/netmon/actor.rs b/net-tools/netwatch/src/netmon/actor.rs
similarity index 99%
rename from iroh-net/src/net/netmon/actor.rs
rename to net-tools/netwatch/src/netmon/actor.rs
index f12f44c873..46df1888f4 100644
--- a/iroh-net/src/net/netmon/actor.rs
+++ b/net-tools/netwatch/src/netmon/actor.rs
@@ -24,7 +24,7 @@ use super::bsd as os;
use super::linux as os;
#[cfg(target_os = "windows")]
use super::windows as os;
-use crate::net::{
+use crate::{
interfaces::{IpNet, State},
ip::is_link_local,
};
diff --git a/iroh-net/src/net/netmon/android.rs b/net-tools/netwatch/src/netmon/android.rs
similarity index 100%
rename from iroh-net/src/net/netmon/android.rs
rename to net-tools/netwatch/src/netmon/android.rs
diff --git a/iroh-net/src/net/netmon/bsd.rs b/net-tools/netwatch/src/netmon/bsd.rs
similarity index 96%
rename from iroh-net/src/net/netmon/bsd.rs
rename to net-tools/netwatch/src/netmon/bsd.rs
index 20bab5aae7..41190afcf5 100644
--- a/iroh-net/src/net/netmon/bsd.rs
+++ b/net-tools/netwatch/src/netmon/bsd.rs
@@ -6,8 +6,8 @@ use tracing::{trace, warn};
use super::actor::NetworkMessage;
#[cfg(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
-use crate::net::interfaces::bsd::{RTAX_DST, RTAX_IFP};
-use crate::net::{interfaces::bsd::WireMessage, ip::is_link_local};
+use crate::interfaces::bsd::{RTAX_DST, RTAX_IFP};
+use crate::{interfaces::bsd::WireMessage, ip::is_link_local};
#[derive(Debug)]
pub(super) struct RouteMonitor {
diff --git a/iroh-net/src/net/netmon/linux.rs b/net-tools/netwatch/src/netmon/linux.rs
similarity index 99%
rename from iroh-net/src/net/netmon/linux.rs
rename to net-tools/netwatch/src/netmon/linux.rs
index 2380ac69d4..95fd8e35eb 100644
--- a/iroh-net/src/net/netmon/linux.rs
+++ b/net-tools/netwatch/src/netmon/linux.rs
@@ -13,7 +13,7 @@ use tokio::{sync::mpsc, task::JoinHandle};
use tracing::{info, trace, warn};
use super::actor::NetworkMessage;
-use crate::net::ip::is_link_local;
+use crate::ip::is_link_local;
#[derive(Debug)]
pub(super) struct RouteMonitor {
diff --git a/iroh-net/src/net/netmon/windows.rs b/net-tools/netwatch/src/netmon/windows.rs
similarity index 100%
rename from iroh-net/src/net/netmon/windows.rs
rename to net-tools/netwatch/src/netmon/windows.rs
diff --git a/iroh-net/src/net/udp.rs b/net-tools/netwatch/src/udp.rs
similarity index 100%
rename from iroh-net/src/net/udp.rs
rename to net-tools/netwatch/src/udp.rs
diff --git a/net-tools/portmapper/Cargo.toml b/net-tools/portmapper/Cargo.toml
new file mode 100644
index 0000000000..e00626ca11
--- /dev/null
+++ b/net-tools/portmapper/Cargo.toml
@@ -0,0 +1,49 @@
+[package]
+name = "portmapper"
+version = "0.1.0"
+edition = "2021"
+readme = "README.md"
+description = "Portmapping utilities"
+license = "MIT OR Apache-2.0"
+authors = ["n0 team"]
+repository = "https://github.com/n0-computer/iroh"
+keywords = ["portmapping", "pmp", "pcp", "upnp"]
+
+[lints]
+workspace = true
+
+[dependencies]
+anyhow = { version = "1" }
+base64 = "0.22.1"
+bytes = "1.7"
+derive_more = { version = "1.0.0", features = ["debug", "display", "from", "try_into", "deref"] }
+futures-lite = "2.3"
+futures-util = "0.3.25"
+igd-next = { version = "0.15.1", features = ["aio_tokio"] }
+iroh-metrics = { version = "0.27.0", default-features = false }
+libc = "0.2.139"
+netwatch = { version = "0.1.0", path = "../netwatch" }
+num_enum = "0.7"
+rand = "0.8"
+serde = { version = "1", features = ["derive", "rc"] }
+smallvec = "1.11.1"
+socket2 = "0.5.3"
+thiserror = "1"
+time = "0.3.20"
+tokio = { version = "1", features = ["io-util", "macros", "sync", "rt", "net", "fs", "io-std", "signal", "process"] }
+tokio-util = { version = "0.7.12", features = ["io-util", "io", "codec", "rt"] }
+tracing = "0.1"
+url = { version = "2.4", features = ["serde"] }
+
+[dev-dependencies]
+ntest = "0.9"
+rand_chacha = "0.3.1"
+tokio = { version = "1", features = ["io-util", "sync", "rt", "net", "fs", "macros", "time", "test-util"] }
+
+[features]
+default = ["metrics"]
+metrics = ["iroh-metrics/metrics"]
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "iroh_docsrs"]
diff --git a/net-tools/portmapper/README.md b/net-tools/portmapper/README.md
new file mode 100644
index 0000000000..7f819769cc
--- /dev/null
+++ b/net-tools/portmapper/README.md
@@ -0,0 +1,24 @@
+# Portmapper
+
+`portmapper` is a library to ensure a mapping for a local port is maintained
+despite network changes. Provides upnp, pcp and nat-pmp protocols support.
+
+Used in [iroh](https://github.com/n0-computer/iroh), created with love by the
+[n0 team](https://n0.computer/).
+
+# License
+
+This project is licensed under either of
+
+ * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
+ http://www.apache.org/licenses/LICENSE-2.0)
+ * MIT license ([LICENSE-MIT](LICENSE-MIT) or
+ http://opensource.org/licenses/MIT)
+
+at your option.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in this project by you, as defined in the Apache-2.0 license,
+shall be dual licensed as above, without any additional terms or conditions.
diff --git a/iroh-net/src/portmapper/current_mapping.rs b/net-tools/portmapper/src/current_mapping.rs
similarity index 100%
rename from iroh-net/src/portmapper/current_mapping.rs
rename to net-tools/portmapper/src/current_mapping.rs
diff --git a/iroh-net/src/portmapper.rs b/net-tools/portmapper/src/lib.rs
similarity index 98%
rename from iroh-net/src/portmapper.rs
rename to net-tools/portmapper/src/lib.rs
index 0b54840213..708d572098 100644
--- a/iroh-net/src/portmapper.rs
+++ b/net-tools/portmapper/src/lib.rs
@@ -10,18 +10,30 @@ use anyhow::{anyhow, Result};
use current_mapping::CurrentMapping;
use futures_lite::StreamExt;
use iroh_metrics::inc;
+use netwatch::interfaces::HomeRouter;
use tokio::sync::{mpsc, oneshot, watch};
use tokio_util::task::AbortOnDropHandle;
use tracing::{debug, info_span, trace, Instrument};
-use crate::{net::interfaces::HomeRouter, util};
-
mod current_mapping;
mod mapping;
mod metrics;
mod nat_pmp;
mod pcp;
mod upnp;
+mod util;
+mod defaults {
+ use std::time::Duration;
+
+ /// Maximum duration a UPnP search can take before timing out.
+ pub(crate) const UPNP_SEARCH_TIMEOUT: Duration = Duration::from_secs(1);
+
+ /// Timeout to receive a response from a PCP server.
+ pub(crate) const PCP_RECV_TIMEOUT: Duration = Duration::from_millis(500);
+
+ /// Timeout to receive a response from a NAT-PMP server.
+ pub(crate) const NAT_PMP_RECV_TIMEOUT: Duration = Duration::from_millis(500);
+}
pub use metrics::Metrics;
diff --git a/iroh-net/src/portmapper/mapping.rs b/net-tools/portmapper/src/mapping.rs
similarity index 100%
rename from iroh-net/src/portmapper/mapping.rs
rename to net-tools/portmapper/src/mapping.rs
diff --git a/iroh-net/src/portmapper/metrics.rs b/net-tools/portmapper/src/metrics.rs
similarity index 100%
rename from iroh-net/src/portmapper/metrics.rs
rename to net-tools/portmapper/src/metrics.rs
diff --git a/iroh-net/src/portmapper/nat_pmp.rs b/net-tools/portmapper/src/nat_pmp.rs
similarity index 98%
rename from iroh-net/src/portmapper/nat_pmp.rs
rename to net-tools/portmapper/src/nat_pmp.rs
index 911b4ace7b..a44c4aeb7e 100644
--- a/iroh-net/src/portmapper/nat_pmp.rs
+++ b/net-tools/portmapper/src/nat_pmp.rs
@@ -2,10 +2,11 @@
use std::{net::Ipv4Addr, num::NonZeroU16, time::Duration};
+use netwatch::UdpSocket;
use tracing::{debug, trace};
use self::protocol::{MapProtocol, Request, Response};
-use crate::{defaults::timeouts::NAT_PMP_RECV_TIMEOUT as RECV_TIMEOUT, net::UdpSocket};
+use crate::defaults::NAT_PMP_RECV_TIMEOUT as RECV_TIMEOUT;
mod protocol;
diff --git a/iroh-net/src/portmapper/nat_pmp/protocol.rs b/net-tools/portmapper/src/nat_pmp/protocol.rs
similarity index 100%
rename from iroh-net/src/portmapper/nat_pmp/protocol.rs
rename to net-tools/portmapper/src/nat_pmp/protocol.rs
diff --git a/iroh-net/src/portmapper/nat_pmp/protocol/request.rs b/net-tools/portmapper/src/nat_pmp/protocol/request.rs
similarity index 100%
rename from iroh-net/src/portmapper/nat_pmp/protocol/request.rs
rename to net-tools/portmapper/src/nat_pmp/protocol/request.rs
diff --git a/iroh-net/src/portmapper/nat_pmp/protocol/response.rs b/net-tools/portmapper/src/nat_pmp/protocol/response.rs
similarity index 100%
rename from iroh-net/src/portmapper/nat_pmp/protocol/response.rs
rename to net-tools/portmapper/src/nat_pmp/protocol/response.rs
diff --git a/iroh-net/src/portmapper/pcp.rs b/net-tools/portmapper/src/pcp.rs
similarity index 98%
rename from iroh-net/src/portmapper/pcp.rs
rename to net-tools/portmapper/src/pcp.rs
index f911a341e5..0f2fe789f5 100644
--- a/iroh-net/src/portmapper/pcp.rs
+++ b/net-tools/portmapper/src/pcp.rs
@@ -2,10 +2,11 @@
use std::{net::Ipv4Addr, num::NonZeroU16, time::Duration};
+use netwatch::UdpSocket;
use rand::RngCore;
use tracing::{debug, trace};
-use crate::{defaults::timeouts::PCP_RECV_TIMEOUT as RECV_TIMEOUT, net::UdpSocket};
+use crate::defaults::PCP_RECV_TIMEOUT as RECV_TIMEOUT;
mod protocol;
diff --git a/iroh-net/src/portmapper/pcp/protocol.rs b/net-tools/portmapper/src/pcp/protocol.rs
similarity index 100%
rename from iroh-net/src/portmapper/pcp/protocol.rs
rename to net-tools/portmapper/src/pcp/protocol.rs
diff --git a/iroh-net/src/portmapper/pcp/protocol/opcode_data.rs b/net-tools/portmapper/src/pcp/protocol/opcode_data.rs
similarity index 100%
rename from iroh-net/src/portmapper/pcp/protocol/opcode_data.rs
rename to net-tools/portmapper/src/pcp/protocol/opcode_data.rs
diff --git a/iroh-net/src/portmapper/pcp/protocol/request.rs b/net-tools/portmapper/src/pcp/protocol/request.rs
similarity index 100%
rename from iroh-net/src/portmapper/pcp/protocol/request.rs
rename to net-tools/portmapper/src/pcp/protocol/request.rs
diff --git a/iroh-net/src/portmapper/pcp/protocol/response.rs b/net-tools/portmapper/src/pcp/protocol/response.rs
similarity index 100%
rename from iroh-net/src/portmapper/pcp/protocol/response.rs
rename to net-tools/portmapper/src/pcp/protocol/response.rs
diff --git a/iroh-net/src/portmapper/upnp.rs b/net-tools/portmapper/src/upnp.rs
similarity index 98%
rename from iroh-net/src/portmapper/upnp.rs
rename to net-tools/portmapper/src/upnp.rs
index 6d202eed35..ebdac4ab7a 100644
--- a/iroh-net/src/portmapper/upnp.rs
+++ b/net-tools/portmapper/src/upnp.rs
@@ -13,7 +13,7 @@ use super::Metrics;
pub type Gateway = aigd::Gateway;
-use crate::defaults::timeouts::UPNP_SEARCH_TIMEOUT as SEARCH_TIMEOUT;
+use crate::defaults::UPNP_SEARCH_TIMEOUT as SEARCH_TIMEOUT;
/// Seconds we ask the router to maintain the port mapping. 0 means infinite.
const PORT_MAPPING_LEASE_DURATION_SECONDS: u32 = 0;
diff --git a/net-tools/portmapper/src/util.rs b/net-tools/portmapper/src/util.rs
new file mode 100644
index 0000000000..5dccd45c00
--- /dev/null
+++ b/net-tools/portmapper/src/util.rs
@@ -0,0 +1,30 @@
+use std::{
+ future::Future,
+ pin::Pin,
+ task::{Context, Poll},
+};
+
+/// Resolves to pending if the inner is `None`.
+#[derive(Debug)]
+pub(crate) struct MaybeFuture {
+ /// Future to be polled.
+ pub inner: Option,
+}
+
+// NOTE: explicit implementation to bypass derive unnecessary bounds
+impl Default for MaybeFuture {
+ fn default() -> Self {
+ MaybeFuture { inner: None }
+ }
+}
+
+impl Future for MaybeFuture {
+ type Output = T::Output;
+
+ fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll {
+ match self.inner {
+ Some(ref mut t) => Pin::new(t).poll(cx),
+ None => Poll::Pending,
+ }
+ }
+}