From 59457b238916facfd79934b6fafe5e3479c246c0 Mon Sep 17 00:00:00 2001 From: Diva M Date: Thu, 24 Oct 2024 15:13:15 -0500 Subject: [PATCH 01/30] add net member --- Cargo.toml | 2 +- net-tools/net/Cargo.toml | 9 +++++++++ net-tools/net/src/main.rs | 3 +++ 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 net-tools/net/Cargo.toml create mode 100644 net-tools/net/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 33651131822..238bebd380c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ members = [ "iroh-net", "iroh-test", "iroh-net/bench", - "iroh-cli", + "iroh-cli", "net-tools/net", ] resolver = "2" diff --git a/net-tools/net/Cargo.toml b/net-tools/net/Cargo.toml new file mode 100644 index 00000000000..a8d32e273d6 --- /dev/null +++ b/net-tools/net/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "net" +version = "0.1.0" +edition = "2021" + +[dependencies] + +[lints] +workspace = true diff --git a/net-tools/net/src/main.rs b/net-tools/net/src/main.rs new file mode 100644 index 00000000000..e7a11a969c0 --- /dev/null +++ b/net-tools/net/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} From 470412422361b5bcbc9d282b862e20ce196427ae Mon Sep 17 00:00:00 2001 From: Diva M Date: Thu, 24 Oct 2024 15:15:13 -0500 Subject: [PATCH 02/30] move net files --- {iroh-net/src/net => net-tools/net/src}/interfaces.rs | 0 {iroh-net/src/net => net-tools/net/src}/interfaces/bsd.rs | 0 .../src/net => net-tools/net/src}/interfaces/bsd/freebsd.rs | 0 .../src/net => net-tools/net/src}/interfaces/bsd/macos.rs | 0 .../src/net => net-tools/net/src}/interfaces/bsd/netbsd.rs | 0 .../src/net => net-tools/net/src}/interfaces/bsd/openbsd.rs | 0 {iroh-net/src/net => net-tools/net/src}/interfaces/linux.rs | 0 {iroh-net/src/net => net-tools/net/src}/interfaces/windows.rs | 0 {iroh-net/src/net => net-tools/net/src}/ip.rs | 0 {iroh-net/src/net => net-tools/net/src}/ip_family.rs | 0 iroh-net/src/net.rs => net-tools/net/src/lib.rs | 0 net-tools/net/src/main.rs | 3 --- {iroh-net/src/net => net-tools/net/src}/netmon.rs | 0 {iroh-net/src/net => net-tools/net/src}/netmon/actor.rs | 0 {iroh-net/src/net => net-tools/net/src}/netmon/android.rs | 0 {iroh-net/src/net => net-tools/net/src}/netmon/bsd.rs | 0 {iroh-net/src/net => net-tools/net/src}/netmon/linux.rs | 0 {iroh-net/src/net => net-tools/net/src}/netmon/windows.rs | 0 {iroh-net/src/net => net-tools/net/src}/udp.rs | 0 19 files changed, 3 deletions(-) rename {iroh-net/src/net => net-tools/net/src}/interfaces.rs (100%) rename {iroh-net/src/net => net-tools/net/src}/interfaces/bsd.rs (100%) rename {iroh-net/src/net => net-tools/net/src}/interfaces/bsd/freebsd.rs (100%) rename {iroh-net/src/net => net-tools/net/src}/interfaces/bsd/macos.rs (100%) rename {iroh-net/src/net => net-tools/net/src}/interfaces/bsd/netbsd.rs (100%) rename {iroh-net/src/net => net-tools/net/src}/interfaces/bsd/openbsd.rs (100%) rename {iroh-net/src/net => net-tools/net/src}/interfaces/linux.rs (100%) rename {iroh-net/src/net => net-tools/net/src}/interfaces/windows.rs (100%) rename {iroh-net/src/net => net-tools/net/src}/ip.rs (100%) rename {iroh-net/src/net => net-tools/net/src}/ip_family.rs (100%) rename iroh-net/src/net.rs => net-tools/net/src/lib.rs (100%) delete mode 100644 net-tools/net/src/main.rs rename {iroh-net/src/net => net-tools/net/src}/netmon.rs (100%) rename {iroh-net/src/net => net-tools/net/src}/netmon/actor.rs (100%) rename {iroh-net/src/net => net-tools/net/src}/netmon/android.rs (100%) rename {iroh-net/src/net => net-tools/net/src}/netmon/bsd.rs (100%) rename {iroh-net/src/net => net-tools/net/src}/netmon/linux.rs (100%) rename {iroh-net/src/net => net-tools/net/src}/netmon/windows.rs (100%) rename {iroh-net/src/net => net-tools/net/src}/udp.rs (100%) diff --git a/iroh-net/src/net/interfaces.rs b/net-tools/net/src/interfaces.rs similarity index 100% rename from iroh-net/src/net/interfaces.rs rename to net-tools/net/src/interfaces.rs diff --git a/iroh-net/src/net/interfaces/bsd.rs b/net-tools/net/src/interfaces/bsd.rs similarity index 100% rename from iroh-net/src/net/interfaces/bsd.rs rename to net-tools/net/src/interfaces/bsd.rs diff --git a/iroh-net/src/net/interfaces/bsd/freebsd.rs b/net-tools/net/src/interfaces/bsd/freebsd.rs similarity index 100% rename from iroh-net/src/net/interfaces/bsd/freebsd.rs rename to net-tools/net/src/interfaces/bsd/freebsd.rs diff --git a/iroh-net/src/net/interfaces/bsd/macos.rs b/net-tools/net/src/interfaces/bsd/macos.rs similarity index 100% rename from iroh-net/src/net/interfaces/bsd/macos.rs rename to net-tools/net/src/interfaces/bsd/macos.rs diff --git a/iroh-net/src/net/interfaces/bsd/netbsd.rs b/net-tools/net/src/interfaces/bsd/netbsd.rs similarity index 100% rename from iroh-net/src/net/interfaces/bsd/netbsd.rs rename to net-tools/net/src/interfaces/bsd/netbsd.rs diff --git a/iroh-net/src/net/interfaces/bsd/openbsd.rs b/net-tools/net/src/interfaces/bsd/openbsd.rs similarity index 100% rename from iroh-net/src/net/interfaces/bsd/openbsd.rs rename to net-tools/net/src/interfaces/bsd/openbsd.rs diff --git a/iroh-net/src/net/interfaces/linux.rs b/net-tools/net/src/interfaces/linux.rs similarity index 100% rename from iroh-net/src/net/interfaces/linux.rs rename to net-tools/net/src/interfaces/linux.rs diff --git a/iroh-net/src/net/interfaces/windows.rs b/net-tools/net/src/interfaces/windows.rs similarity index 100% rename from iroh-net/src/net/interfaces/windows.rs rename to net-tools/net/src/interfaces/windows.rs diff --git a/iroh-net/src/net/ip.rs b/net-tools/net/src/ip.rs similarity index 100% rename from iroh-net/src/net/ip.rs rename to net-tools/net/src/ip.rs diff --git a/iroh-net/src/net/ip_family.rs b/net-tools/net/src/ip_family.rs similarity index 100% rename from iroh-net/src/net/ip_family.rs rename to net-tools/net/src/ip_family.rs diff --git a/iroh-net/src/net.rs b/net-tools/net/src/lib.rs similarity index 100% rename from iroh-net/src/net.rs rename to net-tools/net/src/lib.rs diff --git a/net-tools/net/src/main.rs b/net-tools/net/src/main.rs deleted file mode 100644 index e7a11a969c0..00000000000 --- a/net-tools/net/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} diff --git a/iroh-net/src/net/netmon.rs b/net-tools/net/src/netmon.rs similarity index 100% rename from iroh-net/src/net/netmon.rs rename to net-tools/net/src/netmon.rs diff --git a/iroh-net/src/net/netmon/actor.rs b/net-tools/net/src/netmon/actor.rs similarity index 100% rename from iroh-net/src/net/netmon/actor.rs rename to net-tools/net/src/netmon/actor.rs diff --git a/iroh-net/src/net/netmon/android.rs b/net-tools/net/src/netmon/android.rs similarity index 100% rename from iroh-net/src/net/netmon/android.rs rename to net-tools/net/src/netmon/android.rs diff --git a/iroh-net/src/net/netmon/bsd.rs b/net-tools/net/src/netmon/bsd.rs similarity index 100% rename from iroh-net/src/net/netmon/bsd.rs rename to net-tools/net/src/netmon/bsd.rs diff --git a/iroh-net/src/net/netmon/linux.rs b/net-tools/net/src/netmon/linux.rs similarity index 100% rename from iroh-net/src/net/netmon/linux.rs rename to net-tools/net/src/netmon/linux.rs diff --git a/iroh-net/src/net/netmon/windows.rs b/net-tools/net/src/netmon/windows.rs similarity index 100% rename from iroh-net/src/net/netmon/windows.rs rename to net-tools/net/src/netmon/windows.rs diff --git a/iroh-net/src/net/udp.rs b/net-tools/net/src/udp.rs similarity index 100% rename from iroh-net/src/net/udp.rs rename to net-tools/net/src/udp.rs From 38ba7a3d8b4b8468aa0772ec7eb0f96c191b6234 Mon Sep 17 00:00:00 2001 From: Diva M Date: Thu, 24 Oct 2024 15:22:01 -0500 Subject: [PATCH 03/30] adjust net files --- Cargo.lock | 95 +++++++++++++++++++++ net-tools/net/Cargo.toml | 134 +++++++++++++++++++++++++++++- net-tools/net/src/interfaces.rs | 2 +- net-tools/net/src/netmon/actor.rs | 2 +- net-tools/net/src/netmon/linux.rs | 2 +- 5 files changed, 230 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d8218b8cd66..7d4ec866c28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3217,6 +3217,101 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "net" +version = "0.1.0" +dependencies = [ + "anyhow", + "axum", + "backoff", + "base64 0.22.1", + "bytes", + "clap", + "criterion", + "crypto_box", + "der", + "derive_more", + "duct", + "futures-buffered", + "futures-concurrency", + "futures-lite 2.3.0", + "futures-sink", + "futures-util", + "genawaiter", + "governor", + "hex", + "hickory-proto 0.25.0-alpha.2", + "hickory-resolver", + "hostname", + "http 1.1.0", + "http-body-util", + "hyper", + "hyper-util", + "igd-next", + "iroh-base", + "iroh-metrics", + "iroh-quinn", + "iroh-quinn-proto", + "iroh-quinn-udp", + "iroh-test", + "libc", + "mainline", + "netdev", + "netlink-packet-core", + "netlink-packet-route", + "netlink-sys", + "ntest", + "num_enum", + "once_cell", + "parking_lot", + "pin-project", + "pkarr", + "postcard", + "pretty_assertions", + "proptest", + "rand", + "rand_chacha", + "rcgen", + "regex", + "reqwest", + "ring", + "rtnetlink", + "rustls", + "rustls-pemfile", + "rustls-webpki", + "serde", + "serde_json", + "serde_with", + "smallvec", + "socket2", + "strum 0.26.3", + "stun-rs", + "surge-ping", + "swarm-discovery", + "testdir", + "testresult", + "thiserror", + "time", + "tokio", + "tokio-rustls", + "tokio-rustls-acme", + "tokio-stream", + "tokio-tungstenite", + "tokio-tungstenite-wasm", + "tokio-util", + "toml", + "tracing", + "tracing-subscriber", + "tungstenite", + "url", + "watchable", + "webpki-roots", + "windows 0.51.1", + "wmi", + "x509-parser", + "z32", +] + [[package]] name = "netdev" version = "0.30.0" diff --git a/net-tools/net/Cargo.toml b/net-tools/net/Cargo.toml index a8d32e273d6..1c040fcfcab 100644 --- a/net-tools/net/Cargo.toml +++ b/net-tools/net/Cargo.toml @@ -3,7 +3,137 @@ name = "net" version = "0.1.0" edition = "2021" -[dependencies] - [lints] workspace = true + +[dependencies] +anyhow = { version = "1" } +base64 = "0.22.1" +backoff = "0.4.0" +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" +futures-concurrency = "7.6.0" +futures-lite = "2.3" +futures-sink = "0.3.25" +futures-util = "0.3.25" +governor = "0.6.0" +hex = "0.4.3" +hickory-proto = "=0.25.0-alpha.2" +hickory-resolver = "=0.25.0-alpha.2" +hostname = "0.3.1" +http = "1" +http-body-util = "0.1.0" +hyper = { version = "1", features = ["server", "client", "http1"] } +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" +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"] } +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" } +quinn-udp = { package = "iroh-quinn-udp", version = "0.5" } +rand = "0.8" +rcgen = "0.12" +reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"] } +ring = "0.17" +rustls = { version = "0.23", default-features = false, features = ["ring"] } +serde = { version = "1", features = ["derive", "rc"] } +smallvec = "1.11.1" +socket2 = "0.5.3" +stun-rs = "0.1.5" +surge-ping = "0.8.0" +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-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"] } +watchable = "1.1.2" +webpki = { package = "rustls-webpki", version = "0.102" } +webpki-roots = "0.26" +x509-parser = "0.16" +z32 = "1.0.3" + +# iroh-relay +axum = { version = "0.7.4", optional = true } +clap = { version = "4", features = ["derive"], optional = true } +regex = { version = "1.7.1", optional = true } +rustls-pemfile = { version = "2.1", optional = true } +serde_with = { version = "3.3", optional = true } +toml = { version = "0.8", optional = true } +tracing-subscriber = { version = "0.3", features = ["env-filter"], optional = true } +tokio-rustls-acme = { version = "0.4", optional = true } + +# metrics +iroh-metrics = { version = "0.27.0", default-features = false } +strum = { version = "0.26.2", features = ["derive"] } + +# local-swarm-discovery +swarm-discovery = { version = "0.2.1", optional = true } + +# dht_discovery +genawaiter = { version = "0.99.1", features = ["futures03"], optional = true } + +[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"] } + +[dev-dependencies] +axum = { version = "0.7.4" } +clap = { version = "4", features = ["derive"] } +criterion = "0.5.1" +crypto_box = { version = "0.9.1", features = ["serde", "chacha20"] } +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" +serde_json = "1.0.107" +testresult = "0.4.0" +mainline = "2.0.1" + +[build-dependencies] +duct = "0.13.6" + +[features] +default = ["metrics", "discovery-pkarr-dht"] +iroh-relay = [ + "dep:tokio-rustls-acme", + "dep:axum", + "dep:clap", + "dep:toml", + "dep:rustls-pemfile", + "dep:regex", + "dep:serde_with", + "dep:tracing-subscriber" +] +metrics = ["iroh-metrics/metrics"] +test-utils = ["iroh-relay"] +discovery-local-network = ["dep:swarm-discovery"] +discovery-pkarr-dht = ["pkarr/dht", "dep:genawaiter"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "iroh_docsrs"] diff --git a/net-tools/net/src/interfaces.rs b/net-tools/net/src/interfaces.rs index 759b0925f7b..0b9352f6bcd 100644 --- a/net-tools/net/src/interfaces.rs +++ b/net-tools/net/src/interfaces.rs @@ -29,7 +29,7 @@ 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)] diff --git a/net-tools/net/src/netmon/actor.rs b/net-tools/net/src/netmon/actor.rs index f12f44c8730..46df1888f4c 100644 --- a/net-tools/net/src/netmon/actor.rs +++ b/net-tools/net/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/net-tools/net/src/netmon/linux.rs b/net-tools/net/src/netmon/linux.rs index 2380ac69d4e..95fd8e35eb1 100644 --- a/net-tools/net/src/netmon/linux.rs +++ b/net-tools/net/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 { From 1cb8a7c0bdb41e5f077506b34c5b2291cdd24efb Mon Sep 17 00:00:00 2001 From: Diva M Date: Thu, 24 Oct 2024 15:38:30 -0500 Subject: [PATCH 04/30] check unused deps in the crate --- Cargo.lock | 68 -------------------------- net-tools/net/Cargo.toml | 100 +-------------------------------------- 2 files changed, 1 insertion(+), 167 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7d4ec866c28..7743e582a75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3222,94 +3222,26 @@ name = "net" version = "0.1.0" dependencies = [ "anyhow", - "axum", - "backoff", - "base64 0.22.1", "bytes", - "clap", - "criterion", - "crypto_box", - "der", - "derive_more", - "duct", - "futures-buffered", - "futures-concurrency", "futures-lite 2.3.0", "futures-sink", "futures-util", - "genawaiter", - "governor", - "hex", - "hickory-proto 0.25.0-alpha.2", - "hickory-resolver", - "hostname", - "http 1.1.0", - "http-body-util", - "hyper", - "hyper-util", - "igd-next", - "iroh-base", - "iroh-metrics", - "iroh-quinn", - "iroh-quinn-proto", - "iroh-quinn-udp", "iroh-test", "libc", - "mainline", "netdev", "netlink-packet-core", "netlink-packet-route", "netlink-sys", - "ntest", - "num_enum", "once_cell", - "parking_lot", - "pin-project", - "pkarr", - "postcard", - "pretty_assertions", - "proptest", - "rand", - "rand_chacha", - "rcgen", - "regex", - "reqwest", - "ring", "rtnetlink", - "rustls", - "rustls-pemfile", - "rustls-webpki", - "serde", - "serde_json", - "serde_with", - "smallvec", "socket2", - "strum 0.26.3", - "stun-rs", - "surge-ping", - "swarm-discovery", - "testdir", - "testresult", "thiserror", "time", "tokio", - "tokio-rustls", - "tokio-rustls-acme", - "tokio-stream", - "tokio-tungstenite", - "tokio-tungstenite-wasm", - "tokio-util", - "toml", "tracing", "tracing-subscriber", - "tungstenite", - "url", - "watchable", - "webpki-roots", "windows 0.51.1", "wmi", - "x509-parser", - "z32", ] [[package]] diff --git a/net-tools/net/Cargo.toml b/net-tools/net/Cargo.toml index 1c040fcfcab..edf63bdc6d8 100644 --- a/net-tools/net/Cargo.toml +++ b/net-tools/net/Cargo.toml @@ -8,84 +8,18 @@ workspace = true [dependencies] anyhow = { version = "1" } -base64 = "0.22.1" -backoff = "0.4.0" 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" -futures-concurrency = "7.6.0" futures-lite = "2.3" futures-sink = "0.3.25" futures-util = "0.3.25" -governor = "0.6.0" -hex = "0.4.3" -hickory-proto = "=0.25.0-alpha.2" -hickory-resolver = "=0.25.0-alpha.2" -hostname = "0.3.1" -http = "1" -http-body-util = "0.1.0" -hyper = { version = "1", features = ["server", "client", "http1"] } -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" -num_enum = "0.7" +netdev = "0.30.0" once_cell = "1.18.0" -parking_lot = "0.12.1" -pin-project = "1" -pkarr = { version = "2", default-features = false, features = ["async", "relay"] } -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" } -quinn-udp = { package = "iroh-quinn-udp", version = "0.5" } -rand = "0.8" -rcgen = "0.12" -reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"] } -ring = "0.17" -rustls = { version = "0.23", default-features = false, features = ["ring"] } -serde = { version = "1", features = ["derive", "rc"] } -smallvec = "1.11.1" socket2 = "0.5.3" -stun-rs = "0.1.5" -surge-ping = "0.8.0" 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-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"] } -watchable = "1.1.2" -webpki = { package = "rustls-webpki", version = "0.102" } -webpki-roots = "0.26" -x509-parser = "0.16" -z32 = "1.0.3" - -# iroh-relay -axum = { version = "0.7.4", optional = true } -clap = { version = "4", features = ["derive"], optional = true } -regex = { version = "1.7.1", optional = true } -rustls-pemfile = { version = "2.1", optional = true } -serde_with = { version = "3.3", optional = true } -toml = { version = "0.8", optional = true } -tracing-subscriber = { version = "0.3", features = ["env-filter"], optional = true } -tokio-rustls-acme = { version = "0.4", optional = true } - -# metrics -iroh-metrics = { version = "0.27.0", default-features = false } -strum = { version = "0.26.2", features = ["derive"] } - -# local-swarm-discovery -swarm-discovery = { version = "0.2.1", optional = true } - -# dht_discovery -genawaiter = { version = "0.99.1", features = ["futures03"], optional = true } [target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies] netlink-packet-core = "0.7.0" @@ -98,41 +32,9 @@ wmi = "0.13" windows = { version = "0.51", features = ["Win32_NetworkManagement_IpHelper", "Win32_Foundation", "Win32_NetworkManagement_Ndis", "Win32_Networking_WinSock"] } [dev-dependencies] -axum = { version = "0.7.4" } -clap = { version = "4", features = ["derive"] } -criterion = "0.5.1" -crypto_box = { version = "0.9.1", features = ["serde", "chacha20"] } -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" -serde_json = "1.0.107" -testresult = "0.4.0" -mainline = "2.0.1" - -[build-dependencies] -duct = "0.13.6" - -[features] -default = ["metrics", "discovery-pkarr-dht"] -iroh-relay = [ - "dep:tokio-rustls-acme", - "dep:axum", - "dep:clap", - "dep:toml", - "dep:rustls-pemfile", - "dep:regex", - "dep:serde_with", - "dep:tracing-subscriber" -] -metrics = ["iroh-metrics/metrics"] -test-utils = ["iroh-relay"] -discovery-local-network = ["dep:swarm-discovery"] -discovery-pkarr-dht = ["pkarr/dht", "dep:genawaiter"] [package.metadata.docs.rs] all-features = true From 887041ee46b9459823700a7df77595aa61ca8ce4 Mon Sep 17 00:00:00 2001 From: Diva M Date: Thu, 24 Oct 2024 15:42:08 -0500 Subject: [PATCH 05/30] add net as a dep --- Cargo.lock | 1 + iroh-net/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 7743e582a75..5f5e5f305c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2807,6 +2807,7 @@ dependencies = [ "iroh-test", "libc", "mainline", + "net", "netdev", "netlink-packet-core", "netlink-packet-route", diff --git a/iroh-net/Cargo.toml b/iroh-net/Cargo.toml index 9c6f5140334..cd197bb13c1 100644 --- a/iroh-net/Cargo.toml +++ b/iroh-net/Cargo.toml @@ -95,6 +95,7 @@ swarm-discovery = { version = "0.2.1", optional = true } # dht_discovery genawaiter = { version = "0.99.1", features = ["futures03"], optional = true } +net = { version = "0.1.0", path = "../net-tools/net" } [target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies] netlink-packet-core = "0.7.0" From 2f572e679c675c622d34c6ea229ff80f6d2e4e9d Mon Sep 17 00:00:00 2001 From: Diva M Date: Thu, 24 Oct 2024 15:53:13 -0500 Subject: [PATCH 06/30] adjust visibilities to use --- iroh-net/src/lib.rs | 2 +- net-tools/net/src/interfaces.rs | 22 ++++++++++------------ net-tools/net/src/lib.rs | 2 +- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/iroh-net/src/lib.rs b/iroh-net/src/lib.rs index 759eaefa651..5baabc7c85b 100644 --- a/iroh-net/src/lib.rs +++ b/iroh-net/src/lib.rs @@ -241,7 +241,7 @@ pub mod dns; pub mod endpoint; mod magicsock; pub mod metrics; -pub mod net; +pub use net; pub mod netcheck; pub mod ping; pub mod portmapper; diff --git a/net-tools/net/src/interfaces.rs b/net-tools/net/src/interfaces.rs index 0b9352f6bcd..b0d98894206 100644 --- a/net-tools/net/src/interfaces.rs +++ b/net-tools/net/src/interfaces.rs @@ -33,7 +33,7 @@ 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() @@ -89,7 +89,6 @@ impl Interface { /// free to add parameters or different alternative constructors. /// /// [`ProbePlan`]: crate::netcheck::reportgen::probes::ProbePlan - #[cfg(test)] pub(crate) fn fake() -> Self { use std::net::Ipv4Addr; @@ -126,7 +125,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 +160,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`. @@ -262,8 +261,7 @@ impl State { /// free to add parameters or different alternative constructors. /// /// [`ProbePlan`]: crate::netcheck::reportgen::probes::ProbePlan - #[cfg(test)] - pub(crate) fn fake() -> Self { + pub fn fake() -> Self { let fake = Interface::fake(); let ifname = fake.iface.name.clone(); Self { @@ -341,7 +339,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 +352,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/net-tools/net/src/lib.rs b/net-tools/net/src/lib.rs index a010dc235c0..213fe78e009 100644 --- a/net-tools/net/src/lib.rs +++ b/net-tools/net/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; From befdb354db118a42cf1743aa0eeba3a1f0725e67 Mon Sep 17 00:00:00 2001 From: Diva M Date: Thu, 24 Oct 2024 15:59:13 -0500 Subject: [PATCH 07/30] remove unused deps from iroh-net --- Cargo.lock | 1 - iroh-net/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5f5e5f305c5..a38d89471aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2840,7 +2840,6 @@ dependencies = [ "stun-rs", "surge-ping", "swarm-discovery", - "testdir", "testresult", "thiserror", "time", diff --git a/iroh-net/Cargo.toml b/iroh-net/Cargo.toml index cd197bb13c1..a71d1ca389b 100644 --- a/iroh-net/Cargo.toml +++ b/iroh-net/Cargo.toml @@ -116,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" From 69a5f8f0dca69dc1418c8f2f9198c22b506dbbbe Mon Sep 17 00:00:00 2001 From: Diva M Date: Thu, 24 Oct 2024 16:00:41 -0500 Subject: [PATCH 08/30] add portmapper as a member --- Cargo.lock | 4 ++++ Cargo.toml | 2 +- net-tools/portmapper/Cargo.toml | 9 +++++++++ net-tools/portmapper/src/lib.rs | 14 ++++++++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 net-tools/portmapper/Cargo.toml create mode 100644 net-tools/portmapper/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index a38d89471aa..4b532742e0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3932,6 +3932,10 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" +[[package]] +name = "portmapper" +version = "0.1.0" + [[package]] name = "positioned-io" version = "0.3.3" diff --git a/Cargo.toml b/Cargo.toml index 238bebd380c..7533bf88a3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ members = [ "iroh-net", "iroh-test", "iroh-net/bench", - "iroh-cli", "net-tools/net", + "iroh-cli", "net-tools/net", "net-tools/portmapper", ] resolver = "2" diff --git a/net-tools/portmapper/Cargo.toml b/net-tools/portmapper/Cargo.toml new file mode 100644 index 00000000000..76a4ba1f425 --- /dev/null +++ b/net-tools/portmapper/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "portmapper" +version = "0.1.0" +edition = "2021" + +[dependencies] + +[lints] +workspace = true diff --git a/net-tools/portmapper/src/lib.rs b/net-tools/portmapper/src/lib.rs new file mode 100644 index 00000000000..b93cf3ffd9c --- /dev/null +++ b/net-tools/portmapper/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} From 543fb4175def9fb7ddb0817733a3a84c646a3b43 Mon Sep 17 00:00:00 2001 From: Diva M Date: Thu, 24 Oct 2024 16:04:57 -0500 Subject: [PATCH 09/30] move portmapper files --- iroh-net/src/portmapper.rs | 702 ----------------- net-tools/portmapper/Cargo.toml | 163 +++- .../portmapper/src}/current_mapping.rs | 0 net-tools/portmapper/src/lib.rs | 706 +++++++++++++++++- .../portmapper/src}/mapping.rs | 0 .../portmapper/src}/metrics.rs | 0 .../portmapper/src}/nat_pmp.rs | 0 .../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 | 0 .../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 | 0 16 files changed, 858 insertions(+), 713 deletions(-) delete mode 100644 iroh-net/src/portmapper.rs rename {iroh-net/src/portmapper => net-tools/portmapper/src}/current_mapping.rs (100%) 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 (100%) 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 (100%) 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 (100%) diff --git a/iroh-net/src/portmapper.rs b/iroh-net/src/portmapper.rs deleted file mode 100644 index 0b54840213f..00000000000 --- a/iroh-net/src/portmapper.rs +++ /dev/null @@ -1,702 +0,0 @@ -//! Port mapping client and service. - -use std::{ - net::{Ipv4Addr, SocketAddrV4}, - num::NonZeroU16, - time::{Duration, Instant}, -}; - -use anyhow::{anyhow, Result}; -use current_mapping::CurrentMapping; -use futures_lite::StreamExt; -use iroh_metrics::inc; -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; - -pub use metrics::Metrics; - -/// If a port mapping service has been seen within the last [`AVAILABILITY_TRUST_DURATION`] it will -/// not be probed again. -const AVAILABILITY_TRUST_DURATION: Duration = Duration::from_secs(60 * 10); // 10 minutes - -/// Capacity of the channel to communicate with the long-running service. -const SERVICE_CHANNEL_CAPACITY: usize = 32; // should be plenty - -/// If a port mapping service has not been seen within the last [`UNAVAILABILITY_TRUST_DURATION`] -/// we allow trying a mapping using said protocol. -const UNAVAILABILITY_TRUST_DURATION: Duration = Duration::from_secs(5); - -/// Output of a port mapping probe. -#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)] -#[display("portmap={{ UPnP: {upnp}, PMP: {nat_pmp}, PCP: {pcp} }}")] -pub struct ProbeOutput { - /// If UPnP can be considered available. - pub upnp: bool, - /// If PCP can be considered available. - pub pcp: bool, - /// If PMP can be considered available. - pub nat_pmp: bool, -} - -impl ProbeOutput { - /// Indicates if all port mapping protocols are available. - pub fn all_available(&self) -> bool { - self.upnp && self.pcp && self.nat_pmp - } -} - -#[derive(derive_more::Debug)] -enum Message { - /// Attempt to get a mapping if the local port is set but there is no mapping. - ProcureMapping, - /// Request to update the local port. - /// - /// The resulting external address can be obtained subscribing using - /// [`Client::watch_external_address`]. - /// A value of `None` will deactivate port mapping. - UpdateLocalPort { local_port: Option }, - /// Request to probe the port mapping protocols. - /// - /// The requester should wait for the result at the [`oneshot::Receiver`] counterpart of the - /// [`oneshot::Sender`]. - Probe { - /// Sender side to communicate the result of the probe. - #[debug("_")] - result_tx: oneshot::Sender>, - }, -} - -/// Configures which port mapping protocols are enabled in the [`Service`]. -#[derive(Debug, Clone)] -pub struct Config { - /// Whether UPnP is enabled. - pub enable_upnp: bool, - /// Whether PCP is enabled. - pub enable_pcp: bool, - /// Whether PMP is enabled. - pub enable_nat_pmp: bool, -} - -impl Default for Config { - /// By default all port mapping protocols are enabled. - fn default() -> Self { - Config { - enable_upnp: true, - enable_pcp: true, - enable_nat_pmp: true, - } - } -} - -/// Port mapping client. -#[derive(Debug, Clone)] -pub struct Client { - /// A watcher over the most recent external address obtained from port mapping. - /// - /// See [`watch::Receiver`]. - port_mapping: watch::Receiver>, - /// Channel used to communicate with the port mapping service. - service_tx: mpsc::Sender, - /// A handle to the service that will cancel the spawned task once the client is dropped. - _service_handle: std::sync::Arc>>, -} - -impl Default for Client { - fn default() -> Self { - Self::new(Config::default()) - } -} - -impl Client { - /// Create a new port mapping client. - pub fn new(config: Config) -> Self { - let (service_tx, service_rx) = mpsc::channel(SERVICE_CHANNEL_CAPACITY); - - let (service, watcher) = Service::new(config, service_rx); - - let handle = AbortOnDropHandle::new(tokio::spawn( - async move { service.run().await }.instrument(info_span!("portmapper.service")), - )); - - Client { - port_mapping: watcher, - service_tx, - _service_handle: std::sync::Arc::new(handle), - } - } - - /// Request a probe to the port mapping protocols. - /// - /// Returns the [`oneshot::Receiver`] used to obtain the result of the probe. - pub fn probe(&self) -> oneshot::Receiver> { - let (result_tx, result_rx) = oneshot::channel(); - - if let Err(e) = self.service_tx.try_send(Message::Probe { result_tx }) { - use mpsc::error::TrySendError::*; - - // recover the sender and return the error there - let (result_tx, e) = match e { - Full(Message::Probe { result_tx }) => (result_tx, "Port mapping channel full"), - Closed(Message::Probe { result_tx }) => (result_tx, "Port mapping channel closed"), - Full(_) | Closed(_) => unreachable!("Sent value is a probe."), - }; - - // sender was just created. If it's dropped we have two send error and are likely - // shutting down - // NOTE: second Err is infallible match due to being the sent value - if let Err(Err(e)) = result_tx.send(Err(e.into())) { - trace!("Failed to request probe: {e}") - } - } - result_rx - } - - /// Try to get a mapping for the last local port if there isn't one already. - pub fn procure_mapping(&self) { - // requester can't really do anything with this error if returned, so we log it - if let Err(e) = self.service_tx.try_send(Message::ProcureMapping) { - trace!("Failed to request mapping {e}") - } - } - - /// Update the local port. - /// - /// If the port changes, this will trigger a port mapping attempt. - pub fn update_local_port(&self, local_port: NonZeroU16) { - let local_port = Some(local_port); - // requester can't really do anything with this error if returned, so we log it - if let Err(e) = self - .service_tx - .try_send(Message::UpdateLocalPort { local_port }) - { - trace!("Failed to update local port {e}") - } - } - - /// Deactivate port mapping. - pub fn deactivate(&self) { - // requester can't really do anything with this error if returned, so we log it - if let Err(e) = self - .service_tx - .try_send(Message::UpdateLocalPort { local_port: None }) - { - trace!("Failed to deactivate port mapping {e}") - } - } - - /// Watch the external address for changes in the mappings. - pub fn watch_external_address(&self) -> watch::Receiver> { - self.port_mapping.clone() - } -} - -/// Port mapping protocol information obtained during a probe. -#[derive(Debug)] -struct Probe { - /// When was the probe last updated. - last_probe: Instant, - /// The last [`upnp::Gateway`] and when was it last seen. - last_upnp_gateway_addr: Option<(upnp::Gateway, Instant)>, - /// Last time PCP was seen. - last_pcp: Option, - /// Last time NAT-PMP was seen. - last_nat_pmp: Option, -} - -impl Probe { - /// An empty probe set to `now`. - fn empty() -> Self { - Self { - last_probe: Instant::now(), - last_upnp_gateway_addr: None, - last_pcp: None, - last_nat_pmp: None, - } - } - /// Create a new probe based on a previous output. - async fn from_output( - config: Config, - output: ProbeOutput, - local_ip: Ipv4Addr, - gateway: Ipv4Addr, - ) -> Probe { - let ProbeOutput { upnp, pcp, nat_pmp } = output; - let Config { - enable_upnp, - enable_pcp, - enable_nat_pmp, - } = config; - let mut upnp_probing_task = util::MaybeFuture { - inner: (enable_upnp && !upnp).then(|| { - Box::pin(async { - upnp::probe_available() - .await - .map(|addr| (addr, Instant::now())) - }) - }), - }; - - let mut pcp_probing_task = util::MaybeFuture { - inner: (enable_pcp && !pcp).then(|| { - Box::pin(async { - inc!(Metrics, pcp_probes); - pcp::probe_available(local_ip, gateway) - .await - .then(Instant::now) - }) - }), - }; - - let mut nat_pmp_probing_task = util::MaybeFuture { - inner: (enable_nat_pmp && !nat_pmp).then(|| { - Box::pin(async { - nat_pmp::probe_available(local_ip, gateway) - .await - .then(Instant::now) - }) - }), - }; - - if upnp_probing_task.inner.is_some() { - inc!(Metrics, upnp_probes); - } - - let mut upnp_done = upnp_probing_task.inner.is_none(); - let mut pcp_done = pcp_probing_task.inner.is_none(); - let mut nat_pmp_done = nat_pmp_probing_task.inner.is_none(); - - let mut probe = Probe::empty(); - - while !upnp_done || !pcp_done || !nat_pmp_done { - tokio::select! { - last_upnp_gateway_addr = &mut upnp_probing_task, if !upnp_done => { - trace!("tick: upnp probe ready"); - probe.last_upnp_gateway_addr = last_upnp_gateway_addr; - upnp_done = true; - }, - last_nat_pmp = &mut nat_pmp_probing_task, if !nat_pmp_done => { - trace!("tick: nat_pmp probe ready"); - probe.last_nat_pmp = last_nat_pmp; - nat_pmp_done = true; - }, - last_pcp = &mut pcp_probing_task, if !pcp_done => { - trace!("tick: pcp probe ready"); - probe.last_pcp = last_pcp; - pcp_done = true; - }, - } - } - - probe - } - - /// Returns a [`ProbeOutput`] indicating which services can be considered available. - fn output(&self) -> ProbeOutput { - let now = Instant::now(); - - // check if the last UPnP gateway is valid - let upnp = self - .last_upnp_gateway_addr - .as_ref() - .map(|(_gateway_addr, last_probed)| *last_probed + AVAILABILITY_TRUST_DURATION > now) - .unwrap_or_default(); - - let pcp = self - .last_pcp - .as_ref() - .map(|last_probed| *last_probed + AVAILABILITY_TRUST_DURATION > now) - .unwrap_or_default(); - - let nat_pmp = self - .last_nat_pmp - .as_ref() - .map(|last_probed| *last_probed + AVAILABILITY_TRUST_DURATION > now) - .unwrap_or_default(); - - ProbeOutput { upnp, pcp, nat_pmp } - } - - /// Updates a probe with the `Some` values of another probe that is _assumed_ newer. - fn update(&mut self, probe: Probe) { - let Probe { - last_probe, - last_upnp_gateway_addr, - last_pcp, - last_nat_pmp, - } = probe; - if last_upnp_gateway_addr.is_some() { - inc!(Metrics, upnp_available); - let new_gateway = last_upnp_gateway_addr - .as_ref() - .map(|(addr, _last_seen)| addr); - let old_gateway = self - .last_upnp_gateway_addr - .as_ref() - .map(|(addr, _last_seen)| addr); - if new_gateway != old_gateway { - inc!(Metrics, upnp_gateway_updated); - debug!( - "upnp gateway changed {:?} -> {:?}", - old_gateway - .map(|gw| gw.to_string()) - .unwrap_or("None".into()), - new_gateway - .map(|gw| gw.to_string()) - .unwrap_or("None".into()) - ) - }; - self.last_upnp_gateway_addr = last_upnp_gateway_addr; - } - if last_pcp.is_some() { - inc!(Metrics, pcp_available); - self.last_pcp = last_pcp; - } - if last_nat_pmp.is_some() { - self.last_nat_pmp = last_nat_pmp; - } - - self.last_probe = last_probe; - } -} - -// mainly to make clippy happy -type ProbeResult = Result; - -/// A port mapping client. -#[derive(Debug)] -pub struct Service { - config: Config, - /// Local port to map. - local_port: Option, - /// Channel over which the service is informed of messages. - /// - /// The service will stop when all senders are gone. - rx: mpsc::Receiver, - /// Currently active mapping. - current_mapping: CurrentMapping, - /// Last updated probe. - full_probe: Probe, - /// Task attempting to get a port mapping. - /// - /// This task will be cancelled if a request to set the local port arrives before it's - /// finished. - mapping_task: Option>>, - /// Task probing the necessary protocols. - /// - /// Requests for a probe that arrive while this task is still in progress will receive the same - /// result. - probing_task: Option<(AbortOnDropHandle, Vec>)>, -} - -impl Service { - fn new( - config: Config, - rx: mpsc::Receiver, - ) -> (Self, watch::Receiver>) { - let (current_mapping, watcher) = CurrentMapping::new(); - let mut full_probe = Probe::empty(); - if let Some(in_the_past) = full_probe - .last_probe - .checked_sub(AVAILABILITY_TRUST_DURATION) - { - // we want to do a first full probe, so set is as expired on start-up - full_probe.last_probe = in_the_past; - } - let service = Service { - config, - local_port: None, - rx, - current_mapping, - full_probe, - mapping_task: None, - probing_task: None, - }; - - (service, watcher) - } - - /// Clears the current mapping and releases it. - async fn invalidate_mapping(&mut self) { - if let Some(old_mapping) = self.current_mapping.update(None) { - if let Err(e) = old_mapping.release().await { - debug!("failed to release mapping {e}"); - } - } - } - - async fn run(mut self) -> Result<()> { - debug!("portmap starting"); - loop { - tokio::select! { - msg = self.rx.recv() => { - trace!("tick: msg {msg:?}"); - match msg { - Some(msg) => { - self.handle_msg(msg).await; - }, - None => { - debug!("portmap service channel dropped. Likely shutting down."); - break; - } - } - } - mapping_result = util::MaybeFuture{ inner: self.mapping_task.as_mut() } => { - trace!("tick: mapping ready"); - // regardless of outcome, the task is finished, clear it - self.mapping_task = None; - // there isn't really a way to react to a join error here. Flatten it to make - // it easier to work with - let result = match mapping_result { - Ok(result) => result, - Err(join_err) => Err(anyhow!("Failed to obtain a result {join_err}")) - }; - self.on_mapping_result(result); - } - probe_result = util::MaybeFuture{ inner: self.probing_task.as_mut().map(|(fut, _rec)| fut) } => { - trace!("tick: probe ready"); - // retrieve the receivers and clear the task - let receivers = self.probing_task.take().expect("is some").1; - let probe_result = probe_result.map_err(|join_err| anyhow!("Failed to obtain a result {join_err}")); - self.on_probe_result(probe_result, receivers); - } - Some(event) = self.current_mapping.next() => { - trace!("tick: mapping event {event:?}"); - match event { - current_mapping::Event::Renew { external_ip, external_port } | current_mapping::Event::Expired { external_ip, external_port } => { - self.get_mapping(Some((external_ip, external_port))); - }, - } - - } - } - } - Ok(()) - } - - fn on_probe_result( - &mut self, - result: Result, - receivers: Vec>, - ) { - let result = match result { - Err(e) => Err(e.to_string()), - Ok(probe) => { - self.full_probe.update(probe); - // TODO(@divma): the gateway of the current mapping could have changed. Tailscale - // still assumes the current mapping is valid/active and will return it even after - // this - let output = self.full_probe.output(); - trace!(?output, "probe output"); - Ok(output) - } - }; - for tx in receivers { - // ignore the error. If the receiver is no longer there we don't really care - let _ = tx.send(result.clone()); - } - } - - fn on_mapping_result(&mut self, result: Result) { - match result { - Ok(mapping) => { - self.current_mapping.update(Some(mapping)); - } - Err(e) => { - debug!("failed to get a port mapping {e}"); - inc!(Metrics, mapping_failures); - } - } - } - - async fn handle_msg(&mut self, msg: Message) { - match msg { - Message::ProcureMapping => self.update_local_port(self.local_port).await, - Message::UpdateLocalPort { local_port } => self.update_local_port(local_port).await, - Message::Probe { result_tx } => self.probe_request(result_tx), - } - } - - /// Updates the local port of the port mapping service. - /// - /// If the port changed, any port mapping task is cancelled. If the new port is some, it will - /// start a new port mapping task. - async fn update_local_port(&mut self, local_port: Option) { - // ignore requests to update the local port in a way that does not produce a change - if local_port != self.local_port { - inc!(Metrics, local_port_updates); - let old_port = std::mem::replace(&mut self.local_port, local_port); - - // clear the current mapping task if any - - let dropped_task = self.mapping_task.take(); - // check if the dropped task had finished to reduce log noise - let did_cancel = dropped_task - .map(|task| !task.is_finished()) - .unwrap_or_default(); - - if did_cancel { - debug!( - "canceled mapping task due to local port update. Old: {:?} New: {:?}", - old_port, self.local_port - ) - } - - // get the current external port if any to try to get it again - let external_addr = self.current_mapping.external(); - - // since the port has changed, the current mapping is no longer valid and should be - // released - - if external_addr.is_some() { - self.invalidate_mapping().await; - } - - // start a new mapping task to account for the new port if necessary - self.get_mapping(external_addr); - } else if self.current_mapping.external().is_none() { - // if the local port has not changed, but there is no active mapping try to get one - self.get_mapping(None) - } - } - - fn get_mapping(&mut self, external_addr: Option<(Ipv4Addr, NonZeroU16)>) { - if let Some(local_port) = self.local_port { - inc!(Metrics, mapping_attempts); - - let (local_ip, gateway) = match ip_and_gateway() { - Ok(ip_and_gw) => ip_and_gw, - Err(e) => return debug!("can't get mapping: {e}"), - }; - - let ProbeOutput { upnp, pcp, nat_pmp } = self.full_probe.output(); - - debug!("getting a port mapping for {local_ip}:{local_port} -> {external_addr:?}"); - let recently_probed = - self.full_probe.last_probe + UNAVAILABILITY_TRUST_DURATION > Instant::now(); - // strategy: - // 1. check the available services and prefer pcp, then nat_pmp then upnp since it's - // the most unreliable, but possibly the most deployed one - // 2. if no service was available, fallback to upnp if enabled, followed by pcp and - // nat_pmp - self.mapping_task = if pcp { - // try pcp if available first - let task = mapping::Mapping::new_pcp(local_ip, local_port, gateway, external_addr); - Some(AbortOnDropHandle::new(tokio::spawn( - task.instrument(info_span!("pcp")), - ))) - } else if nat_pmp { - // next nat_pmp if available - let task = - mapping::Mapping::new_nat_pmp(local_ip, local_port, gateway, external_addr); - Some(AbortOnDropHandle::new(tokio::spawn( - task.instrument(info_span!("pmp")), - ))) - } else if upnp || self.config.enable_upnp { - // next upnp if available or enabled - let external_port = external_addr.map(|(_addr, port)| port); - let gateway = self - .full_probe - .last_upnp_gateway_addr - .as_ref() - .map(|(gateway, _last_seen)| gateway.clone()); - let task = mapping::Mapping::new_upnp(local_ip, local_port, gateway, external_port); - Some(AbortOnDropHandle::new(tokio::spawn( - task.instrument(info_span!("upnp")), - ))) - } else if !recently_probed && self.config.enable_pcp { - // if no service is available and the default fallback (upnp) is disabled, try pcp - // first - let task = mapping::Mapping::new_pcp(local_ip, local_port, gateway, external_addr); - Some(AbortOnDropHandle::new(tokio::spawn( - task.instrument(info_span!("pcp")), - ))) - } else if !recently_probed && self.config.enable_nat_pmp { - // finally try nat_pmp if enabled - let task = - mapping::Mapping::new_nat_pmp(local_ip, local_port, gateway, external_addr); - Some(AbortOnDropHandle::new(tokio::spawn( - task.instrument(info_span!("pmp")), - ))) - } else { - // give up - return; - } - } - } - - /// Handles a probe request. - /// - /// If there is a task getting a probe, the receiver will be added with any other waiting for a - /// result. If no probe is underway, a result can be returned immediately if it's still - /// considered valid. Otherwise, a new probe task will be started. - fn probe_request(&mut self, result_tx: oneshot::Sender>) { - match self.probing_task.as_mut() { - Some((_task_handle, receivers)) => receivers.push(result_tx), - None => { - let probe_output = self.full_probe.output(); - if probe_output.all_available() { - // we don't care if the requester is no longer there - let _ = result_tx.send(Ok(probe_output)); - } else { - inc!(Metrics, probes_started); - - let (local_ip, gateway) = match ip_and_gateway() { - Ok(ip_and_gw) => ip_and_gw, - Err(e) => { - // there is no guarantee this will be displayed, so log it anyway - debug!("could not start probe: {e}"); - let _ = result_tx.send(Err(e.to_string())); - return; - } - }; - - let config = self.config.clone(); - let handle = - tokio::spawn( - async move { - Probe::from_output(config, probe_output, local_ip, gateway).await - } - .instrument(info_span!("portmapper.probe")), - ); - let receivers = vec![result_tx]; - self.probing_task = Some((AbortOnDropHandle::new(handle), receivers)); - } - } - } - } -} - -/// Gets the local ip and gateway address for port mapping. -fn ip_and_gateway() -> Result<(Ipv4Addr, Ipv4Addr)> { - let Some(HomeRouter { gateway, my_ip }) = HomeRouter::new() else { - anyhow::bail!("no gateway found for probe"); - }; - - let local_ip = match my_ip { - Some(std::net::IpAddr::V4(ip)) - if !ip.is_unspecified() && !ip.is_loopback() && !ip.is_multicast() => - { - ip - } - other => { - debug!("no address suitable for port mapping found ({other:?}), using localhost"); - Ipv4Addr::LOCALHOST - } - }; - - let std::net::IpAddr::V4(gateway) = gateway else { - anyhow::bail!("gateway found is ipv6, ignoring"); - }; - - Ok((local_ip, gateway)) -} diff --git a/net-tools/portmapper/Cargo.toml b/net-tools/portmapper/Cargo.toml index 76a4ba1f425..46ef7afcc90 100644 --- a/net-tools/portmapper/Cargo.toml +++ b/net-tools/portmapper/Cargo.toml @@ -3,7 +3,166 @@ name = "portmapper" version = "0.1.0" edition = "2021" -[dependencies] - [lints] workspace = true + +[dependencies] +anyhow = { version = "1" } +base64 = "0.22.1" +backoff = "0.4.0" +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" +futures-concurrency = "7.6.0" +futures-lite = "2.3" +futures-sink = "0.3.25" +futures-util = "0.3.25" +governor = "0.6.0" +hex = "0.4.3" +hickory-proto = "=0.25.0-alpha.2" +hickory-resolver = "=0.25.0-alpha.2" +hostname = "0.3.1" +http = "1" +http-body-util = "0.1.0" +hyper = { version = "1", features = ["server", "client", "http1"] } +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" +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"] } +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" } +quinn-udp = { package = "iroh-quinn-udp", version = "0.5" } +rand = "0.8" +rcgen = "0.12" +reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"] } +ring = "0.17" +rustls = { version = "0.23", default-features = false, features = ["ring"] } +serde = { version = "1", features = ["derive", "rc"] } +smallvec = "1.11.1" +socket2 = "0.5.3" +stun-rs = "0.1.5" +surge-ping = "0.8.0" +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-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"] } +watchable = "1.1.2" +webpki = { package = "rustls-webpki", version = "0.102" } +webpki-roots = "0.26" +x509-parser = "0.16" +z32 = "1.0.3" + +# iroh-relay +axum = { version = "0.7.4", optional = true } +clap = { version = "4", features = ["derive"], optional = true } +regex = { version = "1.7.1", optional = true } +rustls-pemfile = { version = "2.1", optional = true } +serde_with = { version = "3.3", optional = true } +toml = { version = "0.8", optional = true } +tracing-subscriber = { version = "0.3", features = ["env-filter"], optional = true } +tokio-rustls-acme = { version = "0.4", optional = true } + +# metrics +iroh-metrics = { version = "0.27.0", default-features = false } +strum = { version = "0.26.2", features = ["derive"] } + +# local-swarm-discovery +swarm-discovery = { version = "0.2.1", optional = true } + +# dht_discovery +genawaiter = { version = "0.99.1", features = ["futures03"], optional = true } +net = { version = "0.1.0", path = "../net-tools/net" } + +[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"] } + +[dev-dependencies] +axum = { version = "0.7.4" } +clap = { version = "4", features = ["derive"] } +criterion = "0.5.1" +crypto_box = { version = "0.9.1", features = ["serde", "chacha20"] } +ntest = "0.9" +pretty_assertions = "1.4" +proptest = "1.2.0" +rand_chacha = "0.3.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" +iroh-net = { path = ".", features = ["iroh-relay"] } +serde_json = "1.0.107" +testresult = "0.4.0" +mainline = "2.0.1" + +[[bench]] +name = "key" +harness = false + +[build-dependencies] +duct = "0.13.6" + +[features] +default = ["metrics", "discovery-pkarr-dht"] +iroh-relay = [ + "dep:tokio-rustls-acme", + "dep:axum", + "dep:clap", + "dep:toml", + "dep:rustls-pemfile", + "dep:regex", + "dep:serde_with", + "dep:tracing-subscriber" +] +metrics = ["iroh-metrics/metrics"] +test-utils = ["iroh-relay"] +discovery-local-network = ["dep:swarm-discovery"] +discovery-pkarr-dht = ["pkarr/dht", "dep:genawaiter"] + +[[bin]] +name = "iroh-relay" +required-features = ["iroh-relay"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "iroh_docsrs"] + +[[example]] +name = "listen" + +[[example]] +name = "connect" + +[[example]] +name = "listen-unreliable" + +[[example]] +name = "connect-unreliable" + +[[example]] +name = "dht_discovery" +required-features = ["discovery-pkarr-dht"] + +[[example]] +name = "locally-discovered-nodes" +required-features = ["discovery-local-network"] 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/net-tools/portmapper/src/lib.rs b/net-tools/portmapper/src/lib.rs index b93cf3ffd9c..0b54840213f 100644 --- a/net-tools/portmapper/src/lib.rs +++ b/net-tools/portmapper/src/lib.rs @@ -1,14 +1,702 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right +//! Port mapping client and service. + +use std::{ + net::{Ipv4Addr, SocketAddrV4}, + num::NonZeroU16, + time::{Duration, Instant}, +}; + +use anyhow::{anyhow, Result}; +use current_mapping::CurrentMapping; +use futures_lite::StreamExt; +use iroh_metrics::inc; +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; + +pub use metrics::Metrics; + +/// If a port mapping service has been seen within the last [`AVAILABILITY_TRUST_DURATION`] it will +/// not be probed again. +const AVAILABILITY_TRUST_DURATION: Duration = Duration::from_secs(60 * 10); // 10 minutes + +/// Capacity of the channel to communicate with the long-running service. +const SERVICE_CHANNEL_CAPACITY: usize = 32; // should be plenty + +/// If a port mapping service has not been seen within the last [`UNAVAILABILITY_TRUST_DURATION`] +/// we allow trying a mapping using said protocol. +const UNAVAILABILITY_TRUST_DURATION: Duration = Duration::from_secs(5); + +/// Output of a port mapping probe. +#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)] +#[display("portmap={{ UPnP: {upnp}, PMP: {nat_pmp}, PCP: {pcp} }}")] +pub struct ProbeOutput { + /// If UPnP can be considered available. + pub upnp: bool, + /// If PCP can be considered available. + pub pcp: bool, + /// If PMP can be considered available. + pub nat_pmp: bool, +} + +impl ProbeOutput { + /// Indicates if all port mapping protocols are available. + pub fn all_available(&self) -> bool { + self.upnp && self.pcp && self.nat_pmp + } +} + +#[derive(derive_more::Debug)] +enum Message { + /// Attempt to get a mapping if the local port is set but there is no mapping. + ProcureMapping, + /// Request to update the local port. + /// + /// The resulting external address can be obtained subscribing using + /// [`Client::watch_external_address`]. + /// A value of `None` will deactivate port mapping. + UpdateLocalPort { local_port: Option }, + /// Request to probe the port mapping protocols. + /// + /// The requester should wait for the result at the [`oneshot::Receiver`] counterpart of the + /// [`oneshot::Sender`]. + Probe { + /// Sender side to communicate the result of the probe. + #[debug("_")] + result_tx: oneshot::Sender>, + }, +} + +/// Configures which port mapping protocols are enabled in the [`Service`]. +#[derive(Debug, Clone)] +pub struct Config { + /// Whether UPnP is enabled. + pub enable_upnp: bool, + /// Whether PCP is enabled. + pub enable_pcp: bool, + /// Whether PMP is enabled. + pub enable_nat_pmp: bool, +} + +impl Default for Config { + /// By default all port mapping protocols are enabled. + fn default() -> Self { + Config { + enable_upnp: true, + enable_pcp: true, + enable_nat_pmp: true, + } + } +} + +/// Port mapping client. +#[derive(Debug, Clone)] +pub struct Client { + /// A watcher over the most recent external address obtained from port mapping. + /// + /// See [`watch::Receiver`]. + port_mapping: watch::Receiver>, + /// Channel used to communicate with the port mapping service. + service_tx: mpsc::Sender, + /// A handle to the service that will cancel the spawned task once the client is dropped. + _service_handle: std::sync::Arc>>, +} + +impl Default for Client { + fn default() -> Self { + Self::new(Config::default()) + } +} + +impl Client { + /// Create a new port mapping client. + pub fn new(config: Config) -> Self { + let (service_tx, service_rx) = mpsc::channel(SERVICE_CHANNEL_CAPACITY); + + let (service, watcher) = Service::new(config, service_rx); + + let handle = AbortOnDropHandle::new(tokio::spawn( + async move { service.run().await }.instrument(info_span!("portmapper.service")), + )); + + Client { + port_mapping: watcher, + service_tx, + _service_handle: std::sync::Arc::new(handle), + } + } + + /// Request a probe to the port mapping protocols. + /// + /// Returns the [`oneshot::Receiver`] used to obtain the result of the probe. + pub fn probe(&self) -> oneshot::Receiver> { + let (result_tx, result_rx) = oneshot::channel(); + + if let Err(e) = self.service_tx.try_send(Message::Probe { result_tx }) { + use mpsc::error::TrySendError::*; + + // recover the sender and return the error there + let (result_tx, e) = match e { + Full(Message::Probe { result_tx }) => (result_tx, "Port mapping channel full"), + Closed(Message::Probe { result_tx }) => (result_tx, "Port mapping channel closed"), + Full(_) | Closed(_) => unreachable!("Sent value is a probe."), + }; + + // sender was just created. If it's dropped we have two send error and are likely + // shutting down + // NOTE: second Err is infallible match due to being the sent value + if let Err(Err(e)) = result_tx.send(Err(e.into())) { + trace!("Failed to request probe: {e}") + } + } + result_rx + } + + /// Try to get a mapping for the last local port if there isn't one already. + pub fn procure_mapping(&self) { + // requester can't really do anything with this error if returned, so we log it + if let Err(e) = self.service_tx.try_send(Message::ProcureMapping) { + trace!("Failed to request mapping {e}") + } + } + + /// Update the local port. + /// + /// If the port changes, this will trigger a port mapping attempt. + pub fn update_local_port(&self, local_port: NonZeroU16) { + let local_port = Some(local_port); + // requester can't really do anything with this error if returned, so we log it + if let Err(e) = self + .service_tx + .try_send(Message::UpdateLocalPort { local_port }) + { + trace!("Failed to update local port {e}") + } + } + + /// Deactivate port mapping. + pub fn deactivate(&self) { + // requester can't really do anything with this error if returned, so we log it + if let Err(e) = self + .service_tx + .try_send(Message::UpdateLocalPort { local_port: None }) + { + trace!("Failed to deactivate port mapping {e}") + } + } + + /// Watch the external address for changes in the mappings. + pub fn watch_external_address(&self) -> watch::Receiver> { + self.port_mapping.clone() + } +} + +/// Port mapping protocol information obtained during a probe. +#[derive(Debug)] +struct Probe { + /// When was the probe last updated. + last_probe: Instant, + /// The last [`upnp::Gateway`] and when was it last seen. + last_upnp_gateway_addr: Option<(upnp::Gateway, Instant)>, + /// Last time PCP was seen. + last_pcp: Option, + /// Last time NAT-PMP was seen. + last_nat_pmp: Option, +} + +impl Probe { + /// An empty probe set to `now`. + fn empty() -> Self { + Self { + last_probe: Instant::now(), + last_upnp_gateway_addr: None, + last_pcp: None, + last_nat_pmp: None, + } + } + /// Create a new probe based on a previous output. + async fn from_output( + config: Config, + output: ProbeOutput, + local_ip: Ipv4Addr, + gateway: Ipv4Addr, + ) -> Probe { + let ProbeOutput { upnp, pcp, nat_pmp } = output; + let Config { + enable_upnp, + enable_pcp, + enable_nat_pmp, + } = config; + let mut upnp_probing_task = util::MaybeFuture { + inner: (enable_upnp && !upnp).then(|| { + Box::pin(async { + upnp::probe_available() + .await + .map(|addr| (addr, Instant::now())) + }) + }), + }; + + let mut pcp_probing_task = util::MaybeFuture { + inner: (enable_pcp && !pcp).then(|| { + Box::pin(async { + inc!(Metrics, pcp_probes); + pcp::probe_available(local_ip, gateway) + .await + .then(Instant::now) + }) + }), + }; + + let mut nat_pmp_probing_task = util::MaybeFuture { + inner: (enable_nat_pmp && !nat_pmp).then(|| { + Box::pin(async { + nat_pmp::probe_available(local_ip, gateway) + .await + .then(Instant::now) + }) + }), + }; + + if upnp_probing_task.inner.is_some() { + inc!(Metrics, upnp_probes); + } + + let mut upnp_done = upnp_probing_task.inner.is_none(); + let mut pcp_done = pcp_probing_task.inner.is_none(); + let mut nat_pmp_done = nat_pmp_probing_task.inner.is_none(); + + let mut probe = Probe::empty(); + + while !upnp_done || !pcp_done || !nat_pmp_done { + tokio::select! { + last_upnp_gateway_addr = &mut upnp_probing_task, if !upnp_done => { + trace!("tick: upnp probe ready"); + probe.last_upnp_gateway_addr = last_upnp_gateway_addr; + upnp_done = true; + }, + last_nat_pmp = &mut nat_pmp_probing_task, if !nat_pmp_done => { + trace!("tick: nat_pmp probe ready"); + probe.last_nat_pmp = last_nat_pmp; + nat_pmp_done = true; + }, + last_pcp = &mut pcp_probing_task, if !pcp_done => { + trace!("tick: pcp probe ready"); + probe.last_pcp = last_pcp; + pcp_done = true; + }, + } + } + + probe + } + + /// Returns a [`ProbeOutput`] indicating which services can be considered available. + fn output(&self) -> ProbeOutput { + let now = Instant::now(); + + // check if the last UPnP gateway is valid + let upnp = self + .last_upnp_gateway_addr + .as_ref() + .map(|(_gateway_addr, last_probed)| *last_probed + AVAILABILITY_TRUST_DURATION > now) + .unwrap_or_default(); + + let pcp = self + .last_pcp + .as_ref() + .map(|last_probed| *last_probed + AVAILABILITY_TRUST_DURATION > now) + .unwrap_or_default(); + + let nat_pmp = self + .last_nat_pmp + .as_ref() + .map(|last_probed| *last_probed + AVAILABILITY_TRUST_DURATION > now) + .unwrap_or_default(); + + ProbeOutput { upnp, pcp, nat_pmp } + } + + /// Updates a probe with the `Some` values of another probe that is _assumed_ newer. + fn update(&mut self, probe: Probe) { + let Probe { + last_probe, + last_upnp_gateway_addr, + last_pcp, + last_nat_pmp, + } = probe; + if last_upnp_gateway_addr.is_some() { + inc!(Metrics, upnp_available); + let new_gateway = last_upnp_gateway_addr + .as_ref() + .map(|(addr, _last_seen)| addr); + let old_gateway = self + .last_upnp_gateway_addr + .as_ref() + .map(|(addr, _last_seen)| addr); + if new_gateway != old_gateway { + inc!(Metrics, upnp_gateway_updated); + debug!( + "upnp gateway changed {:?} -> {:?}", + old_gateway + .map(|gw| gw.to_string()) + .unwrap_or("None".into()), + new_gateway + .map(|gw| gw.to_string()) + .unwrap_or("None".into()) + ) + }; + self.last_upnp_gateway_addr = last_upnp_gateway_addr; + } + if last_pcp.is_some() { + inc!(Metrics, pcp_available); + self.last_pcp = last_pcp; + } + if last_nat_pmp.is_some() { + self.last_nat_pmp = last_nat_pmp; + } + + self.last_probe = last_probe; + } +} + +// mainly to make clippy happy +type ProbeResult = Result; + +/// A port mapping client. +#[derive(Debug)] +pub struct Service { + config: Config, + /// Local port to map. + local_port: Option, + /// Channel over which the service is informed of messages. + /// + /// The service will stop when all senders are gone. + rx: mpsc::Receiver, + /// Currently active mapping. + current_mapping: CurrentMapping, + /// Last updated probe. + full_probe: Probe, + /// Task attempting to get a port mapping. + /// + /// This task will be cancelled if a request to set the local port arrives before it's + /// finished. + mapping_task: Option>>, + /// Task probing the necessary protocols. + /// + /// Requests for a probe that arrive while this task is still in progress will receive the same + /// result. + probing_task: Option<(AbortOnDropHandle, Vec>)>, } -#[cfg(test)] -mod tests { - use super::*; +impl Service { + fn new( + config: Config, + rx: mpsc::Receiver, + ) -> (Self, watch::Receiver>) { + let (current_mapping, watcher) = CurrentMapping::new(); + let mut full_probe = Probe::empty(); + if let Some(in_the_past) = full_probe + .last_probe + .checked_sub(AVAILABILITY_TRUST_DURATION) + { + // we want to do a first full probe, so set is as expired on start-up + full_probe.last_probe = in_the_past; + } + let service = Service { + config, + local_port: None, + rx, + current_mapping, + full_probe, + mapping_task: None, + probing_task: None, + }; + + (service, watcher) + } + + /// Clears the current mapping and releases it. + async fn invalidate_mapping(&mut self) { + if let Some(old_mapping) = self.current_mapping.update(None) { + if let Err(e) = old_mapping.release().await { + debug!("failed to release mapping {e}"); + } + } + } + + async fn run(mut self) -> Result<()> { + debug!("portmap starting"); + loop { + tokio::select! { + msg = self.rx.recv() => { + trace!("tick: msg {msg:?}"); + match msg { + Some(msg) => { + self.handle_msg(msg).await; + }, + None => { + debug!("portmap service channel dropped. Likely shutting down."); + break; + } + } + } + mapping_result = util::MaybeFuture{ inner: self.mapping_task.as_mut() } => { + trace!("tick: mapping ready"); + // regardless of outcome, the task is finished, clear it + self.mapping_task = None; + // there isn't really a way to react to a join error here. Flatten it to make + // it easier to work with + let result = match mapping_result { + Ok(result) => result, + Err(join_err) => Err(anyhow!("Failed to obtain a result {join_err}")) + }; + self.on_mapping_result(result); + } + probe_result = util::MaybeFuture{ inner: self.probing_task.as_mut().map(|(fut, _rec)| fut) } => { + trace!("tick: probe ready"); + // retrieve the receivers and clear the task + let receivers = self.probing_task.take().expect("is some").1; + let probe_result = probe_result.map_err(|join_err| anyhow!("Failed to obtain a result {join_err}")); + self.on_probe_result(probe_result, receivers); + } + Some(event) = self.current_mapping.next() => { + trace!("tick: mapping event {event:?}"); + match event { + current_mapping::Event::Renew { external_ip, external_port } | current_mapping::Event::Expired { external_ip, external_port } => { + self.get_mapping(Some((external_ip, external_port))); + }, + } + + } + } + } + Ok(()) + } + + fn on_probe_result( + &mut self, + result: Result, + receivers: Vec>, + ) { + let result = match result { + Err(e) => Err(e.to_string()), + Ok(probe) => { + self.full_probe.update(probe); + // TODO(@divma): the gateway of the current mapping could have changed. Tailscale + // still assumes the current mapping is valid/active and will return it even after + // this + let output = self.full_probe.output(); + trace!(?output, "probe output"); + Ok(output) + } + }; + for tx in receivers { + // ignore the error. If the receiver is no longer there we don't really care + let _ = tx.send(result.clone()); + } + } - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); + fn on_mapping_result(&mut self, result: Result) { + match result { + Ok(mapping) => { + self.current_mapping.update(Some(mapping)); + } + Err(e) => { + debug!("failed to get a port mapping {e}"); + inc!(Metrics, mapping_failures); + } + } } + + async fn handle_msg(&mut self, msg: Message) { + match msg { + Message::ProcureMapping => self.update_local_port(self.local_port).await, + Message::UpdateLocalPort { local_port } => self.update_local_port(local_port).await, + Message::Probe { result_tx } => self.probe_request(result_tx), + } + } + + /// Updates the local port of the port mapping service. + /// + /// If the port changed, any port mapping task is cancelled. If the new port is some, it will + /// start a new port mapping task. + async fn update_local_port(&mut self, local_port: Option) { + // ignore requests to update the local port in a way that does not produce a change + if local_port != self.local_port { + inc!(Metrics, local_port_updates); + let old_port = std::mem::replace(&mut self.local_port, local_port); + + // clear the current mapping task if any + + let dropped_task = self.mapping_task.take(); + // check if the dropped task had finished to reduce log noise + let did_cancel = dropped_task + .map(|task| !task.is_finished()) + .unwrap_or_default(); + + if did_cancel { + debug!( + "canceled mapping task due to local port update. Old: {:?} New: {:?}", + old_port, self.local_port + ) + } + + // get the current external port if any to try to get it again + let external_addr = self.current_mapping.external(); + + // since the port has changed, the current mapping is no longer valid and should be + // released + + if external_addr.is_some() { + self.invalidate_mapping().await; + } + + // start a new mapping task to account for the new port if necessary + self.get_mapping(external_addr); + } else if self.current_mapping.external().is_none() { + // if the local port has not changed, but there is no active mapping try to get one + self.get_mapping(None) + } + } + + fn get_mapping(&mut self, external_addr: Option<(Ipv4Addr, NonZeroU16)>) { + if let Some(local_port) = self.local_port { + inc!(Metrics, mapping_attempts); + + let (local_ip, gateway) = match ip_and_gateway() { + Ok(ip_and_gw) => ip_and_gw, + Err(e) => return debug!("can't get mapping: {e}"), + }; + + let ProbeOutput { upnp, pcp, nat_pmp } = self.full_probe.output(); + + debug!("getting a port mapping for {local_ip}:{local_port} -> {external_addr:?}"); + let recently_probed = + self.full_probe.last_probe + UNAVAILABILITY_TRUST_DURATION > Instant::now(); + // strategy: + // 1. check the available services and prefer pcp, then nat_pmp then upnp since it's + // the most unreliable, but possibly the most deployed one + // 2. if no service was available, fallback to upnp if enabled, followed by pcp and + // nat_pmp + self.mapping_task = if pcp { + // try pcp if available first + let task = mapping::Mapping::new_pcp(local_ip, local_port, gateway, external_addr); + Some(AbortOnDropHandle::new(tokio::spawn( + task.instrument(info_span!("pcp")), + ))) + } else if nat_pmp { + // next nat_pmp if available + let task = + mapping::Mapping::new_nat_pmp(local_ip, local_port, gateway, external_addr); + Some(AbortOnDropHandle::new(tokio::spawn( + task.instrument(info_span!("pmp")), + ))) + } else if upnp || self.config.enable_upnp { + // next upnp if available or enabled + let external_port = external_addr.map(|(_addr, port)| port); + let gateway = self + .full_probe + .last_upnp_gateway_addr + .as_ref() + .map(|(gateway, _last_seen)| gateway.clone()); + let task = mapping::Mapping::new_upnp(local_ip, local_port, gateway, external_port); + Some(AbortOnDropHandle::new(tokio::spawn( + task.instrument(info_span!("upnp")), + ))) + } else if !recently_probed && self.config.enable_pcp { + // if no service is available and the default fallback (upnp) is disabled, try pcp + // first + let task = mapping::Mapping::new_pcp(local_ip, local_port, gateway, external_addr); + Some(AbortOnDropHandle::new(tokio::spawn( + task.instrument(info_span!("pcp")), + ))) + } else if !recently_probed && self.config.enable_nat_pmp { + // finally try nat_pmp if enabled + let task = + mapping::Mapping::new_nat_pmp(local_ip, local_port, gateway, external_addr); + Some(AbortOnDropHandle::new(tokio::spawn( + task.instrument(info_span!("pmp")), + ))) + } else { + // give up + return; + } + } + } + + /// Handles a probe request. + /// + /// If there is a task getting a probe, the receiver will be added with any other waiting for a + /// result. If no probe is underway, a result can be returned immediately if it's still + /// considered valid. Otherwise, a new probe task will be started. + fn probe_request(&mut self, result_tx: oneshot::Sender>) { + match self.probing_task.as_mut() { + Some((_task_handle, receivers)) => receivers.push(result_tx), + None => { + let probe_output = self.full_probe.output(); + if probe_output.all_available() { + // we don't care if the requester is no longer there + let _ = result_tx.send(Ok(probe_output)); + } else { + inc!(Metrics, probes_started); + + let (local_ip, gateway) = match ip_and_gateway() { + Ok(ip_and_gw) => ip_and_gw, + Err(e) => { + // there is no guarantee this will be displayed, so log it anyway + debug!("could not start probe: {e}"); + let _ = result_tx.send(Err(e.to_string())); + return; + } + }; + + let config = self.config.clone(); + let handle = + tokio::spawn( + async move { + Probe::from_output(config, probe_output, local_ip, gateway).await + } + .instrument(info_span!("portmapper.probe")), + ); + let receivers = vec![result_tx]; + self.probing_task = Some((AbortOnDropHandle::new(handle), receivers)); + } + } + } + } +} + +/// Gets the local ip and gateway address for port mapping. +fn ip_and_gateway() -> Result<(Ipv4Addr, Ipv4Addr)> { + let Some(HomeRouter { gateway, my_ip }) = HomeRouter::new() else { + anyhow::bail!("no gateway found for probe"); + }; + + let local_ip = match my_ip { + Some(std::net::IpAddr::V4(ip)) + if !ip.is_unspecified() && !ip.is_loopback() && !ip.is_multicast() => + { + ip + } + other => { + debug!("no address suitable for port mapping found ({other:?}), using localhost"); + Ipv4Addr::LOCALHOST + } + }; + + let std::net::IpAddr::V4(gateway) = gateway else { + anyhow::bail!("gateway found is ipv6, ignoring"); + }; + + Ok((local_ip, gateway)) } 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 100% rename from iroh-net/src/portmapper/nat_pmp.rs rename to net-tools/portmapper/src/nat_pmp.rs 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 100% rename from iroh-net/src/portmapper/pcp.rs rename to net-tools/portmapper/src/pcp.rs 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 100% rename from iroh-net/src/portmapper/upnp.rs rename to net-tools/portmapper/src/upnp.rs From d2a661632efe7a213767ad4a7ee5bc564d64b2a8 Mon Sep 17 00:00:00 2001 From: Diva M Date: Thu, 24 Oct 2024 23:01:12 -0500 Subject: [PATCH 10/30] adjust files to new structure --- Cargo.lock | 91 +++++++++++++++++++++++++++++ net-tools/portmapper/Cargo.toml | 31 +--------- net-tools/portmapper/src/lib.rs | 15 ++++- net-tools/portmapper/src/nat_pmp.rs | 4 +- net-tools/portmapper/src/pcp.rs | 3 +- net-tools/portmapper/src/upnp.rs | 2 +- net-tools/portmapper/src/util.rs | 30 ++++++++++ 7 files changed, 142 insertions(+), 34 deletions(-) create mode 100644 net-tools/portmapper/src/util.rs diff --git a/Cargo.lock b/Cargo.lock index 4b532742e0a..d7a949e2bcf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3935,6 +3935,97 @@ checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" [[package]] name = "portmapper" version = "0.1.0" +dependencies = [ + "anyhow", + "axum", + "backoff", + "base64 0.22.1", + "bytes", + "clap", + "criterion", + "crypto_box", + "der", + "derive_more", + "duct", + "futures-buffered", + "futures-concurrency", + "futures-lite 2.3.0", + "futures-sink", + "futures-util", + "genawaiter", + "governor", + "hex", + "hickory-proto 0.25.0-alpha.2", + "hickory-resolver", + "hostname", + "http 1.1.0", + "http-body-util", + "hyper", + "hyper-util", + "igd-next", + "iroh-base", + "iroh-metrics", + "iroh-quinn", + "iroh-quinn-proto", + "iroh-quinn-udp", + "iroh-test", + "libc", + "mainline", + "net", + "netdev", + "netlink-packet-core", + "netlink-packet-route", + "netlink-sys", + "ntest", + "num_enum", + "once_cell", + "parking_lot", + "pin-project", + "pkarr", + "postcard", + "pretty_assertions", + "proptest", + "rand", + "rand_chacha", + "rcgen", + "regex", + "reqwest", + "ring", + "rtnetlink", + "rustls", + "rustls-pemfile", + "rustls-webpki", + "serde", + "serde_json", + "serde_with", + "smallvec", + "socket2", + "strum 0.26.3", + "stun-rs", + "surge-ping", + "swarm-discovery", + "testresult", + "thiserror", + "time", + "tokio", + "tokio-rustls", + "tokio-rustls-acme", + "tokio-stream", + "tokio-tungstenite", + "tokio-tungstenite-wasm", + "tokio-util", + "toml", + "tracing", + "tracing-subscriber", + "tungstenite", + "url", + "watchable", + "webpki-roots", + "windows 0.51.1", + "wmi", + "x509-parser", + "z32", +] [[package]] name = "positioned-io" diff --git a/net-tools/portmapper/Cargo.toml b/net-tools/portmapper/Cargo.toml index 46ef7afcc90..7286d75e169 100644 --- a/net-tools/portmapper/Cargo.toml +++ b/net-tools/portmapper/Cargo.toml @@ -86,7 +86,7 @@ swarm-discovery = { version = "0.2.1", optional = true } # dht_discovery genawaiter = { version = "0.99.1", features = ["futures03"], optional = true } -net = { version = "0.1.0", path = "../net-tools/net" } +net = { version = "0.1.0", path = "../net" } [target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies] netlink-packet-core = "0.7.0" @@ -110,15 +110,10 @@ rand_chacha = "0.3.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" -iroh-net = { path = ".", features = ["iroh-relay"] } serde_json = "1.0.107" testresult = "0.4.0" mainline = "2.0.1" -[[bench]] -name = "key" -harness = false - [build-dependencies] duct = "0.13.6" @@ -139,30 +134,6 @@ test-utils = ["iroh-relay"] discovery-local-network = ["dep:swarm-discovery"] discovery-pkarr-dht = ["pkarr/dht", "dep:genawaiter"] -[[bin]] -name = "iroh-relay" -required-features = ["iroh-relay"] - [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "iroh_docsrs"] - -[[example]] -name = "listen" - -[[example]] -name = "connect" - -[[example]] -name = "listen-unreliable" - -[[example]] -name = "connect-unreliable" - -[[example]] -name = "dht_discovery" -required-features = ["discovery-pkarr-dht"] - -[[example]] -name = "locally-discovered-nodes" -required-features = ["discovery-local-network"] diff --git a/net-tools/portmapper/src/lib.rs b/net-tools/portmapper/src/lib.rs index 0b54840213f..acbfbda155c 100644 --- a/net-tools/portmapper/src/lib.rs +++ b/net-tools/portmapper/src/lib.rs @@ -14,7 +14,7 @@ use tokio::sync::{mpsc, oneshot, watch}; use tokio_util::task::AbortOnDropHandle; use tracing::{debug, info_span, trace, Instrument}; -use crate::{net::interfaces::HomeRouter, util}; +use net::interfaces::HomeRouter; mod current_mapping; mod mapping; @@ -22,6 +22,19 @@ 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/net-tools/portmapper/src/nat_pmp.rs b/net-tools/portmapper/src/nat_pmp.rs index 911b4ace7b9..ef63511c20a 100644 --- a/net-tools/portmapper/src/nat_pmp.rs +++ b/net-tools/portmapper/src/nat_pmp.rs @@ -4,8 +4,10 @@ use std::{net::Ipv4Addr, num::NonZeroU16, time::Duration}; use tracing::{debug, trace}; +use crate::defaults::NAT_PMP_RECV_TIMEOUT as RECV_TIMEOUT; + use self::protocol::{MapProtocol, Request, Response}; -use crate::{defaults::timeouts::NAT_PMP_RECV_TIMEOUT as RECV_TIMEOUT, net::UdpSocket}; +use net::UdpSocket; mod protocol; diff --git a/net-tools/portmapper/src/pcp.rs b/net-tools/portmapper/src/pcp.rs index f911a341e5f..769eb5b40a9 100644 --- a/net-tools/portmapper/src/pcp.rs +++ b/net-tools/portmapper/src/pcp.rs @@ -5,7 +5,8 @@ use std::{net::Ipv4Addr, num::NonZeroU16, time::Duration}; 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; +use net::UdpSocket; mod protocol; diff --git a/net-tools/portmapper/src/upnp.rs b/net-tools/portmapper/src/upnp.rs index 6d202eed350..ebdac4ab7a2 100644 --- a/net-tools/portmapper/src/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 00000000000..5dccd45c003 --- /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, + } + } +} From 2dda79ce42f1df236628a5fc8b033d02aa6649c9 Mon Sep 17 00:00:00 2001 From: Diva M Date: Thu, 24 Oct 2024 23:07:40 -0500 Subject: [PATCH 11/30] remove portmapping defaults --- iroh-net/src/defaults.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/iroh-net/src/defaults.rs b/iroh-net/src/defaults.rs index 95ab1029539..fee45ea3057 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::*; From 7bda3d0acbec0aefaf7ecea49704d2ac6cbe4581 Mon Sep 17 00:00:00 2001 From: Diva M Date: Thu, 24 Oct 2024 23:12:38 -0500 Subject: [PATCH 12/30] use portmapper directly --- Cargo.lock | 1 + iroh-net/Cargo.toml | 1 + iroh-net/src/lib.rs | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d7a949e2bcf..f472cd1b41c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2818,6 +2818,7 @@ dependencies = [ "parking_lot", "pin-project", "pkarr", + "portmapper", "postcard", "pretty_assertions", "proptest", diff --git a/iroh-net/Cargo.toml b/iroh-net/Cargo.toml index a71d1ca389b..44ae85bb42f 100644 --- a/iroh-net/Cargo.toml +++ b/iroh-net/Cargo.toml @@ -75,6 +75,7 @@ webpki = { package = "rustls-webpki", version = "0.102" } webpki-roots = "0.26" x509-parser = "0.16" z32 = "1.0.3" +portmapper = { path = "../net-tools/portmapper" } # iroh-relay axum = { version = "0.7.4", optional = true } diff --git a/iroh-net/src/lib.rs b/iroh-net/src/lib.rs index 5baabc7c85b..70f95c15bb5 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 use net; pub mod netcheck; pub mod ping; -pub mod portmapper; pub mod relay; pub mod stun; pub mod ticket; @@ -253,6 +251,8 @@ pub(crate) mod util; pub use endpoint::{AddrInfo, Endpoint, NodeAddr}; pub use iroh_base::{key, key::NodeId}; +pub use net; +pub use portmapper; #[cfg(any(test, feature = "test-utils"))] #[cfg_attr(iroh_docsrs, doc(cfg(any(test, feature = "test-utils"))))] From 378a72a44840e0d805c18348a5dd2c37a4a90098 Mon Sep 17 00:00:00 2001 From: Diva M Date: Thu, 24 Oct 2024 23:39:07 -0500 Subject: [PATCH 13/30] remove Some unused deps --- Cargo.lock | 66 --------------------- net-tools/portmapper/Cargo.toml | 102 +------------------------------- 2 files changed, 3 insertions(+), 165 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f472cd1b41c..728aa6876cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3938,94 +3938,28 @@ name = "portmapper" version = "0.1.0" dependencies = [ "anyhow", - "axum", - "backoff", "base64 0.22.1", "bytes", - "clap", - "criterion", - "crypto_box", - "der", "derive_more", - "duct", - "futures-buffered", - "futures-concurrency", "futures-lite 2.3.0", - "futures-sink", "futures-util", - "genawaiter", - "governor", - "hex", - "hickory-proto 0.25.0-alpha.2", - "hickory-resolver", - "hostname", - "http 1.1.0", - "http-body-util", - "hyper", - "hyper-util", "igd-next", - "iroh-base", "iroh-metrics", - "iroh-quinn", - "iroh-quinn-proto", - "iroh-quinn-udp", - "iroh-test", "libc", - "mainline", "net", - "netdev", - "netlink-packet-core", - "netlink-packet-route", - "netlink-sys", "ntest", "num_enum", - "once_cell", - "parking_lot", - "pin-project", - "pkarr", - "postcard", - "pretty_assertions", - "proptest", "rand", "rand_chacha", - "rcgen", - "regex", - "reqwest", - "ring", - "rtnetlink", - "rustls", - "rustls-pemfile", - "rustls-webpki", "serde", - "serde_json", - "serde_with", "smallvec", "socket2", - "strum 0.26.3", - "stun-rs", - "surge-ping", - "swarm-discovery", - "testresult", "thiserror", "time", "tokio", - "tokio-rustls", - "tokio-rustls-acme", - "tokio-stream", - "tokio-tungstenite", - "tokio-tungstenite-wasm", "tokio-util", - "toml", "tracing", - "tracing-subscriber", - "tungstenite", "url", - "watchable", - "webpki-roots", - "windows 0.51.1", - "wmi", - "x509-parser", - "z32", ] [[package]] diff --git a/net-tools/portmapper/Cargo.toml b/net-tools/portmapper/Cargo.toml index 7286d75e169..c2160f91da2 100644 --- a/net-tools/portmapper/Cargo.toml +++ b/net-tools/portmapper/Cargo.toml @@ -9,130 +9,34 @@ workspace = true [dependencies] anyhow = { version = "1" } base64 = "0.22.1" -backoff = "0.4.0" 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" -futures-concurrency = "7.6.0" futures-lite = "2.3" -futures-sink = "0.3.25" futures-util = "0.3.25" -governor = "0.6.0" -hex = "0.4.3" -hickory-proto = "=0.25.0-alpha.2" -hickory-resolver = "=0.25.0-alpha.2" -hostname = "0.3.1" -http = "1" -http-body-util = "0.1.0" -hyper = { version = "1", features = ["server", "client", "http1"] } -hyper-util = "0.1.1" igd-next = { version = "0.15.1", features = ["aio_tokio"] } -iroh-base = { version = "0.27.0", features = ["key"] } +iroh-metrics = { version = "0.27.0", default-features = false } libc = "0.2.139" +net = { version = "0.1.0", path = "../net" } 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"] } -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" } -quinn-udp = { package = "iroh-quinn-udp", version = "0.5" } rand = "0.8" -rcgen = "0.12" -reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"] } -ring = "0.17" -rustls = { version = "0.23", default-features = false, features = ["ring"] } serde = { version = "1", features = ["derive", "rc"] } smallvec = "1.11.1" socket2 = "0.5.3" -stun-rs = "0.1.5" -surge-ping = "0.8.0" 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-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"] } -watchable = "1.1.2" -webpki = { package = "rustls-webpki", version = "0.102" } -webpki-roots = "0.26" -x509-parser = "0.16" -z32 = "1.0.3" - -# iroh-relay -axum = { version = "0.7.4", optional = true } -clap = { version = "4", features = ["derive"], optional = true } -regex = { version = "1.7.1", optional = true } -rustls-pemfile = { version = "2.1", optional = true } -serde_with = { version = "3.3", optional = true } -toml = { version = "0.8", optional = true } -tracing-subscriber = { version = "0.3", features = ["env-filter"], optional = true } -tokio-rustls-acme = { version = "0.4", optional = true } - -# metrics -iroh-metrics = { version = "0.27.0", default-features = false } -strum = { version = "0.26.2", features = ["derive"] } - -# local-swarm-discovery -swarm-discovery = { version = "0.2.1", optional = true } - -# dht_discovery -genawaiter = { version = "0.99.1", features = ["futures03"], optional = true } -net = { version = "0.1.0", path = "../net" } - -[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"] } [dev-dependencies] -axum = { version = "0.7.4" } -clap = { version = "4", features = ["derive"] } -criterion = "0.5.1" -crypto_box = { version = "0.9.1", features = ["serde", "chacha20"] } ntest = "0.9" -pretty_assertions = "1.4" -proptest = "1.2.0" rand_chacha = "0.3.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" -serde_json = "1.0.107" -testresult = "0.4.0" -mainline = "2.0.1" - -[build-dependencies] -duct = "0.13.6" [features] -default = ["metrics", "discovery-pkarr-dht"] -iroh-relay = [ - "dep:tokio-rustls-acme", - "dep:axum", - "dep:clap", - "dep:toml", - "dep:rustls-pemfile", - "dep:regex", - "dep:serde_with", - "dep:tracing-subscriber" -] +default = ["metrics"] metrics = ["iroh-metrics/metrics"] -test-utils = ["iroh-relay"] -discovery-local-network = ["dep:swarm-discovery"] -discovery-pkarr-dht = ["pkarr/dht", "dep:genawaiter"] [package.metadata.docs.rs] all-features = true From 6cbae774cb1c6e677d97667b43824663a7efae09 Mon Sep 17 00:00:00 2001 From: Diva M Date: Fri, 25 Oct 2024 11:30:33 -0500 Subject: [PATCH 14/30] fmt --- net-tools/portmapper/src/lib.rs | 3 +-- net-tools/portmapper/src/nat_pmp.rs | 5 ++--- net-tools/portmapper/src/pcp.rs | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/net-tools/portmapper/src/lib.rs b/net-tools/portmapper/src/lib.rs index acbfbda155c..07c11f32080 100644 --- a/net-tools/portmapper/src/lib.rs +++ b/net-tools/portmapper/src/lib.rs @@ -10,12 +10,11 @@ use anyhow::{anyhow, Result}; use current_mapping::CurrentMapping; use futures_lite::StreamExt; use iroh_metrics::inc; +use net::interfaces::HomeRouter; use tokio::sync::{mpsc, oneshot, watch}; use tokio_util::task::AbortOnDropHandle; use tracing::{debug, info_span, trace, Instrument}; -use net::interfaces::HomeRouter; - mod current_mapping; mod mapping; mod metrics; diff --git a/net-tools/portmapper/src/nat_pmp.rs b/net-tools/portmapper/src/nat_pmp.rs index ef63511c20a..341c870d9db 100644 --- a/net-tools/portmapper/src/nat_pmp.rs +++ b/net-tools/portmapper/src/nat_pmp.rs @@ -2,12 +2,11 @@ use std::{net::Ipv4Addr, num::NonZeroU16, time::Duration}; +use net::UdpSocket; use tracing::{debug, trace}; -use crate::defaults::NAT_PMP_RECV_TIMEOUT as RECV_TIMEOUT; - use self::protocol::{MapProtocol, Request, Response}; -use net::UdpSocket; +use crate::defaults::NAT_PMP_RECV_TIMEOUT as RECV_TIMEOUT; mod protocol; diff --git a/net-tools/portmapper/src/pcp.rs b/net-tools/portmapper/src/pcp.rs index 769eb5b40a9..6a53d39db98 100644 --- a/net-tools/portmapper/src/pcp.rs +++ b/net-tools/portmapper/src/pcp.rs @@ -2,11 +2,11 @@ use std::{net::Ipv4Addr, num::NonZeroU16, time::Duration}; +use net::UdpSocket; use rand::RngCore; use tracing::{debug, trace}; use crate::defaults::PCP_RECV_TIMEOUT as RECV_TIMEOUT; -use net::UdpSocket; mod protocol; From fbe28a7334a0f4dffbeb212f06a4bb15831f0113 Mon Sep 17 00:00:00 2001 From: Diva M Date: Fri, 25 Oct 2024 11:40:11 -0500 Subject: [PATCH 15/30] fix net imports --- net-tools/net/src/netmon/bsd.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net-tools/net/src/netmon/bsd.rs b/net-tools/net/src/netmon/bsd.rs index 20bab5aae71..41190afcf5d 100644 --- a/net-tools/net/src/netmon/bsd.rs +++ b/net-tools/net/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 { From f2ffed7376dfc12d11e9ef426e85299f9a27140c Mon Sep 17 00:00:00 2001 From: Diva M Date: Fri, 25 Oct 2024 17:15:02 -0500 Subject: [PATCH 16/30] add windows deps in net --- net-tools/net/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net-tools/net/Cargo.toml b/net-tools/net/Cargo.toml index edf63bdc6d8..d3d9e63fdd9 100644 --- a/net-tools/net/Cargo.toml +++ b/net-tools/net/Cargo.toml @@ -30,6 +30,8 @@ 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"] } From 9f83aa7c0b683593bdfd334df969f8e1386900b2 Mon Sep 17 00:00:00 2001 From: Diva M Date: Fri, 25 Oct 2024 18:07:00 -0500 Subject: [PATCH 17/30] use portmapper and net directly everywhere --- Cargo.lock | 3 +++ iroh-cli/Cargo.toml | 1 + iroh-cli/src/commands/doctor.rs | 2 +- iroh-net/src/lib.rs | 4 ++-- iroh-net/src/magicsock.rs | 5 +++-- iroh-net/src/magicsock/node_map/node_state.rs | 2 +- iroh-net/src/magicsock/udp_conn.rs | 2 +- iroh-net/src/metrics.rs | 6 ++---- iroh-net/src/netcheck.rs | 9 +++------ iroh-net/src/netcheck/reportgen.rs | 3 +-- iroh-net/src/netcheck/reportgen/hairpin.rs | 2 +- iroh-net/src/netcheck/reportgen/probes.rs | 2 +- 12 files changed, 20 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3b45bf92191..2852a02d7d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2617,6 +2617,7 @@ dependencies = [ "parking_lot", "pkarr", "portable-atomic", + "portmapper", "postcard", "quic-rpc", "rand", @@ -3290,6 +3291,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bytes", + "derive_more", "futures-lite 2.3.0", "futures-sink", "futures-util", @@ -3301,6 +3303,7 @@ dependencies = [ "netlink-sys", "once_cell", "rtnetlink", + "serde", "socket2", "thiserror", "time", diff --git a/iroh-cli/Cargo.toml b/iroh-cli/Cargo.toml index 2fe907240fd..71071696fc6 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 27a2279d581..c01529c5537 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/src/lib.rs b/iroh-net/src/lib.rs index 70f95c15bb5..999634dd9b9 100644 --- a/iroh-net/src/lib.rs +++ b/iroh-net/src/lib.rs @@ -251,8 +251,8 @@ pub(crate) mod util; pub use endpoint::{AddrInfo, Endpoint, NodeAddr}; pub use iroh_base::{key, key::NodeId}; -pub use net; -pub use portmapper; +// pub use net; +// pub use portmapper; #[cfg(any(test, feature = "test-utils"))] #[cfg_attr(iroh_docsrs, doc(cfg(any(test, feature = "test-utils"))))] diff --git a/iroh-net/src/magicsock.rs b/iroh-net/src/magicsock.rs index 1a9341e18fe..8d6a7a4ef79 100644 --- a/iroh-net/src/magicsock.rs +++ b/iroh-net/src/magicsock.rs @@ -64,11 +64,12 @@ use crate::{ dns::DnsResolver, endpoint::NodeAddr, key::{PublicKey, SecretKey, SharedSecret}, - net::{interfaces, ip::LocalAddresses, netmon}, - netcheck, portmapper, + netcheck, relay::{RelayMap, RelayUrl}, stun, AddrInfo, }; +use net::{interfaces, ip::LocalAddresses, netmon}; +use portmapper; mod metrics; mod node_map; diff --git a/iroh-net/src/magicsock/node_map/node_state.rs b/iroh-net/src/magicsock/node_map/node_state.rs index c7faa12c148..0f35c21684a 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 net::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 db376521c54..d99e7aa9aaa 100644 --- a/iroh-net/src/magicsock/udp_conn.rs +++ b/iroh-net/src/magicsock/udp_conn.rs @@ -14,7 +14,7 @@ use quinn_udp::{Transmit, UdpSockRef}; use tokio::io::Interest; use tracing::{debug, trace}; -use crate::net::UdpSocket; +use net::UdpSocket; /// A UDP socket implementing Quinn's [`AsyncUdpSocket`]. #[derive(Clone, Debug)] diff --git a/iroh-net/src/metrics.rs b/iroh-net/src/metrics.rs index 90ebbae09e1..e9dba3eab5a 100644 --- a/iroh-net/src/metrics.rs +++ b/iroh-net/src/metrics.rs @@ -2,7 +2,5 @@ #[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}; +pub use portmapper::Metrics as PortmapMetrics; diff --git a/iroh-net/src/netcheck.rs b/iroh-net/src/netcheck.rs index 5c5f904a5e7..8bec8257bc9 100644 --- a/iroh-net/src/netcheck.rs +++ b/iroh-net/src/netcheck.rs @@ -23,12 +23,9 @@ 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::{dns::DnsResolver, relay::RelayUrl}; +use net::{IpFamily, UdpSocket}; mod metrics; mod reportgen; diff --git a/iroh-net/src/netcheck/reportgen.rs b/iroh-net/src/netcheck/reportgen.rs index 007a04a410b..7278c6b7c0f 100644 --- a/iroh-net/src/netcheck/reportgen.rs +++ b/iroh-net/src/netcheck/reportgen.rs @@ -39,14 +39,13 @@ 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, }; +use net::{interfaces, UdpSocket}; mod hairpin; mod probes; diff --git a/iroh-net/src/netcheck/reportgen/hairpin.rs b/iroh-net/src/netcheck/reportgen/hairpin.rs index eba5b202eb1..cb9a8a9f75c 100644 --- a/iroh-net/src/netcheck/reportgen/hairpin.rs +++ b/iroh-net/src/netcheck/reportgen/hairpin.rs @@ -21,10 +21,10 @@ use tracing::{debug, error, info_span, trace, warn, Instrument}; use crate::{ defaults::timeouts::HAIRPIN_CHECK_TIMEOUT, - net::UdpSocket, netcheck::{self, reportgen, Inflight}, stun, }; +use net::UdpSocket; /// Handle to the hairpin actor. /// diff --git a/iroh-net/src/netcheck/reportgen/probes.rs b/iroh-net/src/netcheck/reportgen/probes.rs index 5bf62deec9f..eaaaf112470 100644 --- a/iroh-net/src/netcheck/reportgen/probes.rs +++ b/iroh-net/src/netcheck/reportgen/probes.rs @@ -10,10 +10,10 @@ use anyhow::{ensure, Result}; use tokio::time::Duration; use crate::{ - net::interfaces, netcheck::Report, relay::{RelayMap, RelayNode, RelayUrl}, }; +use net::interfaces; /// The retransmit interval used when netcheck first runs. /// From 2feeac850502f319138a40726a5f46c7d3d814da Mon Sep 17 00:00:00 2001 From: Diva M Date: Fri, 25 Oct 2024 18:08:57 -0500 Subject: [PATCH 18/30] re-add re-exports --- iroh-net/src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/iroh-net/src/lib.rs b/iroh-net/src/lib.rs index 999634dd9b9..f90b430c79c 100644 --- a/iroh-net/src/lib.rs +++ b/iroh-net/src/lib.rs @@ -251,8 +251,10 @@ pub(crate) mod util; pub use endpoint::{AddrInfo, Endpoint, NodeAddr}; pub use iroh_base::{key, key::NodeId}; -// pub use net; -// pub use portmapper; +// TODO(@divma): re-evaluate this re-export +pub use net; +// TODO(@divma): re-evaluate this re-export +pub use portmapper; #[cfg(any(test, feature = "test-utils"))] #[cfg_attr(iroh_docsrs, doc(cfg(any(test, feature = "test-utils"))))] From 8a1574d697b2051fca85f3992c3afa5e1ea94bf1 Mon Sep 17 00:00:00 2001 From: Diva M Date: Fri, 25 Oct 2024 18:17:29 -0500 Subject: [PATCH 19/30] remove dependency on dns alias --- iroh-net/src/netcheck.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/iroh-net/src/netcheck.rs b/iroh-net/src/netcheck.rs index 8bec8257bc9..d9e9ad2263d 100644 --- a/iroh-net/src/netcheck.rs +++ b/iroh-net/src/netcheck.rs @@ -24,7 +24,8 @@ use tokio_util::{sync::CancellationToken, task::AbortOnDropHandle}; use tracing::{debug, error, info_span, trace, warn, Instrument}; use super::{relay::RelayMap, stun}; -use crate::{dns::DnsResolver, relay::RelayUrl}; +use crate::relay::RelayUrl; +use hickory_resolver::TokioAsyncResolver as DnsResolver; use net::{IpFamily, UdpSocket}; mod metrics; From 25aaa60e14b7722ee9a74487d24c4707adcd90d6 Mon Sep 17 00:00:00 2001 From: Diva M Date: Fri, 25 Oct 2024 18:18:09 -0500 Subject: [PATCH 20/30] fmt --- iroh-net/src/magicsock.rs | 4 ++-- iroh-net/src/magicsock/udp_conn.rs | 3 +-- iroh-net/src/metrics.rs | 3 ++- iroh-net/src/netcheck.rs | 4 ++-- iroh-net/src/netcheck/reportgen.rs | 2 +- iroh-net/src/netcheck/reportgen/hairpin.rs | 2 +- iroh-net/src/netcheck/reportgen/probes.rs | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/iroh-net/src/magicsock.rs b/iroh-net/src/magicsock.rs index 8d6a7a4ef79..5eeb80a280e 100644 --- a/iroh-net/src/magicsock.rs +++ b/iroh-net/src/magicsock.rs @@ -35,6 +35,8 @@ use futures_lite::{FutureExt, Stream, StreamExt}; use futures_util::stream::BoxStream; use iroh_base::key::NodeId; use iroh_metrics::{inc, inc_by}; +use net::{interfaces, ip::LocalAddresses, netmon}; +use portmapper; use quinn::AsyncUdpSocket; use rand::{seq::SliceRandom, Rng, SeedableRng}; use smallvec::{smallvec, SmallVec}; @@ -68,8 +70,6 @@ use crate::{ relay::{RelayMap, RelayUrl}, stun, AddrInfo, }; -use net::{interfaces, ip::LocalAddresses, netmon}; -use portmapper; mod metrics; mod node_map; diff --git a/iroh-net/src/magicsock/udp_conn.rs b/iroh-net/src/magicsock/udp_conn.rs index d99e7aa9aaa..2001ec3c203 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 net::UdpSocket; use quinn::AsyncUdpSocket; use quinn_udp::{Transmit, UdpSockRef}; use tokio::io::Interest; use tracing::{debug, trace}; -use net::UdpSocket; - /// A UDP socket implementing Quinn's [`AsyncUdpSocket`]. #[derive(Clone, Debug)] pub struct UdpConn { diff --git a/iroh-net/src/metrics.rs b/iroh-net/src/metrics.rs index e9dba3eab5a..db9e77c015b 100644 --- a/iroh-net/src/metrics.rs +++ b/iroh-net/src/metrics.rs @@ -1,6 +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}; -pub use portmapper::Metrics as PortmapMetrics; diff --git a/iroh-net/src/netcheck.rs b/iroh-net/src/netcheck.rs index d9e9ad2263d..6cd578d8d1f 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 net::{IpFamily, UdpSocket}; use tokio::{ sync::{self, mpsc, oneshot}, time::{Duration, Instant}, @@ -25,8 +27,6 @@ use tracing::{debug, error, info_span, trace, warn, Instrument}; use super::{relay::RelayMap, stun}; use crate::relay::RelayUrl; -use hickory_resolver::TokioAsyncResolver as DnsResolver; -use net::{IpFamily, UdpSocket}; mod metrics; mod reportgen; diff --git a/iroh-net/src/netcheck/reportgen.rs b/iroh-net/src/netcheck/reportgen.rs index 7278c6b7c0f..3163c69166e 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 net::{interfaces, UdpSocket}; use rand::seq::IteratorRandom; use tokio::{ sync::{mpsc, oneshot}, @@ -45,7 +46,6 @@ use crate::{ stun, util::MaybeFuture, }; -use net::{interfaces, UdpSocket}; mod hairpin; mod probes; diff --git a/iroh-net/src/netcheck/reportgen/hairpin.rs b/iroh-net/src/netcheck/reportgen/hairpin.rs index cb9a8a9f75c..f5f6cc20b88 100644 --- a/iroh-net/src/netcheck/reportgen/hairpin.rs +++ b/iroh-net/src/netcheck/reportgen/hairpin.rs @@ -15,6 +15,7 @@ use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use anyhow::{bail, Context, Result}; +use net::UdpSocket; use tokio::{sync::oneshot, time::Instant}; use tokio_util::task::AbortOnDropHandle; use tracing::{debug, error, info_span, trace, warn, Instrument}; @@ -24,7 +25,6 @@ use crate::{ netcheck::{self, reportgen, Inflight}, stun, }; -use net::UdpSocket; /// Handle to the hairpin actor. /// diff --git a/iroh-net/src/netcheck/reportgen/probes.rs b/iroh-net/src/netcheck/reportgen/probes.rs index eaaaf112470..b9efd983488 100644 --- a/iroh-net/src/netcheck/reportgen/probes.rs +++ b/iroh-net/src/netcheck/reportgen/probes.rs @@ -7,13 +7,13 @@ use std::{collections::BTreeSet, fmt, sync::Arc}; use anyhow::{ensure, Result}; +use net::interfaces; use tokio::time::Duration; use crate::{ netcheck::Report, relay::{RelayMap, RelayNode, RelayUrl}, }; -use net::interfaces; /// The retransmit interval used when netcheck first runs. /// From 15dbac99f59557f1d34f48cc0f4b370cf6f9a4b7 Mon Sep 17 00:00:00 2001 From: Diva M Date: Fri, 25 Oct 2024 18:19:04 -0500 Subject: [PATCH 21/30] remove redundant import --- iroh-net/src/magicsock.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/iroh-net/src/magicsock.rs b/iroh-net/src/magicsock.rs index 5eeb80a280e..4b2c55b9911 100644 --- a/iroh-net/src/magicsock.rs +++ b/iroh-net/src/magicsock.rs @@ -36,7 +36,6 @@ use futures_util::stream::BoxStream; use iroh_base::key::NodeId; use iroh_metrics::{inc, inc_by}; use net::{interfaces, ip::LocalAddresses, netmon}; -use portmapper; use quinn::AsyncUdpSocket; use rand::{seq::SliceRandom, Rng, SeedableRng}; use smallvec::{smallvec, SmallVec}; From ea0bad04e356312723d147ea97ee00632f9c5181 Mon Sep 17 00:00:00 2001 From: Diva M Date: Sun, 27 Oct 2024 20:45:07 -0500 Subject: [PATCH 22/30] add some love to toml file --- Cargo.toml | 2 +- net-tools/portmapper/Cargo.toml | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d9e8ac72321..03fcd661134 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,9 +8,9 @@ members = [ "iroh-test", "iroh-net/bench", "iroh-cli", + "iroh-router", "net-tools/net", "net-tools/portmapper", - "iroh-router", ] resolver = "2" diff --git a/net-tools/portmapper/Cargo.toml b/net-tools/portmapper/Cargo.toml index c2160f91da2..32c73a5cec3 100644 --- a/net-tools/portmapper/Cargo.toml +++ b/net-tools/portmapper/Cargo.toml @@ -2,6 +2,12 @@ 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 From d61a28222a49eee67a9397aea659ab2069dcd627 Mon Sep 17 00:00:00 2001 From: Diva M Date: Tue, 29 Oct 2024 00:18:18 -0500 Subject: [PATCH 23/30] leave it at netwatch with udp --- Cargo.lock | 62 +++++++++---------- Cargo.toml | 2 +- iroh-net/Cargo.toml | 2 +- iroh-net/src/lib.rs | 2 +- iroh-net/src/magicsock.rs | 2 +- iroh-net/src/magicsock/node_map/node_state.rs | 2 +- iroh-net/src/magicsock/udp_conn.rs | 2 +- iroh-net/src/netcheck.rs | 2 +- iroh-net/src/netcheck/reportgen.rs | 2 +- iroh-net/src/netcheck/reportgen/hairpin.rs | 2 +- iroh-net/src/netcheck/reportgen/probes.rs | 2 +- net-tools/{net => netwatch}/Cargo.toml | 8 ++- net-tools/netwatch/README.md | 7 +++ net-tools/{net => netwatch}/src/interfaces.rs | 0 .../{net => netwatch}/src/interfaces/bsd.rs | 0 .../src/interfaces/bsd/freebsd.rs | 0 .../src/interfaces/bsd/macos.rs | 0 .../src/interfaces/bsd/netbsd.rs | 0 .../src/interfaces/bsd/openbsd.rs | 0 .../{net => netwatch}/src/interfaces/linux.rs | 0 .../src/interfaces/windows.rs | 0 net-tools/{net => netwatch}/src/ip.rs | 0 net-tools/{net => netwatch}/src/ip_family.rs | 0 net-tools/{net => netwatch}/src/lib.rs | 0 net-tools/{net => netwatch}/src/netmon.rs | 0 .../{net => netwatch}/src/netmon/actor.rs | 0 .../{net => netwatch}/src/netmon/android.rs | 0 net-tools/{net => netwatch}/src/netmon/bsd.rs | 0 .../{net => netwatch}/src/netmon/linux.rs | 0 .../{net => netwatch}/src/netmon/windows.rs | 0 net-tools/{net => netwatch}/src/udp.rs | 0 net-tools/portmapper/Cargo.toml | 2 +- net-tools/portmapper/README.md | 6 ++ net-tools/portmapper/src/lib.rs | 2 +- net-tools/portmapper/src/nat_pmp.rs | 2 +- net-tools/portmapper/src/pcp.rs | 2 +- 36 files changed, 65 insertions(+), 46 deletions(-) rename net-tools/{net => netwatch}/Cargo.toml (83%) create mode 100644 net-tools/netwatch/README.md rename net-tools/{net => netwatch}/src/interfaces.rs (100%) rename net-tools/{net => netwatch}/src/interfaces/bsd.rs (100%) rename net-tools/{net => netwatch}/src/interfaces/bsd/freebsd.rs (100%) rename net-tools/{net => netwatch}/src/interfaces/bsd/macos.rs (100%) rename net-tools/{net => netwatch}/src/interfaces/bsd/netbsd.rs (100%) rename net-tools/{net => netwatch}/src/interfaces/bsd/openbsd.rs (100%) rename net-tools/{net => netwatch}/src/interfaces/linux.rs (100%) rename net-tools/{net => netwatch}/src/interfaces/windows.rs (100%) rename net-tools/{net => netwatch}/src/ip.rs (100%) rename net-tools/{net => netwatch}/src/ip_family.rs (100%) rename net-tools/{net => netwatch}/src/lib.rs (100%) rename net-tools/{net => netwatch}/src/netmon.rs (100%) rename net-tools/{net => netwatch}/src/netmon/actor.rs (100%) rename net-tools/{net => netwatch}/src/netmon/android.rs (100%) rename net-tools/{net => netwatch}/src/netmon/bsd.rs (100%) rename net-tools/{net => netwatch}/src/netmon/linux.rs (100%) rename net-tools/{net => netwatch}/src/netmon/windows.rs (100%) rename net-tools/{net => netwatch}/src/udp.rs (100%) create mode 100644 net-tools/portmapper/README.md diff --git a/Cargo.lock b/Cargo.lock index 5905bb640b5..62b2d1341fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2833,11 +2833,11 @@ dependencies = [ "iroh-test", "libc", "mainline", - "net", "netdev", "netlink-packet-core", "netlink-packet-route", "netlink-sys", + "netwatch", "ntest", "num_enum", "once_cell", @@ -3284,35 +3284,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "net" -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 = "netdev" version = "0.30.0" @@ -3395,6 +3366,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" @@ -4014,7 +4014,7 @@ dependencies = [ "igd-next", "iroh-metrics", "libc", - "net", + "netwatch", "ntest", "num_enum", "rand", diff --git a/Cargo.toml b/Cargo.toml index a46d1dc1609..8601a90374b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ members = [ "iroh-net/bench", "iroh-cli", "iroh-router", - "net-tools/net", + "net-tools/netwatch", "net-tools/portmapper", ] resolver = "2" diff --git a/iroh-net/Cargo.toml b/iroh-net/Cargo.toml index d6a66e8245f..f414ed0c45e 100644 --- a/iroh-net/Cargo.toml +++ b/iroh-net/Cargo.toml @@ -95,7 +95,7 @@ swarm-discovery = { version = "0.2.1", optional = true } # dht_discovery genawaiter = { version = "0.99.1", features = ["futures03"], optional = true } -net = { version = "0.1.0", path = "../net-tools/net" } +netwatch = { version = "0.1.0", path = "../net-tools/netwatch" } [target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies] netlink-packet-core = "0.7.0" diff --git a/iroh-net/src/lib.rs b/iroh-net/src/lib.rs index f90b430c79c..9764e04f6e0 100644 --- a/iroh-net/src/lib.rs +++ b/iroh-net/src/lib.rs @@ -252,7 +252,7 @@ pub(crate) mod util; pub use endpoint::{AddrInfo, Endpoint, NodeAddr}; pub use iroh_base::{key, key::NodeId}; // TODO(@divma): re-evaluate this re-export -pub use net; +pub use netwatch as net; // TODO(@divma): re-evaluate this re-export pub use portmapper; diff --git a/iroh-net/src/magicsock.rs b/iroh-net/src/magicsock.rs index 4b2c55b9911..55c009e0e4c 100644 --- a/iroh-net/src/magicsock.rs +++ b/iroh-net/src/magicsock.rs @@ -35,7 +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 net::{interfaces, ip::LocalAddresses, netmon}; +use netwatch::{interfaces, ip::LocalAddresses, netmon}; use quinn::AsyncUdpSocket; use rand::{seq::SliceRandom, Rng, SeedableRng}; use smallvec::{smallvec, SmallVec}; diff --git a/iroh-net/src/magicsock/node_map/node_state.rs b/iroh-net/src/magicsock/node_map/node_state.rs index 0f35c21684a..43b52b18f74 100644 --- a/iroh-net/src/magicsock/node_map/node_state.rs +++ b/iroh-net/src/magicsock/node_map/node_state.rs @@ -6,7 +6,7 @@ use std::{ }; use iroh_metrics::inc; -use net::ip::is_unicast_link_local; +use netwatch::ip::is_unicast_link_local; use serde::{Deserialize, Serialize}; use tokio::sync::mpsc; use tracing::{debug, event, info, instrument, trace, warn, Level}; diff --git a/iroh-net/src/magicsock/udp_conn.rs b/iroh-net/src/magicsock/udp_conn.rs index 2001ec3c203..77883d1d85e 100644 --- a/iroh-net/src/magicsock/udp_conn.rs +++ b/iroh-net/src/magicsock/udp_conn.rs @@ -9,7 +9,7 @@ use std::{ }; use anyhow::{bail, Context as _}; -use net::UdpSocket; +use netwatch::UdpSocket; use quinn::AsyncUdpSocket; use quinn_udp::{Transmit, UdpSockRef}; use tokio::io::Interest; diff --git a/iroh-net/src/netcheck.rs b/iroh-net/src/netcheck.rs index 6cd578d8d1f..5e8d54deeb2 100644 --- a/iroh-net/src/netcheck.rs +++ b/iroh-net/src/netcheck.rs @@ -17,7 +17,7 @@ use anyhow::{anyhow, Context as _, Result}; use bytes::Bytes; use hickory_resolver::TokioAsyncResolver as DnsResolver; use iroh_metrics::inc; -use net::{IpFamily, UdpSocket}; +use netwatch::{IpFamily, UdpSocket}; use tokio::{ sync::{self, mpsc, oneshot}, time::{Duration, Instant}, diff --git a/iroh-net/src/netcheck/reportgen.rs b/iroh-net/src/netcheck/reportgen.rs index 3163c69166e..c9cbb106106 100644 --- a/iroh-net/src/netcheck/reportgen.rs +++ b/iroh-net/src/netcheck/reportgen.rs @@ -26,7 +26,7 @@ use std::{ use anyhow::{anyhow, bail, Context, Result}; use iroh_metrics::inc; -use net::{interfaces, UdpSocket}; +use netwatch::{interfaces, UdpSocket}; use rand::seq::IteratorRandom; use tokio::{ sync::{mpsc, oneshot}, diff --git a/iroh-net/src/netcheck/reportgen/hairpin.rs b/iroh-net/src/netcheck/reportgen/hairpin.rs index f5f6cc20b88..47f23e1a88a 100644 --- a/iroh-net/src/netcheck/reportgen/hairpin.rs +++ b/iroh-net/src/netcheck/reportgen/hairpin.rs @@ -15,7 +15,7 @@ use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use anyhow::{bail, Context, Result}; -use net::UdpSocket; +use netwatch::UdpSocket; use tokio::{sync::oneshot, time::Instant}; use tokio_util::task::AbortOnDropHandle; use tracing::{debug, error, info_span, trace, warn, Instrument}; diff --git a/iroh-net/src/netcheck/reportgen/probes.rs b/iroh-net/src/netcheck/reportgen/probes.rs index b9efd983488..c8bfca845a1 100644 --- a/iroh-net/src/netcheck/reportgen/probes.rs +++ b/iroh-net/src/netcheck/reportgen/probes.rs @@ -7,7 +7,7 @@ use std::{collections::BTreeSet, fmt, sync::Arc}; use anyhow::{ensure, Result}; -use net::interfaces; +use netwatch::interfaces; use tokio::time::Duration; use crate::{ diff --git a/net-tools/net/Cargo.toml b/net-tools/netwatch/Cargo.toml similarity index 83% rename from net-tools/net/Cargo.toml rename to net-tools/netwatch/Cargo.toml index d3d9e63fdd9..8ef5617b6ac 100644 --- a/net-tools/net/Cargo.toml +++ b/net-tools/netwatch/Cargo.toml @@ -1,6 +1,12 @@ [package] -name = "net" +name = "netwatch" version = "0.1.0" +readme = "README.md" +description = "Cross-platform monitoring for network changes" +license = "MIT OR Apache-2.0" +authors = ["n0 team"] +repository = "https://github.com/n0-computer/iroh" +keywords = ["networking", "interfaces"] edition = "2021" [lints] diff --git a/net-tools/netwatch/README.md b/net-tools/netwatch/README.md new file mode 100644 index 00000000000..0077b12fd09 --- /dev/null +++ b/net-tools/netwatch/README.md @@ -0,0 +1,7 @@ +# 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/). diff --git a/net-tools/net/src/interfaces.rs b/net-tools/netwatch/src/interfaces.rs similarity index 100% rename from net-tools/net/src/interfaces.rs rename to net-tools/netwatch/src/interfaces.rs diff --git a/net-tools/net/src/interfaces/bsd.rs b/net-tools/netwatch/src/interfaces/bsd.rs similarity index 100% rename from net-tools/net/src/interfaces/bsd.rs rename to net-tools/netwatch/src/interfaces/bsd.rs diff --git a/net-tools/net/src/interfaces/bsd/freebsd.rs b/net-tools/netwatch/src/interfaces/bsd/freebsd.rs similarity index 100% rename from net-tools/net/src/interfaces/bsd/freebsd.rs rename to net-tools/netwatch/src/interfaces/bsd/freebsd.rs diff --git a/net-tools/net/src/interfaces/bsd/macos.rs b/net-tools/netwatch/src/interfaces/bsd/macos.rs similarity index 100% rename from net-tools/net/src/interfaces/bsd/macos.rs rename to net-tools/netwatch/src/interfaces/bsd/macos.rs diff --git a/net-tools/net/src/interfaces/bsd/netbsd.rs b/net-tools/netwatch/src/interfaces/bsd/netbsd.rs similarity index 100% rename from net-tools/net/src/interfaces/bsd/netbsd.rs rename to net-tools/netwatch/src/interfaces/bsd/netbsd.rs diff --git a/net-tools/net/src/interfaces/bsd/openbsd.rs b/net-tools/netwatch/src/interfaces/bsd/openbsd.rs similarity index 100% rename from net-tools/net/src/interfaces/bsd/openbsd.rs rename to net-tools/netwatch/src/interfaces/bsd/openbsd.rs diff --git a/net-tools/net/src/interfaces/linux.rs b/net-tools/netwatch/src/interfaces/linux.rs similarity index 100% rename from net-tools/net/src/interfaces/linux.rs rename to net-tools/netwatch/src/interfaces/linux.rs diff --git a/net-tools/net/src/interfaces/windows.rs b/net-tools/netwatch/src/interfaces/windows.rs similarity index 100% rename from net-tools/net/src/interfaces/windows.rs rename to net-tools/netwatch/src/interfaces/windows.rs diff --git a/net-tools/net/src/ip.rs b/net-tools/netwatch/src/ip.rs similarity index 100% rename from net-tools/net/src/ip.rs rename to net-tools/netwatch/src/ip.rs diff --git a/net-tools/net/src/ip_family.rs b/net-tools/netwatch/src/ip_family.rs similarity index 100% rename from net-tools/net/src/ip_family.rs rename to net-tools/netwatch/src/ip_family.rs diff --git a/net-tools/net/src/lib.rs b/net-tools/netwatch/src/lib.rs similarity index 100% rename from net-tools/net/src/lib.rs rename to net-tools/netwatch/src/lib.rs diff --git a/net-tools/net/src/netmon.rs b/net-tools/netwatch/src/netmon.rs similarity index 100% rename from net-tools/net/src/netmon.rs rename to net-tools/netwatch/src/netmon.rs diff --git a/net-tools/net/src/netmon/actor.rs b/net-tools/netwatch/src/netmon/actor.rs similarity index 100% rename from net-tools/net/src/netmon/actor.rs rename to net-tools/netwatch/src/netmon/actor.rs diff --git a/net-tools/net/src/netmon/android.rs b/net-tools/netwatch/src/netmon/android.rs similarity index 100% rename from net-tools/net/src/netmon/android.rs rename to net-tools/netwatch/src/netmon/android.rs diff --git a/net-tools/net/src/netmon/bsd.rs b/net-tools/netwatch/src/netmon/bsd.rs similarity index 100% rename from net-tools/net/src/netmon/bsd.rs rename to net-tools/netwatch/src/netmon/bsd.rs diff --git a/net-tools/net/src/netmon/linux.rs b/net-tools/netwatch/src/netmon/linux.rs similarity index 100% rename from net-tools/net/src/netmon/linux.rs rename to net-tools/netwatch/src/netmon/linux.rs diff --git a/net-tools/net/src/netmon/windows.rs b/net-tools/netwatch/src/netmon/windows.rs similarity index 100% rename from net-tools/net/src/netmon/windows.rs rename to net-tools/netwatch/src/netmon/windows.rs diff --git a/net-tools/net/src/udp.rs b/net-tools/netwatch/src/udp.rs similarity index 100% rename from net-tools/net/src/udp.rs rename to net-tools/netwatch/src/udp.rs diff --git a/net-tools/portmapper/Cargo.toml b/net-tools/portmapper/Cargo.toml index 32c73a5cec3..e00626ca111 100644 --- a/net-tools/portmapper/Cargo.toml +++ b/net-tools/portmapper/Cargo.toml @@ -22,7 +22,7 @@ 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" -net = { version = "0.1.0", path = "../net" } +netwatch = { version = "0.1.0", path = "../netwatch" } num_enum = "0.7" rand = "0.8" serde = { version = "1", features = ["derive", "rc"] } diff --git a/net-tools/portmapper/README.md b/net-tools/portmapper/README.md new file mode 100644 index 00000000000..5423add092e --- /dev/null +++ b/net-tools/portmapper/README.md @@ -0,0 +1,6 @@ +# 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/). diff --git a/net-tools/portmapper/src/lib.rs b/net-tools/portmapper/src/lib.rs index 07c11f32080..708d5720987 100644 --- a/net-tools/portmapper/src/lib.rs +++ b/net-tools/portmapper/src/lib.rs @@ -10,7 +10,7 @@ use anyhow::{anyhow, Result}; use current_mapping::CurrentMapping; use futures_lite::StreamExt; use iroh_metrics::inc; -use net::interfaces::HomeRouter; +use netwatch::interfaces::HomeRouter; use tokio::sync::{mpsc, oneshot, watch}; use tokio_util::task::AbortOnDropHandle; use tracing::{debug, info_span, trace, Instrument}; diff --git a/net-tools/portmapper/src/nat_pmp.rs b/net-tools/portmapper/src/nat_pmp.rs index 341c870d9db..a44c4aeb7ee 100644 --- a/net-tools/portmapper/src/nat_pmp.rs +++ b/net-tools/portmapper/src/nat_pmp.rs @@ -2,7 +2,7 @@ use std::{net::Ipv4Addr, num::NonZeroU16, time::Duration}; -use net::UdpSocket; +use netwatch::UdpSocket; use tracing::{debug, trace}; use self::protocol::{MapProtocol, Request, Response}; diff --git a/net-tools/portmapper/src/pcp.rs b/net-tools/portmapper/src/pcp.rs index 6a53d39db98..0f2fe789f50 100644 --- a/net-tools/portmapper/src/pcp.rs +++ b/net-tools/portmapper/src/pcp.rs @@ -2,7 +2,7 @@ use std::{net::Ipv4Addr, num::NonZeroU16, time::Duration}; -use net::UdpSocket; +use netwatch::UdpSocket; use rand::RngCore; use tracing::{debug, trace}; From 9b22efee10ef066d0b939bddbf3e6f446335d449 Mon Sep 17 00:00:00 2001 From: Diva M Date: Tue, 29 Oct 2024 00:21:42 -0500 Subject: [PATCH 24/30] include in ci --- .github/workflows/ci.yml | 2 +- .github/workflows/tests.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dafda9cb78b..ca06beeb065 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 aa52c43b069..7ebd2ab2ce3 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: From eb7d3f8d48feadb9dcce3bfd914677a21b6208ec Mon Sep 17 00:00:00 2001 From: Diva M Date: Tue, 29 Oct 2024 00:28:55 -0500 Subject: [PATCH 25/30] add time to tokio in netwatch --- net-tools/netwatch/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net-tools/netwatch/Cargo.toml b/net-tools/netwatch/Cargo.toml index 8ef5617b6ac..1958ab92f8f 100644 --- a/net-tools/netwatch/Cargo.toml +++ b/net-tools/netwatch/Cargo.toml @@ -24,7 +24,7 @@ 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"] } +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] From 5131a5fc2e75ad5012eab46fbe100d09b29d25ef Mon Sep 17 00:00:00 2001 From: Diva M Date: Tue, 29 Oct 2024 09:31:32 -0500 Subject: [PATCH 26/30] merge and sort imports --- iroh-net/Cargo.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/iroh-net/Cargo.toml b/iroh-net/Cargo.toml index f414ed0c45e..af992ab9b8c 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"] } @@ -75,7 +77,6 @@ webpki = { package = "rustls-webpki", version = "0.102" } webpki-roots = "0.26" x509-parser = "0.16" z32 = "1.0.3" -portmapper = { path = "../net-tools/portmapper" } # iroh-relay axum = { version = "0.7.4", optional = true } @@ -95,7 +96,6 @@ swarm-discovery = { version = "0.2.1", optional = true } # dht_discovery genawaiter = { version = "0.99.1", features = ["futures03"], optional = true } -netwatch = { version = "0.1.0", path = "../net-tools/netwatch" } [target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies] netlink-packet-core = "0.7.0" From 5e1e3e1fe7cc0a4dbf833ecc37340309d103df09 Mon Sep 17 00:00:00 2001 From: Diva M Date: Tue, 29 Oct 2024 09:39:25 -0500 Subject: [PATCH 27/30] add Lincense, Contribution, improve description --- net-tools/netwatch/Cargo.toml | 2 +- net-tools/netwatch/README.md | 17 +++++++++++++++++ net-tools/portmapper/README.md | 20 +++++++++++++++++++- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/net-tools/netwatch/Cargo.toml b/net-tools/netwatch/Cargo.toml index 1958ab92f8f..3628883cfa1 100644 --- a/net-tools/netwatch/Cargo.toml +++ b/net-tools/netwatch/Cargo.toml @@ -2,7 +2,7 @@ name = "netwatch" version = "0.1.0" readme = "README.md" -description = "Cross-platform monitoring for network changes" +description = "Cross-platform monitoring for network interface changes" license = "MIT OR Apache-2.0" authors = ["n0 team"] repository = "https://github.com/n0-computer/iroh" diff --git a/net-tools/netwatch/README.md b/net-tools/netwatch/README.md index 0077b12fd09..e0c8f39b052 100644 --- a/net-tools/netwatch/README.md +++ b/net-tools/netwatch/README.md @@ -5,3 +5,20 @@ 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/net-tools/portmapper/README.md b/net-tools/portmapper/README.md index 5423add092e..7f819769cc5 100644 --- a/net-tools/portmapper/README.md +++ b/net-tools/portmapper/README.md @@ -3,4 +3,22 @@ `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/). +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. From e9959e5eee8b20e6d6ad279b1682470ab9f9dccf Mon Sep 17 00:00:00 2001 From: Diva M Date: Tue, 29 Oct 2024 09:57:29 -0500 Subject: [PATCH 28/30] cleanup docs --- net-tools/netwatch/src/interfaces.rs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/net-tools/netwatch/src/interfaces.rs b/net-tools/netwatch/src/interfaces.rs index b0d98894206..f9f511d8794 100644 --- a/net-tools/netwatch/src/interfaces.rs +++ b/net-tools/netwatch/src/interfaces.rs @@ -82,13 +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 + /// This allows tests to be independent of the host interfaces. pub(crate) fn fake() -> Self { use std::net::Ipv4Addr; @@ -254,13 +248,7 @@ 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 + /// This allows tests to be independent of the host interfaces. pub fn fake() -> Self { let fake = Interface::fake(); let ifname = fake.iface.name.clone(); From a64f8bcf8c2e4435b13f9113141364ea25581d45 Mon Sep 17 00:00:00 2001 From: Diva M Date: Tue, 29 Oct 2024 10:03:26 -0500 Subject: [PATCH 29/30] remove readded reexports rerere as agreed with @dignifiedquire and @flub --- iroh-net/src/lib.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/iroh-net/src/lib.rs b/iroh-net/src/lib.rs index 9764e04f6e0..bda876ccad2 100644 --- a/iroh-net/src/lib.rs +++ b/iroh-net/src/lib.rs @@ -251,10 +251,6 @@ pub(crate) mod util; pub use endpoint::{AddrInfo, Endpoint, NodeAddr}; pub use iroh_base::{key, key::NodeId}; -// TODO(@divma): re-evaluate this re-export -pub use netwatch as net; -// TODO(@divma): re-evaluate this re-export -pub use portmapper; #[cfg(any(test, feature = "test-utils"))] #[cfg_attr(iroh_docsrs, doc(cfg(any(test, feature = "test-utils"))))] From 0d04c369ae0a3fd11023227a52f298d029a02bbf Mon Sep 17 00:00:00 2001 From: Diva M Date: Tue, 29 Oct 2024 10:17:18 -0500 Subject: [PATCH 30/30] fix missed test --- iroh-net/src/magicsock/udp_conn.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/iroh-net/src/magicsock/udp_conn.rs b/iroh-net/src/magicsock/udp_conn.rs index 77883d1d85e..c176ae8144c 100644 --- a/iroh-net/src/magicsock/udp_conn.rs +++ b/iroh-net/src/magicsock/udp_conn.rs @@ -196,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";