From c17a870e1c8396ae22557040288e5f1feaf65e83 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Mon, 7 Jul 2025 13:01:21 +0200 Subject: [PATCH 01/27] feat: use quinn multipath --- Cargo.lock | 1222 +++++++++++++++++++++-------------------- Cargo.toml | 5 + iroh-relay/Cargo.toml | 4 +- iroh/Cargo.toml | 8 +- iroh/bench/Cargo.toml | 2 +- 5 files changed, 639 insertions(+), 602 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d23ac3bdd80..cfaec28b630 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,9 +27,9 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aead" @@ -44,15 +44,15 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.12" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom 0.3.3", + "getrandom 0.2.16", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -93,9 +93,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.19" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -108,36 +108,36 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.7" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", - "once_cell_polyfill", + "once_cell", "windows-sys 0.59.0", ] @@ -198,7 +198,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", "synstructure", ] @@ -210,7 +210,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -234,7 +234,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -276,9 +276,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.5.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" @@ -343,7 +343,7 @@ checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -370,9 +370,9 @@ dependencies = [ [[package]] name = "backon" -version = "1.5.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302eaff5357a264a2c42f127ecb8bac761cf99749fc3dc95677e2743991f99e7" +checksum = "fd0b50b1b78dbadd44ab18b3c794e496f3a139abb9fbc27d9c94c4eebbb96496" dependencies = [ "fastrand", "gloo-timers 0.3.0", @@ -423,9 +423,9 @@ dependencies = [ [[package]] name = "base64ct" -version = "1.8.0" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" [[package]] name = "bincode" @@ -453,9 +453,15 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" -version = "2.9.1" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "blake3" @@ -493,9 +499,15 @@ checksum = "387e80962b798815a2b5c4bcfdb6bf626fa922ffe9f74e373103b858738e9f31" [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "bytemuck" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" [[package]] name = "byteorder" @@ -517,9 +529,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.29" +version = "1.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362" +checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" dependencies = [ "shlex", ] @@ -532,9 +544,9 @@ checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" @@ -606,9 +618,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.41" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" +checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" dependencies = [ "clap_builder", "clap_derive", @@ -616,9 +628,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.41" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" +checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" dependencies = [ "anstream", "anstyle", @@ -628,30 +640,27 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.41" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] name = "clap_lex" -version = "0.7.5" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "cobs" -version = "0.3.0" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" -dependencies = [ - "thiserror 2.0.12", -] +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" [[package]] name = "color-backtrace" @@ -666,9 +675,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.4" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "combine" @@ -682,15 +691,15 @@ dependencies = [ [[package]] name = "console" -version = "0.16.0" +version = "0.15.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e09ced7ebbccb63b4c65413d821f2e00ce54c5ca4514ddc6b3c892fdbcbc69d" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" dependencies = [ "encode_unicode", "libc", "once_cell", "unicode-width", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -717,9 +726,9 @@ checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "cordyceps" -version = "0.3.4" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688d7fbb8092b8de775ef2536f36c8c31f2bc4006ece2e8d8ad2d17d00ce0a2a" +checksum = "a0392f465ceba1713d30708f61c160ebf4dc1cf86bb166039d16b11ad4f3b5b6" dependencies = [ "loom", "tracing", @@ -737,9 +746,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.10.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" dependencies = [ "core-foundation-sys", "libc", @@ -762,9 +771,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.3.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" dependencies = [ "crc-catalog", ] @@ -853,9 +862,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.4" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "crypto-common" @@ -926,7 +935,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -983,7 +992,7 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -1001,16 +1010,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" dependencies = [ - "derive_more-impl 1.0.0", -] - -[[package]] -name = "derive_more" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" -dependencies = [ - "derive_more-impl 2.0.1", + "derive_more-impl", ] [[package]] @@ -1021,19 +1021,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", - "unicode-xid", -] - -[[package]] -name = "derive_more-impl" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", + "syn 2.0.101", "unicode-xid", ] @@ -1089,7 +1077,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -1131,9 +1119,9 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.2.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ "curve25519-dalek", "ed25519", @@ -1177,27 +1165,27 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] name = "enumflags2" -version = "0.7.12" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" dependencies = [ "enumflags2_derive", ] [[package]] name = "enumflags2_derive" -version = "0.7.12" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -1217,12 +1205,12 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.13" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -1231,6 +1219,18 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" +[[package]] +name = "fastbloom" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27cea6e7f512d43b098939ff4d5a5d6fe3db07971e1d05176fe26c642d33f5b8" +dependencies = [ + "getrandom 0.3.2", + "rand 0.9.1", + "siphasher", + "wide", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -1287,9 +1287,9 @@ dependencies = [ [[package]] name = "fs-err" -version = "3.1.1" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d7be93788013f265201256d58f04936a8079ad5dc898743aa20525f503b683" +checksum = "1f89bda4c2a21204059a977ed3bfe746677dfd137b83c339e702b0ac91d482aa" dependencies = [ "autocfg", "tokio", @@ -1377,7 +1377,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -1418,16 +1418,15 @@ dependencies = [ [[package]] name = "generator" -version = "0.8.5" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d18470a76cb7f8ff746cf1f7470914f900252ec36bbc40b569d74b1258446827" +checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd" dependencies = [ - "cc", "cfg-if", "libc", "log", "rustversion", - "windows", + "windows 0.58.0", ] [[package]] @@ -1450,15 +1449,15 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", "js-sys", @@ -1669,7 +1668,7 @@ dependencies = [ "futures-sink", "futures-timer", "futures-util", - "getrandom 0.3.3", + "getrandom 0.3.2", "no-std-compat", "nonzero_ext", "parking_lot", @@ -1683,9 +1682,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.11" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" +checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" dependencies = [ "atomic-waker", "bytes", @@ -1727,9 +1726,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" dependencies = [ "allocator-api2", "equivalent", @@ -1768,9 +1767,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" [[package]] name = "hex" @@ -1882,9 +1881,9 @@ dependencies = [ [[package]] name = "hmac-sha256" -version = "1.1.12" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad6880c8d4a9ebf39c6e8b77007ce223f646a4d21ce29d99f70cb16420545425" +checksum = "4a8575493d277c9092b988c780c94737fb9fd8651a1001e16bee3eccfc1baedb" [[package]] name = "hostname-validator" @@ -1988,10 +1987,11 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.7" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ + "futures-util", "http 1.3.1", "hyper", "hyper-util", @@ -2000,28 +2000,24 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots 1.0.1", + "webpki-roots 0.26.11", ] [[package]] name = "hyper-util" -version = "0.1.15" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" dependencies = [ - "base64", "bytes", "futures-channel", - "futures-core", "futures-util", "http 1.3.1", "http-body", "hyper", - "ipnet", "libc", - "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2", "tokio", "tower-service", "tracing", @@ -2039,7 +2035,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core", + "windows-core 0.61.0", ] [[package]] @@ -2053,22 +2049,21 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ "displaydoc", - "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locale_core" -version = "2.0.0" +name = "icu_locid" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ "displaydoc", "litemap", @@ -2077,11 +2072,31 @@ dependencies = [ "zerovec", ] +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" + [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" dependencies = [ "displaydoc", "icu_collections", @@ -2089,54 +2104,67 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", + "utf16_iter", + "utf8_iter", + "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" [[package]] name = "icu_properties" -version = "2.0.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" dependencies = [ "displaydoc", "icu_collections", - "icu_locale_core", + "icu_locid_transform", "icu_properties_data", "icu_provider", - "potential_utf", - "zerotrie", + "tinystr", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" [[package]] name = "icu_provider" -version = "2.0.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" dependencies = [ "displaydoc", - "icu_locale_core", + "icu_locid", + "icu_provider_macros", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", - "zerotrie", "zerovec", ] +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "idna" version = "1.0.3" @@ -2150,9 +2178,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" dependencies = [ "icu_normalizer", "icu_properties", @@ -2181,25 +2209,25 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.10.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.4", + "hashbrown 0.15.3", ] [[package]] name = "indicatif" -version = "0.18.0" +version = "0.17.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a646d946d06bedbbc4cac4c218acf4bbf2d87757a784857025f4d447e4e1cd" +checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" dependencies = [ "console", + "number_prefix", "portable-atomic", "tokio", "unicode-width", - "unit-prefix", "web-time", ] @@ -2224,24 +2252,13 @@ dependencies = [ "web-sys", ] -[[package]] -name = "io-uring" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" -dependencies = [ - "bitflags", - "cfg-if", - "libc", -] - [[package]] name = "ipconfig" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2 0.5.10", + "socket2", "widestring", "windows-sys 0.48.0", "winreg", @@ -2256,19 +2273,9 @@ dependencies = [ "serde", ] -[[package]] -name = "iri-string" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "iroh" -version = "0.91.0" +version = "0.90.0" dependencies = [ "aead", "axum", @@ -2280,11 +2287,11 @@ dependencies = [ "crypto_box", "data-encoding", "der", - "derive_more 2.0.1", + "derive_more", "ed25519-dalek", "futures-buffered", "futures-util", - "getrandom 0.3.3", + "getrandom 0.3.2", "hickory-resolver", "http 1.3.1", "igd-next", @@ -2320,7 +2327,7 @@ dependencies = [ "smallvec", "snafu", "spki", - "strum 0.27.1", + "strum", "stun-rs", "surge-ping", "swarm-discovery", @@ -2341,11 +2348,11 @@ dependencies = [ [[package]] name = "iroh-base" -version = "0.91.0" +version = "0.90.0" dependencies = [ "curve25519-dalek", "data-encoding", - "derive_more 2.0.1", + "derive_more", "ed25519-dalek", "n0-snafu", "nested_enum_utils", @@ -2362,7 +2369,7 @@ dependencies = [ [[package]] name = "iroh-bench" -version = "0.91.0" +version = "0.90.0" dependencies = [ "bytes", "clap", @@ -2372,8 +2379,9 @@ dependencies = [ "iroh-quinn", "n0-future", "n0-snafu", + "n0-watcher", "rand 0.8.5", - "rcgen 0.14.2", + "rcgen", "rustls", "tokio", "tracing", @@ -2382,7 +2390,7 @@ dependencies = [ [[package]] name = "iroh-dns-server" -version = "0.91.0" +version = "0.90.0" dependencies = [ "async-trait", "axum", @@ -2392,7 +2400,7 @@ dependencies = [ "clap", "criterion", "data-encoding", - "derive_more 2.0.1", + "derive_more", "dirs-next", "governor", "hickory-resolver", @@ -2408,7 +2416,7 @@ dependencies = [ "pkarr", "rand 0.8.5", "rand_chacha 0.3.1", - "rcgen 0.13.2", + "rcgen", "redb", "regex", "rustls", @@ -2416,7 +2424,7 @@ dependencies = [ "serde", "snafu", "struct_iterable", - "strum 0.26.3", + "strum", "tokio", "tokio-rustls", "tokio-rustls-acme", @@ -2461,14 +2469,13 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] name = "iroh-quinn" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde160ebee7aabede6ae887460cd303c8b809054224815addf1469d54a6fcf7" +version = "0.13.0" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#773fceabb27f1e56132198dd960d4bd1493e0ed0" dependencies = [ "bytes", "cfg_aliases", @@ -2477,7 +2484,7 @@ dependencies = [ "pin-project-lite", "rustc-hash", "rustls", - "socket2 0.5.10", + "socket2", "thiserror 2.0.12", "tokio", "tracing", @@ -2487,12 +2494,13 @@ dependencies = [ [[package]] name = "iroh-quinn-proto" version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "929d5d8fa77d5c304d3ee7cae9aede31f13908bd049f9de8c7c0094ad6f7c535" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#773fceabb27f1e56132198dd960d4bd1493e0ed0" dependencies = [ "bytes", - "getrandom 0.2.16", - "rand 0.8.5", + "fastbloom", + "getrandom 0.3.2", + "lru-slab", + "rand 0.9.1", "ring", "rustc-hash", "rustls", @@ -2507,21 +2515,20 @@ dependencies = [ [[package]] name = "iroh-quinn-udp" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c53afaa1049f7c83ea1331f5ebb9e6ebc5fdd69c468b7a22dd598b02c9bcc973" +version = "0.5.12" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#773fceabb27f1e56132198dd960d4bd1493e0ed0" dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2", "tracing", "windows-sys 0.59.0", ] [[package]] name = "iroh-relay" -version = "0.91.0" +version = "0.90.0" dependencies = [ "ahash", "blake3", @@ -2531,8 +2538,8 @@ dependencies = [ "crypto_box", "dashmap", "data-encoding", - "derive_more 2.0.1", - "getrandom 0.3.3", + "derive_more", + "getrandom 0.3.2", "governor", "hickory-proto", "hickory-resolver", @@ -2555,7 +2562,7 @@ dependencies = [ "proptest", "rand 0.8.5", "rand_chacha 0.3.1", - "rcgen 0.14.2", + "rcgen", "regex", "reloadable-state", "reqwest", @@ -2571,7 +2578,7 @@ dependencies = [ "sha1", "simdutf8", "snafu", - "strum 0.27.1", + "strum", "time", "tokio", "tokio-rustls", @@ -2660,17 +2667,17 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libredox" -version = "0.1.4" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags", + "bitflags 2.9.0", "libc", ] @@ -2688,9 +2695,9 @@ checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" -version = "0.8.0" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] name = "litrs" @@ -2700,9 +2707,9 @@ checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -2733,7 +2740,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465" dependencies = [ - "hashbrown 0.15.4", + "hashbrown 0.15.3", ] [[package]] @@ -2787,9 +2794,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" -version = "2.7.5" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mime" @@ -2815,22 +2822,22 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.9" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.4" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", - "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", ] [[package]] @@ -2859,7 +2866,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7bb0e5d99e681ab3c938842b96fcb41bf8a7bb4bfdb11ccbd653a7e83e06c794" dependencies = [ "cfg_aliases", - "derive_more 1.0.0", + "derive_more", "futures-buffered", "futures-lite", "futures-util", @@ -2888,11 +2895,11 @@ dependencies = [ [[package]] name = "n0-watcher" -version = "0.3.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31462392a10d5ada4b945e840cbec2d5f3fee752b96c4b33eb41414d8f45c2a" +checksum = "f216d4ebc5fcf9548244803cbb93f488a2ae160feba3706cd17040d69cf7a368" dependencies = [ - "derive_more 1.0.0", + "derive_more", "n0-future", "snafu", ] @@ -2911,19 +2918,19 @@ dependencies = [ [[package]] name = "netdev" -version = "0.36.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862209dce034f82a44c95ce2b5183730d616f2a68746b9c1959aa2572e77c0a1" +checksum = "f901362e84cd407be6f8cd9d3a46bccf09136b095792785401ea7d283c79b91d" dependencies = [ "dlopen2", "ipnet", "libc", "netlink-packet-core", - "netlink-packet-route 0.22.0", + "netlink-packet-route 0.17.1", "netlink-sys", "once_cell", "system-configuration", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2939,27 +2946,26 @@ dependencies = [ [[package]] name = "netlink-packet-route" -version = "0.22.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0e7987b28514adf555dc1f9a5c30dfc3e50750bbaffb1aec41ca7b23dcd8e4" +checksum = "053998cea5a306971f88580d0829e90f270f940befd7cf928da179d4187a5a66" dependencies = [ "anyhow", - "bitflags", + "bitflags 1.3.2", "byteorder", "libc", - "log", "netlink-packet-core", "netlink-packet-utils", ] [[package]] name = "netlink-packet-route" -version = "0.24.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56d83370a96813d7c977f8b63054f1162df6e5784f1c598d689236564fb5a6f2" +checksum = "0800eae8638a299eaa67476e1c6b6692922273e0f7939fd188fc861c837b9cd2" dependencies = [ "anyhow", - "bitflags", + "bitflags 2.9.0", "byteorder", "libc", "log", @@ -3008,14 +3014,13 @@ dependencies = [ [[package]] name = "netwatch" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901dbb408894af3df3fc51420ba0c6faf3a7d896077b797c39b7001e2f787bd" +version = "0.6.0" +source = "git+https://github.com/n0-computer/net-tools?branch=feat-multipath#b7ab98d4ff9cc947f2f084004b4cc2a979bb4d06" dependencies = [ "atomic-waker", "bytes", "cfg_aliases", - "derive_more 2.0.1", + "derive_more", "iroh-quinn-udp", "js-sys", "libc", @@ -3024,20 +3029,20 @@ dependencies = [ "nested_enum_utils", "netdev", "netlink-packet-core", - "netlink-packet-route 0.24.0", + "netlink-packet-route 0.23.0", "netlink-proto", "netlink-sys", "pin-project-lite", "serde", "snafu", - "socket2 0.6.0", + "socket2", "time", "tokio", "tokio-util", "tracing", "web-sys", - "windows", - "windows-result", + "windows 0.59.0", + "windows-result 0.3.2", "wmi", ] @@ -3136,24 +3141,23 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.4" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ "num_enum_derive", - "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.7.4" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -3165,6 +3169,12 @@ dependencies = [ "libc", ] +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + [[package]] name = "object" version = "0.36.7" @@ -3193,12 +3203,6 @@ dependencies = [ "portable-atomic", ] -[[package]] -name = "once_cell_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" - [[package]] name = "oorandom" version = "11.1.5" @@ -3231,9 +3235,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -3241,9 +3245,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", @@ -3291,9 +3295,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.8.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" +checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" dependencies = [ "memchr", "thiserror 2.0.12", @@ -3302,9 +3306,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" +checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" dependencies = [ "pest", "pest_generator", @@ -3312,23 +3316,24 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" +checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] name = "pest_meta" -version = "2.8.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" +checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" dependencies = [ + "once_cell", "pest", "sha2", ] @@ -3360,7 +3365,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -3377,9 +3382,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkarr" -version = "3.8.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a50f65a2b97031863fbdff2f085ba832360b4bef3106d1fcff9ab5bf4063fe" +checksum = "e32222ae3d617bf92414db29085f8a959a4515effce916e038e9399a335a0d6d" dependencies = [ "async-compat", "base32", @@ -3463,7 +3468,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -3500,19 +3505,19 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" [[package]] name = "portmapper" -version = "0.8.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f1975debe62a70557e42b9ff9466e4890cf9d3d156d296408a711f1c5f642b" +checksum = "2d82975dc029c00d566f4e0f61f567d31f0297a290cb5416b5580dd8b4b54ade" dependencies = [ "base64", "bytes", - "derive_more 2.0.1", + "derive_more", "futures-lite", "futures-util", "hyper-util", @@ -3522,11 +3527,11 @@ dependencies = [ "nested_enum_utils", "netwatch", "num_enum", - "rand 0.9.1", + "rand 0.8.5", "serde", "smallvec", "snafu", - "socket2 0.6.0", + "socket2", "time", "tokio", "tokio-util", @@ -3537,9 +3542,9 @@ dependencies = [ [[package]] name = "postcard" -version = "1.1.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c1de96e20f51df24ca73cafcc4690e044854d803259db27a00a461cb3b9d17a" +checksum = "170a2601f67cc9dba8edd8c4870b15f71a6a2dc196daec8c83f72b59dff628a8" dependencies = [ "cobs", "embedded-io 0.4.0", @@ -3551,22 +3556,13 @@ dependencies = [ [[package]] name = "postcard-derive" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f049d94cb6dda6938cc8a531d2898e7c08d71c6de63d8e67123cca6cdde2cc" +checksum = "0239fa9c1d225d4b7eb69925c25c5e082307a141e470573fbbe3a817ce6a7a37" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", -] - -[[package]] -name = "potential_utf" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" -dependencies = [ - "zerovec", + "syn 1.0.109", ] [[package]] @@ -3581,7 +3577,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy", + "zerocopy 0.8.25", ] [[package]] @@ -3658,17 +3654,17 @@ dependencies = [ [[package]] name = "proptest" -version = "1.7.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" +checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" dependencies = [ "bit-set", "bit-vec", - "bitflags", + "bitflags 2.9.0", "lazy_static", "num-traits", - "rand 0.9.1", - "rand_chacha 0.9.0", + "rand 0.8.5", + "rand_chacha 0.3.1", "rand_xorshift", "regex-syntax 0.8.5", "rusty-fork", @@ -3678,15 +3674,15 @@ dependencies = [ [[package]] name = "quanta" -version = "0.12.6" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7" +checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e" dependencies = [ "crossbeam-utils", "libc", "once_cell", "raw-cpuid", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi 0.11.0+wasi-snapshot-preview1", "web-sys", "winapi", ] @@ -3699,9 +3695,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quinn" -version = "0.11.8" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" +checksum = "c3bd15a6f2967aef83887dcb9fec0014580467e33720d073560cf015a5683012" dependencies = [ "bytes", "cfg_aliases", @@ -3710,7 +3706,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.5.10", + "socket2", "thiserror 2.0.12", "tokio", "tracing", @@ -3719,13 +3715,12 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.12" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" +checksum = "bcbafbbdbb0f638fe3f35f3c56739f77a8a1d070cb25603226c83339b391472b" dependencies = [ "bytes", - "getrandom 0.3.3", - "lru-slab", + "getrandom 0.3.2", "rand 0.9.1", "ring", "rustc-hash", @@ -3740,14 +3735,14 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.13" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970" +checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842" dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2", "tracing", "windows-sys 0.59.0", ] @@ -3773,9 +3768,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.3.0" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" [[package]] name = "rand" @@ -3833,16 +3828,16 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.2", ] [[package]] name = "rand_xorshift" -version = "0.4.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ - "rand_core 0.9.3", + "rand_core 0.6.4", ] [[package]] @@ -3851,7 +3846,7 @@ version = "11.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" dependencies = [ - "bitflags", + "bitflags 2.9.0", ] [[package]] @@ -3887,19 +3882,6 @@ dependencies = [ "yasna", ] -[[package]] -name = "rcgen" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49bc8ffa8a832eb1d7c8000337f8b0d2f4f2f5ec3cf4ddc26f125e3ad2451824" -dependencies = [ - "pem", - "ring", - "rustls-pki-types", - "time", - "yasna", -] - [[package]] name = "redb" version = "2.4.0" @@ -3911,11 +3893,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.13" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ - "bitflags", + "bitflags 2.9.0", ] [[package]] @@ -3998,9 +3980,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.22" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" dependencies = [ "base64", "bytes", @@ -4012,12 +3994,16 @@ dependencies = [ "hyper", "hyper-rustls", "hyper-util", + "ipnet", "js-sys", "log", + "mime", + "once_cell", "percent-encoding", "pin-project-lite", "quinn", "rustls", + "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", @@ -4027,21 +4013,21 @@ dependencies = [ "tokio-rustls", "tokio-util", "tower", - "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 1.0.1", + "webpki-roots 0.26.11", + "windows-registry", ] [[package]] name = "resolv-conf" -version = "0.7.4" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95325155c684b1c89f7765e30bc1c42e4a6da51ca513615660cb8a62ef9a88e3" +checksum = "fc7c8f7f733062b66dc1c63f9db168ac0b97a9210e247fa90fdc9ad08f51b302" [[package]] name = "ring" @@ -4059,9 +4045,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.25" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -4093,7 +4079,7 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ - "bitflags", + "bitflags 2.9.0", "errno", "libc", "linux-raw-sys", @@ -4102,9 +4088,8 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1" +version = "0.23.25" +source = "git+https://github.com/n0-computer/rustls?rev=be02113e7837df60953d02c2bdd0f4634fef3a80#be02113e7837df60953d02c2bdd0f4634fef3a80" dependencies = [ "log", "once_cell", @@ -4183,11 +4168,11 @@ dependencies = [ [[package]] name = "rustls-platform-verifier" -version = "0.5.3" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1" +checksum = "4937d110d34408e9e5ad30ba0b0ca3b6a8a390f8db3636db60144ac4fa792750" dependencies = [ - "core-foundation 0.10.1", + "core-foundation 0.10.0", "core-foundation-sys", "jni", "log", @@ -4210,9 +4195,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.4" +version = "0.103.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +checksum = "7149975849f1abb3832b246010ef62ccc80d3a76169517ada7188252b9cfb437" dependencies = [ "ring", "rustls-pki-types", @@ -4221,9 +4206,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "rusty-fork" @@ -4243,6 +4228,15 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "safe_arch" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" +dependencies = [ + "bytemuck", +] + [[package]] name = "salsa20" version = "0.10.2" @@ -4288,8 +4282,8 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ - "bitflags", - "core-foundation 0.10.1", + "bitflags 2.9.0", + "core-foundation 0.10.0", "core-foundation-sys", "libc", "security-framework-sys", @@ -4370,7 +4364,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -4397,9 +4391,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.0" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -4508,20 +4502,29 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee851d0e5e7af3721faea1843e8015e820a234f81fda3dea9247e15bac9a86a" dependencies = [ - "bitflags", + "bitflags 2.9.0", ] +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "slab" -version = "0.4.10" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "smol_str" @@ -4548,7 +4551,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -4561,16 +4564,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "socket2" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - [[package]] name = "spin" version = "0.9.8" @@ -4631,7 +4624,7 @@ dependencies = [ "proc-macro2", "quote", "struct_iterable_internal", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -4646,16 +4639,7 @@ version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ - "strum_macros 0.26.4", -] - -[[package]] -name = "strum" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" -dependencies = [ - "strum_macros 0.27.1", + "strum_macros", ] [[package]] @@ -4668,20 +4652,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.104", -] - -[[package]] -name = "strum_macros" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -4724,7 +4695,7 @@ dependencies = [ "parking_lot", "pnet_packet", "rand 0.9.1", - "socket2 0.5.10", + "socket2", "thiserror 1.0.69", "tokio", "tracing", @@ -4739,7 +4710,7 @@ dependencies = [ "acto", "hickory-proto", "rand 0.9.1", - "socket2 0.5.10", + "socket2", "thiserror 2.0.12", "tokio", "tracing", @@ -4758,9 +4729,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.104" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -4784,7 +4755,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -4793,7 +4764,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags", + "bitflags 2.9.0", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -4816,12 +4787,12 @@ checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" [[package]] name = "tempfile" -version = "3.20.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom 0.3.2", "once_cell", "rustix", "windows-sys 0.59.0", @@ -4862,7 +4833,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -4873,16 +4844,17 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] name = "thread_local" -version = "1.1.9" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", + "once_cell", ] [[package]] @@ -4921,9 +4893,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.1" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", "zerovec", @@ -4956,20 +4928,18 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.46.1" +version = "1.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" dependencies = [ "backtrace", "bytes", - "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "slab", - "socket2 0.5.10", + "socket2", "tokio-macros", "windows-sys 0.52.0", ] @@ -4982,7 +4952,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -5010,7 +4980,7 @@ dependencies = [ "num-bigint", "pem", "proc-macro2", - "rcgen 0.13.2", + "rcgen", "reqwest", "ring", "rustls", @@ -5046,7 +5016,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "hashbrown 0.15.4", + "hashbrown 0.15.3", "pin-project-lite", "tokio", ] @@ -5061,7 +5031,7 @@ dependencies = [ "bytes", "futures-core", "futures-sink", - "getrandom 0.3.3", + "getrandom 0.3.2", "http 1.3.1", "httparse", "rand 0.9.1", @@ -5075,59 +5045,44 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.2" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" dependencies = [ - "indexmap", "serde", "serde_spanned", - "toml_datetime 0.7.0", - "toml_parser", - "toml_writer", - "winnow", + "toml_datetime", + "toml_edit", ] [[package]] name = "toml_datetime" -version = "0.6.11" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" - -[[package]] -name = "toml_datetime" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.27" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", - "toml_datetime 0.6.11", - "winnow", -] - -[[package]] -name = "toml_parser" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97200572db069e74c512a14117b296ba0a80a30123fbbb5aa1f4a348f639ca30" -dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", "winnow", ] [[package]] -name = "toml_writer" -version = "1.0.2" +name = "toml_write" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" +checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" [[package]] name = "tower" @@ -5147,18 +5102,15 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" dependencies = [ - "bitflags", + "bitflags 2.9.0", "bytes", - "futures-util", "http 1.3.1", "http-body", - "iri-string", "pin-project-lite", - "tower", "tower-layer", "tower-service", "tracing", @@ -5206,20 +5158,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -5294,7 +5246,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568" dependencies = [ "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -5356,9 +5308,9 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.2.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" [[package]] name = "unicode-xid" @@ -5366,12 +5318,6 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" -[[package]] -name = "unit-prefix" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "323402cff2dd658f39ca17c789b502021b3f18707c91cdf22e3838e1b4023817" - [[package]] name = "universal-hash" version = "0.5.1" @@ -5400,6 +5346,12 @@ dependencies = [ "serde", ] +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -5414,13 +5366,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.17.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ - "getrandom 0.3.3", - "js-sys", - "wasm-bindgen", + "getrandom 0.3.2", ] [[package]] @@ -5465,9 +5415,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" @@ -5500,7 +5450,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", "wasm-bindgen-shared", ] @@ -5535,7 +5485,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5570,7 +5520,7 @@ checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] @@ -5612,14 +5562,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" dependencies = [ - "webpki-root-certs 1.0.1", + "webpki-root-certs 1.0.0", ] [[package]] name = "webpki-root-certs" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86138b15b2b7d561bc4469e77027b8dd005a43dc502e9031d1f5afc8ce1f280e" +checksum = "01a83f7e1a9f8712695c03eabe9ed3fbca0feff0152f33f12593e5a6303cb1a4" dependencies = [ "rustls-pki-types", ] @@ -5630,18 +5580,28 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.1", + "webpki-roots 1.0.0", ] [[package]] name = "webpki-roots" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8782dd5a41a24eed3a4f40b606249b3e236ca61adf1f25ea4d45c73de122b502" +checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" dependencies = [ "rustls-pki-types", ] +[[package]] +name = "wide" +version = "0.7.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03" +dependencies = [ + "bytemuck", + "safe_arch", +] + [[package]] name = "widestring" version = "1.2.0" @@ -5681,48 +5641,83 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.61.3" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ - "windows-collections", - "windows-core", - "windows-future", - "windows-link", - "windows-numerics", + "windows-core 0.58.0", + "windows-targets 0.52.6", ] [[package]] -name = "windows-collections" -version = "0.2.0" +name = "windows" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1" dependencies = [ - "windows-core", + "windows-core 0.59.0", + "windows-targets 0.53.0", ] [[package]] name = "windows-core" -version = "0.61.2" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", + "windows-targets 0.52.6", ] [[package]] -name = "windows-future" -version = "0.2.1" +name = "windows-core" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce" dependencies = [ - "windows-core", + "windows-implement 0.59.0", + "windows-interface 0.59.1", + "windows-result 0.3.2", + "windows-strings 0.3.1", + "windows-targets 0.53.0", +] + +[[package]] +name = "windows-core" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +dependencies = [ + "windows-implement 0.60.0", + "windows-interface 0.59.1", "windows-link", - "windows-threading", + "windows-result 0.3.2", + "windows-strings 0.4.0", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-implement" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", ] [[package]] @@ -5733,7 +5728,18 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", ] [[package]] @@ -5744,39 +5750,68 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] [[package]] name = "windows-link" -version = "0.1.3" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-registry" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +dependencies = [ + "windows-result 0.3.2", + "windows-strings 0.3.1", + "windows-targets 0.53.0", +] [[package]] -name = "windows-numerics" +name = "windows-result" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" dependencies = [ - "windows-core", - "windows-link", + "windows-targets 0.52.6", ] [[package]] name = "windows-result" -version = "0.3.4" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" dependencies = [ "windows-link", ] [[package]] name = "windows-strings" -version = "0.4.2" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" dependencies = [ "windows-link", ] @@ -5817,15 +5852,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.2", -] - [[package]] name = "windows-targets" version = "0.42.2" @@ -5874,9 +5900,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.2" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" dependencies = [ "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", @@ -5888,15 +5914,6 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] -[[package]] -name = "windows-threading" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" -dependencies = [ - "windows-link", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -6079,9 +6096,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.12" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] @@ -6102,35 +6119,41 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags", + "bitflags 2.9.0", ] [[package]] name = "wmi" -version = "0.17.2" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3de777dce4cbcdc661d5d18e78ce4b46a37adc2bb7c0078a556c7f07bcce2f" +checksum = "7787dacdd8e71cbc104658aade4009300777f9b5fda6a75f19145fedb8a18e71" dependencies = [ "chrono", "futures", "log", "serde", "thiserror 2.0.12", - "windows", - "windows-core", + "windows 0.59.0", + "windows-core 0.59.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + [[package]] name = "writeable" -version = "0.6.1" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "ws_stream_wasm" -version = "0.7.5" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c173014acad22e83f16403ee360115b38846fe754e735c5d9d3803fe70c6abc" +checksum = "7999f5f4217fe3818726b66257a4475f71e74ffd190776ad053fa159e50737f5" dependencies = [ "async_io_stream", "futures", @@ -6139,7 +6162,7 @@ dependencies = [ "pharos", "rustc_version", "send_wrapper", - "thiserror 2.0.12", + "thiserror 1.0.69", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -6164,9 +6187,9 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.27" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" +checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" [[package]] name = "xmltree" @@ -6194,9 +6217,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.0" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", @@ -6206,13 +6229,13 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", "synstructure", ] @@ -6224,22 +6247,42 @@ checksum = "2164e798d9e3d84ee2c91139ace54638059a3b23e361f5c11781c2c6459bde0f" [[package]] name = "zerocopy" -version = "0.8.26" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +dependencies = [ + "zerocopy-derive 0.8.25", ] [[package]] name = "zerocopy-derive" -version = "0.8.26" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", ] [[package]] @@ -6259,7 +6302,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", "synstructure", ] @@ -6269,22 +6312,11 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" -[[package]] -name = "zerotrie" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - [[package]] name = "zerovec" -version = "0.11.2" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" dependencies = [ "yoke", "zerofrom", @@ -6293,11 +6325,11 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.101", ] diff --git a/Cargo.toml b/Cargo.toml index 593f1d0ec73..d5d37a9b5d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,3 +40,8 @@ unexpected_cfgs = { level = "warn", check-cfg = ["cfg(iroh_docsrs)", "cfg(iroh_l [workspace.lints.clippy] unused-async = "warn" + + +[patch.crates-io] +rustls = { git = "https://github.com/n0-computer/rustls", rev = "be02113e7837df60953d02c2bdd0f4634fef3a80" } +netwatch = { git = "https://github.com/n0-computer/net-tools", branch = "feat-multipath" } diff --git a/iroh-relay/Cargo.toml b/iroh-relay/Cargo.toml index 9f7bc43c93b..8424cfeac94 100644 --- a/iroh-relay/Cargo.toml +++ b/iroh-relay/Cargo.toml @@ -42,8 +42,8 @@ postcard = { version = "1", default-features = false, features = [ "use-std", "experimental-derive", ] } -quinn = { package = "iroh-quinn", version = "0.14.0", default-features = false, features = ["rustls-ring"] } -quinn-proto = { package = "iroh-quinn-proto", version = "0.13.0" } +quinn = { package = "iroh-quinn", git = "https://github.com/n0-computer/quinn", branch = "multipath-quinn-0.11.x", default-features = false, features = ["rustls-ring"] } +quinn-proto = { package = "iroh-quinn-proto", git = "https://github.com/n0-computer/quinn", branch = "multipath-quinn-0.11.x" } rand = "0.8" reqwest = { version = "0.12", default-features = false, features = [ "rustls-tls", diff --git a/iroh/Cargo.toml b/iroh/Cargo.toml index ce02aa4d2d3..713e0ebb67f 100644 --- a/iroh/Cargo.toml +++ b/iroh/Cargo.toml @@ -48,9 +48,9 @@ pin-project = "1" pkarr = { version = "3.7", default-features = false, features = [ "relays", ] } -quinn = { package = "iroh-quinn", version = "0.14.0", default-features = false, features = ["rustls-ring"] } -quinn-proto = { package = "iroh-quinn-proto", version = "0.13.0" } -quinn-udp = { package = "iroh-quinn-udp", version = "0.5.7" } +quinn = { package = "iroh-quinn", git = "https://github.com/n0-computer/quinn", branch = "multipath-quinn-0.11.x", default-features = false, features = ["rustls-ring"] } +quinn-proto = { package = "iroh-quinn-proto", git = "https://github.com/n0-computer/quinn", branch = "multipath-quinn-0.11.x" } +quinn-udp = { package = "iroh-quinn-udp", git = "https://github.com/n0-computer/quinn", branch = "multipath-quinn-0.11.x" } rand = "0.8" reqwest = { version = "0.12", default-features = false, features = [ "rustls-tls", @@ -109,7 +109,7 @@ hickory-resolver = "0.25.1" igd-next = { version = "0.16", features = ["aio_tokio"] } netdev = { version = "0.36.0" } portmapper = { version = "0.8", default-features = false } -quinn = { package = "iroh-quinn", version = "0.14.0", default-features = false, features = ["runtime-tokio", "rustls-ring"] } +quinn = { package = "iroh-quinn", git = "https://github.com/n0-computer/quinn", branch = "multipath-quinn-0.11.x", default-features = false, features = ["runtime-tokio", "rustls-ring"] } tokio = { version = "1", features = [ "io-util", "macros", diff --git a/iroh/bench/Cargo.toml b/iroh/bench/Cargo.toml index 8070e6259a2..97c46065a7e 100644 --- a/iroh/bench/Cargo.toml +++ b/iroh/bench/Cargo.toml @@ -12,7 +12,7 @@ iroh = { path = ".." } iroh-metrics = "0.35" n0-future = "0.1.1" n0-snafu = "0.2.0" -quinn = { package = "iroh-quinn", version = "0.14" } +quinn = { package = "iroh-quinn", git = "https://github.com/n0-computer/quinn", branch = "multipath-quinn-0.11.x" } rand = "0.8" rcgen = "0.14" rustls = { version = "0.23", default-features = false, features = ["ring"] } From 7fe570da1d6064ad83eb5ea22aa094dd8a269044 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Mon, 7 Jul 2025 15:27:42 +0200 Subject: [PATCH 02/27] update iroh-quinn --- Cargo.lock | 10 +++++----- iroh/src/magicsock.rs | 20 ++++++++++++-------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cfaec28b630..aab319073b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2474,8 +2474,8 @@ dependencies = [ [[package]] name = "iroh-quinn" -version = "0.13.0" -source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#773fceabb27f1e56132198dd960d4bd1493e0ed0" +version = "0.14.0" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#e84f16856ea3ac79ed6c206b258d33a30d87834f" dependencies = [ "bytes", "cfg_aliases", @@ -2494,7 +2494,7 @@ dependencies = [ [[package]] name = "iroh-quinn-proto" version = "0.13.0" -source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#773fceabb27f1e56132198dd960d4bd1493e0ed0" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#e84f16856ea3ac79ed6c206b258d33a30d87834f" dependencies = [ "bytes", "fastbloom", @@ -2516,7 +2516,7 @@ dependencies = [ [[package]] name = "iroh-quinn-udp" version = "0.5.12" -source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#773fceabb27f1e56132198dd960d4bd1493e0ed0" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#e84f16856ea3ac79ed6c206b258d33a30d87834f" dependencies = [ "cfg_aliases", "libc", @@ -5630,7 +5630,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index ba6f50f5073..fb2d0c2eec2 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -2624,10 +2624,12 @@ mod tests { info!("stats: {:#?}", stats); // TODO: ensure panics in this function are reported ok if matches!(loss, ExpectedLoss::AlmostNone) { - assert!( - stats.path.lost_packets < 10, - "[receiver] should not loose many packets", - ); + for (id, path) in &stats.paths { + assert!( + path.lost_packets < 10, + "[receiver] path {id:?} should not loose many packets", + ); + } } info!("close"); @@ -2675,10 +2677,12 @@ mod tests { let stats = conn.stats(); info!("stats: {:#?}", stats); if matches!(loss, ExpectedLoss::AlmostNone) { - assert!( - stats.path.lost_packets < 10, - "[sender] should not loose many packets", - ); + for (id, path) in &stats.paths { + assert!( + path.lost_packets < 10, + "[sender] path {id:?} should not loose many packets", + ); + } } info!("close"); From 2f469ac0e21b931ebe75699832d07638f59c85bd Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 8 Jul 2025 11:44:22 +0200 Subject: [PATCH 03/27] start opening paths --- iroh/src/endpoint.rs | 11 +++++++ iroh/src/magicsock.rs | 76 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 79 insertions(+), 8 deletions(-) diff --git a/iroh/src/endpoint.rs b/iroh/src/endpoint.rs index cc9a1cc1fd6..f8545edb212 100644 --- a/iroh/src/endpoint.rs +++ b/iroh/src/endpoint.rs @@ -1771,6 +1771,17 @@ impl Future for Connecting { Poll::Ready(Err(err)) => Poll::Ready(Err(err)), Poll::Ready(Ok(inner)) => { let conn = Connection { inner }; + + // Grab the remote identity and register this connection + + if let Some(remote) = *this.remote_node_id { + let weak_handle = conn.inner.weak_handle(); + this.ep.msock.register_connection(remote, weak_handle); + } else if let Ok(remote) = conn.remote_node_id() { + let weak_handle = conn.inner.weak_handle(); + this.ep.msock.register_connection(remote, weak_handle); + } + try_send_rtt_msg(&conn, this.ep, *this.remote_node_id); Poll::Ready(Ok(conn)) } diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index fb2d0c2eec2..f8454cfc1c8 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -43,7 +43,7 @@ use nested_enum_utils::common_fields; use netwatch::netmon; #[cfg(not(wasm_browser))] use netwatch::{UdpSocket, ip::LocalAddresses}; -use quinn::{AsyncUdpSocket, ServerConfig}; +use quinn::{AsyncUdpSocket, ServerConfig, WeakConnectionHandle}; use rand::Rng; use smallvec::SmallVec; use snafu::{ResultExt, Snafu}; @@ -195,6 +195,9 @@ pub(crate) struct MagicSock { ipv6_reported: Arc, /// Tracks the networkmap node entity for each node discovery key. node_map: NodeMap, + /// Tracks existing connections + connection_map: ConnectionMap, + /// Tracks the mapped IP addresses ip_mapped_addrs: IpMappedAddresses, /// Local addresses @@ -221,6 +224,22 @@ pub(crate) struct MagicSock { pub(crate) metrics: EndpointMetrics, } +#[derive(Default, Debug)] +struct ConnectionMap { + map: std::sync::Mutex>>, +} + +impl ConnectionMap { + fn insert(&self, remote: NodeId, handle: WeakConnectionHandle) { + self.map + .lock() + .expect("poisoned") + .entry(remote) + .or_default() + .push(handle); + } +} + #[allow(missing_docs)] #[common_fields({ backtrace: Option, @@ -271,6 +290,10 @@ impl MagicSock { self.local_addrs_watch.clone().get() } + pub(crate) fn register_connection(&self, remote: NodeId, conn: WeakConnectionHandle) { + self.connection_map.insert(remote, conn); + } + #[cfg(not(wasm_browser))] fn ip_bind_addrs(&self) -> &[SocketAddr] { &self.ip_bind_addrs @@ -393,8 +416,45 @@ impl MagicSock { } } if !addr.is_empty() { + // Add addr to the internal NodeMap self.node_map - .add_node_addr(addr, source, &self.metrics.magicsock); + .add_node_addr(addr.clone(), source, &self.metrics.magicsock); + + // Add paths to the existing connections + { + let mut map = self.connection_map.map.lock().expect("poisoned"); + let mut to_delete = Vec::new(); + if let Some(conns) = map.get_mut(&addr.node_id) { + for (i, conn) in conns.into_iter().enumerate() { + if let Some(conn) = conn.upgrade() { + for addr in addr.direct_addresses() { + let conn = conn.clone(); + let addr = *addr; + task::spawn(async move { + if let Err(err) = conn + .open_path(addr, quinn_proto::PathStatus::Available) + .await + { + warn!("failed to open path {:?}", err); + } + }); + } + // TODO: add relay path as mapped addr + } else { + to_delete.push(i); + } + } + // cleanup dead connections + let mut i = 0; + conns.retain(|_| { + let remove = to_delete.contains(&i); + i += 1; + + !remove + }); + } + } + Ok(()) } else if pruned != 0 { Err(EmptyPrunedSnafu { pruned }.build()) @@ -505,8 +565,8 @@ impl MagicSock { let mut active_paths = SmallVec::<[_; 3]>::new(); match MappedAddr::from(transmit.destination) { - MappedAddr::None(dest) => { - error!(%dest, "Cannot convert to a mapped address."); + MappedAddr::None(addr) => { + active_paths.push(transports::Addr::from(addr)); } MappedAddr::NodeId(dest) => { trace!( @@ -523,15 +583,14 @@ impl MagicSock { self.ipv6_reported.load(Ordering::Relaxed), &self.metrics.magicsock, ) { - Some((node_id, udp_addr, relay_url, ping_actions)) => { + Some((node_id, _udp_addr, relay_url, ping_actions)) => { if !ping_actions.is_empty() { self.actor_sender .try_send(ActorMessage::PingActions(ping_actions)) .ok(); } - if let Some(addr) = udp_addr { - active_paths.push(transports::Addr::from(addr)); - } + // NodeId mapped addrs are only used for relays, currently. + // IP based addrs will have been added as individual paths if let Some(url) = relay_url { active_paths.push(transports::Addr::Relay(url, node_id)); } @@ -1298,6 +1357,7 @@ impl Handle { actor_sender: actor_sender.clone(), ipv6_reported, node_map, + connection_map: Default::default(), ip_mapped_addrs: ip_mapped_addrs.clone(), discovery, discovery_user_data: RwLock::new(discovery_user_data), From 870716feba7e9b1fafef2685c0e73d95e311406e Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 8 Jul 2025 12:15:22 +0200 Subject: [PATCH 04/27] add more paths --- Cargo.lock | 8 ++--- Cargo.toml | 9 +++++ iroh/src/endpoint.rs | 8 ++--- iroh/src/magicsock.rs | 78 +++++++++++++++++++++++++------------------ 4 files changed, 61 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aab319073b6..a29794212ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2475,7 +2475,7 @@ dependencies = [ [[package]] name = "iroh-quinn" version = "0.14.0" -source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#e84f16856ea3ac79ed6c206b258d33a30d87834f" +source = "git+https://github.com//n0-computer/quinn?branch=multipath-misc#0d929df5f69ddc660c8ce81e9c348af7972862db" dependencies = [ "bytes", "cfg_aliases", @@ -2494,7 +2494,7 @@ dependencies = [ [[package]] name = "iroh-quinn-proto" version = "0.13.0" -source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#e84f16856ea3ac79ed6c206b258d33a30d87834f" +source = "git+https://github.com//n0-computer/quinn?branch=multipath-misc#0d929df5f69ddc660c8ce81e9c348af7972862db" dependencies = [ "bytes", "fastbloom", @@ -2516,14 +2516,14 @@ dependencies = [ [[package]] name = "iroh-quinn-udp" version = "0.5.12" -source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#e84f16856ea3ac79ed6c206b258d33a30d87834f" +source = "git+https://github.com//n0-computer/quinn?branch=multipath-misc#0d929df5f69ddc660c8ce81e9c348af7972862db" dependencies = [ "cfg_aliases", "libc", "once_cell", "socket2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d5d37a9b5d5..ac71aecc026 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,3 +45,12 @@ unused-async = "warn" [patch.crates-io] rustls = { git = "https://github.com/n0-computer/rustls", rev = "be02113e7837df60953d02c2bdd0f4634fef3a80" } netwatch = { git = "https://github.com/n0-computer/net-tools", branch = "feat-multipath" } + +[patch."https://github.com/n0-computer/quinn"] +# iroh-quinn = { path = "../iroh-quinn/quinn" } +# iroh-quinn-proto = { path = "../iroh-quinn/quinn-proto" } +# iroh-quinn-udp = { path = "../iroh-quinn/quinn-udp" } + +iroh-quinn = { git = "https://github.com//n0-computer/quinn", branch = "multipath-misc" } +iroh-quinn-proto = { git = "https://github.com//n0-computer/quinn", branch = "multipath-misc" } +iroh-quinn-udp = { git = "https://github.com//n0-computer/quinn", branch = "multipath-misc" } diff --git a/iroh/src/endpoint.rs b/iroh/src/endpoint.rs index f8545edb212..3e1a7e90e6b 100644 --- a/iroh/src/endpoint.rs +++ b/iroh/src/endpoint.rs @@ -772,15 +772,13 @@ impl Endpoint { client_config }; + // TODO: race available addresses, this is currently only using the relay addr to connect + let dest_addr = mapped_addr.private_socket_addr(); let server_name = &tls::name::encode(node_id); let connect = self .msock .endpoint() - .connect_with( - client_config, - mapped_addr.private_socket_addr(), - server_name, - ) + .connect_with(client_config, dest_addr, server_name) .context(QuinnSnafu)?; Ok(Connecting { diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index f8454cfc1c8..d3f4bfe51bd 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -421,39 +421,7 @@ impl MagicSock { .add_node_addr(addr.clone(), source, &self.metrics.magicsock); // Add paths to the existing connections - { - let mut map = self.connection_map.map.lock().expect("poisoned"); - let mut to_delete = Vec::new(); - if let Some(conns) = map.get_mut(&addr.node_id) { - for (i, conn) in conns.into_iter().enumerate() { - if let Some(conn) = conn.upgrade() { - for addr in addr.direct_addresses() { - let conn = conn.clone(); - let addr = *addr; - task::spawn(async move { - if let Err(err) = conn - .open_path(addr, quinn_proto::PathStatus::Available) - .await - { - warn!("failed to open path {:?}", err); - } - }); - } - // TODO: add relay path as mapped addr - } else { - to_delete.push(i); - } - } - // cleanup dead connections - let mut i = 0; - conns.retain(|_| { - let remove = to_delete.contains(&i); - i += 1; - - !remove - }); - } - } + self.add_paths(addr); Ok(()) } else if pruned != 0 { @@ -463,6 +431,41 @@ impl MagicSock { } } + /// Adds all available addresses in the given `addr` as paths + fn add_paths(&self, addr: NodeAddr) { + let mut map = self.connection_map.map.lock().expect("poisoned"); + let mut to_delete = Vec::new(); + if let Some(conns) = map.get_mut(&addr.node_id) { + for (i, conn) in conns.into_iter().enumerate() { + if let Some(conn) = conn.upgrade() { + for addr in addr.direct_addresses() { + let conn = conn.clone(); + let addr = *addr; + task::spawn(async move { + if let Err(err) = conn + .open_path(addr, quinn_proto::PathStatus::Available) + .await + { + warn!("failed to open path {:?}", err); + } + }); + } + // TODO: add relay path as mapped addr + } else { + to_delete.push(i); + } + } + // cleanup dead connections + let mut i = 0; + conns.retain(|_| { + let remove = to_delete.contains(&i); + i += 1; + + !remove + }); + } + } + /// Stores a new set of direct addresses. /// /// If the direct addresses have changed from the previous set, they are published to @@ -873,9 +876,18 @@ impl MagicSock { return; } } + + // Add new addresses as paths + self.add_paths(NodeAddr { + node_id: sender, + relay_url: None, + direct_addresses: cm.my_numbers.iter().copied().collect(), + }); + let ping_actions = self.node_map .handle_call_me_maybe(sender, cm, &self.metrics.magicsock); + for action in ping_actions { match action { PingAction::SendCallMeMaybe { .. } => { From 346a7c2dafa1ce7eec5537b9125ede6ff53049b6 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 8 Jul 2025 12:36:22 +0200 Subject: [PATCH 05/27] set keep alive and idle timeouts for new paths --- iroh/src/magicsock.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index d3f4bfe51bd..ccddd91c01c 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -442,11 +442,20 @@ impl MagicSock { let conn = conn.clone(); let addr = *addr; task::spawn(async move { - if let Err(err) = conn + match conn .open_path(addr, quinn_proto::PathStatus::Available) .await { - warn!("failed to open path {:?}", err); + Ok(path) => { + path.set_max_idle_timeout(Some( + ENDPOINTS_FRESH_ENOUGH_DURATION, + )) + .ok(); + path.set_keep_alive_interval(Some(HEARTBEAT_INTERVAL)).ok(); + } + Err(err) => { + warn!("failed to open path {:?}", err); + } } }); } From 68b1769dc11e0101c102a88bbeb963fe3efa9cef Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 9 Jul 2025 17:34:32 +0200 Subject: [PATCH 06/27] insert relay path --- Cargo.lock | 6 +++--- Cargo.toml | 6 +++--- iroh/src/magicsock.rs | 23 ++++++++++++++++++++++- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a29794212ff..f8b2e3e6c21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2475,7 +2475,7 @@ dependencies = [ [[package]] name = "iroh-quinn" version = "0.14.0" -source = "git+https://github.com//n0-computer/quinn?branch=multipath-misc#0d929df5f69ddc660c8ce81e9c348af7972862db" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#70e28875923db76f8dfbf4f058e682d56e6daea1" dependencies = [ "bytes", "cfg_aliases", @@ -2494,7 +2494,7 @@ dependencies = [ [[package]] name = "iroh-quinn-proto" version = "0.13.0" -source = "git+https://github.com//n0-computer/quinn?branch=multipath-misc#0d929df5f69ddc660c8ce81e9c348af7972862db" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#70e28875923db76f8dfbf4f058e682d56e6daea1" dependencies = [ "bytes", "fastbloom", @@ -2516,7 +2516,7 @@ dependencies = [ [[package]] name = "iroh-quinn-udp" version = "0.5.12" -source = "git+https://github.com//n0-computer/quinn?branch=multipath-misc#0d929df5f69ddc660c8ce81e9c348af7972862db" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#70e28875923db76f8dfbf4f058e682d56e6daea1" dependencies = [ "cfg_aliases", "libc", diff --git a/Cargo.toml b/Cargo.toml index ac71aecc026..edb51ff85b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,6 @@ netwatch = { git = "https://github.com/n0-computer/net-tools", branch = "feat-mu # iroh-quinn-proto = { path = "../iroh-quinn/quinn-proto" } # iroh-quinn-udp = { path = "../iroh-quinn/quinn-udp" } -iroh-quinn = { git = "https://github.com//n0-computer/quinn", branch = "multipath-misc" } -iroh-quinn-proto = { git = "https://github.com//n0-computer/quinn", branch = "multipath-misc" } -iroh-quinn-udp = { git = "https://github.com//n0-computer/quinn", branch = "multipath-misc" } +# iroh-quinn = { git = "https://github.com//n0-computer/quinn", branch = "multipath-misc" } +# iroh-quinn-proto = { git = "https://github.com//n0-computer/quinn", branch = "multipath-misc" } +# iroh-quinn-udp = { git = "https://github.com//n0-computer/quinn", branch = "multipath-misc" } diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index ccddd91c01c..b2e9be26491 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -459,7 +459,28 @@ impl MagicSock { } }); } - // TODO: add relay path as mapped addr + // Insert the relay addr + if let Some(addr) = self.get_mapping_addr(addr.node_id) { + let conn = conn.clone(); + let addr = addr.private_socket_addr(); + task::spawn(async move { + match conn + .open_path(addr, quinn_proto::PathStatus::Available) + .await + { + Ok(path) => { + path.set_max_idle_timeout(Some( + ENDPOINTS_FRESH_ENOUGH_DURATION, + )) + .ok(); + path.set_keep_alive_interval(Some(HEARTBEAT_INTERVAL)).ok(); + } + Err(err) => { + warn!("failed to open path {:?}", err); + } + } + }); + } } else { to_delete.push(i); } From 0eb3fde19268ff71988721aef992457c2d6e3f53 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 11 Jul 2025 12:35:28 +0200 Subject: [PATCH 07/27] set relay path as backup --- iroh/src/magicsock.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index b2e9be26491..f47bcdc996e 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -464,16 +464,11 @@ impl MagicSock { let conn = conn.clone(); let addr = addr.private_socket_addr(); task::spawn(async move { - match conn - .open_path(addr, quinn_proto::PathStatus::Available) - .await - { + match conn.open_path(addr, quinn_proto::PathStatus::Backup).await { Ok(path) => { - path.set_max_idle_timeout(Some( - ENDPOINTS_FRESH_ENOUGH_DURATION, - )) - .ok(); - path.set_keep_alive_interval(Some(HEARTBEAT_INTERVAL)).ok(); + // Keep the relay path open + path.set_max_idle_timeout(None).ok(); + path.set_keep_alive_interval(None).ok(); } Err(err) => { warn!("failed to open path {:?}", err); From 79ec17f4c668bcd61244ec472c57d2b5e0fbad3a Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 11 Jul 2025 13:25:33 +0200 Subject: [PATCH 08/27] start removing ping logic from the node_map --- Cargo.lock | 402 +++++--- iroh/src/magicsock.rs | 129 +-- iroh/src/magicsock/node_map.rs | 117 +-- iroh/src/magicsock/node_map/node_state.rs | 1036 ++++++--------------- iroh/src/magicsock/node_map/path_state.rs | 100 +- iroh/src/magicsock/node_map/udp_paths.rs | 3 +- 6 files changed, 568 insertions(+), 1219 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f8b2e3e6c21..c3a6558f70f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -451,12 +451,6 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.9.0" @@ -691,15 +685,15 @@ dependencies = [ [[package]] name = "console" -version = "0.15.11" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +checksum = "2e09ced7ebbccb63b4c65413d821f2e00ce54c5ca4514ddc6b3c892fdbcbc69d" dependencies = [ "encode_unicode", "libc", "once_cell", "unicode-width", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1010,7 +1004,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" dependencies = [ - "derive_more-impl", + "derive_more-impl 1.0.0", +] + +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl 2.0.1", ] [[package]] @@ -1025,6 +1028,18 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", + "unicode-xid", +] + [[package]] name = "diatomic-waker" version = "0.2.3" @@ -2017,7 +2032,7 @@ dependencies = [ "hyper", "libc", "pin-project-lite", - "socket2", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -2035,7 +2050,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.61.0", + "windows-core 0.59.0", ] [[package]] @@ -2219,15 +2234,15 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.11" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" +checksum = "70a646d946d06bedbbc4cac4c218acf4bbf2d87757a784857025f4d447e4e1cd" dependencies = [ "console", - "number_prefix", "portable-atomic", "tokio", "unicode-width", + "unit-prefix", "web-time", ] @@ -2258,7 +2273,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2", + "socket2 0.5.10", "widestring", "windows-sys 0.48.0", "winreg", @@ -2275,7 +2290,7 @@ dependencies = [ [[package]] name = "iroh" -version = "0.90.0" +version = "0.91.0" dependencies = [ "aead", "axum", @@ -2287,7 +2302,7 @@ dependencies = [ "crypto_box", "data-encoding", "der", - "derive_more", + "derive_more 2.0.1", "ed25519-dalek", "futures-buffered", "futures-util", @@ -2301,7 +2316,7 @@ dependencies = [ "iroh-metrics", "iroh-quinn", "iroh-quinn-proto", - "iroh-quinn-udp", + "iroh-quinn-udp 0.5.12", "iroh-relay", "n0-future", "n0-snafu", @@ -2327,7 +2342,7 @@ dependencies = [ "smallvec", "snafu", "spki", - "strum", + "strum 0.27.2", "stun-rs", "surge-ping", "swarm-discovery", @@ -2348,11 +2363,11 @@ dependencies = [ [[package]] name = "iroh-base" -version = "0.90.0" +version = "0.91.0" dependencies = [ "curve25519-dalek", "data-encoding", - "derive_more", + "derive_more 2.0.1", "ed25519-dalek", "n0-snafu", "nested_enum_utils", @@ -2369,7 +2384,7 @@ dependencies = [ [[package]] name = "iroh-bench" -version = "0.90.0" +version = "0.91.0" dependencies = [ "bytes", "clap", @@ -2379,9 +2394,8 @@ dependencies = [ "iroh-quinn", "n0-future", "n0-snafu", - "n0-watcher", "rand 0.8.5", - "rcgen", + "rcgen 0.14.3", "rustls", "tokio", "tracing", @@ -2390,7 +2404,7 @@ dependencies = [ [[package]] name = "iroh-dns-server" -version = "0.90.0" +version = "0.91.0" dependencies = [ "async-trait", "axum", @@ -2400,7 +2414,7 @@ dependencies = [ "clap", "criterion", "data-encoding", - "derive_more", + "derive_more 2.0.1", "dirs-next", "governor", "hickory-resolver", @@ -2416,7 +2430,7 @@ dependencies = [ "pkarr", "rand 0.8.5", "rand_chacha 0.3.1", - "rcgen", + "rcgen 0.13.2", "redb", "regex", "rustls", @@ -2424,7 +2438,7 @@ dependencies = [ "serde", "snafu", "struct_iterable", - "strum", + "strum 0.26.3", "tokio", "tokio-rustls", "tokio-rustls-acme", @@ -2475,16 +2489,16 @@ dependencies = [ [[package]] name = "iroh-quinn" version = "0.14.0" -source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#70e28875923db76f8dfbf4f058e682d56e6daea1" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#e84f16856ea3ac79ed6c206b258d33a30d87834f" dependencies = [ "bytes", "cfg_aliases", "iroh-quinn-proto", - "iroh-quinn-udp", + "iroh-quinn-udp 0.5.12", "pin-project-lite", "rustc-hash", "rustls", - "socket2", + "socket2 0.5.10", "thiserror 2.0.12", "tokio", "tracing", @@ -2494,7 +2508,7 @@ dependencies = [ [[package]] name = "iroh-quinn-proto" version = "0.13.0" -source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#70e28875923db76f8dfbf4f058e682d56e6daea1" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#e84f16856ea3ac79ed6c206b258d33a30d87834f" dependencies = [ "bytes", "fastbloom", @@ -2513,22 +2527,36 @@ dependencies = [ "web-time", ] +[[package]] +name = "iroh-quinn-udp" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c53afaa1049f7c83ea1331f5ebb9e6ebc5fdd69c468b7a22dd598b02c9bcc973" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2 0.5.10", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "iroh-quinn-udp" version = "0.5.12" -source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#70e28875923db76f8dfbf4f058e682d56e6daea1" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#140fdf97bfb706b1cac591b9711818c5df8012f4" dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2", + "socket2 0.5.10", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "iroh-relay" -version = "0.90.0" +version = "0.91.0" dependencies = [ "ahash", "blake3", @@ -2538,7 +2566,7 @@ dependencies = [ "crypto_box", "dashmap", "data-encoding", - "derive_more", + "derive_more 2.0.1", "getrandom 0.3.2", "governor", "hickory-proto", @@ -2562,7 +2590,7 @@ dependencies = [ "proptest", "rand 0.8.5", "rand_chacha 0.3.1", - "rcgen", + "rcgen 0.14.3", "regex", "reloadable-state", "reqwest", @@ -2578,7 +2606,7 @@ dependencies = [ "sha1", "simdutf8", "snafu", - "strum", + "strum 0.27.2", "time", "tokio", "tokio-rustls", @@ -2677,7 +2705,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.9.0", + "bitflags", "libc", ] @@ -2866,7 +2894,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7bb0e5d99e681ab3c938842b96fcb41bf8a7bb4bfdb11ccbd653a7e83e06c794" dependencies = [ "cfg_aliases", - "derive_more", + "derive_more 1.0.0", "futures-buffered", "futures-lite", "futures-util", @@ -2895,11 +2923,11 @@ dependencies = [ [[package]] name = "n0-watcher" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f216d4ebc5fcf9548244803cbb93f488a2ae160feba3706cd17040d69cf7a368" +checksum = "c31462392a10d5ada4b945e840cbec2d5f3fee752b96c4b33eb41414d8f45c2a" dependencies = [ - "derive_more", + "derive_more 1.0.0", "n0-future", "snafu", ] @@ -2918,19 +2946,19 @@ dependencies = [ [[package]] name = "netdev" -version = "0.31.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f901362e84cd407be6f8cd9d3a46bccf09136b095792785401ea7d283c79b91d" +checksum = "862209dce034f82a44c95ce2b5183730d616f2a68746b9c1959aa2572e77c0a1" dependencies = [ "dlopen2", "ipnet", "libc", "netlink-packet-core", - "netlink-packet-route 0.17.1", + "netlink-packet-route 0.22.0", "netlink-sys", "once_cell", "system-configuration", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2946,26 +2974,27 @@ dependencies = [ [[package]] name = "netlink-packet-route" -version = "0.17.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053998cea5a306971f88580d0829e90f270f940befd7cf928da179d4187a5a66" +checksum = "fc0e7987b28514adf555dc1f9a5c30dfc3e50750bbaffb1aec41ca7b23dcd8e4" dependencies = [ "anyhow", - "bitflags 1.3.2", + "bitflags", "byteorder", "libc", + "log", "netlink-packet-core", "netlink-packet-utils", ] [[package]] name = "netlink-packet-route" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0800eae8638a299eaa67476e1c6b6692922273e0f7939fd188fc861c837b9cd2" +checksum = "56d83370a96813d7c977f8b63054f1162df6e5784f1c598d689236564fb5a6f2" dependencies = [ "anyhow", - "bitflags 2.9.0", + "bitflags", "byteorder", "libc", "log", @@ -3014,14 +3043,15 @@ dependencies = [ [[package]] name = "netwatch" -version = "0.6.0" -source = "git+https://github.com/n0-computer/net-tools?branch=feat-multipath#b7ab98d4ff9cc947f2f084004b4cc2a979bb4d06" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901dbb408894af3df3fc51420ba0c6faf3a7d896077b797c39b7001e2f787bd" dependencies = [ "atomic-waker", "bytes", "cfg_aliases", - "derive_more", - "iroh-quinn-udp", + "derive_more 2.0.1", + "iroh-quinn-udp 0.5.7", "js-sys", "libc", "n0-future", @@ -3029,20 +3059,20 @@ dependencies = [ "nested_enum_utils", "netdev", "netlink-packet-core", - "netlink-packet-route 0.23.0", + "netlink-packet-route 0.24.0", "netlink-proto", "netlink-sys", "pin-project-lite", "serde", "snafu", - "socket2", + "socket2 0.6.0", "time", "tokio", "tokio-util", "tracing", "web-sys", - "windows 0.59.0", - "windows-result 0.3.2", + "windows 0.61.3", + "windows-result 0.3.4", "wmi", ] @@ -3169,12 +3199,6 @@ dependencies = [ "libc", ] -[[package]] -name = "number_prefix" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" - [[package]] name = "object" version = "0.36.7" @@ -3511,13 +3535,13 @@ checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" [[package]] name = "portmapper" -version = "0.6.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d82975dc029c00d566f4e0f61f567d31f0297a290cb5416b5580dd8b4b54ade" +checksum = "62f1975debe62a70557e42b9ff9466e4890cf9d3d156d296408a711f1c5f642b" dependencies = [ "base64", "bytes", - "derive_more", + "derive_more 2.0.1", "futures-lite", "futures-util", "hyper-util", @@ -3527,11 +3551,11 @@ dependencies = [ "nested_enum_utils", "netwatch", "num_enum", - "rand 0.8.5", + "rand 0.9.1", "serde", "smallvec", "snafu", - "socket2", + "socket2 0.6.0", "time", "tokio", "tokio-util", @@ -3660,7 +3684,7 @@ checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.9.0", + "bitflags", "lazy_static", "num-traits", "rand 0.8.5", @@ -3706,7 +3730,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2", + "socket2 0.5.10", "thiserror 2.0.12", "tokio", "tracing", @@ -3742,7 +3766,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2", + "socket2 0.5.10", "tracing", "windows-sys 0.59.0", ] @@ -3846,7 +3870,7 @@ version = "11.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" dependencies = [ - "bitflags 2.9.0", + "bitflags", ] [[package]] @@ -3882,6 +3906,19 @@ dependencies = [ "yasna", ] +[[package]] +name = "rcgen" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0068c5b3cab1d4e271e0bb6539c87563c43411cad90b057b15c79958fbeb41f7" +dependencies = [ + "pem", + "ring", + "rustls-pki-types", + "time", + "yasna", +] + [[package]] name = "redb" version = "2.4.0" @@ -3897,7 +3934,7 @@ version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ - "bitflags 2.9.0", + "bitflags", ] [[package]] @@ -4079,7 +4116,7 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ - "bitflags 2.9.0", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -4282,7 +4319,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ - "bitflags 2.9.0", + "bitflags", "core-foundation 0.10.0", "core-foundation-sys", "libc", @@ -4391,9 +4428,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.8" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" dependencies = [ "serde", ] @@ -4502,7 +4539,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee851d0e5e7af3721faea1843e8015e820a234f81fda3dea9247e15bac9a86a" dependencies = [ - "bitflags 2.9.0", + "bitflags", ] [[package]] @@ -4564,6 +4601,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "spin" version = "0.9.8" @@ -4639,7 +4686,16 @@ version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ - "strum_macros", + "strum_macros 0.26.4", +] + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros 0.27.2", ] [[package]] @@ -4655,6 +4711,18 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "stun-rs" version = "0.1.11" @@ -4695,7 +4763,7 @@ dependencies = [ "parking_lot", "pnet_packet", "rand 0.9.1", - "socket2", + "socket2 0.5.10", "thiserror 1.0.69", "tokio", "tracing", @@ -4710,7 +4778,7 @@ dependencies = [ "acto", "hickory-proto", "rand 0.9.1", - "socket2", + "socket2 0.5.10", "thiserror 2.0.12", "tokio", "tracing", @@ -4764,7 +4832,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.0", + "bitflags", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -4939,7 +5007,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.10", "tokio-macros", "windows-sys 0.52.0", ] @@ -4980,7 +5048,7 @@ dependencies = [ "num-bigint", "pem", "proc-macro2", - "rcgen", + "rcgen 0.13.2", "reqwest", "ring", "rustls", @@ -5045,14 +5113,17 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.22" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +checksum = "41ae868b5a0f67631c14589f7e250c1ea2c574ee5ba21c6c8dd4b1485705a5a1" dependencies = [ + "indexmap", "serde", "serde_spanned", - "toml_datetime", - "toml_edit", + "toml_datetime 0.7.0", + "toml_parser", + "toml_writer", + "winnow", ] [[package]] @@ -5060,6 +5131,12 @@ name = "toml_datetime" version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" + +[[package]] +name = "toml_datetime" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" dependencies = [ "serde", ] @@ -5071,18 +5148,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "toml_write", + "toml_datetime 0.6.9", "winnow", ] [[package]] -name = "toml_write" -version = "0.1.1" +name = "toml_parser" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" +checksum = "97200572db069e74c512a14117b296ba0a80a30123fbbb5aa1f4a348f639ca30" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" [[package]] name = "tower" @@ -5106,7 +5189,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" dependencies = [ - "bitflags 2.9.0", + "bitflags", "bytes", "http 1.3.1", "http-body", @@ -5318,6 +5401,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unit-prefix" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "323402cff2dd658f39ca17c789b502021b3f18707c91cdf22e3838e1b4023817" + [[package]] name = "universal-hash" version = "0.5.1" @@ -5630,7 +5719,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -5651,12 +5740,24 @@ dependencies = [ [[package]] name = "windows" -version = "0.59.0" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ - "windows-core 0.59.0", - "windows-targets 0.53.0", + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", ] [[package]] @@ -5680,22 +5781,33 @@ checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce" dependencies = [ "windows-implement 0.59.0", "windows-interface 0.59.1", - "windows-result 0.3.2", + "windows-result 0.3.4", "windows-strings 0.3.1", - "windows-targets 0.53.0", + "windows-targets 0.53.3", ] [[package]] name = "windows-core" -version = "0.61.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement 0.60.0", "windows-interface 0.59.1", "windows-link", - "windows-result 0.3.2", - "windows-strings 0.4.0", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link", + "windows-threading", ] [[package]] @@ -5755,9 +5867,19 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link", +] [[package]] name = "windows-registry" @@ -5765,9 +5887,9 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ - "windows-result 0.3.2", + "windows-result 0.3.4", "windows-strings 0.3.1", - "windows-targets 0.53.0", + "windows-targets 0.53.3", ] [[package]] @@ -5781,9 +5903,9 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ "windows-link", ] @@ -5809,9 +5931,9 @@ dependencies = [ [[package]] name = "windows-strings" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ "windows-link", ] @@ -5852,6 +5974,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -5900,10 +6031,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.0" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ + "windows-link", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -5914,6 +6046,15 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -6119,22 +6260,22 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.0", + "bitflags", ] [[package]] name = "wmi" -version = "0.14.5" +version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7787dacdd8e71cbc104658aade4009300777f9b5fda6a75f19145fedb8a18e71" +checksum = "3d3de777dce4cbcdc661d5d18e78ce4b46a37adc2bb7c0078a556c7f07bcce2f" dependencies = [ "chrono", "futures", "log", "serde", "thiserror 2.0.12", - "windows 0.59.0", - "windows-core 0.59.0", + "windows 0.61.3", + "windows-core 0.61.2", ] [[package]] @@ -6333,3 +6474,8 @@ dependencies = [ "quote", "syn 2.0.101", ] + +[[patch.unused]] +name = "netwatch" +version = "0.6.0" +source = "git+https://github.com/n0-computer/net-tools?branch=feat-multipath#b7ab98d4ff9cc947f2f084004b4cc2a979bb4d06" diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index f47bcdc996e..892000b2d3b 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -29,7 +29,6 @@ use std::{ }; use bytes::Bytes; -use data_encoding::HEXLOWER; use iroh_base::{NodeAddr, NodeId, PublicKey, RelayUrl, SecretKey}; use iroh_relay::RelayMap; use n0_future::{ @@ -59,7 +58,7 @@ use url::Url; use self::transports::IpTransport; use self::{ metrics::Metrics as MagicsockMetrics, - node_map::{NodeMap, PingAction, PingRole, SendPing}, + node_map::{NodeMap, PingAction}, transports::{RelayActorConfig, RelayTransport, Transports, UdpSender}, }; #[cfg(not(wasm_browser))] @@ -876,13 +875,8 @@ impl MagicSock { let _guard = span.enter(); trace!("receive disco message"); match dm { - disco::Message::Ping(ping) => { - self.metrics.magicsock.recv_disco_ping.inc(); - self.handle_ping(ping, sender, src); - } - disco::Message::Pong(pong) => { - self.metrics.magicsock.recv_disco_pong.inc(); - self.node_map.handle_pong(sender, src, pong); + disco::Message::Ping(..) | disco::Message::Pong(..) => { + unreachable!("not used anymore"); } disco::Message::CallMeMaybe(cm) => { self.metrics.magicsock.recv_disco_call_me_maybe.inc(); @@ -909,99 +903,19 @@ impl MagicSock { direct_addresses: cm.my_numbers.iter().copied().collect(), }); - let ping_actions = - self.node_map - .handle_call_me_maybe(sender, cm, &self.metrics.magicsock); - - for action in ping_actions { - match action { - PingAction::SendCallMeMaybe { .. } => { - warn!("Unexpected CallMeMaybe as response of handling a CallMeMaybe"); - } - PingAction::SendPing(ping) => { - self.send_ping_queued(ping); - } - } - } + self.node_map + .handle_call_me_maybe(sender, cm, &self.metrics.magicsock); } } trace!("disco message handled"); } - /// Handle a ping message. - fn handle_ping(&self, dm: disco::Ping, sender: NodeId, src: &transports::Addr) { - // Insert the ping into the node map, and return whether a ping with this tx_id was already - // received. - let addr: SendAddr = src.clone().into(); - let handled = self.node_map.handle_ping(sender, addr.clone(), dm.tx_id); - match handled.role { - PingRole::Duplicate => { - debug!(?src, tx = %HEXLOWER.encode(&dm.tx_id), "received ping: path already confirmed, skip"); - return; - } - PingRole::LikelyHeartbeat => {} - PingRole::NewPath => { - debug!(?src, tx = %HEXLOWER.encode(&dm.tx_id), "received ping: new path"); - } - PingRole::Activate => { - debug!(?src, tx = %HEXLOWER.encode(&dm.tx_id), "received ping: path active"); - } - } - - // Send a pong. - debug!(tx = %HEXLOWER.encode(&dm.tx_id), %addr, dstkey = %sender.fmt_short(), - "sending pong"); - let pong = disco::Message::Pong(disco::Pong { - tx_id: dm.tx_id, - ping_observed_addr: addr.clone(), - }); - event!( - target: "iroh::_events::pong::sent", - Level::DEBUG, - remote_node = %sender.fmt_short(), - dst = ?addr, - txn = ?dm.tx_id, - ); - - if !self.disco.try_send(addr.clone(), sender, pong) { - warn!(%addr, "failed to queue pong"); - } - - if let Some(ping) = handled.needs_ping_back { - debug!( - %addr, - dstkey = %sender.fmt_short(), - "sending direct ping back", - ); - self.send_ping_queued(ping); - } - } - - fn send_ping_queued(&self, ping: SendPing) { - let SendPing { - id, - dst, - dst_node, - tx_id, - purpose, - } = ping; - let msg = disco::Message::Ping(disco::Ping { - tx_id, - node_key: self.public_key, - }); - let sent = self.disco.try_send(dst.clone(), dst_node, msg); - if sent { - let msg_sender = self.actor_sender.clone(); - trace!(%dst, tx = %HEXLOWER.encode(&tx_id), ?purpose, "ping sent (queued)"); - self.node_map - .notify_ping_sent(id, dst, tx_id, purpose, msg_sender); - } else { - warn!(dst = ?dst, tx = %HEXLOWER.encode(&tx_id), ?purpose, "failed to send ping: queues full"); - } - } - /// Send the given ping actions out. - async fn send_ping_actions(&self, sender: &UdpSender, msgs: Vec) -> io::Result<()> { + async fn send_ping_actions( + &self, + _sender: &UdpSender, + msgs: Vec, + ) -> io::Result<()> { for msg in msgs { // Abort sending as soon as we know we are shutting down. if self.is_closing() || self.is_closed() { @@ -1046,25 +960,6 @@ impl MagicSock { } } } - PingAction::SendPing(SendPing { - id, - dst, - dst_node, - tx_id, - purpose, - }) => { - let msg = disco::Message::Ping(disco::Ping { - tx_id, - node_key: self.public_key, - }); - - self.send_disco_message(sender, dst.clone(), dst_node, msg) - .await?; - debug!(%dst, tx = %HEXLOWER.encode(&tx_id), ?purpose, "ping sent"); - let msg_sender = self.actor_sender.clone(); - self.node_map - .notify_ping_sent(id, dst, tx_id, purpose, msg_sender); - } } } Ok(()) @@ -1741,7 +1636,6 @@ impl AsyncUdpSocket for MagicUdpSocket { #[derive(Debug)] enum ActorMessage { PingActions(Vec), - EndpointPingExpired(usize, stun_rs::TransactionId), NetworkChange, ScheduleDirectAddrUpdate(UpdateReason, Option<(NodeId, RelayUrl)>), #[cfg(test)] @@ -2036,9 +1930,6 @@ impl Actor { /// Returns `true` if it was a shutdown. async fn handle_actor_message(&mut self, msg: ActorMessage, sender: &UdpSender) { match msg { - ActorMessage::EndpointPingExpired(id, txid) => { - self.msock.node_map.notify_ping_timeout(id, txid); - } ActorMessage::NetworkChange => { self.network_monitor.network_change().await.ok(); } diff --git a/iroh/src/magicsock/node_map.rs b/iroh/src/magicsock/node_map.rs index a67a570c3dd..11b135046e3 100644 --- a/iroh/src/magicsock/node_map.rs +++ b/iroh/src/magicsock/node_map.rs @@ -8,10 +8,9 @@ use std::{ use iroh_base::{NodeAddr, NodeId, PublicKey, RelayUrl}; use n0_future::time::Instant; use serde::{Deserialize, Serialize}; -use stun_rs::TransactionId; use tracing::{debug, info, instrument, trace, warn}; -use self::node_state::{NodeState, Options, PingHandled}; +use self::node_state::{NodeState, Options}; use super::{ActorMessage, NodeIdMappedAddr, metrics::Metrics, transports}; use crate::disco::{CallMeMaybe, Pong, SendAddr}; #[cfg(any(test, feature = "test-utils"))] @@ -22,8 +21,8 @@ mod path_state; mod path_validity; mod udp_paths; +pub(super) use node_state::PingAction; pub use node_state::{ConnectionType, ControlMsg, DirectAddrInfo, RemoteInfo}; -pub(super) use node_state::{DiscoPingPurpose, PingAction, PingRole, SendPing}; /// Number of nodes that are inactive for which we keep info about. This limit is enforced /// periodically via [`NodeMap::prune_inactive`]. @@ -68,7 +67,6 @@ pub(super) struct NodeMapInner { /// have for the node. These are all the keys the [`NodeMap`] can use. #[derive(Debug, Clone)] enum NodeStateKey { - Idx(usize), NodeId(NodeId), NodeIdMappedAddr(NodeIdMappedAddr), IpPort(IpPort), @@ -168,35 +166,6 @@ impl NodeMap { .receive_relay(relay_url, src) } - pub(super) fn notify_ping_sent( - &self, - id: usize, - dst: SendAddr, - tx_id: stun_rs::TransactionId, - purpose: DiscoPingPurpose, - msg_sender: tokio::sync::mpsc::Sender, - ) { - if let Some(ep) = self - .inner - .lock() - .expect("poisoned") - .get_mut(NodeStateKey::Idx(id)) - { - ep.ping_sent(dst, tx_id, purpose, msg_sender); - } - } - - pub(super) fn notify_ping_timeout(&self, id: usize, tx_id: stun_rs::TransactionId) { - if let Some(ep) = self - .inner - .lock() - .expect("poisoned") - .get_mut(NodeStateKey::Idx(id)) - { - ep.ping_timeout(tx_id, Instant::now()); - } - } - pub(super) fn get_quic_mapped_addr_for_node_key( &self, node_key: NodeId, @@ -208,38 +177,16 @@ impl NodeMap { .map(|ep| *ep.quic_mapped_addr()) } - /// Insert a received ping into the node map, and return whether a ping with this tx_id was already - /// received. - pub(super) fn handle_ping( - &self, - sender: PublicKey, - src: SendAddr, - tx_id: TransactionId, - ) -> PingHandled { - self.inner - .lock() - .expect("poisoned") - .handle_ping(sender, src, tx_id) - } - - pub(super) fn handle_pong(&self, sender: PublicKey, src: &transports::Addr, pong: Pong) { - self.inner - .lock() - .expect("poisoned") - .handle_pong(sender, src, pong) - } - - #[must_use = "actions must be handled"] pub(super) fn handle_call_me_maybe( &self, sender: PublicKey, cm: CallMeMaybe, metrics: &Metrics, - ) -> Vec { + ) { self.inner .lock() .expect("poisoned") - .handle_call_me_maybe(sender, cm, metrics) + .handle_call_me_maybe(sender, cm, metrics); } #[allow(clippy::type_complexity)] @@ -406,7 +353,6 @@ impl NodeMapInner { fn get_id(&self, id: NodeStateKey) -> Option { match id { - NodeStateKey::Idx(id) => Some(id), NodeStateKey::NodeId(node_key) => self.by_node_key.get(&node_key).copied(), NodeStateKey::NodeIdMappedAddr(addr) => self.by_quic_mapped_addr.get(&addr).copied(), NodeStateKey::IpPort(ipp) => self.by_ip_port.get(&ipp).copied(), @@ -502,25 +448,7 @@ impl NodeMapInner { .map(|ep| ep.conn_type()) } - fn handle_pong(&mut self, sender: NodeId, src: &transports::Addr, pong: Pong) { - if let Some(ns) = self.get_mut(NodeStateKey::NodeId(sender)).as_mut() { - let insert = ns.handle_pong(&pong, src.clone().into()); - if let Some((src, key)) = insert { - self.set_node_key_for_ip_port(src, &key); - } - trace!(?insert, "received pong") - } else { - warn!("received pong: node unknown, ignore") - } - } - - #[must_use = "actions must be handled"] - fn handle_call_me_maybe( - &mut self, - sender: NodeId, - cm: CallMeMaybe, - metrics: &Metrics, - ) -> Vec { + fn handle_call_me_maybe(&mut self, sender: NodeId, cm: CallMeMaybe, metrics: &Metrics) { let ns_id = NodeStateKey::NodeId(sender); if let Some(id) = self.get_id(ns_id.clone()) { for number in &cm.my_numbers { @@ -532,43 +460,13 @@ impl NodeMapInner { None => { debug!("received call-me-maybe: ignore, node is unknown"); metrics.recv_disco_call_me_maybe_bad_disco.inc(); - vec![] } Some(ns) => { debug!(endpoints = ?cm.my_numbers, "received call-me-maybe"); - ns.handle_call_me_maybe(cm) - } - } - } - - fn handle_ping(&mut self, sender: NodeId, src: SendAddr, tx_id: TransactionId) -> PingHandled { - #[cfg(any(test, feature = "test-utils"))] - let path_selection = self.path_selection; - let node_state = self.get_or_insert_with(NodeStateKey::NodeId(sender), || { - debug!("received ping: node unknown, add to node map"); - let source = if src.is_relay() { - Source::Relay - } else { - Source::Udp - }; - Options { - node_id: sender, - relay_url: src.relay_url(), - active: true, - source, - #[cfg(any(test, feature = "test-utils"))] - path_selection, - } - }); - - let handled = node_state.handle_ping(src.clone(), tx_id); - if let SendAddr::Udp(ref addr) = src { - if matches!(handled.role, PingRole::NewPath) { - self.set_node_key_for_ip_port(*addr, &sender); + ns.handle_call_me_maybe(cm); } } - handled } /// Inserts a new node into the [`NodeMap`]. @@ -706,6 +604,7 @@ mod tests { use tracing_test::traced_test; use super::{node_state::MAX_INACTIVE_DIRECT_ADDRESSES, *}; + use crate::disco::SendAddr; impl NodeMap { #[track_caller] @@ -838,7 +737,7 @@ mod tests { let txid = stun_rs::TransactionId::from([i as u8; 12]); // Note that this already invokes .prune_direct_addresses() because these are // new UDP paths. - endpoint.handle_ping(addr, txid); + // endpoint.handle_ping(addr, txid); } info!("Pruning addresses"); diff --git a/iroh/src/magicsock/node_map/node_state.rs b/iroh/src/magicsock/node_map/node_state.rs index 5a84f5ae378..26a4a6d63a5 100644 --- a/iroh/src/magicsock/node_map/node_state.rs +++ b/iroh/src/magicsock/node_map/node_state.rs @@ -1,19 +1,13 @@ use std::{ - collections::{BTreeSet, HashMap, btree_map::Entry}, - hash::Hash, + collections::{BTreeSet, HashMap}, net::{IpAddr, SocketAddr}, sync::atomic::AtomicBool, }; -use data_encoding::HEXLOWER; use iroh_base::{NodeAddr, NodeId, PublicKey, RelayUrl}; -use n0_future::{ - task::{self, AbortOnDropHandle}, - time::{self, Duration, Instant}, -}; +use n0_future::time::{Duration, Instant}; use n0_watcher::Watchable; use serde::{Deserialize, Serialize}; -use tokio::sync::mpsc; use tracing::{Level, debug, event, info, instrument, trace, warn}; use super::{ @@ -39,9 +33,6 @@ pub(super) const MAX_INACTIVE_DIRECT_ADDRESSES: usize = 20; /// How long since an endpoint path was last alive before it might be pruned. const LAST_ALIVE_PRUNE_DURATION: Duration = Duration::from_secs(120); -/// How long we wait for a pong reply before assuming it's never coming. -const PING_TIMEOUT_DURATION: Duration = Duration::from_secs(5); - /// The latency at or under which we don't try to upgrade to a better path. const GOOD_ENOUGH_LATENCY: Duration = Duration::from_millis(5); @@ -52,46 +43,12 @@ pub(super) const SESSION_ACTIVE_TIMEOUT: Duration = Duration::from_secs(45); /// How often we try to upgrade to a better patheven if we have some non-relay route that works. const UPGRADE_INTERVAL: Duration = Duration::from_secs(60); -/// How long until we send a stayin alive ping -const STAYIN_ALIVE_MIN_ELAPSED: Duration = Duration::from_secs(2); - #[derive(Debug)] pub(in crate::magicsock) enum PingAction { SendCallMeMaybe { relay_url: RelayUrl, dst_node: NodeId, }, - SendPing(SendPing), -} - -#[derive(Debug)] -pub(in crate::magicsock) struct SendPing { - pub id: usize, - pub dst: SendAddr, - pub dst_node: NodeId, - pub tx_id: stun_rs::TransactionId, - pub purpose: DiscoPingPurpose, -} - -/// Indicating an [`NodeState`] has handled a ping. -#[derive(Debug)] -pub struct PingHandled { - /// What this ping did to the [`NodeState`]. - pub role: PingRole, - /// Whether the sender path should also be pinged. - /// - /// This is the case if an [`NodeState`] does not yet have a direct path, i.e. it has no - /// best_addr. In this case we want to ping right back to open the direct path in this - /// direction as well. - pub needs_ping_back: Option, -} - -#[derive(Debug)] -pub enum PingRole { - Duplicate, - NewPath, - LikelyHeartbeat, - Activate, } /// An iroh node, which we can have connections with. @@ -109,14 +66,11 @@ pub(super) struct NodeState { quic_mapped_addr: NodeIdMappedAddr, /// The global identifier for this endpoint. node_id: NodeId, - /// The last time we pinged all endpoints. - last_full_ping: Option, /// The url of relay node that we can relay over to communicate. /// /// The fallback/bootstrap path, if non-zero (non-zero for well-behaved clients). relay_url: Option<(RelayUrl, PathState)>, udp_paths: NodeUdpPaths, - sent_pings: HashMap, /// Last time this node was used. /// /// A node is marked as in use when sending datagrams to them, or when having received @@ -174,7 +128,6 @@ impl NodeState { id, quic_mapped_addr, node_id: options.node_id, - last_full_ping: None, relay_url: options.relay_url.map(|url| { ( url.clone(), @@ -182,7 +135,6 @@ impl NodeState { ) }), udp_paths: NodeUdpPaths::new(), - sent_pings: HashMap::new(), last_used: options.active.then(Instant::now), last_call_me_maybe: None, conn_type: Watchable::new(ConnectionType::None), @@ -211,32 +163,7 @@ impl NodeState { /// Returns info about this node. pub(super) fn info(&self, now: Instant) -> RemoteInfo { let conn_type = self.conn_type.get(); - let latency = match conn_type { - ConnectionType::Direct(addr) => self - .udp_paths - .paths - .get(&addr.into()) - .and_then(|state| state.latency()), - ConnectionType::Relay(ref url) => self - .relay_url - .as_ref() - .filter(|(relay_url, _)| relay_url == url) - .and_then(|(_, state)| state.latency()), - ConnectionType::Mixed(addr, ref url) => { - let addr_latency = self - .udp_paths - .paths - .get(&addr.into()) - .and_then(|state| state.latency()); - let relay_latency = self - .relay_url - .as_ref() - .filter(|(relay_url, _)| relay_url == url) - .and_then(|(_, state)| state.latency()); - addr_latency.min(relay_latency) - } - ConnectionType::None => None, - }; + let latency = None; let addrs = self .udp_paths @@ -396,10 +323,6 @@ impl NodeState { #[instrument("want_call_me_maybe", skip_all)] fn want_call_me_maybe(&self, now: &Instant) -> bool { trace!("full ping: wanted?"); - let Some(last_full_ping) = self.last_full_ping else { - debug!("no previous full ping: need full ping"); - return true; - }; match &self.udp_paths.best { UdpSendAddr::None | UdpSendAddr::Unconfirmed(_) => { debug!("best addr not set: need full ping"); @@ -417,7 +340,7 @@ impl NodeState { .expect("send path not tracked?") .latency() .expect("send_addr marked valid incorrectly"); - if latency > GOOD_ENOUGH_LATENCY && *now - last_full_ping >= UPGRADE_INTERVAL { + if latency > GOOD_ENOUGH_LATENCY { debug!( "full ping interval expired and latency is only {}ms: need full ping", latency.as_millis() @@ -437,131 +360,6 @@ impl NodeState { false } - /// Cleanup the expired ping for the passed in txid. - #[instrument("disco", skip_all, fields(node = %self.node_id.fmt_short()))] - pub(super) fn ping_timeout(&mut self, txid: stun_rs::TransactionId, now: Instant) { - if let Some(sp) = self.sent_pings.remove(&txid) { - debug!(tx = %HEXLOWER.encode(&txid), addr = %sp.to, "pong not received in timeout"); - match sp.to { - SendAddr::Udp(addr) => { - if let Some(path_state) = self.udp_paths.paths.get_mut(&addr.into()) { - path_state.last_ping = None; - let consider_alive = path_state - .last_alive() - .map(|last_alive| last_alive.elapsed() <= PING_TIMEOUT_DURATION) - .unwrap_or(false); - if !consider_alive { - // If there was no sign of life from this path during the time - // which we should have received the pong, clear best addr and - // pong. Both are used to select this path again, but we know - // it's not a usable path now. - path_state.validity = PathValidity::empty(); - self.udp_paths.update_to_best_addr(now); - } - } else { - // If we have no state for the best addr it should have been cleared - // anyway. - self.udp_paths.update_to_best_addr(now); - } - } - SendAddr::Relay(ref url) => { - if let Some((home_relay, relay_state)) = self.relay_url.as_mut() { - if home_relay == url { - // lost connectivity via relay - relay_state.last_ping = None; - } - } - } - } - } - } - - #[must_use = "pings must be handled"] - fn start_ping(&self, dst: SendAddr, purpose: DiscoPingPurpose) -> Option { - #[cfg(any(test, feature = "test-utils"))] - if self.path_selection == PathSelection::RelayOnly && !dst.is_relay() { - // don't attempt any hole punching in relay only mode - warn!("in `RelayOnly` mode, ignoring request to start a hole punching attempt."); - return None; - } - #[cfg(wasm_browser)] - if !dst.is_relay() { - return None; // Similar to `RelayOnly` mode, we don't send UDP pings for hole-punching. - } - - let tx_id = stun_rs::TransactionId::default(); - trace!(tx = %HEXLOWER.encode(&tx_id), %dst, ?purpose, - dst = %self.node_id.fmt_short(), "start ping"); - event!( - target: "iroh::_events::ping::sent", - Level::DEBUG, - remote_node = %self.node_id.fmt_short(), - ?dst, - txn = ?tx_id, - ?purpose, - ); - Some(SendPing { - id: self.id, - dst, - dst_node: self.node_id, - tx_id, - purpose, - }) - } - - /// Record the fact that a ping has been sent out. - pub(super) fn ping_sent( - &mut self, - to: SendAddr, - tx_id: stun_rs::TransactionId, - purpose: DiscoPingPurpose, - sender: mpsc::Sender, - ) { - trace!(%to, tx = %HEXLOWER.encode(&tx_id), ?purpose, "record ping sent"); - - let now = Instant::now(); - let mut path_found = false; - match to { - SendAddr::Udp(addr) => { - if let Some(st) = self.udp_paths.paths.get_mut(&addr.into()) { - st.last_ping.replace(now); - path_found = true - } - } - SendAddr::Relay(ref url) => { - if let Some((home_relay, relay_state)) = self.relay_url.as_mut() { - if home_relay == url { - relay_state.last_ping.replace(now); - path_found = true - } - } - } - } - if !path_found { - // Shouldn't happen. But don't ping an endpoint that's not active for us. - warn!(%to, ?purpose, "unexpected attempt to ping no longer live path"); - return; - } - - let id = self.id; - let _expiry_task = AbortOnDropHandle::new(task::spawn(async move { - time::sleep(PING_TIMEOUT_DURATION).await; - sender - .send(ActorMessage::EndpointPingExpired(id, tx_id)) - .await - .ok(); - })); - self.sent_pings.insert( - tx_id, - SentPing { - to, - at: now, - purpose, - _expiry_task, - }, - ); - } - /// Send a DISCO call-me-maybe message to the peer. /// /// This takes care of sending the needed pings beforehand. This ensures that we open @@ -588,11 +386,7 @@ impl NodeState { } } } - // We send pings regardless of whether we have a RelayUrl. If we were given any - // direct address paths to contact but no RelayUrl, we still need to send a DISCO - // ping to the direct address paths so that the other node will learn about us and - // accepts the connection. - let mut msgs = self.send_pings(now); + let mut msgs = Vec::new(); if let Some(url) = self.relay_url() { debug!(%url, "queue call-me-maybe"); @@ -608,59 +402,6 @@ impl NodeState { msgs } - /// Send DISCO Pings to all the paths of this node. - /// - /// Any paths to the node which have not been recently pinged will be sent a disco - /// ping. - /// - /// The caller is responsible for sending the messages. - #[must_use = "actions must be handled"] - fn send_pings(&mut self, now: Instant) -> Vec { - // We allocate +1 in case the caller wants to add a call-me-maybe message. - let mut ping_msgs = Vec::with_capacity(self.udp_paths.paths.len() + 1); - - if let Some((url, state)) = self.relay_url.as_ref() { - if state.needs_ping(&now) { - debug!(%url, "relay path needs ping"); - if let Some(msg) = - self.start_ping(SendAddr::Relay(url.clone()), DiscoPingPurpose::Discovery) - { - ping_msgs.push(PingAction::SendPing(msg)) - } - } - } - - #[cfg(any(test, feature = "test-utils"))] - if self.path_selection == PathSelection::RelayOnly { - warn!("in `RelayOnly` mode, ignoring request to respond to a hole punching attempt."); - return ping_msgs; - } - - self.prune_direct_addresses(now); - let mut ping_dsts = String::from("["); - self.udp_paths - .paths - .iter() - .filter_map(|(ipp, state)| state.needs_ping(&now).then_some(*ipp)) - .filter_map(|ipp| { - self.start_ping(SendAddr::Udp(ipp.into()), DiscoPingPurpose::Discovery) - }) - .for_each(|msg| { - use std::fmt::Write; - write!(&mut ping_dsts, " {} ", msg.dst).ok(); - ping_msgs.push(PingAction::SendPing(msg)); - }); - ping_dsts.push(']'); - debug!( - %ping_dsts, - dst = %self.node_id.fmt_short(), - paths = %summarize_node_paths(&self.udp_paths.paths), - "sending pings to node", - ); - self.last_full_ping.replace(now); - ping_msgs - } - pub(super) fn update_from_node_addr( &mut self, new_relay_url: Option<&RelayUrl>, @@ -713,114 +454,6 @@ impl NodeState { debug!(new = ?new_addrs , %paths, "added new direct paths for endpoint"); } - /// Handle a received Disco Ping. - /// - /// - Ensures the paths the ping was received on is a known path for this endpoint. - /// - /// - If there is no best_addr for this endpoint yet, sends a ping itself to try and - /// establish one. - /// - /// This is called once we've already verified that we got a valid discovery message - /// from `self` via ep. - pub(super) fn handle_ping( - &mut self, - path: SendAddr, - tx_id: stun_rs::TransactionId, - ) -> PingHandled { - let now = Instant::now(); - - let role = match path { - SendAddr::Udp(addr) => match self.udp_paths.paths.entry(addr.into()) { - Entry::Occupied(mut occupied) => occupied.get_mut().handle_ping(tx_id, now), - Entry::Vacant(vacant) => { - info!(%addr, "new direct addr for node"); - vacant.insert(PathState::with_ping( - self.node_id, - path.clone(), - tx_id, - Source::Udp, - now, - )); - PingRole::NewPath - } - }, - SendAddr::Relay(ref url) => { - match self.relay_url.as_mut() { - Some((home_url, _state)) if home_url != url => { - // either the node changed relays or we didn't have a relay address for the - // node. In both cases, trust the new confirmed url - info!(%url, "new relay addr for node"); - self.relay_url = Some(( - url.clone(), - PathState::with_ping( - self.node_id, - path.clone(), - tx_id, - Source::Relay, - now, - ), - )); - PingRole::NewPath - } - Some((_home_url, state)) => state.handle_ping(tx_id, now), - None => { - info!(%url, "new relay addr for node"); - self.relay_url = Some(( - url.clone(), - PathState::with_ping( - self.node_id, - path.clone(), - tx_id, - Source::Relay, - now, - ), - )); - PingRole::NewPath - } - } - } - }; - event!( - target: "iroh::_events::ping::recv", - Level::DEBUG, - remote_node = %self.node_id.fmt_short(), - src = ?path, - txn = ?tx_id, - ?role, - ); - - if matches!(path, SendAddr::Udp(_)) && matches!(role, PingRole::NewPath) { - self.prune_direct_addresses(now); - } - - // if the endpoint does not yet have a best_addr - let needs_ping_back = if matches!(path, SendAddr::Udp(_)) - && matches!( - self.udp_paths.best, - UdpSendAddr::None | UdpSendAddr::Unconfirmed(_) | UdpSendAddr::Outdated(_) - ) { - // We also need to send a ping to make this path available to us as well. This - // is always sent together with a pong. So in the worst case the pong gets lost - // and this ping does not. In that case we ping-pong until both sides have - // received at least one pong. Once both sides have received one pong they both - // have a best_addr and this ping will stop being sent. - self.start_ping(path, DiscoPingPurpose::PingBack) - } else { - None - }; - - debug!( - ?role, - needs_ping_back = ?needs_ping_back.is_some(), - paths = %summarize_node_paths(&self.udp_paths.paths), - "endpoint handled ping", - ); - PingHandled { - role, - needs_ping_back, - } - } - /// Prune inactive paths. /// /// This trims the list of inactive paths for an endpoint. At most @@ -873,105 +506,6 @@ impl NodeState { self.udp_paths.update_to_best_addr(now); } - /// Handles a Pong message (a reply to an earlier ping). - /// - /// It reports the address and key that should be inserted for the endpoint if any. - #[instrument(skip(self))] - pub(super) fn handle_pong( - &mut self, - m: &disco::Pong, - src: SendAddr, - ) -> Option<(SocketAddr, PublicKey)> { - event!( - target: "iroh::_events::pong::recv", - Level::DEBUG, - remote_node = %self.node_id.fmt_short(), - ?src, - txn = ?m.tx_id, - ); - let is_relay = src.is_relay(); - match self.sent_pings.remove(&m.tx_id) { - None => { - // This is not a pong for a ping we sent. In reality however we probably - // did send this ping but it has timed-out by the time we receive this pong - // so we removed the state already. - debug!(tx = %HEXLOWER.encode(&m.tx_id), "received unknown pong (did it timeout?)"); - None - } - Some(sp) => { - let mut node_map_insert = None; - - let now = Instant::now(); - let latency = now - sp.at; - - debug!( - tx = %HEXLOWER.encode(&m.tx_id), - src = %src, - reported_ping_src = %m.ping_observed_addr, - ping_dst = %sp.to, - is_relay = %src.is_relay(), - latency = %latency.as_millis(), - "received pong", - ); - - match src { - SendAddr::Udp(addr) => { - match self.udp_paths.paths.get_mut(&addr.into()) { - None => { - warn!("ignoring pong: no state for src addr"); - // This is no longer an endpoint we care about. - return node_map_insert; - } - Some(st) => { - node_map_insert = Some((addr, self.node_id)); - st.add_pong_reply(PongReply { - latency, - pong_at: now, - from: src, - pong_src: m.ping_observed_addr.clone(), - }); - } - } - debug!( - paths = %summarize_node_paths(&self.udp_paths.paths), - "handled pong", - ); - } - SendAddr::Relay(ref url) => match self.relay_url.as_mut() { - Some((home_url, state)) if home_url == url => { - state.add_pong_reply(PongReply { - latency, - pong_at: now, - from: src, - pong_src: m.ping_observed_addr.clone(), - }); - } - other => { - // if we are here then we sent this ping, but the url changed - // waiting for the response. It was either set to None or changed to - // another relay. This should either never happen or be extremely - // unlikely. Log and ignore for now - warn!( - stored=?other, - received=?url, - "ignoring pong via relay for different relay from last one", - ); - } - }, - } - - // Promote this pong response to our current best address if it's lower latency. - // TODO(bradfitz): decide how latency vs. preference order affects decision - if let SendAddr::Udp(_to) = sp.to { - debug_assert!(!is_relay, "mismatching relay & udp"); - self.udp_paths.update_to_best_addr(now); - } - - node_map_insert - } - } - } - /// Handles a DISCO CallMeMaybe discovery message. /// /// The contract for use of this message is that the node has already pinged to us via @@ -982,7 +516,7 @@ impl NodeState { /// had any [`IpPort`]s to send pings to and our pings might end up blocked. But at /// least open the firewalls on our side, giving the other side another change of making /// it through when it pings in response. - pub(super) fn handle_call_me_maybe(&mut self, m: disco::CallMeMaybe) -> Vec { + pub(super) fn handle_call_me_maybe(&mut self, m: disco::CallMeMaybe) { let now = Instant::now(); let mut call_me_maybe_ipps = BTreeSet::new(); @@ -1035,7 +569,6 @@ impl NodeState { paths = %summarize_node_paths(&self.udp_paths.paths), "updated endpoint paths from call-me-maybe", ); - self.send_pings(now) } /// Marks this node as having received a UDP payload message. @@ -1114,29 +647,6 @@ impl NodeState { return self.send_call_me_maybe(now, SendCallMeMaybe::Always); } - // Send heartbeat ping to keep the current addr going as long as we need it. - if let Some(udp_addr) = self.udp_paths.best.get_addr() { - let elapsed = self.last_ping(&SendAddr::Udp(udp_addr)).map(|l| now - l); - // Send a ping if the last ping is older than 2 seconds. - let needs_ping = match elapsed { - Some(e) => e >= STAYIN_ALIVE_MIN_ELAPSED, - None => false, - }; - - if needs_ping { - debug!( - dst = %udp_addr, - since_last_ping=?elapsed, - "send stayin alive ping", - ); - if let Some(msg) = - self.start_ping(SendAddr::Udp(udp_addr), DiscoPingPurpose::StayinAlive) - { - return vec![PingAction::SendPing(msg)]; - } - } - } - Vec::new() } @@ -1214,26 +724,6 @@ enum SendCallMeMaybe { IfNoRecent, } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub(super) struct PongReply { - pub(super) latency: Duration, - /// When we received the pong. - pub(super) pong_at: Instant, - /// The pong's src (usually same as endpoint map key). - pub(super) from: SendAddr, - /// What they reported they heard. - pub(super) pong_src: SendAddr, -} - -#[derive(Debug)] -pub(super) struct SentPing { - pub(super) to: SendAddr, - pub(super) at: Instant, - #[allow(dead_code)] - pub(super) purpose: DiscoPingPurpose, - pub(super) _expiry_task: AbortOnDropHandle<()>, -} - /// The reason why a discovery ping message was sent. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum DiscoPingPurpose { @@ -1330,7 +820,7 @@ impl From<(RelayUrl, PathState)> for RelayUrlInfo { RelayUrlInfo { relay_url: value.0, last_alive: value.1.last_alive().map(|i| i.elapsed()), - latency: value.1.latency(), + latency: None, } } } @@ -1442,256 +932,262 @@ mod tests { use super::*; use crate::magicsock::node_map::{NodeMap, NodeMapInner}; - #[test] - fn test_remote_infos() { - let now = Instant::now(); - let elapsed = Duration::from_secs(3); - let later = now + elapsed; - let send_addr: RelayUrl = "https://my-relay.com".parse().unwrap(); - let pong_src = SendAddr::Udp("0.0.0.0:1".parse().unwrap()); - let latency = Duration::from_millis(50); - - let relay_and_state = |node_id: NodeId, url: RelayUrl| { - let relay_state = PathState::with_pong_reply( - node_id, - PongReply { - latency, - pong_at: now, - from: SendAddr::Relay(send_addr.clone()), - pong_src: pong_src.clone(), - }, - ); - Some((url, relay_state)) - }; - - // endpoint with a `best_addr` that has a latency but no relay - let (a_endpoint, a_socket_addr) = { - let key = SecretKey::generate(rand::thread_rng()); - let node_id = key.public(); - let ip_port = IpPort { - ip: Ipv4Addr::UNSPECIFIED.into(), - port: 10, - }; - let endpoint_state = BTreeMap::from([( - ip_port, - PathState::with_pong_reply( - node_id, - PongReply { - latency, - pong_at: now, - from: SendAddr::Udp(ip_port.into()), - pong_src: pong_src.clone(), - }, - ), - )]); - ( - NodeState { - id: 0, - quic_mapped_addr: NodeIdMappedAddr::generate(), - node_id: key.public(), - last_full_ping: None, - relay_url: None, - udp_paths: NodeUdpPaths::from_parts( - endpoint_state, - UdpSendAddr::Valid(ip_port.into()), - ), - sent_pings: HashMap::new(), - last_used: Some(now), - last_call_me_maybe: None, - conn_type: Watchable::new(ConnectionType::Direct(ip_port.into())), - has_been_direct: AtomicBool::new(true), - #[cfg(any(test, feature = "test-utils"))] - path_selection: PathSelection::default(), - }, - ip_port.into(), - ) - }; - // endpoint w/ no best addr but a relay w/ latency - let b_endpoint = { - // let socket_addr = "0.0.0.0:9".parse().unwrap(); - let key = SecretKey::generate(rand::thread_rng()); - NodeState { - id: 1, - quic_mapped_addr: NodeIdMappedAddr::generate(), - node_id: key.public(), - last_full_ping: None, - relay_url: relay_and_state(key.public(), send_addr.clone()), - udp_paths: NodeUdpPaths::new(), - sent_pings: HashMap::new(), - last_used: Some(now), - last_call_me_maybe: None, - conn_type: Watchable::new(ConnectionType::Relay(send_addr.clone())), - has_been_direct: AtomicBool::new(false), - #[cfg(any(test, feature = "test-utils"))] - path_selection: PathSelection::default(), - } - }; - - // endpoint w/ no best addr but a relay w/ no latency - let c_endpoint = { - // let socket_addr = "0.0.0.0:8".parse().unwrap(); - let key = SecretKey::generate(rand::thread_rng()); - NodeState { - id: 2, - quic_mapped_addr: NodeIdMappedAddr::generate(), - node_id: key.public(), - last_full_ping: None, - relay_url: Some(( - send_addr.clone(), - PathState::new( - key.public(), - SendAddr::from(send_addr.clone()), - Source::App, - now, - ), - )), - udp_paths: NodeUdpPaths::new(), - sent_pings: HashMap::new(), - last_used: Some(now), - last_call_me_maybe: None, - conn_type: Watchable::new(ConnectionType::Relay(send_addr.clone())), - has_been_direct: AtomicBool::new(false), - #[cfg(any(test, feature = "test-utils"))] - path_selection: PathSelection::default(), - } - }; - - // endpoint w/ expired best addr and relay w/ latency - let (d_endpoint, d_socket_addr) = { - let socket_addr: SocketAddr = "0.0.0.0:7".parse().unwrap(); - let key = SecretKey::generate(rand::thread_rng()); - let node_id = key.public(); - let endpoint_state = BTreeMap::from([( - IpPort::from(socket_addr), - PathState::with_pong_reply( - node_id, - PongReply { - latency, - pong_at: now, - from: SendAddr::Udp(socket_addr), - pong_src: pong_src.clone(), - }, - ), - )]); - ( - NodeState { - id: 3, - quic_mapped_addr: NodeIdMappedAddr::generate(), - node_id: key.public(), - last_full_ping: None, - relay_url: relay_and_state(key.public(), send_addr.clone()), - udp_paths: NodeUdpPaths::from_parts( - endpoint_state, - UdpSendAddr::Outdated(socket_addr), - ), - sent_pings: HashMap::new(), - last_used: Some(now), - last_call_me_maybe: None, - conn_type: Watchable::new(ConnectionType::Mixed( - socket_addr, - send_addr.clone(), - )), - has_been_direct: AtomicBool::new(false), - #[cfg(any(test, feature = "test-utils"))] - path_selection: PathSelection::default(), - }, - socket_addr, - ) - }; - - let mut expect = Vec::from([ - RemoteInfo { - node_id: a_endpoint.node_id, - relay_url: None, - addrs: Vec::from([DirectAddrInfo { - addr: a_socket_addr, - latency: Some(latency), - last_control: Some((elapsed, ControlMsg::Pong)), - last_payload: None, - last_alive: Some(elapsed), - sources: HashMap::new(), - }]), - conn_type: ConnectionType::Direct(a_socket_addr), - latency: Some(latency), - last_used: Some(elapsed), - }, - RemoteInfo { - node_id: b_endpoint.node_id, - relay_url: Some(RelayUrlInfo { - relay_url: b_endpoint.relay_url.as_ref().unwrap().0.clone(), - last_alive: None, - latency: Some(latency), - }), - addrs: Vec::new(), - conn_type: ConnectionType::Relay(send_addr.clone()), - latency: Some(latency), - last_used: Some(elapsed), - }, - RemoteInfo { - node_id: c_endpoint.node_id, - relay_url: Some(RelayUrlInfo { - relay_url: c_endpoint.relay_url.as_ref().unwrap().0.clone(), - last_alive: None, - latency: None, - }), - addrs: Vec::new(), - conn_type: ConnectionType::Relay(send_addr.clone()), - latency: None, - last_used: Some(elapsed), - }, - RemoteInfo { - node_id: d_endpoint.node_id, - relay_url: Some(RelayUrlInfo { - relay_url: d_endpoint.relay_url.as_ref().unwrap().0.clone(), - last_alive: None, - latency: Some(latency), - }), - addrs: Vec::from([DirectAddrInfo { - addr: d_socket_addr, - latency: Some(latency), - last_control: Some((elapsed, ControlMsg::Pong)), - last_payload: None, - last_alive: Some(elapsed), - sources: HashMap::new(), - }]), - conn_type: ConnectionType::Mixed(d_socket_addr, send_addr.clone()), - latency: Some(Duration::from_millis(50)), - last_used: Some(elapsed), - }, - ]); - - let node_map = NodeMap::from_inner(NodeMapInner { - by_node_key: HashMap::from([ - (a_endpoint.node_id, a_endpoint.id), - (b_endpoint.node_id, b_endpoint.id), - (c_endpoint.node_id, c_endpoint.id), - (d_endpoint.node_id, d_endpoint.id), - ]), - by_ip_port: HashMap::from([ - (a_socket_addr.into(), a_endpoint.id), - (d_socket_addr.into(), d_endpoint.id), - ]), - by_quic_mapped_addr: HashMap::from([ - (a_endpoint.quic_mapped_addr, a_endpoint.id), - (b_endpoint.quic_mapped_addr, b_endpoint.id), - (c_endpoint.quic_mapped_addr, c_endpoint.id), - (d_endpoint.quic_mapped_addr, d_endpoint.id), - ]), - by_id: HashMap::from([ - (a_endpoint.id, a_endpoint), - (b_endpoint.id, b_endpoint), - (c_endpoint.id, c_endpoint), - (d_endpoint.id, d_endpoint), - ]), - next_id: 5, - path_selection: PathSelection::default(), - }); - let mut got = node_map.list_remote_infos(later); - got.sort_by_key(|p| p.node_id); - expect.sort_by_key(|p| p.node_id); - remove_non_deterministic_fields(&mut got); - assert_eq!(expect, got); - } + // #[test] + // fn test_remote_infos() { + // let now = Instant::now(); + // let elapsed = Duration::from_secs(3); + // let later = now + elapsed; + // let send_addr: RelayUrl = "https://my-relay.com".parse().unwrap(); + // let pong_src = SendAddr::Udp("0.0.0.0:1".parse().unwrap()); + // let latency = Duration::from_millis(50); + + // let relay_and_state = |node_id: NodeId, url: RelayUrl| { + // let relay_state = PathState::with_pong_reply( + // node_id, + // PongReply { + // latency, + // pong_at: now, + // from: SendAddr::Relay(send_addr.clone()), + // pong_src: pong_src.clone(), + // }, + // ); + // Some((url, relay_state)) + // }; + + // // endpoint with a `best_addr` that has a latency but no relay + // let (a_endpoint, a_socket_addr) = { + // let key = SecretKey::generate(rand::thread_rng()); + // let node_id = key.public(); + // let ip_port = IpPort { + // ip: Ipv4Addr::UNSPECIFIED.into(), + // port: 10, + // }; + // let endpoint_state = BTreeMap::from([( + // ip_port, + // PathState::with_pong_reply( + // node_id, + // PongReply { + // latency, + // pong_at: now, + // from: SendAddr::Udp(ip_port.into()), + // pong_src: pong_src.clone(), + // }, + // ), + // )]); + // ( + // NodeState { + // id: 0, + // quic_mapped_addr: NodeIdMappedAddr::generate(), + // node_id: key.public(), + // last_full_ping: None, + // relay_url: None, + // udp_paths: NodeUdpPaths::from_parts( + // endpoint_state, + // BestAddr::from_parts( + // ip_port.into(), + // latency, + // now, + // now + Duration::from_secs(100), + // ), + // ), + // sent_pings: HashMap::new(), + // last_used: Some(now), + // last_call_me_maybe: None, + // conn_type: Watchable::new(ConnectionType::Direct(ip_port.into())), + // has_been_direct: true, + // #[cfg(any(test, feature = "test-utils"))] + // path_selection: PathSelection::default(), + // }, + // ip_port.into(), + // ) + // }; + // // endpoint w/ no best addr but a relay w/ latency + // let b_endpoint = { + // // let socket_addr = "0.0.0.0:9".parse().unwrap(); + // let key = SecretKey::generate(rand::thread_rng()); + // NodeState { + // id: 1, + // quic_mapped_addr: NodeIdMappedAddr::generate(), + // node_id: key.public(), + // last_full_ping: None, + // relay_url: relay_and_state(key.public(), send_addr.clone()), + // udp_paths: NodeUdpPaths::new(), + // sent_pings: HashMap::new(), + // last_used: Some(now), + // last_call_me_maybe: None, + // conn_type: Watchable::new(ConnectionType::Relay(send_addr.clone())), + // has_been_direct: false, + // #[cfg(any(test, feature = "test-utils"))] + // path_selection: PathSelection::default(), + // } + // }; + + // // endpoint w/ no best addr but a relay w/ no latency + // let c_endpoint = { + // // let socket_addr = "0.0.0.0:8".parse().unwrap(); + // let key = SecretKey::generate(rand::thread_rng()); + // NodeState { + // id: 2, + // quic_mapped_addr: NodeIdMappedAddr::generate(), + // node_id: key.public(), + // last_full_ping: None, + // relay_url: Some(( + // send_addr.clone(), + // PathState::new( + // key.public(), + // SendAddr::from(send_addr.clone()), + // Source::App, + // now, + // ), + // )), + // udp_paths: NodeUdpPaths::new(), + // sent_pings: HashMap::new(), + // last_used: Some(now), + // last_call_me_maybe: None, + // conn_type: Watchable::new(ConnectionType::Relay(send_addr.clone())), + // has_been_direct: false, + // #[cfg(any(test, feature = "test-utils"))] + // path_selection: PathSelection::default(), + // } + // }; + + // // endpoint w/ expired best addr and relay w/ latency + // let (d_endpoint, d_socket_addr) = { + // let socket_addr: SocketAddr = "0.0.0.0:7".parse().unwrap(); + // let expired = now.checked_sub(Duration::from_secs(100)).unwrap(); + // let key = SecretKey::generate(rand::thread_rng()); + // let node_id = key.public(); + // let endpoint_state = BTreeMap::from([( + // IpPort::from(socket_addr), + // PathState::with_pong_reply( + // node_id, + // PongReply { + // latency, + // pong_at: now, + // from: SendAddr::Udp(socket_addr), + // pong_src: pong_src.clone(), + // }, + // ), + // )]); + // ( + // NodeState { + // id: 3, + // quic_mapped_addr: NodeIdMappedAddr::generate(), + // node_id: key.public(), + // last_full_ping: None, + // relay_url: relay_and_state(key.public(), send_addr.clone()), + // udp_paths: NodeUdpPaths::from_parts( + // endpoint_state, + // BestAddr::from_parts(socket_addr, Duration::from_millis(80), now, expired), + // ), + // sent_pings: HashMap::new(), + // last_used: Some(now), + // last_call_me_maybe: None, + // conn_type: Watchable::new(ConnectionType::Mixed( + // socket_addr, + // send_addr.clone(), + // )), + // has_been_direct: false, + // #[cfg(any(test, feature = "test-utils"))] + // path_selection: PathSelection::default(), + // }, + // socket_addr, + // ) + // }; + + // let mut expect = Vec::from([ + // RemoteInfo { + // node_id: a_endpoint.node_id, + // relay_url: None, + // addrs: Vec::from([DirectAddrInfo { + // addr: a_socket_addr, + // latency: Some(latency), + // last_control: Some((elapsed, ControlMsg::Pong)), + // last_payload: None, + // last_alive: Some(elapsed), + // sources: HashMap::new(), + // }]), + // conn_type: ConnectionType::Direct(a_socket_addr), + // latency: Some(latency), + // last_used: Some(elapsed), + // }, + // RemoteInfo { + // node_id: b_endpoint.node_id, + // relay_url: Some(RelayUrlInfo { + // relay_url: b_endpoint.relay_url.as_ref().unwrap().0.clone(), + // last_alive: None, + // latency: Some(latency), + // }), + // addrs: Vec::new(), + // conn_type: ConnectionType::Relay(send_addr.clone()), + // latency: Some(latency), + // last_used: Some(elapsed), + // }, + // RemoteInfo { + // node_id: c_endpoint.node_id, + // relay_url: Some(RelayUrlInfo { + // relay_url: c_endpoint.relay_url.as_ref().unwrap().0.clone(), + // last_alive: None, + // latency: None, + // }), + // addrs: Vec::new(), + // conn_type: ConnectionType::Relay(send_addr.clone()), + // latency: None, + // last_used: Some(elapsed), + // }, + // RemoteInfo { + // node_id: d_endpoint.node_id, + // relay_url: Some(RelayUrlInfo { + // relay_url: d_endpoint.relay_url.as_ref().unwrap().0.clone(), + // last_alive: None, + // latency: Some(latency), + // }), + // addrs: Vec::from([DirectAddrInfo { + // addr: d_socket_addr, + // latency: Some(latency), + // last_control: Some((elapsed, ControlMsg::Pong)), + // last_payload: None, + // last_alive: Some(elapsed), + // sources: HashMap::new(), + // }]), + // conn_type: ConnectionType::Mixed(d_socket_addr, send_addr.clone()), + // latency: Some(Duration::from_millis(50)), + // last_used: Some(elapsed), + // }, + // ]); + + // let node_map = NodeMap::from_inner(NodeMapInner { + // by_node_key: HashMap::from([ + // (a_endpoint.node_id, a_endpoint.id), + // (b_endpoint.node_id, b_endpoint.id), + // (c_endpoint.node_id, c_endpoint.id), + // (d_endpoint.node_id, d_endpoint.id), + // ]), + // by_ip_port: HashMap::from([ + // (a_socket_addr.into(), a_endpoint.id), + // (d_socket_addr.into(), d_endpoint.id), + // ]), + // by_quic_mapped_addr: HashMap::from([ + // (a_endpoint.quic_mapped_addr, a_endpoint.id), + // (b_endpoint.quic_mapped_addr, b_endpoint.id), + // (c_endpoint.quic_mapped_addr, c_endpoint.id), + // (d_endpoint.quic_mapped_addr, d_endpoint.id), + // ]), + // by_id: HashMap::from([ + // (a_endpoint.id, a_endpoint), + // (b_endpoint.id, b_endpoint), + // (c_endpoint.id, c_endpoint), + // (d_endpoint.id, d_endpoint), + // ]), + // next_id: 5, + // path_selection: PathSelection::default(), + // }); + // let mut got = node_map.list_remote_infos(later); + // got.sort_by_key(|p| p.node_id); + // expect.sort_by_key(|p| p.node_id); + // remove_non_deterministic_fields(&mut got); + // assert_eq!(expect, got); + // } fn remove_non_deterministic_fields(infos: &mut [RemoteInfo]) { for info in infos.iter_mut() { @@ -1724,10 +1220,6 @@ mod tests { .collect(); let call_me_maybe = disco::CallMeMaybe { my_numbers }; - let ping_messages = ep.handle_call_me_maybe(call_me_maybe); - - // We have no relay server and no previous direct addresses, so we should get the same - // number of pings as direct addresses in the call-me-maybe. - assert_eq!(ping_messages.len(), my_numbers_count as usize); + ep.handle_call_me_maybe(call_me_maybe); } } diff --git a/iroh/src/magicsock/node_map/path_state.rs b/iroh/src/magicsock/node_map/path_state.rs index b5047129278..9277d0615ff 100644 --- a/iroh/src/magicsock/node_map/path_state.rs +++ b/iroh/src/magicsock/node_map/path_state.rs @@ -4,11 +4,10 @@ use std::collections::{BTreeMap, HashMap}; use iroh_base::NodeId; use n0_future::time::{Duration, Instant}; -use tracing::{Level, debug, event}; use super::{ - IpPort, PingRole, Source, - node_state::{ControlMsg, PongReply, SESSION_ACTIVE_TIMEOUT}, + IpPort, Source, + node_state::{ControlMsg, SESSION_ACTIVE_TIMEOUT}, }; use crate::{ disco::SendAddr, @@ -52,6 +51,7 @@ pub(super) struct PathState { /// /// See [`PathValidity`] docs. pub(super) validity: PathValidity, + /// When the last payload data was **received** via this path. /// /// This excludes DISCO messages. @@ -100,54 +100,6 @@ impl PathState { } } - pub(super) fn with_ping( - node_id: NodeId, - path: SendAddr, - tx_id: stun_rs::TransactionId, - source: Source, - now: Instant, - ) -> Self { - let mut new = PathState::new(node_id, path, source, now); - new.handle_ping(tx_id, now); - new - } - - pub(super) fn add_pong_reply(&mut self, r: PongReply) { - if let SendAddr::Udp(ref path) = self.path { - if self.validity.is_empty() { - event!( - target: "iroh::_events::holepunched", - Level::DEBUG, - remote_node = %self.node_id.fmt_short(), - path = ?path, - direction = "outgoing", - ); - } - } - - self.validity = PathValidity::new(r.pong_at, r.latency); - } - - pub(super) fn receive_payload(&mut self, now: Instant) { - self.last_payload_msg = Some(now); - self.validity - .receive_payload(now, path_validity::Source::QuicPayload); - } - - #[cfg(test)] - pub(super) fn with_pong_reply(node_id: NodeId, r: PongReply) -> Self { - PathState { - node_id, - path: r.from.clone(), - last_ping: None, - last_got_ping: None, - call_me_maybe_time: None, - validity: PathValidity::new(r.pong_at, r.latency), - last_payload_msg: None, - sources: HashMap::new(), - } - } - /// Check whether this path is considered active. /// /// Active means the path has received payload messages within the last @@ -167,6 +119,12 @@ impl PathState { self.last_got_ping.as_ref().map(|(time, _tx_id)| time) } + pub(super) fn receive_payload(&mut self, now: Instant) { + self.last_payload_msg = Some(now); + self.validity + .receive_payload(now, path_validity::Source::QuicPayload); + } + /// Reports the last instant this path was considered alive. /// /// Alive means the path is considered in use by the remote endpoint. Either because we @@ -198,10 +156,6 @@ impl PathState { /// Returns the time elapsed since the last control message, and the type of control message. pub(super) fn last_control_msg(&self, now: Instant) -> Option<(Duration, ControlMsg)> { // get every control message and assign it its kind - let last_pong = self - .validity - .latest_pong() - .map(|pong_at| (pong_at, ControlMsg::Pong)); let last_call_me_maybe = self .call_me_maybe_time .as_ref() @@ -210,9 +164,8 @@ impl PathState { .last_incoming_ping() .map(|ping| (*ping, ControlMsg::Ping)); - last_pong + last_call_me_maybe .into_iter() - .chain(last_call_me_maybe) .chain(last_ping) .max_by_key(|(instant, _kind)| *instant) .map(|(instant, kind)| (now.duration_since(instant), kind)) @@ -240,39 +193,6 @@ impl PathState { } } - pub(super) fn handle_ping(&mut self, tx_id: stun_rs::TransactionId, now: Instant) -> PingRole { - if Some(&tx_id) == self.last_got_ping.as_ref().map(|(_t, tx_id)| tx_id) { - PingRole::Duplicate - } else { - let prev = self.last_got_ping.replace((now, tx_id)); - let heartbeat_deadline = HEARTBEAT_INTERVAL + (HEARTBEAT_INTERVAL / 2); - match prev { - Some((prev_time, _tx)) if now.duration_since(prev_time) <= heartbeat_deadline => { - PingRole::LikelyHeartbeat - } - Some((prev_time, _tx)) => { - debug!( - elapsed = ?now.duration_since(prev_time), - "heartbeat missed, reactivating", - ); - PingRole::Activate - } - None => { - if let SendAddr::Udp(ref addr) = self.path { - event!( - target: "iroh::_events::holepunched", - Level::DEBUG, - remote_node = %self.node_id.fmt_short(), - path = ?addr, - direction = "incoming", - ); - } - PingRole::Activate - } - } - } - } - pub(super) fn add_source(&mut self, source: Source, now: Instant) { self.sources.insert(source, now); } diff --git a/iroh/src/magicsock/node_map/udp_paths.rs b/iroh/src/magicsock/node_map/udp_paths.rs index 29c43ccc99d..475f865e59b 100644 --- a/iroh/src/magicsock/node_map/udp_paths.rs +++ b/iroh/src/magicsock/node_map/udp_paths.rs @@ -7,7 +7,8 @@ //! [`NodeState`]: super::node_state::NodeState use std::{collections::BTreeMap, net::SocketAddr}; -use n0_future::time::Instant; +use n0_future::time::{Duration, Instant}; +use rand::seq::IteratorRandom; use tracing::{Level, event}; use super::{IpPort, path_state::PathState}; From c4baca831ace378aec27f3a55bb688cde0061802 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 11 Jul 2025 14:01:32 +0200 Subject: [PATCH 09/27] start tracking path events --- Cargo.lock | 45 +++++-------------- Cargo.toml | 6 +-- iroh/src/endpoint.rs | 13 ++++-- iroh/src/magicsock.rs | 24 +++++++++- iroh/src/magicsock/node_map/node_state.rs | 16 ------- iroh/src/magicsock/node_map/path_state.rs | 55 +---------------------- 6 files changed, 47 insertions(+), 112 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c3a6558f70f..aeb847038a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2050,7 +2050,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.59.0", + "windows-core 0.61.2", ] [[package]] @@ -2489,7 +2489,7 @@ dependencies = [ [[package]] name = "iroh-quinn" version = "0.14.0" -source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#e84f16856ea3ac79ed6c206b258d33a30d87834f" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#0dc50edf689ee5c6cf21b4ee5c0fea6af548680d" dependencies = [ "bytes", "cfg_aliases", @@ -2508,7 +2508,7 @@ dependencies = [ [[package]] name = "iroh-quinn-proto" version = "0.13.0" -source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#e84f16856ea3ac79ed6c206b258d33a30d87834f" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#0dc50edf689ee5c6cf21b4ee5c0fea6af548680d" dependencies = [ "bytes", "fastbloom", @@ -2544,7 +2544,7 @@ dependencies = [ [[package]] name = "iroh-quinn-udp" version = "0.5.12" -source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#140fdf97bfb706b1cac591b9711818c5df8012f4" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#79e3fcc710de68b40fd05be5421048bab658ddf4" dependencies = [ "cfg_aliases", "libc", @@ -5113,9 +5113,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.4" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ae868b5a0f67631c14589f7e250c1ea2c574ee5ba21c6c8dd4b1485705a5a1" +checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac" dependencies = [ "indexmap", "serde", @@ -5773,19 +5773,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-core" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce" -dependencies = [ - "windows-implement 0.59.0", - "windows-interface 0.59.1", - "windows-result 0.3.4", - "windows-strings 0.3.1", - "windows-targets 0.53.3", -] - [[package]] name = "windows-core" version = "0.61.2" @@ -5821,17 +5808,6 @@ dependencies = [ "syn 2.0.101", ] -[[package]] -name = "windows-implement" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "windows-implement" version = "0.60.0" @@ -5889,7 +5865,7 @@ checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ "windows-result 0.3.4", "windows-strings 0.3.1", - "windows-targets 0.53.3", + "windows-targets 0.53.2", ] [[package]] @@ -5980,7 +5956,7 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.3", + "windows-targets 0.53.2", ] [[package]] @@ -6031,11 +6007,10 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.3" +version = "0.53.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" dependencies = [ - "windows-link", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", diff --git a/Cargo.toml b/Cargo.toml index edb51ff85b4..eb098793da4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,6 @@ netwatch = { git = "https://github.com/n0-computer/net-tools", branch = "feat-mu # iroh-quinn-proto = { path = "../iroh-quinn/quinn-proto" } # iroh-quinn-udp = { path = "../iroh-quinn/quinn-udp" } -# iroh-quinn = { git = "https://github.com//n0-computer/quinn", branch = "multipath-misc" } -# iroh-quinn-proto = { git = "https://github.com//n0-computer/quinn", branch = "multipath-misc" } -# iroh-quinn-udp = { git = "https://github.com//n0-computer/quinn", branch = "multipath-misc" } +# iroh-quinn = { git = "https://github.com//n0-computer/quinn", branch = "flub/quinn-path-events-status" } +# iroh-quinn-proto = { git = "https://github.com//n0-computer/quinn", branch = "flub/quinn-path-events-status" } +# iroh-quinn-udp = { git = "https://github.com//n0-computer/quinn", branch = "flub/quinn-path-events-status" } diff --git a/iroh/src/endpoint.rs b/iroh/src/endpoint.rs index 3e1a7e90e6b..9d107511027 100644 --- a/iroh/src/endpoint.rs +++ b/iroh/src/endpoint.rs @@ -1771,13 +1771,20 @@ impl Future for Connecting { let conn = Connection { inner }; // Grab the remote identity and register this connection - if let Some(remote) = *this.remote_node_id { let weak_handle = conn.inner.weak_handle(); - this.ep.msock.register_connection(remote, weak_handle); + let path_events = conn.inner.path_events(); + this.ep + .msock + .register_connection(remote, weak_handle, path_events); } else if let Ok(remote) = conn.remote_node_id() { let weak_handle = conn.inner.weak_handle(); - this.ep.msock.register_connection(remote, weak_handle); + let path_events = conn.inner.path_events(); + this.ep + .msock + .register_connection(remote, weak_handle, path_events); + } else { + warn!("unable to determine node id for the remote"); } try_send_rtt_msg(&conn, this.ep, *this.remote_node_id); diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index 892000b2d3b..3067730de6f 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -43,6 +43,7 @@ use netwatch::netmon; #[cfg(not(wasm_browser))] use netwatch::{UdpSocket, ip::LocalAddresses}; use quinn::{AsyncUdpSocket, ServerConfig, WeakConnectionHandle}; +use quinn_proto::PathEvent; use rand::Rng; use smallvec::SmallVec; use snafu::{ResultExt, Snafu}; @@ -289,8 +290,29 @@ impl MagicSock { self.local_addrs_watch.clone().get() } - pub(crate) fn register_connection(&self, remote: NodeId, conn: WeakConnectionHandle) { + pub(crate) fn register_connection( + &self, + remote: NodeId, + conn: WeakConnectionHandle, + mut path_events: tokio::sync::broadcast::Receiver, + ) { self.connection_map.insert(remote, conn); + + // TODO: track task + // TODO: find a good home for this + task::spawn(async move { + loop { + match path_events.recv().await { + Ok(event) => { + info!(remote = %remote, "path event: {:?}", event); + } + Err(tokio::sync::broadcast::error::RecvError::Lagged(_)) => { + warn!("lagged path events"); + } + Err(tokio::sync::broadcast::error::RecvError::Closed) => break, + } + } + }); } #[cfg(not(wasm_browser))] diff --git a/iroh/src/magicsock/node_map/node_state.rs b/iroh/src/magicsock/node_map/node_state.rs index 26a4a6d63a5..96162223663 100644 --- a/iroh/src/magicsock/node_map/node_state.rs +++ b/iroh/src/magicsock/node_map/node_state.rs @@ -549,7 +549,6 @@ impl NodeState { // it's been less than 5 seconds ago. Also clear pongs for direct addresses not // included in the updated set. for (ipp, st) in self.udp_paths.paths.iter_mut() { - st.last_ping = None; if !call_me_maybe_ipps.contains(ipp) { // TODO: This seems like a weird way to signal that the endpoint no longer // thinks it has this IpPort as an available path. @@ -607,21 +606,6 @@ impl NodeState { self.last_used = Some(now); } - pub(super) fn last_ping(&self, addr: &SendAddr) -> Option { - match addr { - SendAddr::Udp(addr) => self - .udp_paths - .paths - .get(&(*addr).into()) - .and_then(|ep| ep.last_ping), - SendAddr::Relay(url) => self - .relay_url - .as_ref() - .filter(|(home_url, _state)| home_url == url) - .and_then(|(_home_url, state)| state.last_ping), - } - } - /// Checks if this `Endpoint` is currently actively being used. pub(super) fn is_active(&self, now: &Instant) -> bool { match self.last_used { diff --git a/iroh/src/magicsock/node_map/path_state.rs b/iroh/src/magicsock/node_map/path_state.rs index 9277d0615ff..ce3121539b0 100644 --- a/iroh/src/magicsock/node_map/path_state.rs +++ b/iroh/src/magicsock/node_map/path_state.rs @@ -17,12 +17,6 @@ use crate::{ }, }; -/// The minimum time between pings to an endpoint. -/// -/// Except in the case of CallMeMaybe frames resetting the counter, as the first pings -/// likely didn't through the firewall. -const DISCO_PING_INTERVAL: Duration = Duration::from_secs(5); - /// State about a particular path to another [`NodeState`]. /// /// This state is used for both the relay path and any direct UDP paths. @@ -34,13 +28,6 @@ pub(super) struct PathState { node_id: NodeId, /// The path this applies for. path: SendAddr, - /// The last (outgoing) ping time. - pub(super) last_ping: Option, - - /// If non-zero, means that this was an endpoint that we learned about at runtime (from an - /// incoming ping). If so, we keep the time updated and use it to discard old candidates. - // NOTE: tx_id Originally added in tailscale due to . - last_got_ping: Option<(Instant, stun_rs::TransactionId)>, /// The time this endpoint was last advertised via a call-me-maybe DISCO message. pub(super) call_me_maybe_time: Option, @@ -71,8 +58,6 @@ impl PathState { Self { node_id, path, - last_ping: None, - last_got_ping: None, call_me_maybe_time: None, validity: PathValidity::empty(), last_payload_msg: None, @@ -91,8 +76,6 @@ impl PathState { PathState { node_id, path, - last_ping: None, - last_got_ping: None, call_me_maybe_time: None, validity: PathValidity::empty(), last_payload_msg: Some(now), @@ -114,17 +97,11 @@ impl PathState { .unwrap_or(false) } - /// Returns the instant the last incoming ping was received. - pub(super) fn last_incoming_ping(&self) -> Option<&Instant> { - self.last_got_ping.as_ref().map(|(time, _tx_id)| time) - } - pub(super) fn receive_payload(&mut self, now: Instant) { self.last_payload_msg = Some(now); self.validity .receive_payload(now, path_validity::Source::QuicPayload); } - /// Reports the last instant this path was considered alive. /// /// Alive means the path is considered in use by the remote endpoint. Either because we @@ -142,14 +119,12 @@ impl PathState { .into_iter() .chain(self.last_payload_msg) .chain(self.call_me_maybe_time) - .chain(self.last_incoming_ping().cloned()) .max() } /// The last control or DISCO message **about** this path. /// /// This is the most recent instant among: - /// - when last pong was received. /// - when this path was last advertised in a received CallMeMaybe message. /// - when the last ping from them was received. /// @@ -160,13 +135,9 @@ impl PathState { .call_me_maybe_time .as_ref() .map(|call_me| (*call_me, ControlMsg::CallMeMaybe)); - let last_ping = self - .last_incoming_ping() - .map(|ping| (*ping, ControlMsg::Ping)); last_call_me_maybe .into_iter() - .chain(last_ping) .max_by_key(|(instant, _kind)| *instant) .map(|(instant, kind)| (now.duration_since(instant), kind)) } @@ -176,30 +147,11 @@ impl PathState { self.validity.latency() } - pub(super) fn needs_ping(&self, now: &Instant) -> bool { - match self.last_ping { - None => true, - Some(last_ping) => { - let elapsed = now.duration_since(last_ping); - - // TODO: remove! - // This logs "ping is too new" for each send whenever the endpoint does *not* need - // a ping. Pretty sure this is not a useful log, but maybe there was a reason? - // if !needs_ping { - // debug!("ping is too new: {}ms", elapsed.as_millis()); - // } - elapsed > DISCO_PING_INTERVAL - } - } - } - pub(super) fn add_source(&mut self, source: Source, now: Instant) { self.sources.insert(source, now); } pub(super) fn clear(&mut self) { - self.last_ping = None; - self.last_got_ping = None; self.call_me_maybe_time = None; self.validity = PathValidity::empty(); } @@ -212,12 +164,7 @@ impl PathState { if let Some(pong_at) = self.validity.latest_pong() { write!(w, "pong-received({:?} ago) ", pong_at.elapsed())?; } - if let Some(when) = self.last_incoming_ping() { - write!(w, "ping-received({:?} ago) ", when.elapsed())?; - } - if let Some(ref when) = self.last_ping { - write!(w, "ping-sent({:?} ago) ", when.elapsed())?; - } + if let Some(last_source) = self.sources.iter().max_by_key(|&(_, instant)| instant) { write!( w, From 549adee0491610dc2ed74a1c27b09a5d2fa87eea Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sat, 12 Jul 2025 15:42:27 +0200 Subject: [PATCH 10/27] start figuring out more details --- iroh/src/endpoint.rs | 46 +++++++++----- iroh/src/magicsock.rs | 112 ++++++++++++++++++++++++--------- iroh/src/magicsock/node_map.rs | 9 +++ 3 files changed, 125 insertions(+), 42 deletions(-) diff --git a/iroh/src/endpoint.rs b/iroh/src/endpoint.rs index 9d107511027..760673ad12f 100644 --- a/iroh/src/endpoint.rs +++ b/iroh/src/endpoint.rs @@ -735,14 +735,13 @@ impl Endpoint { self.add_node_addr(node_addr.clone())?; } let node_id = node_addr.node_id; - let direct_addresses = node_addr.direct_addresses.clone(); let relay_url = node_addr.relay_url.clone(); // Get the mapped IPv6 address from the magic socket. Quinn will connect to this // address. Start discovery for this node if it's enabled and we have no valid or // verified address information for this node. Dropping the discovery cancels any // still running task. - let (mapped_addr, _discovery_drop_guard) = self + let (mapped_addr, direct_addresses, _discovery_drop_guard) = self .get_mapping_addr_and_maybe_start_discovery(node_addr) .await .context(NoAddressSnafu)?; @@ -773,7 +772,11 @@ impl Endpoint { }; // TODO: race available addresses, this is currently only using the relay addr to connect - let dest_addr = mapped_addr.private_socket_addr(); + let dest_addr = if relay_url.is_none() && !direct_addresses.is_empty() { + direct_addresses[0] + } else { + mapped_addr.private_socket_addr() + }; let server_name = &tls::name::encode(node_id); let connect = self .msock @@ -781,10 +784,14 @@ impl Endpoint { .connect_with(client_config, dest_addr, server_name) .context(QuinnSnafu)?; + let mut paths = direct_addresses; + paths.push(mapped_addr.private_socket_addr()); + Ok(Connecting { inner: connect, ep: self.clone(), remote_node_id: Some(node_id), + paths, _discovery_drop_guard, }) } @@ -1379,18 +1386,20 @@ impl Endpoint { async fn get_mapping_addr_and_maybe_start_discovery( &self, node_addr: NodeAddr, - ) -> Result<(NodeIdMappedAddr, Option), GetMappingAddressError> { + ) -> Result<(NodeIdMappedAddr, Vec, Option), GetMappingAddressError> + { let node_id = node_addr.node_id; // Only return a mapped addr if we have some way of dialing this node, in other // words, we have either a relay URL or at least one direct address. let addr = if self.msock.has_send_address(node_id) { - self.msock.get_mapping_addr(node_id) + let maddr = self.msock.get_mapping_addr(node_id); + maddr.map(|maddr| (maddr, self.msock.get_direct_addrs(node_id))) } else { None }; match addr { - Some(addr) => { + Some((maddr, direct)) => { // We have some way of dialing this node, but that doesn't actually mean // we can actually connect to any of these addresses. // Therefore, we will invoke the discovery service if we haven't received from the @@ -1402,7 +1411,7 @@ impl Endpoint { let discovery = DiscoveryTask::maybe_start_after_delay(self, node_id, delay) .ok() .flatten(); - Ok((addr, discovery)) + Ok((maddr, direct, discovery)) } None => { @@ -1417,7 +1426,8 @@ impl Endpoint { .await .context(get_mapping_address_error::DiscoverSnafu)?; if let Some(addr) = self.msock.get_mapping_addr(node_id) { - Ok((addr, Some(discovery))) + let direct = self.msock.get_direct_addrs(node_id); + Ok((addr, direct, Some(discovery))) } else { Err(get_mapping_address_error::NoAddressSnafu.build()) } @@ -1646,6 +1656,8 @@ pub struct Connecting { inner: quinn::Connecting, ep: Endpoint, remote_node_id: Option, + /// Additional paths to open once a connection is created + paths: Vec, /// We run discovery as long as we haven't established a connection yet. #[debug("Option")] _discovery_drop_guard: Option, @@ -1774,15 +1786,21 @@ impl Future for Connecting { if let Some(remote) = *this.remote_node_id { let weak_handle = conn.inner.weak_handle(); let path_events = conn.inner.path_events(); - this.ep - .msock - .register_connection(remote, weak_handle, path_events); + this.ep.msock.register_connection( + remote, + weak_handle, + path_events, + this.paths.clone(), + ); } else if let Ok(remote) = conn.remote_node_id() { let weak_handle = conn.inner.weak_handle(); let path_events = conn.inner.path_events(); - this.ep - .msock - .register_connection(remote, weak_handle, path_events); + this.ep.msock.register_connection( + remote, + weak_handle, + path_events, + this.paths.clone(), + ); } else { warn!("unable to determine node id for the remote"); } diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index 3067730de6f..af9f8501ee9 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -295,8 +295,24 @@ impl MagicSock { remote: NodeId, conn: WeakConnectionHandle, mut path_events: tokio::sync::broadcast::Receiver, + paths: Vec, ) { self.connection_map.insert(remote, conn); + task::spawn(async move { + let conn = conn.clone(); + for addr in paths { + match conn.open_path(addr, quinn_proto::PathStatus::Backup).await { + Ok(path) => { + path.set_max_idle_timeout(Some(ENDPOINTS_FRESH_ENOUGH_DURATION)) + .ok(); + path.set_keep_alive_interval(Some(HEARTBEAT_INTERVAL)).ok(); + } + Err(err) => { + warn!("failed to open path {:?}", err); + } + } + } + }); // TODO: track task // TODO: find a good home for this @@ -422,6 +438,10 @@ impl MagicSock { self.node_map.get_quic_mapped_addr_for_node_key(node_id) } + pub(crate) fn get_direct_addrs(&self, node_id: NodeId) -> Vec { + self.node_map.get_direct_addrs(node_id) + } + /// Add addresses for a node to the magic socket's addresbook. #[instrument(skip_all)] pub fn add_node_addr( @@ -1055,6 +1075,40 @@ impl MagicSock { } } +/// Definies the translation of addresses in quinn land vs iroh land. +/// +/// This is necessary, because quinn can only reason about `SocketAddr`s. +#[derive(Clone, Debug)] +pub(crate) enum MultipathMappedAddr { + /// Used for the initial connection. + /// - Only used for sending + /// - This means send on all known paths/transports + Mixed(NodeIdMappedAddr), + /// Relay based transport address + Relay(IpMappedAddr), // TODO: RelayMappedAddr? + /// IP based transport address + #[cfg(not(wasm_browser))] + Ip(SocketAddr), +} + +impl From for MultipathMappedAddr { + fn from(value: SocketAddr) -> Self { + match value.ip() { + IpAddr::V4(_) => Self::Ip(value), + IpAddr::V6(addr) => { + if let Ok(node_id_mapped_addr) = NodeIdMappedAddr::try_from(addr) { + return Self::Mixed(node_id_mapped_addr); + } + #[cfg(not(wasm_browser))] + if let Ok(ip_mapped_addr) = IpMappedAddr::try_from(addr) { + return Self::Relay(ip_mapped_addr); + } + MappedAddr::Self(value) + } + } + } +} + #[derive(Clone, Debug)] enum MappedAddr { NodeId(NodeIdMappedAddr), @@ -3188,17 +3242,18 @@ mod tests { let _accept_task = AbortOnDropHandle::new(accept_task); // Add an empty entry in the NodeMap of ep_1 - msock_1.node_map.add_node_addr( - NodeAddr { - node_id: node_id_2, - relay_url: None, - direct_addresses: Default::default(), - }, - Source::NamedApp { - name: "test".into(), - }, - &msock_1.metrics.magicsock, - ); + msock_1 + .add_node_addr( + NodeAddr { + node_id: node_id_2, + relay_url: None, + direct_addresses: Default::default(), + }, + Source::NamedApp { + name: "test".into(), + }, + ) + .unwrap(); let addr_2 = msock_1.get_mapping_addr(node_id_2).unwrap(); // Set a low max_idle_timeout so quinn gives up on this quickly and our test does @@ -3225,23 +3280,24 @@ mod tests { info!("first connect timed out as expected"); // Provide correct addressing information - msock_1.node_map.add_node_addr( - NodeAddr { - node_id: node_id_2, - relay_url: None, - direct_addresses: msock_2 - .direct_addresses() - .initialized() - .await - .into_iter() - .map(|x| x.addr) - .collect(), - }, - Source::NamedApp { - name: "test".into(), - }, - &msock_1.metrics.magicsock, - ); + msock_1 + .add_node_addr( + NodeAddr { + node_id: node_id_2, + relay_url: None, + direct_addresses: msock_2 + .direct_addresses() + .initialized() + .await + .into_iter() + .map(|x| x.addr) + .collect(), + }, + Source::NamedApp { + name: "test".into(), + }, + ) + .unwrap(); // We can now connect tokio::time::timeout(Duration::from_secs(10), async move { diff --git a/iroh/src/magicsock/node_map.rs b/iroh/src/magicsock/node_map.rs index 11b135046e3..4eb8e3df716 100644 --- a/iroh/src/magicsock/node_map.rs +++ b/iroh/src/magicsock/node_map.rs @@ -177,6 +177,15 @@ impl NodeMap { .map(|ep| *ep.quic_mapped_addr()) } + pub(super) fn get_direct_addrs(&self, node_key: NodeId) -> Vec { + self.inner + .lock() + .expect("poisoned") + .get(NodeStateKey::NodeId(node_key)) + .map(|ep| ep.direct_addresses().map(Into::into).collect()) + .unwrap_or_default() + } + pub(super) fn handle_call_me_maybe( &self, sender: PublicKey, From 9ef5765db433a149c10fe84fc1d069e4f1cf3d96 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Mon, 14 Jul 2025 12:27:01 +0200 Subject: [PATCH 11/27] wip --- iroh/src/endpoint.rs | 60 ++++++------------- iroh/src/magicsock.rs | 74 +++++++----------------- iroh/src/magicsock/relay_mapped_addrs.rs | 54 +++++++++++++++++ iroh/src/magicsock/transports/relay.rs | 2 +- iroh/src/net_report/ip_mapped_addrs.rs | 2 +- 5 files changed, 96 insertions(+), 96 deletions(-) create mode 100644 iroh/src/magicsock/relay_mapped_addrs.rs diff --git a/iroh/src/endpoint.rs b/iroh/src/endpoint.rs index 760673ad12f..fdde0720611 100644 --- a/iroh/src/endpoint.rs +++ b/iroh/src/endpoint.rs @@ -34,9 +34,8 @@ use url::Url; #[cfg(wasm_browser)] use crate::discovery::pkarr::PkarrResolver; -#[cfg(not(wasm_browser))] -use crate::{discovery::dns::DnsDiscovery, dns::DnsResolver}; use crate::{ + RelayProtocol, discovery::{ ConcurrentDiscovery, Discovery, DiscoveryContext, DiscoveryError, DiscoveryItem, DiscoverySubscribers, DiscoveryTask, DynIntoDiscovery, IntoDiscovery, IntoDiscoveryError, @@ -47,6 +46,8 @@ use crate::{ net_report::Report, tls, }; +#[cfg(not(wasm_browser))] +use crate::{discovery::dns::DnsDiscovery, dns::DnsResolver}; mod rtt_actor; @@ -735,13 +736,12 @@ impl Endpoint { self.add_node_addr(node_addr.clone())?; } let node_id = node_addr.node_id; - let relay_url = node_addr.relay_url.clone(); // Get the mapped IPv6 address from the magic socket. Quinn will connect to this // address. Start discovery for this node if it's enabled and we have no valid or // verified address information for this node. Dropping the discovery cancels any // still running task. - let (mapped_addr, direct_addresses, _discovery_drop_guard) = self + let (mapped_addr, _discovery_drop_guard) = self .get_mapping_addr_and_maybe_start_discovery(node_addr) .await .context(NoAddressSnafu)?; @@ -753,12 +753,7 @@ impl Endpoint { // Start connecting via quinn. This will time out after 10 seconds if no reachable // address is available. - debug!( - ?mapped_addr, - ?direct_addresses, - ?relay_url, - "Attempting connection..." - ); + debug!(?mapped_addr, "Attempting connection..."); let client_config = { let mut alpn_protocols = vec![alpn.to_vec()]; alpn_protocols.extend(options.additional_alpns); @@ -771,12 +766,7 @@ impl Endpoint { client_config }; - // TODO: race available addresses, this is currently only using the relay addr to connect - let dest_addr = if relay_url.is_none() && !direct_addresses.is_empty() { - direct_addresses[0] - } else { - mapped_addr.private_socket_addr() - }; + let dest_addr = mapped_addr.private_socket_addr(); let server_name = &tls::name::encode(node_id); let connect = self .msock @@ -784,14 +774,10 @@ impl Endpoint { .connect_with(client_config, dest_addr, server_name) .context(QuinnSnafu)?; - let mut paths = direct_addresses; - paths.push(mapped_addr.private_socket_addr()); - Ok(Connecting { inner: connect, ep: self.clone(), remote_node_id: Some(node_id), - paths, _discovery_drop_guard, }) } @@ -1386,20 +1372,18 @@ impl Endpoint { async fn get_mapping_addr_and_maybe_start_discovery( &self, node_addr: NodeAddr, - ) -> Result<(NodeIdMappedAddr, Vec, Option), GetMappingAddressError> - { + ) -> Result<(NodeIdMappedAddr, Option), GetMappingAddressError> { let node_id = node_addr.node_id; // Only return a mapped addr if we have some way of dialing this node, in other // words, we have either a relay URL or at least one direct address. let addr = if self.msock.has_send_address(node_id) { - let maddr = self.msock.get_mapping_addr(node_id); - maddr.map(|maddr| (maddr, self.msock.get_direct_addrs(node_id))) + self.msock.get_mapping_addr(node_id) } else { None }; match addr { - Some((maddr, direct)) => { + Some(maddr) => { // We have some way of dialing this node, but that doesn't actually mean // we can actually connect to any of these addresses. // Therefore, we will invoke the discovery service if we haven't received from the @@ -1411,7 +1395,7 @@ impl Endpoint { let discovery = DiscoveryTask::maybe_start_after_delay(self, node_id, delay) .ok() .flatten(); - Ok((maddr, direct, discovery)) + Ok((maddr, discovery)) } None => { @@ -1426,8 +1410,7 @@ impl Endpoint { .await .context(get_mapping_address_error::DiscoverSnafu)?; if let Some(addr) = self.msock.get_mapping_addr(node_id) { - let direct = self.msock.get_direct_addrs(node_id); - Ok((addr, direct, Some(discovery))) + Ok((addr, Some(discovery))) } else { Err(get_mapping_address_error::NoAddressSnafu.build()) } @@ -1656,8 +1639,6 @@ pub struct Connecting { inner: quinn::Connecting, ep: Endpoint, remote_node_id: Option, - /// Additional paths to open once a connection is created - paths: Vec, /// We run discovery as long as we haven't established a connection yet. #[debug("Option")] _discovery_drop_guard: Option, @@ -1786,21 +1767,15 @@ impl Future for Connecting { if let Some(remote) = *this.remote_node_id { let weak_handle = conn.inner.weak_handle(); let path_events = conn.inner.path_events(); - this.ep.msock.register_connection( - remote, - weak_handle, - path_events, - this.paths.clone(), - ); + this.ep + .msock + .register_connection(remote, weak_handle, path_events); } else if let Ok(remote) = conn.remote_node_id() { let weak_handle = conn.inner.weak_handle(); let path_events = conn.inner.path_events(); - this.ep.msock.register_connection( - remote, - weak_handle, - path_events, - this.paths.clone(), - ); + this.ep + .msock + .register_connection(remote, weak_handle, path_events); } else { warn!("unable to determine node id for the remote"); } @@ -2287,6 +2262,7 @@ mod tests { }; use iroh_base::{NodeAddr, NodeId, SecretKey}; + use iroh_relay::http::Protocol; use n0_future::{StreamExt, task::AbortOnDropHandle}; use n0_snafu::{Error, Result, ResultExt}; use n0_watcher::Watcher; diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index af9f8501ee9..0c5ab9c3534 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -45,6 +45,7 @@ use netwatch::{UdpSocket, ip::LocalAddresses}; use quinn::{AsyncUdpSocket, ServerConfig, WeakConnectionHandle}; use quinn_proto::PathEvent; use rand::Rng; +use relay_mapped_addrs::RelayMappedAddresses; use smallvec::SmallVec; use snafu::{ResultExt, Snafu}; use tokio::sync::{Mutex as AsyncMutex, mpsc}; @@ -79,6 +80,7 @@ use crate::{ mod metrics; mod node_map; +mod relay_mapped_addrs; pub(crate) mod transports; @@ -200,6 +202,8 @@ pub(crate) struct MagicSock { /// Tracks the mapped IP addresses ip_mapped_addrs: IpMappedAddresses, + /// Tracks the mapped IP addresses + relay_mapped_addrs: RelayMappedAddresses, /// Local addresses local_addrs_watch: LocalAddrsWatch, /// Currently bound IP addresses of all sockets @@ -295,25 +299,10 @@ impl MagicSock { remote: NodeId, conn: WeakConnectionHandle, mut path_events: tokio::sync::broadcast::Receiver, - paths: Vec, ) { self.connection_map.insert(remote, conn); - task::spawn(async move { - let conn = conn.clone(); - for addr in paths { - match conn.open_path(addr, quinn_proto::PathStatus::Backup).await { - Ok(path) => { - path.set_max_idle_timeout(Some(ENDPOINTS_FRESH_ENOUGH_DURATION)) - .ok(); - path.set_keep_alive_interval(Some(HEARTBEAT_INTERVAL)).ok(); - } - Err(err) => { - warn!("failed to open path {:?}", err); - } - } - } - }); + // TODO: open additional paths // TODO: track task // TODO: find a good home for this task::spawn(async move { @@ -461,6 +450,11 @@ impl MagicSock { self.node_map .add_node_addr(addr.clone(), source, &self.metrics.magicsock); + if let Some(url) = addr.relay_url() { + self.relay_mapped_addrs + .get_or_register(url.clone(), addr.node_id); + } + // Add paths to the existing connections self.add_paths(addr); @@ -633,11 +627,8 @@ impl MagicSock { let mut active_paths = SmallVec::<[_; 3]>::new(); - match MappedAddr::from(transmit.destination) { - MappedAddr::None(addr) => { - active_paths.push(transports::Addr::from(addr)); - } - MappedAddr::NodeId(dest) => { + match MultipathMappedAddr::from(transmit.destination) { + MultipathMappedAddr::Mixed(dest) => { trace!( dst = %dest, src = ?transmit.src_ip, @@ -670,7 +661,10 @@ impl MagicSock { } } #[cfg(not(wasm_browser))] - MappedAddr::Ip(dest) => { + MultipathMappedAddr::Ip(addr) => { + active_paths.push(transports::Addr::Ip(addr)); + } + MultipathMappedAddr::Relay(dest) => { trace!( dst = %dest, src = ?transmit.src_ip, @@ -680,9 +674,9 @@ impl MagicSock { // Check if this is a known IpMappedAddr, and if so, send over UDP // Get the socket addr - match self.ip_mapped_addrs.get_ip_addr(&dest) { - Some(addr) => { - active_paths.push(transports::Addr::from(addr)); + match self.relay_mapped_addrs.get_url(&dest) { + Some((relay, node_id)) => { + active_paths.push(transports::Addr::Relay(relay, node_id)); } None => { error!(%dest, "unknown mapped address"); @@ -1103,33 +1097,7 @@ impl From for MultipathMappedAddr { if let Ok(ip_mapped_addr) = IpMappedAddr::try_from(addr) { return Self::Relay(ip_mapped_addr); } - MappedAddr::Self(value) - } - } - } -} - -#[derive(Clone, Debug)] -enum MappedAddr { - NodeId(NodeIdMappedAddr), - #[cfg(not(wasm_browser))] - Ip(IpMappedAddr), - None(SocketAddr), -} - -impl From for MappedAddr { - fn from(value: SocketAddr) -> Self { - match value.ip() { - IpAddr::V4(_) => MappedAddr::None(value), - IpAddr::V6(addr) => { - if let Ok(node_id_mapped_addr) = NodeIdMappedAddr::try_from(addr) { - return MappedAddr::NodeId(node_id_mapped_addr); - } - #[cfg(not(wasm_browser))] - if let Ok(ip_mapped_addr) = IpMappedAddr::try_from(addr) { - return MappedAddr::Ip(ip_mapped_addr); - } - MappedAddr::None(value) + Self::Ip(value) } } } @@ -1318,6 +1286,7 @@ impl Handle { bind_ip(addr_v4, addr_v6, &metrics).context(BindSocketsSnafu)?; let ip_mapped_addrs = IpMappedAddresses::default(); + let relay_mapped_addrs = RelayMappedAddresses::default(); let (actor_sender, actor_receiver) = mpsc::channel(256); @@ -1367,6 +1336,7 @@ impl Handle { node_map, connection_map: Default::default(), ip_mapped_addrs: ip_mapped_addrs.clone(), + relay_mapped_addrs, discovery, discovery_user_data: RwLock::new(discovery_user_data), direct_addrs: Default::default(), diff --git a/iroh/src/magicsock/relay_mapped_addrs.rs b/iroh/src/magicsock/relay_mapped_addrs.rs new file mode 100644 index 00000000000..32c8b866220 --- /dev/null +++ b/iroh/src/magicsock/relay_mapped_addrs.rs @@ -0,0 +1,54 @@ +use std::{collections::BTreeMap, sync::Arc}; + +use iroh_base::{NodeId, RelayUrl}; +use snafu::Snafu; + +use crate::net_report::IpMappedAddr; + +/// Can occur when converting a [`SocketAddr`] to an [`RelayMappedAddr`] +#[derive(Debug, Snafu)] +#[snafu(display("Failed to convert"))] +pub struct RelayMappedAddrError; + +/// A Map of [`RelayMappedAddresses`] to [`SocketAddr`]. +#[derive(Debug, Clone, Default)] +pub(crate) struct RelayMappedAddresses(Arc>); + +#[derive(Debug, Default)] +pub(super) struct Inner { + by_mapped_addr: BTreeMap, + by_url: BTreeMap<(RelayUrl, NodeId), IpMappedAddr>, +} + +impl RelayMappedAddresses { + /// Adds a [`RelayUrl`] to the map and returns the generated [`IpMappedAddr`]. + /// + /// If this [`RelayUrl`] already exists in the map, it returns its + /// associated [`IpMappedAddr`]. + /// + /// Otherwise a new [`IpMappedAddr`] is generated for it and returned. + pub(super) fn get_or_register(&self, relay: RelayUrl, node: NodeId) -> IpMappedAddr { + let mut inner = self.0.lock().expect("poisoned"); + if let Some(mapped_addr) = inner.by_url.get(&(relay.clone(), node)) { + return *mapped_addr; + } + let ip_mapped_addr = IpMappedAddr::generate(); + inner + .by_mapped_addr + .insert(ip_mapped_addr, (relay.clone(), node)); + inner.by_url.insert((relay, node), ip_mapped_addr); + ip_mapped_addr + } + + /// Returns the [`IpMappedAddr`] for the given [`RelayUrl`]. + pub(crate) fn get_mapped_addr(&self, relay: RelayUrl, node: NodeId) -> Option { + let inner = self.0.lock().expect("poisoned"); + inner.by_url.get(&(relay, node)).copied() + } + + /// Returns the [`RelayUrl`] for the given [`IpMappedAddr`]. + pub(crate) fn get_url(&self, mapped_addr: &IpMappedAddr) -> Option<(RelayUrl, NodeId)> { + let inner = self.0.lock().expect("poisoned"); + inner.by_mapped_addr.get(mapped_addr).cloned() + } +} diff --git a/iroh/src/magicsock/transports/relay.rs b/iroh/src/magicsock/transports/relay.rs index 295266ec559..33760fdcde3 100644 --- a/iroh/src/magicsock/transports/relay.rs +++ b/iroh/src/magicsock/transports/relay.rs @@ -126,7 +126,7 @@ impl RelayTransport { .segment_size .map_or(dm.datagrams.contents.len(), |s| u16::from(s) as usize); meta_out.ecn = None; - meta_out.dst_ip = None; // TODO: insert the relay url for this relay + meta_out.dst_ip = None; *addr = (dm.url, dm.src).into(); num_msgs += 1; diff --git a/iroh/src/net_report/ip_mapped_addrs.rs b/iroh/src/net_report/ip_mapped_addrs.rs index be7da1867ae..564134e7be9 100644 --- a/iroh/src/net_report/ip_mapped_addrs.rs +++ b/iroh/src/net_report/ip_mapped_addrs.rs @@ -38,7 +38,7 @@ impl IpMappedAddr { /// /// This generates a new IPv6 address in the Unique Local Address range (RFC 4193) /// which is recognised by iroh as an IP mapped address. - pub(super) fn generate() -> Self { + pub(crate) fn generate() -> Self { let mut addr = [0u8; 16]; addr[0] = Self::ADDR_PREFIXL; addr[1..6].copy_from_slice(&Self::ADDR_GLOBAL_ID); From 75d55252c64e4749b756b1c17f510bd5a48edf19 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 18 Jul 2025 17:21:25 +0200 Subject: [PATCH 12/27] get some stuff to work again --- iroh/src/endpoint.rs | 1 - iroh/src/magicsock.rs | 20 ++++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/iroh/src/endpoint.rs b/iroh/src/endpoint.rs index fdde0720611..856ca358104 100644 --- a/iroh/src/endpoint.rs +++ b/iroh/src/endpoint.rs @@ -2262,7 +2262,6 @@ mod tests { }; use iroh_base::{NodeAddr, NodeId, SecretKey}; - use iroh_relay::http::Protocol; use n0_future::{StreamExt, task::AbortOnDropHandle}; use n0_snafu::{Error, Result, ResultExt}; use n0_watcher::Watcher; diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index 0c5ab9c3534..cf034703259 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -643,17 +643,20 @@ impl MagicSock { self.ipv6_reported.load(Ordering::Relaxed), &self.metrics.magicsock, ) { - Some((node_id, _udp_addr, relay_url, ping_actions)) => { + Some((node_id, udp_addr, relay_url, ping_actions)) => { if !ping_actions.is_empty() { self.actor_sender .try_send(ActorMessage::PingActions(ping_actions)) .ok(); } - // NodeId mapped addrs are only used for relays, currently. - // IP based addrs will have been added as individual paths + // Mixed will send all available addrs + if let Some(url) = relay_url { active_paths.push(transports::Addr::Relay(url, node_id)); } + if let Some(addr) = udp_addr { + active_paths.push(transports::Addr::Ip(addr)); + } } None => { error!(%dest, "no NodeState for mapped address"); @@ -812,15 +815,16 @@ impl MagicSock { quic_packets_total += quic_datagram_count; quinn_meta.addr = ip_mapped_addr.private_socket_addr(); } else { - warn!( + trace!( src = %addr, count = %quic_datagram_count, len = quinn_meta.len, - "UDP recv quic packets: no node state found, skipping", + "UDP recv quic packets: no node state found", ); - // If we have no node state for the from addr, set len to 0 to make - // quinn skip the buf completely. - quinn_meta.len = 0; + + // TODO: register in node map + quic_packets_total += quic_datagram_count; + quinn_meta.addr = *addr; } } Some((node_id, quic_mapped_addr)) => { From 4f78898cdf7040636f82b16aa9b837f10db21a12 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Mon, 21 Jul 2025 12:14:27 +0200 Subject: [PATCH 13/27] remove ip_mapped_addresses --- iroh/src/magicsock.rs | 44 ++------ iroh/src/magicsock/relay_mapped_addrs.rs | 83 +++++++++++++- iroh/src/net_report.rs | 33 ++---- iroh/src/net_report/ip_mapped_addrs.rs | 134 ----------------------- iroh/src/net_report/reportgen.rs | 19 +--- 5 files changed, 105 insertions(+), 208 deletions(-) delete mode 100644 iroh/src/net_report/ip_mapped_addrs.rs diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index cf034703259..515be0ce9c6 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -45,7 +45,7 @@ use netwatch::{UdpSocket, ip::LocalAddresses}; use quinn::{AsyncUdpSocket, ServerConfig, WeakConnectionHandle}; use quinn_proto::PathEvent; use rand::Rng; -use relay_mapped_addrs::RelayMappedAddresses; +use relay_mapped_addrs::{IpMappedAddr, RelayMappedAddresses}; use smallvec::SmallVec; use snafu::{ResultExt, Snafu}; use tokio::sync::{Mutex as AsyncMutex, mpsc}; @@ -68,14 +68,14 @@ use crate::dns::DnsResolver; #[cfg(any(test, feature = "test-utils"))] use crate::endpoint::PathSelection; #[cfg(not(wasm_browser))] -use crate::net_report::{IpMappedAddr, QuicConfig}; +use crate::net_report::QuicConfig; use crate::{ defaults::timeouts::NET_REPORT_TIMEOUT, disco::{self, SendAddr}, discovery::{Discovery, DiscoveryItem, DiscoverySubscribers, NodeData, UserData}, key::{DecryptionError, SharedSecret, public_ed_box, secret_ed_box}, metrics::EndpointMetrics, - net_report::{self, IfStateDetails, IpMappedAddresses, Report}, + net_report::{self, IfStateDetails, Report}, }; mod metrics; @@ -200,8 +200,6 @@ pub(crate) struct MagicSock { /// Tracks existing connections connection_map: ConnectionMap, - /// Tracks the mapped IP addresses - ip_mapped_addrs: IpMappedAddresses, /// Tracks the mapped IP addresses relay_mapped_addrs: RelayMappedAddresses, /// Local addresses @@ -802,30 +800,15 @@ impl MagicSock { // Update the NodeMap and remap RecvMeta to the NodeIdMappedAddr. match self.node_map.receive_udp(*addr) { None => { - // Check if this address is mapped to an IpMappedAddr - if let Some(ip_mapped_addr) = - self.ip_mapped_addrs.get_mapped_addr(addr) - { - trace!( - src = %addr, - count = %quic_datagram_count, - len = quinn_meta.len, - "UDP recv QUIC address discovery packets", - ); - quic_packets_total += quic_datagram_count; - quinn_meta.addr = ip_mapped_addr.private_socket_addr(); - } else { - trace!( - src = %addr, - count = %quic_datagram_count, - len = quinn_meta.len, - "UDP recv quic packets: no node state found", - ); - - // TODO: register in node map - quic_packets_total += quic_datagram_count; - quinn_meta.addr = *addr; - } + trace!( + src = %addr, + count = %quic_datagram_count, + len = quinn_meta.len, + "UDP recv quic packets", + ); + + quic_packets_total += quic_datagram_count; + quinn_meta.addr = *addr; } Some((node_id, quic_mapped_addr)) => { trace!( @@ -1289,7 +1272,6 @@ impl Handle { let (ip_transports, port_mapper) = bind_ip(addr_v4, addr_v6, &metrics).context(BindSocketsSnafu)?; - let ip_mapped_addrs = IpMappedAddresses::default(); let relay_mapped_addrs = RelayMappedAddresses::default(); let (actor_sender, actor_receiver) = mpsc::channel(256); @@ -1339,7 +1321,6 @@ impl Handle { ipv6_reported, node_map, connection_map: Default::default(), - ip_mapped_addrs: ip_mapped_addrs.clone(), relay_mapped_addrs, discovery, discovery_user_data: RwLock::new(discovery_user_data), @@ -1412,7 +1393,6 @@ impl Handle { #[cfg(not(wasm_browser))] dns_resolver, #[cfg(not(wasm_browser))] - Some(ip_mapped_addrs), relay_map.clone(), net_report_config, metrics.net_report.clone(), diff --git a/iroh/src/magicsock/relay_mapped_addrs.rs b/iroh/src/magicsock/relay_mapped_addrs.rs index 32c8b866220..1ac1eaabacc 100644 --- a/iroh/src/magicsock/relay_mapped_addrs.rs +++ b/iroh/src/magicsock/relay_mapped_addrs.rs @@ -1,9 +1,88 @@ -use std::{collections::BTreeMap, sync::Arc}; +use std::{ + collections::BTreeMap, + net::{IpAddr, Ipv6Addr, SocketAddr}, + sync::{ + atomic::{AtomicU64, Ordering}, + Arc, + }, +}; use iroh_base::{NodeId, RelayUrl}; use snafu::Snafu; -use crate::net_report::IpMappedAddr; +/// Can occur when converting a [`SocketAddr`] to an [`IpMappedAddr`] +#[derive(Debug, Snafu)] +#[snafu(display("Failed to convert"))] +pub struct IpMappedAddrError; + +/// A map fake Ipv6 address with an actual IP address. +/// +/// It is essentially a lookup key for an IP that iroh's magicsocket knows about. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub(crate) struct IpMappedAddr(Ipv6Addr); + +/// Counter to always generate unique addresses for [`IpMappedAddr`]. +static IP_ADDR_COUNTER: AtomicU64 = AtomicU64::new(1); + +impl IpMappedAddr { + /// The Prefix/L of our Unique Local Addresses. + const ADDR_PREFIXL: u8 = 0xfd; + /// The Global ID used in our Unique Local Addresses. + const ADDR_GLOBAL_ID: [u8; 5] = [21, 7, 10, 81, 11]; + /// The Subnet ID used in our Unique Local Addresses. + const ADDR_SUBNET: [u8; 2] = [0, 1]; + + /// The dummy port used for all mapped addresses. + const MAPPED_ADDR_PORT: u16 = 12345; + + /// Generates a globally unique fake UDP address. + /// + /// This generates a new IPv6 address in the Unique Local Address range (RFC 4193) + /// which is recognised by iroh as an IP mapped address. + pub(crate) fn generate() -> Self { + let mut addr = [0u8; 16]; + addr[0] = Self::ADDR_PREFIXL; + addr[1..6].copy_from_slice(&Self::ADDR_GLOBAL_ID); + addr[6..8].copy_from_slice(&Self::ADDR_SUBNET); + + let counter = IP_ADDR_COUNTER.fetch_add(1, Ordering::Relaxed); + addr[8..16].copy_from_slice(&counter.to_be_bytes()); + + Self(Ipv6Addr::from(addr)) + } + + /// Returns a consistent [`SocketAddr`] for the [`IpMappedAddr`]. + /// + /// This does not have a routable IP address. + /// + /// This uses a made-up, but fixed port number. The [IpMappedAddresses`] map this is + /// made for creates a unique [`IpMappedAddr`] for each IP+port and thus does not use + /// the port to map back to the original [`SocketAddr`]. + pub(crate) fn private_socket_addr(&self) -> SocketAddr { + SocketAddr::new(IpAddr::from(self.0), Self::MAPPED_ADDR_PORT) + } +} + +impl TryFrom for IpMappedAddr { + type Error = IpMappedAddrError; + + fn try_from(value: Ipv6Addr) -> std::result::Result { + let octets = value.octets(); + if octets[0] == Self::ADDR_PREFIXL + && octets[1..6] == Self::ADDR_GLOBAL_ID + && octets[6..8] == Self::ADDR_SUBNET + { + return Ok(Self(value)); + } + Err(IpMappedAddrError) + } +} + +impl std::fmt::Display for IpMappedAddr { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "IpMappedAddr({})", self.0) + } +} /// Can occur when converting a [`SocketAddr`] to an [`RelayMappedAddr`] #[derive(Debug, Snafu)] diff --git a/iroh/src/net_report.rs b/iroh/src/net_report.rs index e3ab4f5bb45..78b1c28c5e3 100644 --- a/iroh/src/net_report.rs +++ b/iroh/src/net_report.rs @@ -46,7 +46,6 @@ use self::reportgen::QadProbeReport; use self::reportgen::{ProbeFinished, ProbeReport}; mod defaults; -mod ip_mapped_addrs; mod metrics; mod probes; mod report; @@ -73,8 +72,6 @@ pub(crate) mod portmapper { } } -pub(crate) use ip_mapped_addrs::{IpMappedAddr, IpMappedAddresses}; - pub(crate) use self::reportgen::IfStateDetails; #[cfg(not(wasm_browser))] use self::reportgen::SocketState; @@ -215,7 +212,6 @@ impl Client { /// Creates a new net_report client. pub(crate) fn new( #[cfg(not(wasm_browser))] dns_resolver: DnsResolver, - #[cfg(not(wasm_browser))] ip_mapped_addrs: Option, relay_map: RelayMap, opts: Options, metrics: Arc, @@ -233,7 +229,6 @@ impl Client { let socket_state = SocketState { quic_client, dns_resolver, - ip_mapped_addrs, }; Client { @@ -438,7 +433,6 @@ impl Client { for relay_node in self.relay_map.nodes().take(MAX_RELAYS) { if if_state.have_v4 { debug!(?relay_node.url, "v4 QAD probe"); - let ip_mapped_addrs = self.socket_state.ip_mapped_addrs.clone(); let relay_node = relay_node.clone(); let dns_resolver = self.socket_state.dns_resolver.clone(); let quic_client = quic_client.clone(); @@ -448,7 +442,7 @@ impl Client { .child_token() .run_until_cancelled_owned(time::timeout( PROBES_TIMEOUT, - run_probe_v4(ip_mapped_addrs, relay_node, quic_client, dns_resolver), + run_probe_v4(relay_node, quic_client, dns_resolver), )) .instrument(info_span!("QAD-IPv4", %relay_url)), ); @@ -456,7 +450,6 @@ impl Client { if if_state.have_v6 { debug!(?relay_node.url, "v6 QAD probe"); - let ip_mapped_addrs = self.socket_state.ip_mapped_addrs.clone(); let relay_node = relay_node.clone(); let dns_resolver = self.socket_state.dns_resolver.clone(); let quic_client = quic_client.clone(); @@ -466,7 +459,7 @@ impl Client { .child_token() .run_until_cancelled_owned(time::timeout( PROBES_TIMEOUT, - run_probe_v6(ip_mapped_addrs, relay_node, quic_client, dns_resolver), + run_probe_v6(relay_node, quic_client, dns_resolver), )) .instrument(info_span!("QAD-IPv6", %relay_url)), ); @@ -682,20 +675,17 @@ impl Client { #[cfg(not(wasm_browser))] async fn run_probe_v4( - ip_mapped_addrs: Option, relay_node: Arc, quic_client: QuicClient, dns_resolver: DnsResolver, ) -> n0_snafu::Result<(QadProbeReport, QadConn)> { use n0_snafu::ResultExt; - let relay_addr_orig = reportgen::get_relay_addr_ipv4(&dns_resolver, &relay_node).await?; - let relay_addr = - reportgen::maybe_to_mapped_addr(ip_mapped_addrs.as_ref(), relay_addr_orig.into()); + let relay_addr = reportgen::get_relay_addr_ipv4(&dns_resolver, &relay_node).await?; - debug!(?relay_addr_orig, ?relay_addr, "relay addr v4"); + debug!(?relay_addr, "relay addr v4"); let host = relay_node.url.host_str().context("missing host url")?; - let conn = quic_client.create_conn(relay_addr, host).await?; + let conn = quic_client.create_conn(relay_addr.into(), host).await?; let mut receiver = conn.observed_external_addr(); // wait for an addr @@ -750,19 +740,16 @@ async fn run_probe_v4( #[cfg(not(wasm_browser))] async fn run_probe_v6( - ip_mapped_addrs: Option, relay_node: Arc, quic_client: QuicClient, dns_resolver: DnsResolver, ) -> n0_snafu::Result<(QadProbeReport, QadConn)> { use n0_snafu::ResultExt; - let relay_addr_orig = reportgen::get_relay_addr_ipv6(&dns_resolver, &relay_node).await?; - let relay_addr = - reportgen::maybe_to_mapped_addr(ip_mapped_addrs.as_ref(), relay_addr_orig.into()); + let relay_addr = reportgen::get_relay_addr_ipv6(&dns_resolver, &relay_node).await?; - debug!(?relay_addr_orig, ?relay_addr, "relay addr v6"); + debug!(?relay_addr, "relay addr v6"); let host = relay_node.url.host_str().context("missing host url")?; - let conn = quic_client.create_conn(relay_addr, host).await?; + let conn = quic_client.create_conn(relay_addr.into(), host).await?; let mut receiver = conn.observed_external_addr(); // wait for an addr @@ -885,7 +872,6 @@ mod tests { .insecure_skip_relay_cert_verify(true); let mut client = Client::new( resolver.clone(), - None, relay_map.clone(), opts.clone(), Default::default(), @@ -1086,8 +1072,7 @@ mod tests { println!("test: {}", tt.name); let relay_map = RelayMap::empty(); let opts = Options::default(); - let mut client = - Client::new(resolver.clone(), None, relay_map, opts, Default::default()); + let mut client = Client::new(resolver.clone(), relay_map, opts, Default::default()); for s in &mut tt.steps { // trigger the timer tokio::time::advance(Duration::from_secs(s.after)).await; diff --git a/iroh/src/net_report/ip_mapped_addrs.rs b/iroh/src/net_report/ip_mapped_addrs.rs deleted file mode 100644 index 564134e7be9..00000000000 --- a/iroh/src/net_report/ip_mapped_addrs.rs +++ /dev/null @@ -1,134 +0,0 @@ -use std::{ - collections::BTreeMap, - net::{IpAddr, Ipv6Addr, SocketAddr}, - sync::{ - Arc, - atomic::{AtomicU64, Ordering}, - }, -}; - -use snafu::Snafu; - -/// Can occur when converting a [`SocketAddr`] to an [`IpMappedAddr`] -#[derive(Debug, Snafu)] -#[snafu(display("Failed to convert"))] -pub struct IpMappedAddrError; - -/// A map fake Ipv6 address with an actual IP address. -/// -/// It is essentially a lookup key for an IP that iroh's magicsocket knows about. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub(crate) struct IpMappedAddr(Ipv6Addr); - -/// Counter to always generate unique addresses for [`IpMappedAddr`]. -static IP_ADDR_COUNTER: AtomicU64 = AtomicU64::new(1); - -impl IpMappedAddr { - /// The Prefix/L of our Unique Local Addresses. - const ADDR_PREFIXL: u8 = 0xfd; - /// The Global ID used in our Unique Local Addresses. - const ADDR_GLOBAL_ID: [u8; 5] = [21, 7, 10, 81, 11]; - /// The Subnet ID used in our Unique Local Addresses. - const ADDR_SUBNET: [u8; 2] = [0, 1]; - - /// The dummy port used for all mapped addresses. - const MAPPED_ADDR_PORT: u16 = 12345; - - /// Generates a globally unique fake UDP address. - /// - /// This generates a new IPv6 address in the Unique Local Address range (RFC 4193) - /// which is recognised by iroh as an IP mapped address. - pub(crate) fn generate() -> Self { - let mut addr = [0u8; 16]; - addr[0] = Self::ADDR_PREFIXL; - addr[1..6].copy_from_slice(&Self::ADDR_GLOBAL_ID); - addr[6..8].copy_from_slice(&Self::ADDR_SUBNET); - - let counter = IP_ADDR_COUNTER.fetch_add(1, Ordering::Relaxed); - addr[8..16].copy_from_slice(&counter.to_be_bytes()); - - Self(Ipv6Addr::from(addr)) - } - - /// Returns a consistent [`SocketAddr`] for the [`IpMappedAddr`]. - /// - /// This does not have a routable IP address. - /// - /// This uses a made-up, but fixed port number. The [IpMappedAddresses`] map this is - /// made for creates a unique [`IpMappedAddr`] for each IP+port and thus does not use - /// the port to map back to the original [`SocketAddr`]. - pub(crate) fn private_socket_addr(&self) -> SocketAddr { - SocketAddr::new(IpAddr::from(self.0), Self::MAPPED_ADDR_PORT) - } -} - -impl TryFrom for IpMappedAddr { - type Error = IpMappedAddrError; - - fn try_from(value: Ipv6Addr) -> std::result::Result { - let octets = value.octets(); - if octets[0] == Self::ADDR_PREFIXL - && octets[1..6] == Self::ADDR_GLOBAL_ID - && octets[6..8] == Self::ADDR_SUBNET - { - return Ok(Self(value)); - } - Err(IpMappedAddrError) - } -} - -impl std::fmt::Display for IpMappedAddr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "IpMappedAddr({})", self.0) - } -} - -/// A Map of [`IpMappedAddresses`] to [`SocketAddr`]. -// TODO(ramfox): before this is ready to be used beyond QAD, we should add -// mechanisms for keeping track of "aliveness" and pruning address, as we do -// with the `NodeMap` -#[derive(Debug, Clone, Default)] -pub(crate) struct IpMappedAddresses(Arc>); - -#[derive(Debug, Default)] -pub(super) struct Inner { - by_mapped_addr: BTreeMap, - /// Because [`std::net::SocketAddrV6`] contains extra fields besides the IP - /// address and port (ie, flow_info and scope_id), the a [`std::net::SocketAddrV6`] - /// with the same IP addr and port might Hash to something different. - /// So to get a hashable key for the map, we are using `(IpAddr, u6)`. - by_ip_port: BTreeMap<(IpAddr, u16), IpMappedAddr>, -} - -impl IpMappedAddresses { - /// Adds a [`SocketAddr`] to the map and returns the generated [`IpMappedAddr`]. - /// - /// If this [`SocketAddr`] already exists in the map, it returns its - /// associated [`IpMappedAddr`]. - /// - /// Otherwise a new [`IpMappedAddr`] is generated for it and returned. - pub(super) fn get_or_register(&self, socket_addr: SocketAddr) -> IpMappedAddr { - let ip_port = (socket_addr.ip(), socket_addr.port()); - let mut inner = self.0.lock().expect("poisoned"); - if let Some(mapped_addr) = inner.by_ip_port.get(&ip_port) { - return *mapped_addr; - } - let ip_mapped_addr = IpMappedAddr::generate(); - inner.by_mapped_addr.insert(ip_mapped_addr, socket_addr); - inner.by_ip_port.insert(ip_port, ip_mapped_addr); - ip_mapped_addr - } - - /// Returns the [`IpMappedAddr`] for the given [`SocketAddr`]. - pub(crate) fn get_mapped_addr(&self, socket_addr: &SocketAddr) -> Option { - let ip_port = (socket_addr.ip(), socket_addr.port()); - let inner = self.0.lock().expect("poisoned"); - inner.by_ip_port.get(&ip_port).copied() - } - - /// Returns the [`SocketAddr`] for the given [`IpMappedAddr`]. - pub(crate) fn get_ip_addr(&self, mapped_addr: &IpMappedAddr) -> Option { - let inner = self.0.lock().expect("poisoned"); - inner.by_mapped_addr.get(mapped_addr).copied() - } -} diff --git a/iroh/src/net_report/reportgen.rs b/iroh/src/net_report/reportgen.rs index 4c26bbea32d..2829b9ab8f1 100644 --- a/iroh/src/net_report/reportgen.rs +++ b/iroh/src/net_report/reportgen.rs @@ -45,6 +45,8 @@ use tokio_util::sync::CancellationToken; use tracing::{Instrument, debug, debug_span, error, info_span, trace, warn}; use url::Host; +#[cfg(not(wasm_browser))] +use super::defaults::timeouts::DNS_TIMEOUT; #[cfg(wasm_browser)] use super::portmapper; // We stub the library use super::{ @@ -52,8 +54,6 @@ use super::{ probes::{Probe, ProbePlan}, }; #[cfg(not(wasm_browser))] -use super::{defaults::timeouts::DNS_TIMEOUT, ip_mapped_addrs::IpMappedAddresses}; -#[cfg(not(wasm_browser))] use crate::discovery::dns::DNS_STAGGERING_MS; use crate::net_report::defaults::timeouts::{ CAPTIVE_PORTAL_DELAY, CAPTIVE_PORTAL_TIMEOUT, OVERALL_REPORT_TIMEOUT, PROBES_TIMEOUT, @@ -105,8 +105,6 @@ pub(crate) struct SocketState { pub(crate) quic_client: Option, /// The DNS resolver to use for probes that need to resolve DNS records. pub(crate) dns_resolver: DnsResolver, - /// Optional [`IpMappedAddresses`] used to enable QAD in iroh - pub(crate) ip_mapped_addrs: Option, } impl Client { @@ -518,17 +516,6 @@ impl Probe { } } -#[cfg(not(wasm_browser))] -pub(super) fn maybe_to_mapped_addr( - ip_mapped_addrs: Option<&IpMappedAddresses>, - addr: SocketAddr, -) -> SocketAddr { - if let Some(ip_mapped_addrs) = ip_mapped_addrs { - return ip_mapped_addrs.get_or_register(addr).private_socket_addr(); - } - addr -} - #[cfg(not(wasm_browser))] #[derive(Debug, Snafu)] #[snafu(module)] @@ -874,7 +861,7 @@ mod tests { let quic_client = iroh_relay::quic::QuicClient::new(ep.clone(), client_config); let dns_resolver = DnsResolver::default(); - let (report, conn) = super::super::run_probe_v4(None, relay, quic_client, dns_resolver) + let (report, conn) = super::super::run_probe_v4(relay, quic_client, dns_resolver) .await .unwrap(); From 130710fb43c8eca93b4db8165e71ae6a1d745d3e Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Mon, 21 Jul 2025 12:38:16 +0200 Subject: [PATCH 14/27] use correct relay addr on recv --- iroh/src/magicsock.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index 515be0ce9c6..4729873c26d 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -825,8 +825,11 @@ impl MagicSock { } transports::Addr::Relay(src_url, src_node) => { // Relay - let quic_mapped_addr = self.node_map.receive_relay(src_url, *src_node); - quinn_meta.addr = quic_mapped_addr.private_socket_addr(); + let _quic_mapped_addr = self.node_map.receive_relay(src_url, *src_node); + let mapped_addr = self + .relay_mapped_addrs + .get_or_register(src_url.clone(), *src_node); + quinn_meta.addr = mapped_addr.private_socket_addr(); } } } else { From dba89df89ad29443d2c30d41fd84887fc8b45eb8 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Mon, 21 Jul 2025 12:45:42 +0200 Subject: [PATCH 15/27] ensure connection registration --- iroh/src/endpoint.rs | 45 ++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/iroh/src/endpoint.rs b/iroh/src/endpoint.rs index 856ca358104..22c0df41eaf 100644 --- a/iroh/src/endpoint.rs +++ b/iroh/src/endpoint.rs @@ -1623,7 +1623,7 @@ impl Future for IncomingFuture { Poll::Pending => Poll::Pending, Poll::Ready(Err(err)) => Poll::Ready(Err(err)), Poll::Ready(Ok(inner)) => { - let conn = Connection { inner }; + let conn = Connection::new(inner, None, &this.ep); try_send_rtt_msg(&conn, this.ep, None); Poll::Ready(Ok(conn)) } @@ -1711,11 +1711,12 @@ impl Connecting { pub fn into_0rtt(self) -> Result<(Connection, ZeroRttAccepted), Self> { match self.inner.into_0rtt() { Ok((inner, zrtt_accepted)) => { - let conn = Connection { inner }; + let conn = Connection::new(inner, self.remote_node_id, &self.ep); let zrtt_accepted = ZeroRttAccepted { inner: zrtt_accepted, _discovery_drop_guard: self._discovery_drop_guard, }; + // This call is why `self.remote_node_id` was introduced. // When we `Connecting::into_0rtt`, then we don't yet have `handshake_data` // in our `Connection`, thus `try_send_rtt_msg` won't be able to pick up @@ -1761,24 +1762,7 @@ impl Future for Connecting { Poll::Pending => Poll::Pending, Poll::Ready(Err(err)) => Poll::Ready(Err(err)), Poll::Ready(Ok(inner)) => { - let conn = Connection { inner }; - - // Grab the remote identity and register this connection - if let Some(remote) = *this.remote_node_id { - let weak_handle = conn.inner.weak_handle(); - let path_events = conn.inner.path_events(); - this.ep - .msock - .register_connection(remote, weak_handle, path_events); - } else if let Ok(remote) = conn.remote_node_id() { - let weak_handle = conn.inner.weak_handle(); - let path_events = conn.inner.path_events(); - this.ep - .msock - .register_connection(remote, weak_handle, path_events); - } else { - warn!("unable to determine node id for the remote"); - } + let conn = Connection::new(inner, *this.remote_node_id, &this.ep); try_send_rtt_msg(&conn, this.ep, *this.remote_node_id); Poll::Ready(Ok(conn)) @@ -1838,6 +1822,27 @@ pub struct RemoteNodeIdError { } impl Connection { + fn new(inner: quinn::Connection, remote_id: Option, ep: &Endpoint) -> Self { + let conn = Connection { inner }; + + // Grab the remote identity and register this connection + if let Some(remote) = remote_id { + let weak_handle = conn.inner.weak_handle(); + let path_events = conn.inner.path_events(); + ep.msock + .register_connection(remote, weak_handle, path_events); + } else if let Ok(remote) = conn.remote_node_id() { + let weak_handle = conn.inner.weak_handle(); + let path_events = conn.inner.path_events(); + ep.msock + .register_connection(remote, weak_handle, path_events); + } else { + warn!("unable to determine node id for the remote"); + } + + conn + } + /// Initiates a new outgoing unidirectional stream. /// /// Streams are cheap and instantaneous to open unless blocked by flow control. As a From aab083d5b0316153b532dffb2a2f19e9b0d5afe6 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Mon, 21 Jul 2025 12:50:42 +0200 Subject: [PATCH 16/27] remove rtt_actor, this is now done inside quinn on a per path basis --- iroh/src/endpoint.rs | 84 +++++----------- iroh/src/endpoint/rtt_actor.rs | 171 --------------------------------- 2 files changed, 24 insertions(+), 231 deletions(-) delete mode 100644 iroh/src/endpoint/rtt_actor.rs diff --git a/iroh/src/endpoint.rs b/iroh/src/endpoint.rs index 22c0df41eaf..15ab21abda9 100644 --- a/iroh/src/endpoint.rs +++ b/iroh/src/endpoint.rs @@ -28,28 +28,6 @@ use n0_future::{Stream, time::Duration}; use n0_watcher::Watcher; use nested_enum_utils::common_fields; use pin_project::pin_project; -use snafu::{ResultExt, Snafu, ensure}; -use tracing::{debug, instrument, trace, warn}; -use url::Url; - -#[cfg(wasm_browser)] -use crate::discovery::pkarr::PkarrResolver; -use crate::{ - RelayProtocol, - discovery::{ - ConcurrentDiscovery, Discovery, DiscoveryContext, DiscoveryError, DiscoveryItem, - DiscoverySubscribers, DiscoveryTask, DynIntoDiscovery, IntoDiscovery, IntoDiscoveryError, - Lagged, UserData, pkarr::PkarrPublisher, - }, - magicsock::{self, Handle, NodeIdMappedAddr, OwnAddressSnafu}, - metrics::EndpointMetrics, - net_report::Report, - tls, -}; -#[cfg(not(wasm_browser))] -use crate::{discovery::dns::DnsDiscovery, dns::DnsResolver}; - -mod rtt_actor; // Missing still: SendDatagram and ConnectionClose::frame_type's Type. pub use quinn::{ @@ -67,12 +45,29 @@ pub use quinn_proto::{ ServerConfig as CryptoServerConfig, UnsupportedVersion, }, }; +use snafu::{ResultExt, Snafu, ensure}; +use tracing::{debug, instrument, trace, warn}; +use url::Url; -use self::rtt_actor::RttMessage; pub use super::magicsock::{ AddNodeAddrError, ConnectionType, ControlMsg, DirectAddr, DirectAddrInfo, DirectAddrType, RemoteInfo, Source, }; +#[cfg(wasm_browser)] +use crate::discovery::pkarr::PkarrResolver; +#[cfg(not(wasm_browser))] +use crate::{discovery::dns::DnsDiscovery, dns::DnsResolver}; +use crate::{ + discovery::{ + ConcurrentDiscovery, Discovery, DiscoveryContext, DiscoveryError, DiscoveryItem, + DiscoverySubscribers, DiscoveryTask, DynIntoDiscovery, IntoDiscovery, IntoDiscoveryError, + Lagged, UserData, pkarr::PkarrPublisher, + }, + magicsock::{self, Handle, NodeIdMappedAddr, OwnAddressSnafu}, + metrics::EndpointMetrics, + net_report::Report, + tls, +}; /// The delay to fall back to discovery when direct addresses fail. /// @@ -527,8 +522,6 @@ impl StaticConfig { pub struct Endpoint { /// Handle to the magicsocket/actor msock: Handle, - /// Handle to the actor that resets the quinn RTT estimator - rtt_actor: Arc, /// Configuration structs for quinn, holds the transport config, certificate setup, secret key etc. static_config: Arc, } @@ -638,7 +631,6 @@ impl Endpoint { let metrics = msock.metrics.magicsock.clone(); let ep = Self { msock, - rtt_actor: Arc::new(rtt_actor::RttHandle::new(metrics)), static_config: Arc::new(static_config), }; Ok(ep) @@ -1624,7 +1616,6 @@ impl Future for IncomingFuture { Poll::Ready(Err(err)) => Poll::Ready(Err(err)), Poll::Ready(Ok(inner)) => { let conn = Connection::new(inner, None, &this.ep); - try_send_rtt_msg(&conn, this.ep, None); Poll::Ready(Ok(conn)) } } @@ -1711,19 +1702,18 @@ impl Connecting { pub fn into_0rtt(self) -> Result<(Connection, ZeroRttAccepted), Self> { match self.inner.into_0rtt() { Ok((inner, zrtt_accepted)) => { + // This call is why `self.remote_node_id` was introduced. + // When we `Connecting::into_0rtt`, then we don't yet have `handshake_data` + // in our `Connection`, thus we won't be able to pick up + // `Connection::remote_node_id`. + // Instead, we provide `self.remote_node_id` here - we know it in advance, + // after all. let conn = Connection::new(inner, self.remote_node_id, &self.ep); let zrtt_accepted = ZeroRttAccepted { inner: zrtt_accepted, _discovery_drop_guard: self._discovery_drop_guard, }; - // This call is why `self.remote_node_id` was introduced. - // When we `Connecting::into_0rtt`, then we don't yet have `handshake_data` - // in our `Connection`, thus `try_send_rtt_msg` won't be able to pick up - // `Connection::remote_node_id`. - // Instead, we provide `self.remote_node_id` here - we know it in advance, - // after all. - try_send_rtt_msg(&conn, &self.ep, self.remote_node_id); Ok((conn, zrtt_accepted)) } Err(inner) => Err(Self { @@ -1763,8 +1753,6 @@ impl Future for Connecting { Poll::Ready(Err(err)) => Poll::Ready(Err(err)), Poll::Ready(Ok(inner)) => { let conn = Connection::new(inner, *this.remote_node_id, &this.ep); - - try_send_rtt_msg(&conn, this.ep, *this.remote_node_id); Poll::Ready(Ok(conn)) } } @@ -2139,30 +2127,6 @@ impl Connection { } } -/// Try send a message to the rtt-actor. -/// -/// If we can't notify the actor that will impact performance a little, but we can still -/// function. -fn try_send_rtt_msg(conn: &Connection, magic_ep: &Endpoint, remote_node_id: Option) { - // If we can't notify the rtt-actor that's not great but not critical. - let Some(node_id) = remote_node_id.or_else(|| conn.remote_node_id().ok()) else { - warn!(?conn, "failed to get remote node id"); - return; - }; - let Some(conn_type_changes) = magic_ep.conn_type(node_id) else { - warn!(?conn, "failed to create conn_type stream"); - return; - }; - let rtt_msg = RttMessage::NewConnection { - connection: conn.inner.weak_handle(), - conn_type_changes: conn_type_changes.stream(), - node_id, - }; - if let Err(err) = magic_ep.rtt_actor.msg_tx.try_send(rtt_msg) { - warn!(?conn, "rtt-actor not reachable: {err:#}"); - } -} - /// Read a proxy url from the environment, in this order /// /// - `HTTP_PROXY` diff --git a/iroh/src/endpoint/rtt_actor.rs b/iroh/src/endpoint/rtt_actor.rs deleted file mode 100644 index 5bc9e6310f8..00000000000 --- a/iroh/src/endpoint/rtt_actor.rs +++ /dev/null @@ -1,171 +0,0 @@ -//! Actor which coordinates the congestion controller for the magic socket - -use std::{pin::Pin, sync::Arc, task::Poll}; - -use iroh_base::NodeId; -use n0_future::{ - MergeUnbounded, Stream, StreamExt, - task::{self, AbortOnDropHandle}, -}; -use tokio::sync::mpsc; -use tracing::{Instrument, debug, info_span}; - -use crate::{magicsock::ConnectionType, metrics::MagicsockMetrics}; - -#[derive(Debug)] -pub(super) struct RttHandle { - // We should and some point use this to propagate panics and errors. - pub(super) _handle: AbortOnDropHandle<()>, - pub(super) msg_tx: mpsc::Sender, -} - -impl RttHandle { - pub(super) fn new(metrics: Arc) -> Self { - let mut actor = RttActor { - connection_events: Default::default(), - metrics, - }; - let (msg_tx, msg_rx) = mpsc::channel(16); - let handle = task::spawn( - async move { - actor.run(msg_rx).await; - } - .instrument(info_span!("rtt-actor")), - ); - Self { - _handle: AbortOnDropHandle::new(handle), - msg_tx, - } - } -} - -/// Messages to send to the [`RttActor`]. -#[derive(Debug)] -pub(super) enum RttMessage { - /// Informs the [`RttActor`] of a new connection is should monitor. - NewConnection { - /// The connection. - connection: quinn::WeakConnectionHandle, - /// Path changes for this connection from the magic socket. - conn_type_changes: n0_watcher::Stream>, - /// For reporting-only, the Node ID of this connection. - node_id: NodeId, - }, -} - -/// Actor to coordinate congestion controller state with magic socket state. -/// -/// The magic socket can change the underlying network path, between two nodes. If we can -/// inform the QUIC congestion controller of this event it will work much more efficiently. -#[derive(derive_more::Debug)] -struct RttActor { - /// Stream of connection type changes. - #[debug("MergeUnbounded>")] - connection_events: MergeUnbounded, - metrics: Arc, -} - -#[derive(Debug)] -struct MappedStream { - stream: n0_watcher::Stream>, - node_id: NodeId, - /// Reference to the connection. - connection: quinn::WeakConnectionHandle, - /// This an indiciator of whether this connection was direct before. - /// This helps establish metrics on number of connections that became direct. - was_direct_before: bool, -} - -struct ConnectionEvent { - became_direct: bool, -} - -impl Stream for MappedStream { - type Item = ConnectionEvent; - - /// Performs the congestion controller reset for a magic socket path change. - /// - /// Regardless of which kind of path we are changed to, the congestion controller needs - /// resetting. Even when switching to mixed we should reset the state as e.g. switching - /// from direct to mixed back to direct should be a rare exception and is a bug if this - /// happens commonly. - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> Poll> { - match Pin::new(&mut self.stream).poll_next(cx) { - Poll::Ready(Some(new_conn_type)) => { - let mut became_direct = false; - if self.connection.network_path_changed() { - debug!( - node_id = %self.node_id.fmt_short(), - new_type = ?new_conn_type, - "Congestion controller state reset", - ); - if !self.was_direct_before && matches!(new_conn_type, ConnectionType::Direct(_)) - { - self.was_direct_before = true; - became_direct = true - } - }; - Poll::Ready(Some(ConnectionEvent { became_direct })) - } - Poll::Ready(None) => Poll::Ready(None), - Poll::Pending => Poll::Pending, - } - } -} - -impl RttActor { - /// Runs the actor main loop. - /// - /// The main loop will finish when the sender is dropped. - async fn run(&mut self, mut msg_rx: mpsc::Receiver) { - loop { - tokio::select! { - biased; - msg = msg_rx.recv() => { - match msg { - Some(msg) => self.handle_msg(msg), - None => break, - } - } - event = self.connection_events.next(), if !self.connection_events.is_empty() => { - if event.map(|e| e.became_direct).unwrap_or(false) { - self.metrics.connection_became_direct.inc(); - } - } - } - } - debug!("rtt-actor finished"); - } - - /// Handle actor messages. - fn handle_msg(&mut self, msg: RttMessage) { - match msg { - RttMessage::NewConnection { - connection, - conn_type_changes, - node_id, - } => { - self.handle_new_connection(connection, conn_type_changes, node_id); - } - } - } - - /// Handles the new connection message. - fn handle_new_connection( - &mut self, - connection: quinn::WeakConnectionHandle, - conn_type_changes: n0_watcher::Stream>, - node_id: NodeId, - ) { - self.connection_events.push(MappedStream { - stream: conn_type_changes, - connection, - node_id, - was_direct_before: false, - }); - self.metrics.connection_handshake_success.inc(); - } -} From d4484dae0cfdd035f25a1ffb575bccf71b911c27 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Mon, 21 Jul 2025 13:07:11 +0200 Subject: [PATCH 17/27] open additional paths after the initial connection --- Cargo.lock | 4 ++-- iroh/src/endpoint.rs | 10 ++-------- iroh/src/magicsock.rs | 20 ++++++++++---------- iroh/src/magicsock/node_map.rs | 8 ++++++++ iroh/src/magicsock/node_map/node_state.rs | 11 +++++++++++ iroh/src/magicsock/node_map/udp_paths.rs | 3 +++ 6 files changed, 36 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aeb847038a4..45faf1e4dd8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2489,7 +2489,7 @@ dependencies = [ [[package]] name = "iroh-quinn" version = "0.14.0" -source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#0dc50edf689ee5c6cf21b4ee5c0fea6af548680d" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#89df901286301de17aac88d42adc2aa7de32bc18" dependencies = [ "bytes", "cfg_aliases", @@ -2508,7 +2508,7 @@ dependencies = [ [[package]] name = "iroh-quinn-proto" version = "0.13.0" -source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#0dc50edf689ee5c6cf21b4ee5c0fea6af548680d" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#89df901286301de17aac88d42adc2aa7de32bc18" dependencies = [ "bytes", "fastbloom", diff --git a/iroh/src/endpoint.rs b/iroh/src/endpoint.rs index 15ab21abda9..cb2c8dae87a 100644 --- a/iroh/src/endpoint.rs +++ b/iroh/src/endpoint.rs @@ -1815,15 +1815,9 @@ impl Connection { // Grab the remote identity and register this connection if let Some(remote) = remote_id { - let weak_handle = conn.inner.weak_handle(); - let path_events = conn.inner.path_events(); - ep.msock - .register_connection(remote, weak_handle, path_events); + ep.msock.register_connection(remote, &conn.inner); } else if let Ok(remote) = conn.remote_node_id() { - let weak_handle = conn.inner.weak_handle(); - let path_events = conn.inner.path_events(); - ep.msock - .register_connection(remote, weak_handle, path_events); + ep.msock.register_connection(remote, &conn.inner); } else { warn!("unable to determine node id for the remote"); } diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index 4729873c26d..e5bc896672a 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -43,7 +43,6 @@ use netwatch::netmon; #[cfg(not(wasm_browser))] use netwatch::{UdpSocket, ip::LocalAddresses}; use quinn::{AsyncUdpSocket, ServerConfig, WeakConnectionHandle}; -use quinn_proto::PathEvent; use rand::Rng; use relay_mapped_addrs::{IpMappedAddr, RelayMappedAddresses}; use smallvec::SmallVec; @@ -292,18 +291,14 @@ impl MagicSock { self.local_addrs_watch.clone().get() } - pub(crate) fn register_connection( - &self, - remote: NodeId, - conn: WeakConnectionHandle, - mut path_events: tokio::sync::broadcast::Receiver, - ) { - self.connection_map.insert(remote, conn); + pub(crate) fn register_connection(&self, remote: NodeId, conn: &quinn::Connection) { + let weak_handle = conn.weak_handle(); + self.connection_map.insert(remote, weak_handle); - // TODO: open additional paths // TODO: track task // TODO: find a good home for this - task::spawn(async move { + let mut path_events = conn.path_events(); + let _task = task::spawn(async move { loop { match path_events.recv().await { Ok(event) => { @@ -316,6 +311,11 @@ impl MagicSock { } } }); + + // open additional paths + if let Some(addr) = self.node_map.get_current_addr(remote) { + self.add_paths(addr); + } } #[cfg(not(wasm_browser))] diff --git a/iroh/src/magicsock/node_map.rs b/iroh/src/magicsock/node_map.rs index 4eb8e3df716..bb0fd98734b 100644 --- a/iroh/src/magicsock/node_map.rs +++ b/iroh/src/magicsock/node_map.rs @@ -186,6 +186,14 @@ impl NodeMap { .unwrap_or_default() } + pub(super) fn get_current_addr(&self, node_key: NodeId) -> Option { + self.inner + .lock() + .expect("poisoned") + .get(NodeStateKey::NodeId(node_key)) + .map(|ep| ep.get_current_addr()) + } + pub(super) fn handle_call_me_maybe( &self, sender: PublicKey, diff --git a/iroh/src/magicsock/node_map/node_state.rs b/iroh/src/magicsock/node_map/node_state.rs index 96162223663..36ad6a0de13 100644 --- a/iroh/src/magicsock/node_map/node_state.rs +++ b/iroh/src/magicsock/node_map/node_state.rs @@ -667,6 +667,17 @@ impl NodeState { (udp_addr, relay_url, ping_msgs) } + pub(crate) fn get_current_addr(&self) -> NodeAddr { + // TODO: more selective? + let mut node_addr = + NodeAddr::new(self.node_id).with_direct_addresses(self.udp_paths.addrs()); + if let Some((url, _)) = &self.relay_url { + node_addr = node_addr.with_relay_url(url.clone()); + } + + node_addr + } + /// Get the direct addresses for this endpoint. pub(super) fn direct_addresses(&self) -> impl Iterator + '_ { self.udp_paths.paths.keys().copied() diff --git a/iroh/src/magicsock/node_map/udp_paths.rs b/iroh/src/magicsock/node_map/udp_paths.rs index 475f865e59b..d89cf10ed2c 100644 --- a/iroh/src/magicsock/node_map/udp_paths.rs +++ b/iroh/src/magicsock/node_map/udp_paths.rs @@ -106,6 +106,9 @@ impl NodeUdpPaths { best, } } + pub(super) fn addrs(&self) -> Vec { + self.paths.keys().map(|ip| (*ip).into()).collect() + } /// Returns the current UDP address to send on. pub(super) fn send_addr(&self, have_ipv6: bool) -> &UdpSendAddr { From 6cb94e4bdf2132a813ebee00f1e754a573daa695 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 22 Jul 2025 12:10:14 +0200 Subject: [PATCH 18/27] ensure path open --- Cargo.lock | 6 +++--- Cargo.toml | 6 +++--- iroh/src/magicsock.rs | 7 +++++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 45faf1e4dd8..92171f43d7f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2489,7 +2489,7 @@ dependencies = [ [[package]] name = "iroh-quinn" version = "0.14.0" -source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#89df901286301de17aac88d42adc2aa7de32bc18" +source = "git+https://github.com//n0-computer/quinn?branch=flub%2Fopen-path-ensure#646e849d540886ee58e25e3da509d6021ec94430" dependencies = [ "bytes", "cfg_aliases", @@ -2508,7 +2508,7 @@ dependencies = [ [[package]] name = "iroh-quinn-proto" version = "0.13.0" -source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#89df901286301de17aac88d42adc2aa7de32bc18" +source = "git+https://github.com//n0-computer/quinn?branch=flub%2Fopen-path-ensure#646e849d540886ee58e25e3da509d6021ec94430" dependencies = [ "bytes", "fastbloom", @@ -2544,7 +2544,7 @@ dependencies = [ [[package]] name = "iroh-quinn-udp" version = "0.5.12" -source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#79e3fcc710de68b40fd05be5421048bab658ddf4" +source = "git+https://github.com//n0-computer/quinn?branch=flub%2Fopen-path-ensure#646e849d540886ee58e25e3da509d6021ec94430" dependencies = [ "cfg_aliases", "libc", diff --git a/Cargo.toml b/Cargo.toml index eb098793da4..4e6324f349d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,6 @@ netwatch = { git = "https://github.com/n0-computer/net-tools", branch = "feat-mu # iroh-quinn-proto = { path = "../iroh-quinn/quinn-proto" } # iroh-quinn-udp = { path = "../iroh-quinn/quinn-udp" } -# iroh-quinn = { git = "https://github.com//n0-computer/quinn", branch = "flub/quinn-path-events-status" } -# iroh-quinn-proto = { git = "https://github.com//n0-computer/quinn", branch = "flub/quinn-path-events-status" } -# iroh-quinn-udp = { git = "https://github.com//n0-computer/quinn", branch = "flub/quinn-path-events-status" } +iroh-quinn = { git = "https://github.com//n0-computer/quinn", branch = "flub/open-path-ensure" } +iroh-quinn-proto = { git = "https://github.com//n0-computer/quinn", branch = "flub/open-path-ensure" } +iroh-quinn-udp = { git = "https://github.com//n0-computer/quinn", branch = "flub/open-path-ensure" } diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index e5bc896672a..626621ac37b 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -476,7 +476,7 @@ impl MagicSock { let addr = *addr; task::spawn(async move { match conn - .open_path(addr, quinn_proto::PathStatus::Available) + .open_path_ensure(addr, quinn_proto::PathStatus::Available) .await { Ok(path) => { @@ -497,7 +497,10 @@ impl MagicSock { let conn = conn.clone(); let addr = addr.private_socket_addr(); task::spawn(async move { - match conn.open_path(addr, quinn_proto::PathStatus::Backup).await { + match conn + .open_path_ensure(addr, quinn_proto::PathStatus::Backup) + .await + { Ok(path) => { // Keep the relay path open path.set_max_idle_timeout(None).ok(); From 998e2833a21b4dd47da6bb3b4d8e2ce2d77528c7 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 23 Jul 2025 11:44:28 +0200 Subject: [PATCH 19/27] some debugging --- iroh/src/magicsock.rs | 128 +++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 57 deletions(-) diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index 626621ac37b..c0cb0b0a256 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -292,25 +292,29 @@ impl MagicSock { } pub(crate) fn register_connection(&self, remote: NodeId, conn: &quinn::Connection) { + debug!(%remote, "register connection"); let weak_handle = conn.weak_handle(); self.connection_map.insert(remote, weak_handle); // TODO: track task // TODO: find a good home for this let mut path_events = conn.path_events(); - let _task = task::spawn(async move { - loop { - match path_events.recv().await { - Ok(event) => { - info!(remote = %remote, "path event: {:?}", event); - } - Err(tokio::sync::broadcast::error::RecvError::Lagged(_)) => { - warn!("lagged path events"); + let _task = task::spawn( + async move { + loop { + match path_events.recv().await { + Ok(event) => { + info!(remote = %remote, "path event: {:?}", event); + } + Err(tokio::sync::broadcast::error::RecvError::Lagged(_)) => { + warn!("lagged path events"); + } + Err(tokio::sync::broadcast::error::RecvError::Closed) => break, } - Err(tokio::sync::broadcast::error::RecvError::Closed) => break, } } - }); + .instrument(info_span!("path events", %remote)), + ); // open additional paths if let Some(addr) = self.node_map.get_current_addr(remote) { @@ -474,43 +478,51 @@ impl MagicSock { for addr in addr.direct_addresses() { let conn = conn.clone(); let addr = *addr; - task::spawn(async move { - match conn - .open_path_ensure(addr, quinn_proto::PathStatus::Available) - .await - { - Ok(path) => { - path.set_max_idle_timeout(Some( - ENDPOINTS_FRESH_ENOUGH_DURATION, - )) - .ok(); - path.set_keep_alive_interval(Some(HEARTBEAT_INTERVAL)).ok(); - } - Err(err) => { - warn!("failed to open path {:?}", err); + task::spawn( + async move { + debug!(%addr, "open path IP"); + match conn + .open_path_ensure(addr, quinn_proto::PathStatus::Available) + .await + { + Ok(path) => { + path.set_max_idle_timeout(Some( + ENDPOINTS_FRESH_ENOUGH_DURATION, + )) + .ok(); + path.set_keep_alive_interval(Some(HEARTBEAT_INTERVAL)).ok(); + } + Err(err) => { + warn!("failed to open path {:?}", err); + } } } - }); + .instrument(info_span!("open path IP")), + ); } // Insert the relay addr if let Some(addr) = self.get_mapping_addr(addr.node_id) { let conn = conn.clone(); let addr = addr.private_socket_addr(); - task::spawn(async move { - match conn - .open_path_ensure(addr, quinn_proto::PathStatus::Backup) - .await - { - Ok(path) => { - // Keep the relay path open - path.set_max_idle_timeout(None).ok(); - path.set_keep_alive_interval(None).ok(); - } - Err(err) => { - warn!("failed to open path {:?}", err); + task::spawn( + async move { + debug!(%addr, "open path relay"); + match conn + .open_path_ensure(addr, quinn_proto::PathStatus::Backup) + .await + { + Ok(path) => { + // Keep the relay path open + path.set_max_idle_timeout(None).ok(); + path.set_keep_alive_interval(None).ok(); + } + Err(err) => { + warn!("failed to open path {:?}", err); + } } } - }); + .instrument(info_span!("open path relay")), + ); } } else { to_delete.push(i); @@ -644,19 +656,21 @@ impl MagicSock { self.ipv6_reported.load(Ordering::Relaxed), &self.metrics.magicsock, ) { - Some((node_id, udp_addr, relay_url, ping_actions)) => { + Some((node_id, _udp_addr, _relay_url, ping_actions)) => { if !ping_actions.is_empty() { self.actor_sender .try_send(ActorMessage::PingActions(ping_actions)) .ok(); } - // Mixed will send all available addrs - if let Some(url) = relay_url { - active_paths.push(transports::Addr::Relay(url, node_id)); - } - if let Some(addr) = udp_addr { - active_paths.push(transports::Addr::Ip(addr)); + if let Some(addr) = self.node_map.get_current_addr(node_id) { + // Mixed will send all available addrs + if let Some(ref url) = addr.relay_url { + active_paths.push(transports::Addr::Relay(url.clone(), node_id)); + } + for ip in addr.direct_addresses() { + active_paths.push(transports::Addr::Ip(*ip)); + } } } None => { @@ -3202,18 +3216,18 @@ mod tests { let _accept_task = AbortOnDropHandle::new(accept_task); // Add an empty entry in the NodeMap of ep_1 - msock_1 - .add_node_addr( - NodeAddr { - node_id: node_id_2, - relay_url: None, - direct_addresses: Default::default(), - }, - Source::NamedApp { - name: "test".into(), - }, - ) - .unwrap(); + msock_1.node_map.add_node_addr( + NodeAddr { + node_id: node_id_2, + relay_url: None, + direct_addresses: Default::default(), + }, + Source::NamedApp { + name: "test".into(), + }, + &msock_1.metrics.magicsock, + ); + let addr_2 = msock_1.get_mapping_addr(node_id_2).unwrap(); // Set a low max_idle_timeout so quinn gives up on this quickly and our test does From 99cee6121e5d2047d3b7d129a7d967b7125e95f5 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 23 Jul 2025 15:21:20 +0200 Subject: [PATCH 20/27] update quinn branch --- Cargo.lock | 6 +++--- Cargo.toml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 92171f43d7f..88205723258 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2489,7 +2489,7 @@ dependencies = [ [[package]] name = "iroh-quinn" version = "0.14.0" -source = "git+https://github.com//n0-computer/quinn?branch=flub%2Fopen-path-ensure#646e849d540886ee58e25e3da509d6021ec94430" +source = "git+https://github.com//n0-computer/quinn?branch=server-migrations#bc86957aa4ccb72fad70e75a6ce9fc8198f09afc" dependencies = [ "bytes", "cfg_aliases", @@ -2508,7 +2508,7 @@ dependencies = [ [[package]] name = "iroh-quinn-proto" version = "0.13.0" -source = "git+https://github.com//n0-computer/quinn?branch=flub%2Fopen-path-ensure#646e849d540886ee58e25e3da509d6021ec94430" +source = "git+https://github.com//n0-computer/quinn?branch=server-migrations#bc86957aa4ccb72fad70e75a6ce9fc8198f09afc" dependencies = [ "bytes", "fastbloom", @@ -2544,7 +2544,7 @@ dependencies = [ [[package]] name = "iroh-quinn-udp" version = "0.5.12" -source = "git+https://github.com//n0-computer/quinn?branch=flub%2Fopen-path-ensure#646e849d540886ee58e25e3da509d6021ec94430" +source = "git+https://github.com//n0-computer/quinn?branch=server-migrations#bc86957aa4ccb72fad70e75a6ce9fc8198f09afc" dependencies = [ "cfg_aliases", "libc", diff --git a/Cargo.toml b/Cargo.toml index 4e6324f349d..8e7b8f11f49 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,6 @@ netwatch = { git = "https://github.com/n0-computer/net-tools", branch = "feat-mu # iroh-quinn-proto = { path = "../iroh-quinn/quinn-proto" } # iroh-quinn-udp = { path = "../iroh-quinn/quinn-udp" } -iroh-quinn = { git = "https://github.com//n0-computer/quinn", branch = "flub/open-path-ensure" } -iroh-quinn-proto = { git = "https://github.com//n0-computer/quinn", branch = "flub/open-path-ensure" } -iroh-quinn-udp = { git = "https://github.com//n0-computer/quinn", branch = "flub/open-path-ensure" } +iroh-quinn = { git = "https://github.com//n0-computer/quinn", branch = "server-migrations" } +iroh-quinn-proto = { git = "https://github.com//n0-computer/quinn", branch = "server-migrations" } +iroh-quinn-udp = { git = "https://github.com//n0-computer/quinn", branch = "server-migrations" } From 4b60a9a2c47e986f1787b2f6dbdc833cd2df6076 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Mon, 28 Jul 2025 16:40:18 +0200 Subject: [PATCH 21/27] fixups --- Cargo.lock | 4 ++-- iroh/src/endpoint.rs | 1 - iroh/src/magicsock/relay_mapped_addrs.rs | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88205723258..9d6f1e92b40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6452,5 +6452,5 @@ dependencies = [ [[patch.unused]] name = "netwatch" -version = "0.6.0" -source = "git+https://github.com/n0-computer/net-tools?branch=feat-multipath#b7ab98d4ff9cc947f2f084004b4cc2a979bb4d06" +version = "0.7.0" +source = "git+https://github.com/n0-computer/net-tools?branch=feat-multipath#5196858f5754f906e6d205a3f3623831c9236965" diff --git a/iroh/src/endpoint.rs b/iroh/src/endpoint.rs index cb2c8dae87a..e79efd77555 100644 --- a/iroh/src/endpoint.rs +++ b/iroh/src/endpoint.rs @@ -28,7 +28,6 @@ use n0_future::{Stream, time::Duration}; use n0_watcher::Watcher; use nested_enum_utils::common_fields; use pin_project::pin_project; - // Missing still: SendDatagram and ConnectionClose::frame_type's Type. pub use quinn::{ AcceptBi, AcceptUni, AckFrequencyConfig, ApplicationClose, Chunk, ClosedStream, diff --git a/iroh/src/magicsock/relay_mapped_addrs.rs b/iroh/src/magicsock/relay_mapped_addrs.rs index 1ac1eaabacc..b473501ea60 100644 --- a/iroh/src/magicsock/relay_mapped_addrs.rs +++ b/iroh/src/magicsock/relay_mapped_addrs.rs @@ -2,8 +2,8 @@ use std::{ collections::BTreeMap, net::{IpAddr, Ipv6Addr, SocketAddr}, sync::{ - atomic::{AtomicU64, Ordering}, Arc, + atomic::{AtomicU64, Ordering}, }, }; From 04b714cced1b5898c77b5408d2c8e14436ffa2ef Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 1 Aug 2025 11:27:16 +0200 Subject: [PATCH 22/27] update deps --- Cargo.lock | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9d6f1e92b40..fc60e20fe7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2316,7 +2316,7 @@ dependencies = [ "iroh-metrics", "iroh-quinn", "iroh-quinn-proto", - "iroh-quinn-udp 0.5.12", + "iroh-quinn-udp", "iroh-relay", "n0-future", "n0-snafu", @@ -2494,7 +2494,7 @@ dependencies = [ "bytes", "cfg_aliases", "iroh-quinn-proto", - "iroh-quinn-udp 0.5.12", + "iroh-quinn-udp", "pin-project-lite", "rustc-hash", "rustls", @@ -2527,20 +2527,6 @@ dependencies = [ "web-time", ] -[[package]] -name = "iroh-quinn-udp" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c53afaa1049f7c83ea1331f5ebb9e6ebc5fdd69c468b7a22dd598b02c9bcc973" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2 0.5.10", - "tracing", - "windows-sys 0.59.0", -] - [[package]] name = "iroh-quinn-udp" version = "0.5.12" @@ -3044,14 +3030,13 @@ dependencies = [ [[package]] name = "netwatch" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901dbb408894af3df3fc51420ba0c6faf3a7d896077b797c39b7001e2f787bd" +source = "git+https://github.com/n0-computer/net-tools?branch=feat-multipath#e824fd0fb03839c4523c18cd3ab861d6b3e66a16" dependencies = [ "atomic-waker", "bytes", "cfg_aliases", "derive_more 2.0.1", - "iroh-quinn-udp 0.5.7", + "iroh-quinn-udp", "js-sys", "libc", "n0-future", @@ -5719,7 +5704,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] @@ -6449,8 +6434,3 @@ dependencies = [ "quote", "syn 2.0.101", ] - -[[patch.unused]] -name = "netwatch" -version = "0.7.0" -source = "git+https://github.com/n0-computer/net-tools?branch=feat-multipath#5196858f5754f906e6d205a3f3623831c9236965" From 59efdcf9f33f12358aafe4a2b44ed3c1d3d30695 Mon Sep 17 00:00:00 2001 From: Floris Bruynooghe Date: Thu, 28 Aug 2025 15:06:13 +0200 Subject: [PATCH 23/27] bunch of renames and doc updates, no functional changes --- iroh/src/endpoint.rs | 4 +- iroh/src/magicsock.rs | 71 +++++++++++++---------- iroh/src/magicsock/node_map.rs | 19 +++--- iroh/src/magicsock/node_map/node_state.rs | 9 +-- iroh/src/magicsock/relay_mapped_addrs.rs | 55 +++++++++--------- 5 files changed, 87 insertions(+), 71 deletions(-) diff --git a/iroh/src/endpoint.rs b/iroh/src/endpoint.rs index e79efd77555..a6ba61c4bce 100644 --- a/iroh/src/endpoint.rs +++ b/iroh/src/endpoint.rs @@ -62,7 +62,7 @@ use crate::{ DiscoverySubscribers, DiscoveryTask, DynIntoDiscovery, IntoDiscovery, IntoDiscoveryError, Lagged, UserData, pkarr::PkarrPublisher, }, - magicsock::{self, Handle, NodeIdMappedAddr, OwnAddressSnafu}, + magicsock::{self, AllPathsMappedAddr, Handle, OwnAddressSnafu}, metrics::EndpointMetrics, net_report::Report, tls, @@ -1363,7 +1363,7 @@ impl Endpoint { async fn get_mapping_addr_and_maybe_start_discovery( &self, node_addr: NodeAddr, - ) -> Result<(NodeIdMappedAddr, Option), GetMappingAddressError> { + ) -> Result<(AllPathsMappedAddr, Option), GetMappingAddressError> { let node_id = node_addr.node_id; // Only return a mapped addr if we have some way of dialing this node, in other diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index c0cb0b0a256..c5c57055c0c 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -44,7 +44,7 @@ use netwatch::netmon; use netwatch::{UdpSocket, ip::LocalAddresses}; use quinn::{AsyncUdpSocket, ServerConfig, WeakConnectionHandle}; use rand::Rng; -use relay_mapped_addrs::{IpMappedAddr, RelayMappedAddresses}; +use relay_mapped_addrs::{RelayAddrMap, RelayMappedAddr}; use smallvec::SmallVec; use snafu::{ResultExt, Snafu}; use tokio::sync::{Mutex as AsyncMutex, mpsc}; @@ -200,7 +200,7 @@ pub(crate) struct MagicSock { connection_map: ConnectionMap, /// Tracks the mapped IP addresses - relay_mapped_addrs: RelayMappedAddresses, + relay_mapped_addrs: RelayAddrMap, /// Local addresses local_addrs_watch: LocalAddrsWatch, /// Currently bound IP addresses of all sockets @@ -291,6 +291,11 @@ impl MagicSock { self.local_addrs_watch.clone().get() } + /// Registers the connection in the connection map and opens additional paths. + /// + /// In addition to storing the connection reference this requests the current + /// [`NodeAddr`] for remote node from the [`NodeMap`] and adds all paths to the + /// connection. It also listens and logs path events. pub(crate) fn register_connection(&self, remote: NodeId, conn: &quinn::Connection) { debug!(%remote, "register connection"); let weak_handle = conn.weak_handle(); @@ -425,7 +430,7 @@ impl MagicSock { } /// Returns the socket address which can be used by the QUIC layer to dial this node. - pub(crate) fn get_mapping_addr(&self, node_id: NodeId) -> Option { + pub(crate) fn get_mapping_addr(&self, node_id: NodeId) -> Option { self.node_map.get_quic_mapped_addr_for_node_key(node_id) } @@ -616,7 +621,11 @@ impl MagicSock { } } - /// Searches the `node_map` to determine the current transports to be used. + /// Returns the transport addresses for the [`quinn_udp::Transmit`]'s destination. + /// + /// Because Quinn does only know about IP transports we map other transports to private + /// IPv6 Unique Local Address ranges. This extracts the transport addresses out of the + /// transmit's destination. #[instrument(skip_all)] fn prepare_send( &self, @@ -646,7 +655,7 @@ impl MagicSock { dst = %dest, src = ?transmit.src_ip, len = %transmit.contents.len(), - "sending", + "sending mixed", ); // Get the node's relay address and best direct address, as well @@ -680,6 +689,12 @@ impl MagicSock { } #[cfg(not(wasm_browser))] MultipathMappedAddr::Ip(addr) => { + trace!( + dst = %addr, + src = ?transmit.src_ip, + len = %transmit.contents.len(), + "sending IP", + ); active_paths.push(transports::Addr::Ip(addr)); } MultipathMappedAddr::Relay(dest) => { @@ -687,7 +702,7 @@ impl MagicSock { dst = %dest, src = ?transmit.src_ip, len = %transmit.contents.len(), - "sending", + "sending relay", ); // Check if this is a known IpMappedAddr, and if so, send over UDP @@ -706,12 +721,12 @@ impl MagicSock { Ok(active_paths) } - /// Process datagrams received from UDP sockets. + /// Process datagrams received from all the transports. /// /// All the `bufs` and `metas` should have initialized packets in them. /// - /// This fixes up the datagrams to use the correct [`NodeIdMappedAddr`] and extracts DISCO - /// packets, processing them inside the magic socket. + /// This fixes up the datagrams to use the correct [`MultipathMappedAddr`] and extracts + /// DISCO packets, processing them inside the magic socket. fn process_datagrams( &self, bufs: &mut [io::IoSliceMut<'_>], @@ -1084,9 +1099,9 @@ pub(crate) enum MultipathMappedAddr { /// Used for the initial connection. /// - Only used for sending /// - This means send on all known paths/transports - Mixed(NodeIdMappedAddr), + Mixed(AllPathsMappedAddr), /// Relay based transport address - Relay(IpMappedAddr), // TODO: RelayMappedAddr? + Relay(RelayMappedAddr), /// IP based transport address #[cfg(not(wasm_browser))] Ip(SocketAddr), @@ -1097,11 +1112,11 @@ impl From for MultipathMappedAddr { match value.ip() { IpAddr::V4(_) => Self::Ip(value), IpAddr::V6(addr) => { - if let Ok(node_id_mapped_addr) = NodeIdMappedAddr::try_from(addr) { + if let Ok(node_id_mapped_addr) = AllPathsMappedAddr::try_from(addr) { return Self::Mixed(node_id_mapped_addr); } #[cfg(not(wasm_browser))] - if let Ok(ip_mapped_addr) = IpMappedAddr::try_from(addr) { + if let Ok(ip_mapped_addr) = RelayMappedAddr::try_from(addr) { return Self::Relay(ip_mapped_addr); } Self::Ip(value) @@ -1292,7 +1307,7 @@ impl Handle { let (ip_transports, port_mapper) = bind_ip(addr_v4, addr_v6, &metrics).context(BindSocketsSnafu)?; - let relay_mapped_addrs = RelayMappedAddresses::default(); + let relay_mapped_addrs = RelayAddrMap::default(); let (actor_sender, actor_receiver) = mpsc::channel(256); @@ -2293,21 +2308,17 @@ impl DiscoveredDirectAddrs { } } -/// The fake address used by the QUIC layer to address a node. -/// -/// You can consider this as nothing more than a lookup key for a node the [`MagicSock`] knows -/// about. +/// An address used by the QUIC layer to address a node on all paths. /// -/// [`MagicSock`] can reach a node by several real socket addresses, or maybe even via the relay -/// node. The QUIC layer however needs to address a node by a stable [`SocketAddr`] so -/// that normal socket APIs can function. Thus when a new node is introduced to a [`MagicSock`] -/// it is given a new fake address. This is the type of that address. +/// This is only used for initially connecting to a remote node. We instruct Quinn to send +/// to this address, and duplicate all packets for this address to send on all paths we know +/// for the node. /// /// It is but a newtype. And in our QUIC-facing socket APIs like [`AsyncUdpSocket`] it /// comes in as the inner [`Ipv6Addr`], in those interfaces we have to be careful to do /// the conversion to this type. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub(crate) struct NodeIdMappedAddr(Ipv6Addr); +pub(crate) struct AllPathsMappedAddr(Ipv6Addr); /// Can occur when converting a [`SocketAddr`] to an [`NodeIdMappedAddr`] #[derive(Debug, Snafu)] @@ -2317,7 +2328,7 @@ pub struct NodeIdMappedAddrError; /// Counter to always generate unique addresses for [`NodeIdMappedAddr`]. static NODE_ID_ADDR_COUNTER: AtomicU64 = AtomicU64::new(1); -impl NodeIdMappedAddr { +impl AllPathsMappedAddr { /// The Prefix/L of our Unique Local Addresses. const ADDR_PREFIXL: u8 = 0xfd; /// The Global ID used in our Unique Local Addresses. @@ -2355,7 +2366,7 @@ impl NodeIdMappedAddr { } } -impl TryFrom for NodeIdMappedAddr { +impl TryFrom for AllPathsMappedAddr { type Error = NodeIdMappedAddrError; fn try_from(value: Ipv6Addr) -> Result { @@ -2370,7 +2381,7 @@ impl TryFrom for NodeIdMappedAddr { } } -impl std::fmt::Display for NodeIdMappedAddr { +impl std::fmt::Display for AllPathsMappedAddr { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "NodeIdMappedAddr({})", self.0) } @@ -2460,7 +2471,7 @@ mod tests { use tracing::{Instrument, error, info, info_span, instrument}; use tracing_test::traced_test; - use super::{NodeIdMappedAddr, Options}; + use super::{AllPathsMappedAddr, Options}; use crate::{ Endpoint, RelayMap, RelayMode, SecretKey, dns::DnsResolver, @@ -3037,7 +3048,7 @@ mod tests { async fn magicsock_connect( ep: &quinn::Endpoint, ep_secret_key: SecretKey, - addr: NodeIdMappedAddr, + addr: AllPathsMappedAddr, node_id: NodeId, ) -> Result { // Endpoint::connect sets this, do the same to have similar behaviour. @@ -3063,7 +3074,7 @@ mod tests { async fn magicsock_connect_with_transport_config( ep: &quinn::Endpoint, ep_secret_key: SecretKey, - mapped_addr: NodeIdMappedAddr, + mapped_addr: AllPathsMappedAddr, node_id: NodeId, transport_config: Arc, ) -> Result { @@ -3098,7 +3109,7 @@ mod tests { let msock_1 = magicsock_ep(secret_key_1.clone()).await.unwrap(); // Generate an address not present in the NodeMap. - let bad_addr = NodeIdMappedAddr::generate(); + let bad_addr = AllPathsMappedAddr::generate(); // 500ms is rather fast here. Running this locally it should always be the correct // timeout. If this is too slow however the test will not become flaky as we are diff --git a/iroh/src/magicsock/node_map.rs b/iroh/src/magicsock/node_map.rs index bb0fd98734b..36d4b9cdfba 100644 --- a/iroh/src/magicsock/node_map.rs +++ b/iroh/src/magicsock/node_map.rs @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize}; use tracing::{debug, info, instrument, trace, warn}; use self::node_state::{NodeState, Options}; -use super::{ActorMessage, NodeIdMappedAddr, metrics::Metrics, transports}; +use super::{ActorMessage, AllPathsMappedAddr, metrics::Metrics, transports}; use crate::disco::{CallMeMaybe, Pong, SendAddr}; #[cfg(any(test, feature = "test-utils"))] use crate::endpoint::PathSelection; @@ -54,7 +54,7 @@ pub(super) struct NodeMap { pub(super) struct NodeMapInner { by_node_key: HashMap, by_ip_port: HashMap, - by_quic_mapped_addr: HashMap, + by_quic_mapped_addr: HashMap, by_id: HashMap, next_id: usize, #[cfg(any(test, feature = "test-utils"))] @@ -68,7 +68,7 @@ pub(super) struct NodeMapInner { #[derive(Debug, Clone)] enum NodeStateKey { NodeId(NodeId), - NodeIdMappedAddr(NodeIdMappedAddr), + NodeIdMappedAddr(AllPathsMappedAddr), IpPort(IpPort), } @@ -155,11 +155,11 @@ impl NodeMap { pub(super) fn receive_udp( &self, udp_addr: SocketAddr, - ) -> Option<(PublicKey, NodeIdMappedAddr)> { + ) -> Option<(PublicKey, AllPathsMappedAddr)> { self.inner.lock().expect("poisoned").receive_udp(udp_addr) } - pub(super) fn receive_relay(&self, relay_url: &RelayUrl, src: NodeId) -> NodeIdMappedAddr { + pub(super) fn receive_relay(&self, relay_url: &RelayUrl, src: NodeId) -> AllPathsMappedAddr { self.inner .lock() .expect("poisoned") @@ -169,7 +169,7 @@ impl NodeMap { pub(super) fn get_quic_mapped_addr_for_node_key( &self, node_key: NodeId, - ) -> Option { + ) -> Option { self.inner .lock() .expect("poisoned") @@ -186,6 +186,7 @@ impl NodeMap { .unwrap_or_default() } + /// Returns a [`NodeAddr`] with all the currently known direct addresses and the relay URL. pub(super) fn get_current_addr(&self, node_key: NodeId) -> Option { self.inner .lock() @@ -209,7 +210,7 @@ impl NodeMap { #[allow(clippy::type_complexity)] pub(super) fn get_send_addrs( &self, - addr: NodeIdMappedAddr, + addr: AllPathsMappedAddr, have_ipv6: bool, metrics: &Metrics, ) -> Option<( @@ -403,7 +404,7 @@ impl NodeMapInner { /// Marks the node we believe to be at `ipp` as recently used. #[cfg(not(wasm_browser))] - fn receive_udp(&mut self, udp_addr: SocketAddr) -> Option<(NodeId, NodeIdMappedAddr)> { + fn receive_udp(&mut self, udp_addr: SocketAddr) -> Option<(NodeId, AllPathsMappedAddr)> { let ip_port: IpPort = udp_addr.into(); let Some(node_state) = self.get_mut(NodeStateKey::IpPort(ip_port)) else { trace!(src=%udp_addr, "receive_udp: no node_state found for addr, ignore"); @@ -414,7 +415,7 @@ impl NodeMapInner { } #[instrument(skip_all, fields(src = %src.fmt_short()))] - fn receive_relay(&mut self, relay_url: &RelayUrl, src: NodeId) -> NodeIdMappedAddr { + fn receive_relay(&mut self, relay_url: &RelayUrl, src: NodeId) -> AllPathsMappedAddr { #[cfg(any(test, feature = "test-utils"))] let path_selection = self.path_selection; let node_state = self.get_or_insert_with(NodeStateKey::NodeId(src), || { diff --git a/iroh/src/magicsock/node_map/node_state.rs b/iroh/src/magicsock/node_map/node_state.rs index 36ad6a0de13..3f211ed359a 100644 --- a/iroh/src/magicsock/node_map/node_state.rs +++ b/iroh/src/magicsock/node_map/node_state.rs @@ -20,7 +20,7 @@ use crate::endpoint::PathSelection; use crate::{ disco::{self, SendAddr}, magicsock::{ - ActorMessage, HEARTBEAT_INTERVAL, MagicsockMetrics, NodeIdMappedAddr, + ActorMessage, AllPathsMappedAddr, HEARTBEAT_INTERVAL, MagicsockMetrics, node_map::path_validity::PathValidity, }, }; @@ -63,7 +63,7 @@ pub(super) struct NodeState { /// [`NodeMap`]: super::NodeMap id: usize, /// The UDP address used on the QUIC-layer to address this node. - quic_mapped_addr: NodeIdMappedAddr, + quic_mapped_addr: AllPathsMappedAddr, /// The global identifier for this endpoint. node_id: NodeId, /// The url of relay node that we can relay over to communicate. @@ -113,7 +113,7 @@ pub(super) struct Options { impl NodeState { pub(super) fn new(id: usize, options: Options) -> Self { - let quic_mapped_addr = NodeIdMappedAddr::generate(); + let quic_mapped_addr = AllPathsMappedAddr::generate(); // TODO(frando): I don't think we need to track the `num_relay_conns_added` // metric here. We do so in `Self::addr_for_send`. @@ -148,7 +148,7 @@ impl NodeState { &self.node_id } - pub(super) fn quic_mapped_addr(&self) -> &NodeIdMappedAddr { + pub(super) fn quic_mapped_addr(&self) -> &AllPathsMappedAddr { &self.quic_mapped_addr } @@ -667,6 +667,7 @@ impl NodeState { (udp_addr, relay_url, ping_msgs) } + /// Returns a [`NodeAddr`] with all the currently known direct addresses and the relay URL. pub(crate) fn get_current_addr(&self) -> NodeAddr { // TODO: more selective? let mut node_addr = diff --git a/iroh/src/magicsock/relay_mapped_addrs.rs b/iroh/src/magicsock/relay_mapped_addrs.rs index b473501ea60..1b21f1377e2 100644 --- a/iroh/src/magicsock/relay_mapped_addrs.rs +++ b/iroh/src/magicsock/relay_mapped_addrs.rs @@ -15,16 +15,19 @@ use snafu::Snafu; #[snafu(display("Failed to convert"))] pub struct IpMappedAddrError; -/// A map fake Ipv6 address with an actual IP address. +/// An Ipv6 ULA address, identifying a relay path for a [`NodeId`]. /// -/// It is essentially a lookup key for an IP that iroh's magicsocket knows about. +/// Since iroh nodes are reachable via a relay server we have a network path indicated by +/// the `(NodeId, RelayUrl)`. However Quinn can only handle socket addresses, so we use +/// IPv6 addresses in a private IPv6 Unique Local Address range, which map to a unique +/// `(NodeId, RelayUrl)` pair. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub(crate) struct IpMappedAddr(Ipv6Addr); +pub(crate) struct RelayMappedAddr(Ipv6Addr); /// Counter to always generate unique addresses for [`IpMappedAddr`]. static IP_ADDR_COUNTER: AtomicU64 = AtomicU64::new(1); -impl IpMappedAddr { +impl RelayMappedAddr { /// The Prefix/L of our Unique Local Addresses. const ADDR_PREFIXL: u8 = 0xfd; /// The Global ID used in our Unique Local Addresses. @@ -51,19 +54,19 @@ impl IpMappedAddr { Self(Ipv6Addr::from(addr)) } - /// Returns a consistent [`SocketAddr`] for the [`IpMappedAddr`]. + /// Returns a consistent [`SocketAddr`] for the [`RelayMappedAddr`]. /// /// This does not have a routable IP address. /// - /// This uses a made-up, but fixed port number. The [IpMappedAddresses`] map this is - /// made for creates a unique [`IpMappedAddr`] for each IP+port and thus does not use - /// the port to map back to the original [`SocketAddr`]. + /// This uses a made-up, but fixed port number. The [`RelayAddrMap`] creates a unique + /// [`RelayMappedAddr`] for each `(NodeId, RelayUrl)` pair and thus does not use the + /// port to map back to the original [`SocketAddr`]. pub(crate) fn private_socket_addr(&self) -> SocketAddr { SocketAddr::new(IpAddr::from(self.0), Self::MAPPED_ADDR_PORT) } } -impl TryFrom for IpMappedAddr { +impl TryFrom for RelayMappedAddr { type Error = IpMappedAddrError; fn try_from(value: Ipv6Addr) -> std::result::Result { @@ -78,7 +81,7 @@ impl TryFrom for IpMappedAddr { } } -impl std::fmt::Display for IpMappedAddr { +impl std::fmt::Display for RelayMappedAddr { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "IpMappedAddr({})", self.0) } @@ -87,31 +90,31 @@ impl std::fmt::Display for IpMappedAddr { /// Can occur when converting a [`SocketAddr`] to an [`RelayMappedAddr`] #[derive(Debug, Snafu)] #[snafu(display("Failed to convert"))] -pub struct RelayMappedAddrError; +pub struct RelayAddrMapError; -/// A Map of [`RelayMappedAddresses`] to [`SocketAddr`]. +/// A Map of [`RelayMappedAddr`] to `(RelayUrl, NodeId)`. #[derive(Debug, Clone, Default)] -pub(crate) struct RelayMappedAddresses(Arc>); +pub(crate) struct RelayAddrMap(Arc>); #[derive(Debug, Default)] pub(super) struct Inner { - by_mapped_addr: BTreeMap, - by_url: BTreeMap<(RelayUrl, NodeId), IpMappedAddr>, + by_mapped_addr: BTreeMap, + by_url: BTreeMap<(RelayUrl, NodeId), RelayMappedAddr>, } -impl RelayMappedAddresses { - /// Adds a [`RelayUrl`] to the map and returns the generated [`IpMappedAddr`]. +impl RelayAddrMap { + /// Adds a new entry to the map and returns the generated [`RelayMappedAddr`]. /// - /// If this [`RelayUrl`] already exists in the map, it returns its - /// associated [`IpMappedAddr`]. + /// If this `(RelayUrl, NodeId)` already exists in the map, it returns its associated + /// [`RelayMappedAddr`]. /// - /// Otherwise a new [`IpMappedAddr`] is generated for it and returned. - pub(super) fn get_or_register(&self, relay: RelayUrl, node: NodeId) -> IpMappedAddr { + /// Otherwise a new [`RelayMappedAddr`] is generated for it and returned. + pub(super) fn get_or_register(&self, relay: RelayUrl, node: NodeId) -> RelayMappedAddr { let mut inner = self.0.lock().expect("poisoned"); if let Some(mapped_addr) = inner.by_url.get(&(relay.clone(), node)) { return *mapped_addr; } - let ip_mapped_addr = IpMappedAddr::generate(); + let ip_mapped_addr = RelayMappedAddr::generate(); inner .by_mapped_addr .insert(ip_mapped_addr, (relay.clone(), node)); @@ -119,14 +122,14 @@ impl RelayMappedAddresses { ip_mapped_addr } - /// Returns the [`IpMappedAddr`] for the given [`RelayUrl`]. - pub(crate) fn get_mapped_addr(&self, relay: RelayUrl, node: NodeId) -> Option { + /// Returns the [`RelayMappedAddr`] for the given [`RelayUrl`] and [`NodeId`]. + pub(crate) fn get_mapped_addr(&self, relay: RelayUrl, node: NodeId) -> Option { let inner = self.0.lock().expect("poisoned"); inner.by_url.get(&(relay, node)).copied() } - /// Returns the [`RelayUrl`] for the given [`IpMappedAddr`]. - pub(crate) fn get_url(&self, mapped_addr: &IpMappedAddr) -> Option<(RelayUrl, NodeId)> { + /// Returns the [`RelayUrl`] and [`NodeId`] for the given [`IpMappedAddr`]. + pub(crate) fn get_url(&self, mapped_addr: &RelayMappedAddr) -> Option<(RelayUrl, NodeId)> { let inner = self.0.lock().expect("poisoned"); inner.by_mapped_addr.get(mapped_addr).cloned() } From f9924cd5c9e106a322122b01d67be2fa69f10d67 Mon Sep 17 00:00:00 2001 From: Floris Bruynooghe Date: Fri, 29 Aug 2025 15:08:57 +0200 Subject: [PATCH 24/27] switch to main multipath branch --- Cargo.lock | 61 ++++++++++++------------------------------------------ Cargo.toml | 6 +----- 2 files changed, 14 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fc60e20fe7a..6c1feec38b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -497,12 +497,6 @@ version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" -[[package]] -name = "bytemuck" -version = "1.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" - [[package]] name = "byteorder" version = "1.5.0" @@ -1236,14 +1230,13 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fastbloom" -version = "0.9.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27cea6e7f512d43b098939ff4d5a5d6fe3db07971e1d05176fe26c642d33f5b8" +checksum = "29ec576c163744bef8707859f6aeb322bcf56b8da61215d99f77d6e33160ff01" dependencies = [ "getrandom 0.3.2", "rand 0.9.1", "siphasher", - "wide", ] [[package]] @@ -2489,7 +2482,7 @@ dependencies = [ [[package]] name = "iroh-quinn" version = "0.14.0" -source = "git+https://github.com//n0-computer/quinn?branch=server-migrations#bc86957aa4ccb72fad70e75a6ce9fc8198f09afc" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#a68cb468bb44b1ec83a1b6ba110b2e7ecae00d91" dependencies = [ "bytes", "cfg_aliases", @@ -2498,7 +2491,7 @@ dependencies = [ "pin-project-lite", "rustc-hash", "rustls", - "socket2 0.5.10", + "socket2 0.6.0", "thiserror 2.0.12", "tokio", "tracing", @@ -2508,7 +2501,7 @@ dependencies = [ [[package]] name = "iroh-quinn-proto" version = "0.13.0" -source = "git+https://github.com//n0-computer/quinn?branch=server-migrations#bc86957aa4ccb72fad70e75a6ce9fc8198f09afc" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#a68cb468bb44b1ec83a1b6ba110b2e7ecae00d91" dependencies = [ "bytes", "fastbloom", @@ -2530,12 +2523,12 @@ dependencies = [ [[package]] name = "iroh-quinn-udp" version = "0.5.12" -source = "git+https://github.com//n0-computer/quinn?branch=server-migrations#bc86957aa4ccb72fad70e75a6ce9fc8198f09afc" +source = "git+https://github.com/n0-computer/quinn?branch=multipath-quinn-0.11.x#a68cb468bb44b1ec83a1b6ba110b2e7ecae00d91" dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.0", "tracing", "windows-sys 0.59.0", ] @@ -4110,8 +4103,8 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.25" -source = "git+https://github.com/n0-computer/rustls?rev=be02113e7837df60953d02c2bdd0f4634fef3a80#be02113e7837df60953d02c2bdd0f4634fef3a80" +version = "0.23.27" +source = "git+https://github.com/n0-computer/rustls?rev=c636f89ae00aee19ddd5e6df4150cec5c031fa31#c636f89ae00aee19ddd5e6df4150cec5c031fa31" dependencies = [ "log", "once_cell", @@ -4190,9 +4183,9 @@ dependencies = [ [[package]] name = "rustls-platform-verifier" -version = "0.5.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4937d110d34408e9e5ad30ba0b0ca3b6a8a390f8db3636db60144ac4fa792750" +checksum = "be59af91596cac372a6942530653ad0c3a246cdd491aaa9dcaee47f88d67d5a0" dependencies = [ "core-foundation 0.10.0", "core-foundation-sys", @@ -4205,7 +4198,7 @@ dependencies = [ "rustls-webpki", "security-framework", "security-framework-sys", - "webpki-root-certs 0.26.11", + "webpki-root-certs", "windows-sys 0.59.0", ] @@ -4250,15 +4243,6 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" -[[package]] -name = "safe_arch" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" -dependencies = [ - "bytemuck", -] - [[package]] name = "salsa20" version = "0.10.2" @@ -5630,15 +5614,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki-root-certs" -version = "0.26.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" -dependencies = [ - "webpki-root-certs 1.0.0", -] - [[package]] name = "webpki-root-certs" version = "1.0.0" @@ -5666,16 +5641,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "wide" -version = "0.7.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03" -dependencies = [ - "bytemuck", - "safe_arch", -] - [[package]] name = "widestring" version = "1.2.0" @@ -5704,7 +5669,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 8e7b8f11f49..5d06c0f4eaa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,14 +43,10 @@ unused-async = "warn" [patch.crates-io] -rustls = { git = "https://github.com/n0-computer/rustls", rev = "be02113e7837df60953d02c2bdd0f4634fef3a80" } +rustls = { git = "https://github.com/n0-computer/rustls", rev = "c636f89ae00aee19ddd5e6df4150cec5c031fa31" } netwatch = { git = "https://github.com/n0-computer/net-tools", branch = "feat-multipath" } [patch."https://github.com/n0-computer/quinn"] # iroh-quinn = { path = "../iroh-quinn/quinn" } # iroh-quinn-proto = { path = "../iroh-quinn/quinn-proto" } # iroh-quinn-udp = { path = "../iroh-quinn/quinn-udp" } - -iroh-quinn = { git = "https://github.com//n0-computer/quinn", branch = "server-migrations" } -iroh-quinn-proto = { git = "https://github.com//n0-computer/quinn", branch = "server-migrations" } -iroh-quinn-udp = { git = "https://github.com//n0-computer/quinn", branch = "server-migrations" } From 3058a8e2b1a28407eb170244f7c9eaf14c9a7ce7 Mon Sep 17 00:00:00 2001 From: Floris Bruynooghe Date: Fri, 29 Aug 2025 15:09:39 +0200 Subject: [PATCH 25/27] another rename --- iroh/src/endpoint.rs | 9 +++++---- iroh/src/magicsock.rs | 2 +- iroh/src/magicsock/node_map.rs | 17 ++++++++++------- iroh/src/magicsock/node_map/node_state.rs | 2 +- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/iroh/src/endpoint.rs b/iroh/src/endpoint.rs index a6ba61c4bce..708510a5e34 100644 --- a/iroh/src/endpoint.rs +++ b/iroh/src/endpoint.rs @@ -728,10 +728,11 @@ impl Endpoint { } let node_id = node_addr.node_id; - // Get the mapped IPv6 address from the magic socket. Quinn will connect to this - // address. Start discovery for this node if it's enabled and we have no valid or - // verified address information for this node. Dropping the discovery cancels any - // still running task. + // When we start a connection we want to send the QUIC Initial packets on all the + // known paths for the remote node. For this we use an AllPathsMappedAddr as + // destination for Quinn. Start discovery for this node if it's enabled and we have + // no valid or verified address information for this node. Dropping the discovery + // cancels any still running task. let (mapped_addr, _discovery_drop_guard) = self .get_mapping_addr_and_maybe_start_discovery(node_addr) .await diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index c5c57055c0c..c453527df15 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -431,7 +431,7 @@ impl MagicSock { /// Returns the socket address which can be used by the QUIC layer to dial this node. pub(crate) fn get_mapping_addr(&self, node_id: NodeId) -> Option { - self.node_map.get_quic_mapped_addr_for_node_key(node_id) + self.node_map.get_all_paths_add_for_node(node_id) } pub(crate) fn get_direct_addrs(&self, node_id: NodeId) -> Vec { diff --git a/iroh/src/magicsock/node_map.rs b/iroh/src/magicsock/node_map.rs index 36d4b9cdfba..69d313b9717 100644 --- a/iroh/src/magicsock/node_map.rs +++ b/iroh/src/magicsock/node_map.rs @@ -166,7 +166,7 @@ impl NodeMap { .receive_relay(relay_url, src) } - pub(super) fn get_quic_mapped_addr_for_node_key( + pub(super) fn get_all_paths_add_for_node( &self, node_key: NodeId, ) -> Option { @@ -174,7 +174,7 @@ impl NodeMap { .lock() .expect("poisoned") .get(NodeStateKey::NodeId(node_key)) - .map(|ep| *ep.quic_mapped_addr()) + .map(|ep| *ep.all_paths_mapped_addr()) } pub(super) fn get_direct_addrs(&self, node_key: NodeId) -> Vec { @@ -359,7 +359,7 @@ impl NodeMapInner { node.remove_direct_addr(&ipp, now, why); if node.direct_addresses().count() == 0 { let node_id = node.public_key(); - let mapped_addr = node.quic_mapped_addr(); + let mapped_addr = node.all_paths_mapped_addr(); self.by_node_key.remove(node_id); self.by_quic_mapped_addr.remove(mapped_addr); debug!(node_id=%node_id.fmt_short(), why, "removing node"); @@ -411,7 +411,10 @@ impl NodeMapInner { return None; }; node_state.receive_udp(ip_port, Instant::now()); - Some((*node_state.public_key(), *node_state.quic_mapped_addr())) + Some(( + *node_state.public_key(), + *node_state.all_paths_mapped_addr(), + )) } #[instrument(skip_all, fields(src = %src.fmt_short()))] @@ -430,7 +433,7 @@ impl NodeMapInner { } }); node_state.receive_relay(relay_url, src, Instant::now()); - *node_state.quic_mapped_addr() + *node_state.all_paths_mapped_addr() } fn node_states(&self) -> impl Iterator { @@ -501,7 +504,7 @@ impl NodeMapInner { // update indices self.by_quic_mapped_addr - .insert(*node_state.quic_mapped_addr(), id); + .insert(*node_state.all_paths_mapped_addr(), id); self.by_node_key.insert(*node_state.public_key(), id); self.by_id.insert(id, node_state); @@ -572,7 +575,7 @@ impl NodeMapInner { self.by_ip_port.remove(&ip_port); } - self.by_quic_mapped_addr.remove(ep.quic_mapped_addr()); + self.by_quic_mapped_addr.remove(ep.all_paths_mapped_addr()); } } } diff --git a/iroh/src/magicsock/node_map/node_state.rs b/iroh/src/magicsock/node_map/node_state.rs index 3f211ed359a..ec28bc5969b 100644 --- a/iroh/src/magicsock/node_map/node_state.rs +++ b/iroh/src/magicsock/node_map/node_state.rs @@ -148,7 +148,7 @@ impl NodeState { &self.node_id } - pub(super) fn quic_mapped_addr(&self) -> &AllPathsMappedAddr { + pub(super) fn all_paths_mapped_addr(&self) -> &AllPathsMappedAddr { &self.quic_mapped_addr } From 11dd04d84cc9aaf0437c3bd5fdb55d21727ea857 Mon Sep 17 00:00:00 2001 From: Floris Bruynooghe Date: Fri, 29 Aug 2025 18:05:46 +0200 Subject: [PATCH 26/27] Set max_idle_time to a good value This is what we used to do --- iroh/src/endpoint.rs | 1 - iroh/src/magicsock.rs | 15 +++++++++++---- iroh/src/magicsock/node_map.rs | 4 ++-- iroh/src/magicsock/node_map/node_state.rs | 6 +++--- iroh/src/magicsock/node_map/path_state.rs | 5 +---- iroh/src/magicsock/node_map/udp_paths.rs | 3 +-- 6 files changed, 18 insertions(+), 16 deletions(-) diff --git a/iroh/src/endpoint.rs b/iroh/src/endpoint.rs index 708510a5e34..c3a73b74ab7 100644 --- a/iroh/src/endpoint.rs +++ b/iroh/src/endpoint.rs @@ -627,7 +627,6 @@ impl Endpoint { trace!("created magicsock"); debug!(version = env!("CARGO_PKG_VERSION"), "iroh Endpoint created"); - let metrics = msock.metrics.magicsock.clone(); let ep = Self { msock, static_config: Arc::new(static_config), diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index c453527df15..165bf65d949 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -94,8 +94,18 @@ pub use self::{ /// expire at 30 seconds, so this is a few seconds shy of that. const ENDPOINTS_FRESH_ENOUGH_DURATION: Duration = Duration::from_secs(27); +/// The duration in which we send keep-alives. +/// +/// If a path is idle for this long, a PING frame will be sent to keep the connection +/// alive. const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5); +/// The maximum time a path can stay idle before being closed. +/// +/// This is [`HEARTBEAT_INTERVAL`] + 1.5s. This gives us a chance to send a PING frame and +/// some retries. +const MAX_IDLE_TIMEOUT: Duration = Duration::from_millis(6500); + /// Contains options for `MagicSock::listen`. #[derive(derive_more::Debug)] pub(crate) struct Options { @@ -491,10 +501,7 @@ impl MagicSock { .await { Ok(path) => { - path.set_max_idle_timeout(Some( - ENDPOINTS_FRESH_ENOUGH_DURATION, - )) - .ok(); + path.set_max_idle_timeout(Some(MAX_IDLE_TIMEOUT)).ok(); path.set_keep_alive_interval(Some(HEARTBEAT_INTERVAL)).ok(); } Err(err) => { diff --git a/iroh/src/magicsock/node_map.rs b/iroh/src/magicsock/node_map.rs index 69d313b9717..e05d96029ff 100644 --- a/iroh/src/magicsock/node_map.rs +++ b/iroh/src/magicsock/node_map.rs @@ -11,8 +11,8 @@ use serde::{Deserialize, Serialize}; use tracing::{debug, info, instrument, trace, warn}; use self::node_state::{NodeState, Options}; -use super::{ActorMessage, AllPathsMappedAddr, metrics::Metrics, transports}; -use crate::disco::{CallMeMaybe, Pong, SendAddr}; +use super::{AllPathsMappedAddr, metrics::Metrics}; +use crate::disco::CallMeMaybe; #[cfg(any(test, feature = "test-utils"))] use crate::endpoint::PathSelection; diff --git a/iroh/src/magicsock/node_map/node_state.rs b/iroh/src/magicsock/node_map/node_state.rs index ec28bc5969b..b86d2f8802b 100644 --- a/iroh/src/magicsock/node_map/node_state.rs +++ b/iroh/src/magicsock/node_map/node_state.rs @@ -20,7 +20,7 @@ use crate::endpoint::PathSelection; use crate::{ disco::{self, SendAddr}, magicsock::{ - ActorMessage, AllPathsMappedAddr, HEARTBEAT_INTERVAL, MagicsockMetrics, + AllPathsMappedAddr, HEARTBEAT_INTERVAL, MagicsockMetrics, node_map::path_validity::PathValidity, }, }; @@ -921,12 +921,12 @@ pub enum ConnectionType { #[cfg(test)] mod tests { - use std::{collections::BTreeMap, net::Ipv4Addr}; + use std::net::Ipv4Addr; use iroh_base::SecretKey; use super::*; - use crate::magicsock::node_map::{NodeMap, NodeMapInner}; + // use crate::magicsock::node_map::{NodeMap, NodeMapInner}; // #[test] // fn test_remote_infos() { diff --git a/iroh/src/magicsock/node_map/path_state.rs b/iroh/src/magicsock/node_map/path_state.rs index ce3121539b0..7fd4fc7fe78 100644 --- a/iroh/src/magicsock/node_map/path_state.rs +++ b/iroh/src/magicsock/node_map/path_state.rs @@ -11,10 +11,7 @@ use super::{ }; use crate::{ disco::SendAddr, - magicsock::{ - HEARTBEAT_INTERVAL, - node_map::path_validity::{self, PathValidity}, - }, + magicsock::node_map::path_validity::{self, PathValidity}, }; /// State about a particular path to another [`NodeState`]. diff --git a/iroh/src/magicsock/node_map/udp_paths.rs b/iroh/src/magicsock/node_map/udp_paths.rs index d89cf10ed2c..2c72a95c842 100644 --- a/iroh/src/magicsock/node_map/udp_paths.rs +++ b/iroh/src/magicsock/node_map/udp_paths.rs @@ -7,8 +7,7 @@ //! [`NodeState`]: super::node_state::NodeState use std::{collections::BTreeMap, net::SocketAddr}; -use n0_future::time::{Duration, Instant}; -use rand::seq::IteratorRandom; +use n0_future::time::Instant; use tracing::{Level, event}; use super::{IpPort, path_state::PathState}; From 6869faaf04204e60864847d037367d939a540ae4 Mon Sep 17 00:00:00 2001 From: Floris Bruynooghe Date: Mon, 1 Sep 2025 14:49:17 +0200 Subject: [PATCH 27/27] fix typo --- iroh/src/magicsock.rs | 4 ++-- iroh/src/magicsock/node_map.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index 165bf65d949..fe6efdf12d8 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -439,9 +439,9 @@ impl MagicSock { self.node_map.conn_type(node_id) } - /// Returns the socket address which can be used by the QUIC layer to dial this node. + /// Returns the socket address which can be used by the QUIC layer to *dial* this node. pub(crate) fn get_mapping_addr(&self, node_id: NodeId) -> Option { - self.node_map.get_all_paths_add_for_node(node_id) + self.node_map.get_all_paths_addr_for_node(node_id) } pub(crate) fn get_direct_addrs(&self, node_id: NodeId) -> Vec { diff --git a/iroh/src/magicsock/node_map.rs b/iroh/src/magicsock/node_map.rs index e05d96029ff..96acd7ea146 100644 --- a/iroh/src/magicsock/node_map.rs +++ b/iroh/src/magicsock/node_map.rs @@ -166,7 +166,7 @@ impl NodeMap { .receive_relay(relay_url, src) } - pub(super) fn get_all_paths_add_for_node( + pub(super) fn get_all_paths_addr_for_node( &self, node_key: NodeId, ) -> Option {