Implement opt-in overlay TUN runtime
This commit is contained in:
parent
c2dee50dae
commit
d9728a326d
15 changed files with 2315 additions and 84 deletions
377
Cargo.lock
generated
377
Cargo.lock
generated
|
|
@ -210,6 +210,12 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-task"
|
||||||
|
version = "4.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.89"
|
version = "0.1.89"
|
||||||
|
|
@ -334,7 +340,7 @@ dependencies = [
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
"object",
|
"object",
|
||||||
"rustc-demangle",
|
"rustc-demangle",
|
||||||
"windows-link",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -425,6 +431,19 @@ dependencies = [
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blocking"
|
||||||
|
version = "1.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21"
|
||||||
|
dependencies = [
|
||||||
|
"async-channel",
|
||||||
|
"async-task",
|
||||||
|
"futures-io",
|
||||||
|
"futures-lite",
|
||||||
|
"piper",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "borsh"
|
name = "borsh"
|
||||||
version = "1.6.1"
|
version = "1.6.1"
|
||||||
|
|
@ -462,6 +481,26 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "c2rust-bitfields"
|
||||||
|
version = "0.21.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dcee50917f9de1a018e3f4f9a8f2ff3d030a288cffa4b18d9b391e97c12e4cfb"
|
||||||
|
dependencies = [
|
||||||
|
"c2rust-bitfields-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "c2rust-bitfields-derive"
|
||||||
|
version = "0.21.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b457277798202ccd365b9c112ebee08ddd57f1033916c8b8ea52f222e5b715d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.117",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.62"
|
version = "1.2.62"
|
||||||
|
|
@ -515,7 +554,7 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"serde",
|
"serde",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"windows-link",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1076,6 +1115,15 @@ version = "0.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
|
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encoding_rs"
|
||||||
|
version = "0.8.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "enum-as-inner"
|
name = "enum-as-inner"
|
||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
|
|
@ -1142,6 +1190,9 @@ name = "fastrand"
|
||||||
version = "2.4.1"
|
version = "2.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
|
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.3.4",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fiat-crypto"
|
name = "fiat-crypto"
|
||||||
|
|
@ -1177,6 +1228,18 @@ dependencies = [
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flume"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be"
|
||||||
|
dependencies = [
|
||||||
|
"fastrand",
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"spin 0.9.8",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fnv"
|
name = "fnv"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
|
|
@ -1379,8 +1442,8 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
"windows-link",
|
"windows-link 0.2.1",
|
||||||
"windows-result",
|
"windows-result 0.4.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1407,6 +1470,7 @@ dependencies = [
|
||||||
"geth-iroh",
|
"geth-iroh",
|
||||||
"geth-keychain",
|
"geth-keychain",
|
||||||
"geth-node",
|
"geth-node",
|
||||||
|
"geth-overlay",
|
||||||
"geth-ssh-identity",
|
"geth-ssh-identity",
|
||||||
"geth-store",
|
"geth-store",
|
||||||
"geth-types",
|
"geth-types",
|
||||||
|
|
@ -1637,12 +1701,14 @@ dependencies = [
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
"tun-rs",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "geth-overlay"
|
name = "geth-overlay"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"blake3",
|
||||||
"geth-types",
|
"geth-types",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
|
|
@ -1734,6 +1800,17 @@ dependencies = [
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getifaddrs"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a542e1b7ac1f3d62c5777d430d66eca9cb59e813c46b86e29fa9ce94ff9a4810"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
|
|
@ -2083,7 +2160,7 @@ dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"log",
|
"log",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"windows-core",
|
"windows-core 0.62.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2275,7 +2352,7 @@ dependencies = [
|
||||||
"socket2 0.6.3",
|
"socket2 0.6.3",
|
||||||
"widestring",
|
"widestring",
|
||||||
"windows-registry",
|
"windows-registry",
|
||||||
"windows-result",
|
"windows-result 0.4.1",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -2712,7 +2789,7 @@ dependencies = [
|
||||||
"simd_cesu8",
|
"simd_cesu8",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
"windows-link",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2802,6 +2879,16 @@ version = "0.2.186"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libloading"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"windows-link 0.2.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libredox"
|
name = "libredox"
|
||||||
version = "0.1.16"
|
version = "0.1.16"
|
||||||
|
|
@ -2898,6 +2985,15 @@ version = "2.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoffset"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minimal-lexical"
|
name = "minimal-lexical"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
|
@ -3021,6 +3117,27 @@ dependencies = [
|
||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "netconfig-rs"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "733ad7a1352f0748a620368f2ea5cd94f0e449c5108fbac86ace31df1601bf6f"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"core-foundation 0.10.1",
|
||||||
|
"ipnet",
|
||||||
|
"libc",
|
||||||
|
"netlink-packet-core",
|
||||||
|
"netlink-packet-route 0.25.1",
|
||||||
|
"netlink-sys",
|
||||||
|
"nix 0.30.1",
|
||||||
|
"scopeguard",
|
||||||
|
"system-configuration-sys",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
"widestring",
|
||||||
|
"windows 0.61.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "netdev"
|
name = "netdev"
|
||||||
version = "0.38.2"
|
version = "0.38.2"
|
||||||
|
|
@ -3031,7 +3148,7 @@ dependencies = [
|
||||||
"ipnet",
|
"ipnet",
|
||||||
"libc",
|
"libc",
|
||||||
"netlink-packet-core",
|
"netlink-packet-core",
|
||||||
"netlink-packet-route",
|
"netlink-packet-route 0.25.1",
|
||||||
"netlink-sys",
|
"netlink-sys",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"system-configuration",
|
"system-configuration",
|
||||||
|
|
@ -3059,6 +3176,18 @@ dependencies = [
|
||||||
"netlink-packet-core",
|
"netlink-packet-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "netlink-packet-route"
|
||||||
|
version = "0.28.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ce3636fa715e988114552619582b530481fd5ef176a1e5c1bf024077c2c9445"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"netlink-packet-core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "netlink-proto"
|
name = "netlink-proto"
|
||||||
version = "0.12.0"
|
version = "0.12.0"
|
||||||
|
|
@ -3104,7 +3233,7 @@ dependencies = [
|
||||||
"n0-watcher",
|
"n0-watcher",
|
||||||
"netdev",
|
"netdev",
|
||||||
"netlink-packet-core",
|
"netlink-packet-core",
|
||||||
"netlink-packet-route",
|
"netlink-packet-route 0.25.1",
|
||||||
"netlink-proto",
|
"netlink-proto",
|
||||||
"netlink-sys",
|
"netlink-sys",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
|
@ -3115,11 +3244,37 @@ dependencies = [
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tracing",
|
"tracing",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"windows",
|
"windows 0.62.2",
|
||||||
"windows-result",
|
"windows-result 0.4.1",
|
||||||
"wmi",
|
"wmi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.30.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if",
|
||||||
|
"cfg_aliases",
|
||||||
|
"libc",
|
||||||
|
"memoffset",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.31.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if",
|
||||||
|
"cfg_aliases",
|
||||||
|
"libc",
|
||||||
|
"memoffset",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nom"
|
name = "nom"
|
||||||
version = "7.1.3"
|
version = "7.1.3"
|
||||||
|
|
@ -3282,7 +3437,7 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows-link",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -3352,6 +3507,17 @@ version = "0.2.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "piper"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-waker",
|
||||||
|
"fastrand",
|
||||||
|
"futures-io",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkarr"
|
name = "pkarr"
|
||||||
version = "5.0.3"
|
version = "5.0.3"
|
||||||
|
|
@ -3795,7 +3961,7 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows",
|
"windows 0.62.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -3911,6 +4077,20 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "route_manager"
|
||||||
|
version = "0.2.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "319bb478ff9aae1dc7a544fa599e9eb47e2521621dc3921fba6abcaaa312092c"
|
||||||
|
dependencies = [
|
||||||
|
"flume",
|
||||||
|
"libc",
|
||||||
|
"netlink-packet-core",
|
||||||
|
"netlink-packet-route 0.28.0",
|
||||||
|
"netlink-sys",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rusqlite"
|
name = "rusqlite"
|
||||||
version = "0.32.1"
|
version = "0.32.1"
|
||||||
|
|
@ -5001,6 +5181,32 @@ version = "0.2.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tun-rs"
|
||||||
|
version = "2.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a88c6aea62111e48fb67b733d6bbdc2019272e6d6f7108c9b0f4cd25d715d38a"
|
||||||
|
dependencies = [
|
||||||
|
"blocking",
|
||||||
|
"byteorder",
|
||||||
|
"bytes",
|
||||||
|
"c2rust-bitfields",
|
||||||
|
"encoding_rs",
|
||||||
|
"getifaddrs",
|
||||||
|
"ipnet",
|
||||||
|
"libc",
|
||||||
|
"libloading",
|
||||||
|
"log",
|
||||||
|
"netconfig-rs",
|
||||||
|
"nix 0.31.3",
|
||||||
|
"route_manager",
|
||||||
|
"scopeguard",
|
||||||
|
"tokio",
|
||||||
|
"widestring",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
"winreg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.20.0"
|
version = "1.20.0"
|
||||||
|
|
@ -5324,16 +5530,38 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows"
|
||||||
|
version = "0.61.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
|
||||||
|
dependencies = [
|
||||||
|
"windows-collections 0.2.0",
|
||||||
|
"windows-core 0.61.2",
|
||||||
|
"windows-future 0.2.1",
|
||||||
|
"windows-link 0.1.3",
|
||||||
|
"windows-numerics 0.2.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows"
|
name = "windows"
|
||||||
version = "0.62.2"
|
version = "0.62.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
|
checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-collections",
|
"windows-collections 0.3.2",
|
||||||
"windows-core",
|
"windows-core 0.62.2",
|
||||||
"windows-future",
|
"windows-future 0.3.2",
|
||||||
"windows-numerics",
|
"windows-numerics 0.3.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
|
|
@ -5342,7 +5570,20 @@ version = "0.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
|
checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-core",
|
"windows-core 0.62.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-core"
|
||||||
|
version = "0.61.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
|
||||||
|
dependencies = [
|
||||||
|
"windows-implement",
|
||||||
|
"windows-interface",
|
||||||
|
"windows-link 0.1.3",
|
||||||
|
"windows-result 0.3.4",
|
||||||
|
"windows-strings 0.4.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -5353,9 +5594,20 @@ checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-implement",
|
"windows-implement",
|
||||||
"windows-interface",
|
"windows-interface",
|
||||||
"windows-link",
|
"windows-link 0.2.1",
|
||||||
"windows-result",
|
"windows-result 0.4.1",
|
||||||
"windows-strings",
|
"windows-strings 0.5.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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 0.1.3",
|
||||||
|
"windows-threading 0.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -5364,9 +5616,9 @@ version = "0.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
|
checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-core",
|
"windows-core 0.62.2",
|
||||||
"windows-link",
|
"windows-link 0.2.1",
|
||||||
"windows-threading",
|
"windows-threading 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -5391,20 +5643,36 @@ dependencies = [
|
||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-link"
|
name = "windows-link"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[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 0.1.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-numerics"
|
name = "windows-numerics"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
|
checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-core",
|
"windows-core 0.62.2",
|
||||||
"windows-link",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -5413,9 +5681,18 @@ version = "0.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
|
checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-link",
|
"windows-link 0.2.1",
|
||||||
"windows-result",
|
"windows-result 0.4.1",
|
||||||
"windows-strings",
|
"windows-strings 0.5.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-result"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link 0.1.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -5424,7 +5701,16 @@ version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-link",
|
"windows-link 0.2.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-strings"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link 0.1.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -5433,7 +5719,7 @@ version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-link",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -5487,7 +5773,7 @@ version = "0.61.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-link",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -5542,7 +5828,7 @@ version = "0.53.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-link",
|
"windows-link 0.2.1",
|
||||||
"windows_aarch64_gnullvm 0.53.1",
|
"windows_aarch64_gnullvm 0.53.1",
|
||||||
"windows_aarch64_msvc 0.53.1",
|
"windows_aarch64_msvc 0.53.1",
|
||||||
"windows_i686_gnu 0.53.1",
|
"windows_i686_gnu 0.53.1",
|
||||||
|
|
@ -5553,13 +5839,22 @@ dependencies = [
|
||||||
"windows_x86_64_msvc 0.53.1",
|
"windows_x86_64_msvc 0.53.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-threading"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link 0.1.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-threading"
|
name = "windows-threading"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
|
checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-link",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -5751,6 +6046,16 @@ dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winreg"
|
||||||
|
version = "0.55.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wit-bindgen"
|
name = "wit-bindgen"
|
||||||
version = "0.46.0"
|
version = "0.46.0"
|
||||||
|
|
@ -5856,8 +6161,8 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
"windows",
|
"windows 0.62.2",
|
||||||
"windows-core",
|
"windows-core 0.62.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ tokio = { version = "1", features = ["fs", "io-util", "macros", "net", "rt-multi
|
||||||
toml_edit = "0.25.11"
|
toml_edit = "0.25.11"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
|
||||||
|
tun-rs = { version = "2.7.7", features = ["async"] }
|
||||||
url = "2"
|
url = "2"
|
||||||
|
|
||||||
[workspace.lints.rust]
|
[workspace.lints.rust]
|
||||||
|
|
|
||||||
20
README.md
20
README.md
|
|
@ -117,10 +117,22 @@ The bootstrap implementation provides:
|
||||||
`geth overlay status`,
|
`geth overlay status`,
|
||||||
`geth overlay plan <name> [--cidr 172.22.0.0/24]`,
|
`geth overlay plan <name> [--cidr 172.22.0.0/24]`,
|
||||||
`geth overlay join <name> --secret <resource-secret> [--cidr 172.22.0.0/24]`,
|
`geth overlay join <name> --secret <resource-secret> [--cidr 172.22.0.0/24]`,
|
||||||
and `geth overlay leave <name>`. These commands expose the resource,
|
`geth overlay interface-plan <name> [--platform linux|macos|windows]`,
|
||||||
capability, and ALPN shape for a future Iroh-carried packet overlay inspired
|
`geth overlay up <name> [--bearer-secret <route-token>] [--mtu 1280]`,
|
||||||
by iroh-lan. The prototype does not create TUN/Wintun interfaces or route
|
`geth overlay down <name>`,
|
||||||
packets yet.
|
`geth overlay peers <name>`, `geth overlay send <name> <node>
|
||||||
|
--packet-base64 <ipv4-packet>`, `geth overlay recv <name>`, and
|
||||||
|
`geth overlay leave <name>`. Join persists local overlay membership, creates
|
||||||
|
the overlay resource when needed, assigns a deterministic virtual IP, and
|
||||||
|
stores only a BLAKE3 fingerprint of the supplied secret. If the overlay
|
||||||
|
resource already has bearer invites, join requires a bearer token with
|
||||||
|
`overlay.join`. Packet send validates IPv4 packets and carries them over the
|
||||||
|
dedicated `/geth/overlay/1` Iroh ALPN after `overlay.route` authorization.
|
||||||
|
`overlay up` creates a real L3 TUN/Wintun-style interface through `tun-rs`,
|
||||||
|
reads IPv4 packets from that interface, maps destination overlay IPs to
|
||||||
|
imported peer cards, and routes packets over `/geth/overlay/1`. Creating the
|
||||||
|
interface is explicit opt-in and may require `CAP_NET_ADMIN`, sudo, or
|
||||||
|
platform-specific network entitlements.
|
||||||
- `geth resource list`
|
- `geth resource list`
|
||||||
- `geth resource create <kind> <name>`
|
- `geth resource create <kind> <name>`
|
||||||
- `geth keychain init [--admin-key <path>] [--signing-key <path>]`
|
- `geth keychain init [--admin-key <path>] [--signing-key <path>]`
|
||||||
|
|
|
||||||
|
|
@ -189,14 +189,25 @@ Current prototype commands:
|
||||||
geth overlay plan home
|
geth overlay plan home
|
||||||
geth overlay plan home --cidr 172.22.0.0/24
|
geth overlay plan home --cidr 172.22.0.0/24
|
||||||
geth overlay join home --secret <resource-secret>
|
geth overlay join home --secret <resource-secret>
|
||||||
|
geth overlay interface-plan home --platform linux
|
||||||
|
geth overlay up home
|
||||||
|
geth overlay down home
|
||||||
|
geth overlay send home <node> --packet-base64 <ipv4-packet>
|
||||||
|
geth overlay recv home
|
||||||
geth overlay leave home
|
geth overlay leave home
|
||||||
|
|
||||||
Current limits:
|
Current limits:
|
||||||
- join/leave are planning stubs; no TUN/Wintun interface is created yet
|
- overlay up creates a real TUN/Wintun-style L3 device and usually needs
|
||||||
- host network changes must remain explicit opt-in in future versions
|
privileges or host network entitlements
|
||||||
|
- host network changes are explicit opt-in only
|
||||||
- discovery can suggest peers, but never grants overlay access
|
- discovery can suggest peers, but never grants overlay access
|
||||||
- overlay access must be resource-authorized with overlay.join/overlay.route
|
- overlay access must be resource-authorized with overlay.join/overlay.route
|
||||||
- all overlay packets must be carried over Iroh, not SSH or another transport
|
- all overlay packets must be carried over Iroh, not SSH or another transport
|
||||||
|
|
||||||
|
Bearer invite flow:
|
||||||
|
geth resource create overlay home
|
||||||
|
geth secret bearer create resource:overlay:home --capability overlay.join
|
||||||
|
geth overlay join home --secret <bearer-token>
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
const GUIDE_SMOKE_TEST: &str = r#"Minimal smoke test:
|
const GUIDE_SMOKE_TEST: &str = r#"Minimal smoke test:
|
||||||
|
|
@ -540,6 +551,37 @@ pub enum OverlayCommand {
|
||||||
Leave {
|
Leave {
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
InterfacePlan {
|
||||||
|
name: String,
|
||||||
|
#[arg(long)]
|
||||||
|
platform: Option<String>,
|
||||||
|
},
|
||||||
|
Up {
|
||||||
|
name: String,
|
||||||
|
#[arg(long)]
|
||||||
|
bearer_secret: Option<String>,
|
||||||
|
#[arg(long)]
|
||||||
|
mtu: Option<u16>,
|
||||||
|
},
|
||||||
|
Down {
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
Peers {
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
Send {
|
||||||
|
name: String,
|
||||||
|
node: String,
|
||||||
|
#[arg(long)]
|
||||||
|
packet_base64: String,
|
||||||
|
#[arg(long)]
|
||||||
|
bearer_secret: Option<String>,
|
||||||
|
},
|
||||||
|
Recv {
|
||||||
|
name: String,
|
||||||
|
#[arg(long)]
|
||||||
|
peek: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Subcommand)]
|
#[derive(Debug, Subcommand)]
|
||||||
|
|
@ -1297,6 +1339,32 @@ fn request_for_command(command: Command) -> Result<ControlRequest> {
|
||||||
ControlRequest::OverlayJoin { name, secret, cidr }
|
ControlRequest::OverlayJoin { name, secret, cidr }
|
||||||
}
|
}
|
||||||
OverlayCommand::Leave { name } => ControlRequest::OverlayLeave { name },
|
OverlayCommand::Leave { name } => ControlRequest::OverlayLeave { name },
|
||||||
|
OverlayCommand::InterfacePlan { name, platform } => {
|
||||||
|
ControlRequest::OverlayInterfacePlan { name, platform }
|
||||||
|
}
|
||||||
|
OverlayCommand::Up {
|
||||||
|
name,
|
||||||
|
bearer_secret,
|
||||||
|
mtu,
|
||||||
|
} => ControlRequest::OverlayUp {
|
||||||
|
name,
|
||||||
|
bearer_secret,
|
||||||
|
mtu,
|
||||||
|
},
|
||||||
|
OverlayCommand::Down { name } => ControlRequest::OverlayDown { name },
|
||||||
|
OverlayCommand::Peers { name } => ControlRequest::OverlayPeers { name },
|
||||||
|
OverlayCommand::Send {
|
||||||
|
name,
|
||||||
|
node,
|
||||||
|
packet_base64,
|
||||||
|
bearer_secret,
|
||||||
|
} => ControlRequest::OverlaySend {
|
||||||
|
name,
|
||||||
|
node,
|
||||||
|
packet_base64,
|
||||||
|
bearer_secret,
|
||||||
|
},
|
||||||
|
OverlayCommand::Recv { name, peek } => ControlRequest::OverlayRecv { name, peek },
|
||||||
},
|
},
|
||||||
Command::Resource {
|
Command::Resource {
|
||||||
command: ResourceCommand::List,
|
command: ResourceCommand::List,
|
||||||
|
|
@ -1989,6 +2057,12 @@ fn print_response(response: ControlResponse, json: bool) -> Result<()> {
|
||||||
ControlResponse::OverlayJoined { join } => {
|
ControlResponse::OverlayJoined { join } => {
|
||||||
println!("overlay: {}", join.plan.name);
|
println!("overlay: {}", join.plan.name);
|
||||||
println!("resource: {}", join.plan.resource);
|
println!("resource: {}", join.plan.resource);
|
||||||
|
println!("cidr: {}", join.network.cidr);
|
||||||
|
println!(
|
||||||
|
"virtual_ip: {}",
|
||||||
|
join.network.virtual_ip.as_deref().unwrap_or("unassigned")
|
||||||
|
);
|
||||||
|
println!("state: {:?}", join.network.state);
|
||||||
println!("enabled: {}", join.enabled);
|
println!("enabled: {}", join.enabled);
|
||||||
println!("note: {}", join.note);
|
println!("note: {}", join.note);
|
||||||
}
|
}
|
||||||
|
|
@ -2001,6 +2075,99 @@ fn print_response(response: ControlResponse, json: bool) -> Result<()> {
|
||||||
println!("stopped: {stopped}");
|
println!("stopped: {stopped}");
|
||||||
println!("note: {note}");
|
println!("note: {note}");
|
||||||
}
|
}
|
||||||
|
ControlResponse::OverlayInterfacePlanned { plan } => {
|
||||||
|
println!("overlay: {}", plan.name);
|
||||||
|
println!("platform: {}", plan.platform);
|
||||||
|
println!("interface: {}", plan.interface_name);
|
||||||
|
println!("cidr: {}", plan.cidr);
|
||||||
|
println!(
|
||||||
|
"virtual_ip: {}",
|
||||||
|
plan.virtual_ip.as_deref().unwrap_or("unassigned")
|
||||||
|
);
|
||||||
|
println!("requires_privileges: {}", plan.requires_privileges);
|
||||||
|
for command in plan.commands {
|
||||||
|
println!("command: {command}");
|
||||||
|
}
|
||||||
|
for note in plan.notes {
|
||||||
|
println!("note: {note}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ControlResponse::OverlayRuntimeStarted { status } => {
|
||||||
|
println!("overlay: {}", status.name);
|
||||||
|
println!("interface: {}", status.interface_name);
|
||||||
|
println!("virtual_ip: {}", status.virtual_ip);
|
||||||
|
println!("cidr: {}", status.cidr);
|
||||||
|
println!("mtu: {}", status.mtu);
|
||||||
|
println!("packets_from_tun: {}", status.packets_from_tun);
|
||||||
|
println!("packets_to_tun: {}", status.packets_to_tun);
|
||||||
|
println!("packets_to_peers: {}", status.packets_to_peers);
|
||||||
|
if let Some(error) = status.last_error {
|
||||||
|
println!("last_error: {error}");
|
||||||
|
}
|
||||||
|
println!("note: {}", status.note);
|
||||||
|
}
|
||||||
|
ControlResponse::OverlayRuntimeStopped {
|
||||||
|
name,
|
||||||
|
stopped,
|
||||||
|
note,
|
||||||
|
} => {
|
||||||
|
println!("overlay: {name}");
|
||||||
|
println!("stopped: {stopped}");
|
||||||
|
println!("note: {note}");
|
||||||
|
}
|
||||||
|
ControlResponse::OverlayPeers { name, peers, note } => {
|
||||||
|
println!("overlay: {name}");
|
||||||
|
if peers.is_empty() {
|
||||||
|
println!("no overlay peer candidates");
|
||||||
|
} else {
|
||||||
|
for peer in peers {
|
||||||
|
println!(
|
||||||
|
"{}\t{}\t{}\t{}",
|
||||||
|
peer.node_id,
|
||||||
|
peer.endpoint_id.as_deref().unwrap_or("no-endpoint"),
|
||||||
|
peer.virtual_ip.as_deref().unwrap_or("no-virtual-ip"),
|
||||||
|
peer.state
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("note: {note}");
|
||||||
|
}
|
||||||
|
ControlResponse::OverlayPacketSent {
|
||||||
|
peer_node_id,
|
||||||
|
peer_agent_id,
|
||||||
|
endpoint_id,
|
||||||
|
packet,
|
||||||
|
allowed,
|
||||||
|
reason,
|
||||||
|
note,
|
||||||
|
} => {
|
||||||
|
println!("peer: {peer_node_id}");
|
||||||
|
println!("agent: {peer_agent_id}");
|
||||||
|
println!("endpoint: {endpoint_id}");
|
||||||
|
println!("allowed: {allowed}");
|
||||||
|
println!("reason: {reason}");
|
||||||
|
if let Some(packet) = packet {
|
||||||
|
println!("packet: {}", packet.id);
|
||||||
|
println!("size_bytes: {}", packet.size_bytes);
|
||||||
|
}
|
||||||
|
println!("note: {note}");
|
||||||
|
}
|
||||||
|
ControlResponse::OverlayPackets {
|
||||||
|
name,
|
||||||
|
packets,
|
||||||
|
drained,
|
||||||
|
note,
|
||||||
|
} => {
|
||||||
|
println!("overlay: {name}");
|
||||||
|
println!("drained: {drained}");
|
||||||
|
for packet in packets {
|
||||||
|
println!(
|
||||||
|
"{}\t{}\t{}\t{} bytes",
|
||||||
|
packet.id, packet.source_node, packet.destination_node, packet.size_bytes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
println!("note: {note}");
|
||||||
|
}
|
||||||
ControlResponse::CasAdded { hash, size_bytes } => {
|
ControlResponse::CasAdded { hash, size_bytes } => {
|
||||||
println!("{hash} {size_bytes} bytes");
|
println!("{hash} {size_bytes} bytes");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,10 @@ use geth_discovery::{DiscoveredPeer, PeerCard};
|
||||||
use geth_document::{DocumentResource, DocumentState};
|
use geth_document::{DocumentResource, DocumentState};
|
||||||
use geth_keychain::{KeychainOp, KeychainOpSignature, NodeEnrollmentRequest, NodeRecord};
|
use geth_keychain::{KeychainOp, KeychainOpSignature, NodeEnrollmentRequest, NodeRecord};
|
||||||
use geth_kv::{KvEntry, KvResource, KvSyncEntry};
|
use geth_kv::{KvEntry, KvResource, KvSyncEntry};
|
||||||
use geth_overlay::{OverlayJoinPlan, OverlayNetworkStatus, OverlayPlan};
|
use geth_overlay::{
|
||||||
|
OverlayInterfacePlan, OverlayJoinPlan, OverlayNetworkStatus, OverlayPacket, OverlayPlan,
|
||||||
|
OverlayRuntimeStatus,
|
||||||
|
};
|
||||||
use geth_pipe::{PipeConnection, PipeListener, PipeMessage};
|
use geth_pipe::{PipeConnection, PipeListener, PipeMessage};
|
||||||
use geth_pubsub::PubsubMessage;
|
use geth_pubsub::PubsubMessage;
|
||||||
use geth_resource::ResourceDescriptor;
|
use geth_resource::ResourceDescriptor;
|
||||||
|
|
@ -57,6 +60,31 @@ pub enum ControlRequest {
|
||||||
OverlayLeave {
|
OverlayLeave {
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
OverlayInterfacePlan {
|
||||||
|
name: String,
|
||||||
|
platform: Option<String>,
|
||||||
|
},
|
||||||
|
OverlayUp {
|
||||||
|
name: String,
|
||||||
|
bearer_secret: Option<String>,
|
||||||
|
mtu: Option<u16>,
|
||||||
|
},
|
||||||
|
OverlayDown {
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
OverlayPeers {
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
OverlaySend {
|
||||||
|
name: String,
|
||||||
|
node: String,
|
||||||
|
packet_base64: String,
|
||||||
|
bearer_secret: Option<String>,
|
||||||
|
},
|
||||||
|
OverlayRecv {
|
||||||
|
name: String,
|
||||||
|
peek: bool,
|
||||||
|
},
|
||||||
CasAdd {
|
CasAdd {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
},
|
},
|
||||||
|
|
@ -501,6 +529,37 @@ pub enum ControlResponse {
|
||||||
stopped: bool,
|
stopped: bool,
|
||||||
note: String,
|
note: String,
|
||||||
},
|
},
|
||||||
|
OverlayInterfacePlanned {
|
||||||
|
plan: OverlayInterfacePlan,
|
||||||
|
},
|
||||||
|
OverlayRuntimeStarted {
|
||||||
|
status: OverlayRuntimeStatus,
|
||||||
|
},
|
||||||
|
OverlayRuntimeStopped {
|
||||||
|
name: String,
|
||||||
|
stopped: bool,
|
||||||
|
note: String,
|
||||||
|
},
|
||||||
|
OverlayPeers {
|
||||||
|
name: String,
|
||||||
|
peers: Vec<OverlayPeer>,
|
||||||
|
note: String,
|
||||||
|
},
|
||||||
|
OverlayPacketSent {
|
||||||
|
peer_node_id: String,
|
||||||
|
peer_agent_id: String,
|
||||||
|
endpoint_id: String,
|
||||||
|
packet: Option<OverlayPacket>,
|
||||||
|
allowed: bool,
|
||||||
|
reason: String,
|
||||||
|
note: String,
|
||||||
|
},
|
||||||
|
OverlayPackets {
|
||||||
|
name: String,
|
||||||
|
packets: Vec<OverlayPacket>,
|
||||||
|
drained: bool,
|
||||||
|
note: String,
|
||||||
|
},
|
||||||
CasAdded {
|
CasAdded {
|
||||||
hash: BlobHash,
|
hash: BlobHash,
|
||||||
size_bytes: u64,
|
size_bytes: u64,
|
||||||
|
|
@ -990,6 +1049,14 @@ pub struct CasProvider {
|
||||||
pub last_seen_ms: i64,
|
pub last_seen_ms: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct OverlayPeer {
|
||||||
|
pub node_id: String,
|
||||||
|
pub endpoint_id: Option<String>,
|
||||||
|
pub virtual_ip: Option<String>,
|
||||||
|
pub state: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(tag = "type", rename_all = "kebab-case")]
|
#[serde(tag = "type", rename_all = "kebab-case")]
|
||||||
pub enum PeerControlRequest {
|
pub enum PeerControlRequest {
|
||||||
|
|
@ -1408,6 +1475,38 @@ pub enum PipeWireResponse {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "type", rename_all = "kebab-case")]
|
||||||
|
pub enum OverlayWireRequest {
|
||||||
|
Packet {
|
||||||
|
peer_card: PeerCard,
|
||||||
|
network: String,
|
||||||
|
packet_base64: String,
|
||||||
|
nonce: String,
|
||||||
|
bearer_proof: Option<BearerProof>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "type", rename_all = "kebab-case")]
|
||||||
|
pub enum OverlayWireResponse {
|
||||||
|
PacketAccepted {
|
||||||
|
node_id: String,
|
||||||
|
agent_id: String,
|
||||||
|
endpoint_id: String,
|
||||||
|
remote_endpoint_id: String,
|
||||||
|
packet: Option<Box<OverlayPacket>>,
|
||||||
|
allowed: bool,
|
||||||
|
reason: String,
|
||||||
|
evaluated_ops: usize,
|
||||||
|
nonce: String,
|
||||||
|
note: String,
|
||||||
|
},
|
||||||
|
Error {
|
||||||
|
message: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct SyncWatermark {
|
pub struct SyncWatermark {
|
||||||
pub stream: String,
|
pub stream: String,
|
||||||
|
|
@ -1518,6 +1617,28 @@ pub fn decode_pipe_wire_response(line: &str) -> Result<PipeWireResponse, Control
|
||||||
serde_json::from_str(line).map_err(ControlError::from)
|
serde_json::from_str(line).map_err(ControlError::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn encode_overlay_wire_request(request: &OverlayWireRequest) -> Result<String, ControlError> {
|
||||||
|
let mut line = serde_json::to_string(request)?;
|
||||||
|
line.push('\n');
|
||||||
|
Ok(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode_overlay_wire_request(line: &str) -> Result<OverlayWireRequest, ControlError> {
|
||||||
|
serde_json::from_str(line).map_err(ControlError::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode_overlay_wire_response(
|
||||||
|
response: &OverlayWireResponse,
|
||||||
|
) -> Result<String, ControlError> {
|
||||||
|
let mut line = serde_json::to_string(response)?;
|
||||||
|
line.push('\n');
|
||||||
|
Ok(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode_overlay_wire_response(line: &str) -> Result<OverlayWireResponse, ControlError> {
|
||||||
|
serde_json::from_str(line).map_err(ControlError::from)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
@ -1587,6 +1708,27 @@ mod tests {
|
||||||
request
|
request
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let request = ControlRequest::OverlayUp {
|
||||||
|
name: "home-lan".to_owned(),
|
||||||
|
bearer_secret: Some("gbt_route".to_owned()),
|
||||||
|
mtu: Some(1280),
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
decode_request(&encode_request(&request).expect("encode")).expect("decode"),
|
||||||
|
request
|
||||||
|
);
|
||||||
|
|
||||||
|
let request = ControlRequest::OverlaySend {
|
||||||
|
name: "home-lan".to_owned(),
|
||||||
|
node: "node:peer".to_owned(),
|
||||||
|
packet_base64: "RQAAFAAAQABAAQAAqBYAAawWAAI=".to_owned(),
|
||||||
|
bearer_secret: Some("gbt_invite".to_owned()),
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
decode_request(&encode_request(&request).expect("encode")).expect("decode"),
|
||||||
|
request
|
||||||
|
);
|
||||||
|
|
||||||
let response = ControlResponse::CasHas {
|
let response = ControlResponse::CasHas {
|
||||||
hash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".into(),
|
hash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".into(),
|
||||||
present: true,
|
present: true,
|
||||||
|
|
@ -1605,6 +1747,69 @@ mod tests {
|
||||||
response
|
response
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let response = ControlResponse::OverlayJoined {
|
||||||
|
join: geth_overlay::OverlayJoinPlan {
|
||||||
|
plan: geth_overlay::plan_overlay("home-lan", None, geth_overlay::OVERLAY_ALPN)
|
||||||
|
.expect("overlay plan"),
|
||||||
|
network: geth_overlay::membership_status(
|
||||||
|
&geth_overlay::joined_overlay_membership(
|
||||||
|
"home-lan",
|
||||||
|
None,
|
||||||
|
"node:local",
|
||||||
|
"invite",
|
||||||
|
10,
|
||||||
|
)
|
||||||
|
.expect("overlay membership"),
|
||||||
|
),
|
||||||
|
enabled: true,
|
||||||
|
note: "membership persisted".to_owned(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
decode_response(&encode_response(&response).expect("encode")).expect("decode"),
|
||||||
|
response
|
||||||
|
);
|
||||||
|
|
||||||
|
let response = ControlResponse::OverlayRuntimeStarted {
|
||||||
|
status: geth_overlay::OverlayRuntimeStatus {
|
||||||
|
name: "home-lan".to_owned(),
|
||||||
|
interface_name: "geth-homelan".to_owned(),
|
||||||
|
virtual_ip: "172.22.0.10".to_owned(),
|
||||||
|
cidr: "172.22.0.0/24".to_owned(),
|
||||||
|
mtu: 1280,
|
||||||
|
started_at_ms: 12,
|
||||||
|
packets_from_tun: 1,
|
||||||
|
packets_to_tun: 2,
|
||||||
|
packets_to_peers: 3,
|
||||||
|
last_error: None,
|
||||||
|
note: "active".to_owned(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
decode_response(&encode_response(&response).expect("encode")).expect("decode"),
|
||||||
|
response
|
||||||
|
);
|
||||||
|
|
||||||
|
let response = ControlResponse::OverlayPackets {
|
||||||
|
name: "home-lan".to_owned(),
|
||||||
|
packets: vec![geth_overlay::OverlayPacket {
|
||||||
|
id: "overlay-packet:test".to_owned(),
|
||||||
|
network: "home-lan".to_owned(),
|
||||||
|
source_node: "node:peer".to_owned(),
|
||||||
|
destination_node: "node:local".to_owned(),
|
||||||
|
packet_base64: "RQAAFAAAQABAAQAAqBYAAawWAAI=".to_owned(),
|
||||||
|
size_bytes: 20,
|
||||||
|
received_at_ms: 11,
|
||||||
|
note: "received".to_owned(),
|
||||||
|
}],
|
||||||
|
drained: true,
|
||||||
|
note: "packets".to_owned(),
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
decode_response(&encode_response(&response).expect("encode")).expect("decode"),
|
||||||
|
response
|
||||||
|
);
|
||||||
|
|
||||||
let response = ControlResponse::NodeList {
|
let response = ControlResponse::NodeList {
|
||||||
nodes: vec![NodeRecord {
|
nodes: vec![NodeRecord {
|
||||||
id: geth_types::NodeId::new("node:local"),
|
id: geth_types::NodeId::new("node:local"),
|
||||||
|
|
@ -2641,6 +2846,63 @@ mod tests {
|
||||||
response
|
response
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let request = OverlayWireRequest::Packet {
|
||||||
|
peer_card: PeerCard {
|
||||||
|
node_id: "node:caller".into(),
|
||||||
|
agent_id: "agent:caller".into(),
|
||||||
|
endpoints: Vec::new(),
|
||||||
|
issued_at: geth_types::UnixMillis(1),
|
||||||
|
signature: geth_discovery::SignatureMetadata {
|
||||||
|
namespace: "geth.peer-card.v1@geth.local".to_owned(),
|
||||||
|
signer: "agent:caller".to_owned(),
|
||||||
|
public_key: "key".to_owned(),
|
||||||
|
signature: "sig".to_owned(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
network: "home".to_owned(),
|
||||||
|
packet_base64: "RQAAFAAAQABAAQAAqBYAAawWAAI=".to_owned(),
|
||||||
|
nonce: "nonce".to_owned(),
|
||||||
|
bearer_proof: Some(BearerProof {
|
||||||
|
secret: "bearer:test".into(),
|
||||||
|
resource: "resource:overlay:home".into(),
|
||||||
|
capabilities: vec!["overlay.route".into()],
|
||||||
|
nonce: "nonce".to_owned(),
|
||||||
|
response: "response".to_owned(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
decode_overlay_wire_request(&encode_overlay_wire_request(&request).expect("encode"))
|
||||||
|
.expect("decode"),
|
||||||
|
request
|
||||||
|
);
|
||||||
|
|
||||||
|
let response = OverlayWireResponse::PacketAccepted {
|
||||||
|
node_id: "node:peer".to_owned(),
|
||||||
|
agent_id: "agent:peer".to_owned(),
|
||||||
|
endpoint_id: "endpoint:peer".to_owned(),
|
||||||
|
remote_endpoint_id: "endpoint:caller".to_owned(),
|
||||||
|
packet: Some(Box::new(geth_overlay::OverlayPacket {
|
||||||
|
id: "overlay-packet:test".to_owned(),
|
||||||
|
network: "home".to_owned(),
|
||||||
|
source_node: "node:caller".to_owned(),
|
||||||
|
destination_node: "node:peer".to_owned(),
|
||||||
|
packet_base64: "RQAAFAAAQABAAQAAqBYAAawWAAI=".to_owned(),
|
||||||
|
size_bytes: 20,
|
||||||
|
received_at_ms: 12,
|
||||||
|
note: "overlay".to_owned(),
|
||||||
|
})),
|
||||||
|
allowed: true,
|
||||||
|
reason: "direct grant".to_owned(),
|
||||||
|
evaluated_ops: 1,
|
||||||
|
nonce: "nonce".to_owned(),
|
||||||
|
note: "overlay wire".to_owned(),
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
decode_overlay_wire_response(&encode_overlay_wire_response(&response).expect("encode"))
|
||||||
|
.expect("decode"),
|
||||||
|
response
|
||||||
|
);
|
||||||
|
|
||||||
let request = PipeWireRequest::TcpConnect {
|
let request = PipeWireRequest::TcpConnect {
|
||||||
peer_card: PeerCard {
|
peer_card: PeerCard {
|
||||||
node_id: "node:caller".into(),
|
node_id: "node:caller".into(),
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ iroh-blobs.workspace = true
|
||||||
iroh-docs.workspace = true
|
iroh-docs.workspace = true
|
||||||
iroh-gossip.workspace = true
|
iroh-gossip.workspace = true
|
||||||
swarm-discovery.workspace = true
|
swarm-discovery.workspace = true
|
||||||
|
tun-rs.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rusqlite.workspace = true
|
rusqlite.workspace = true
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -6,6 +6,7 @@ rust-version.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
blake3.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
geth-types = { path = "../geth-types" }
|
geth-types = { path = "../geth-types" }
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ pub struct OverlayPlan {
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct OverlayJoinPlan {
|
pub struct OverlayJoinPlan {
|
||||||
pub plan: OverlayPlan,
|
pub plan: OverlayPlan,
|
||||||
|
pub network: OverlayNetworkStatus,
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub note: String,
|
pub note: String,
|
||||||
}
|
}
|
||||||
|
|
@ -57,6 +58,59 @@ pub struct OverlayNetworkStatus {
|
||||||
pub note: String,
|
pub note: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct OverlayInterfacePlan {
|
||||||
|
pub name: String,
|
||||||
|
pub platform: String,
|
||||||
|
pub interface_name: String,
|
||||||
|
pub cidr: String,
|
||||||
|
pub virtual_ip: Option<String>,
|
||||||
|
pub requires_privileges: bool,
|
||||||
|
pub commands: Vec<String>,
|
||||||
|
pub notes: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct OverlayRuntimeStatus {
|
||||||
|
pub name: String,
|
||||||
|
pub interface_name: String,
|
||||||
|
pub virtual_ip: String,
|
||||||
|
pub cidr: String,
|
||||||
|
pub mtu: u16,
|
||||||
|
pub started_at_ms: i64,
|
||||||
|
pub packets_from_tun: u64,
|
||||||
|
pub packets_to_tun: u64,
|
||||||
|
pub packets_to_peers: u64,
|
||||||
|
pub last_error: Option<String>,
|
||||||
|
pub note: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct OverlayPacket {
|
||||||
|
pub id: String,
|
||||||
|
pub network: String,
|
||||||
|
pub source_node: String,
|
||||||
|
pub destination_node: String,
|
||||||
|
pub packet_base64: String,
|
||||||
|
pub size_bytes: usize,
|
||||||
|
pub received_at_ms: i64,
|
||||||
|
pub note: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct OverlayMembership {
|
||||||
|
pub name: String,
|
||||||
|
pub resource: ResourceId,
|
||||||
|
pub cidr: String,
|
||||||
|
pub state: OverlayState,
|
||||||
|
pub local_node_id: String,
|
||||||
|
pub virtual_ip: Option<String>,
|
||||||
|
pub secret_fingerprint: String,
|
||||||
|
pub joined_at_ms: i64,
|
||||||
|
pub updated_at_ms: i64,
|
||||||
|
pub packet_runtime: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum OverlayError {
|
pub enum OverlayError {
|
||||||
#[error("invalid overlay name `{0}`")]
|
#[error("invalid overlay name `{0}`")]
|
||||||
|
|
@ -65,6 +119,12 @@ pub enum OverlayError {
|
||||||
InvalidCidr(String),
|
InvalidCidr(String),
|
||||||
#[error("overlay join requires a non-empty resource secret")]
|
#[error("overlay join requires a non-empty resource secret")]
|
||||||
EmptySecret,
|
EmptySecret,
|
||||||
|
#[error("overlay CIDR `{0}` has no usable host addresses")]
|
||||||
|
NoUsableHostAddress(String),
|
||||||
|
#[error("invalid IPv4 packet: {0}")]
|
||||||
|
InvalidIpv4Packet(String),
|
||||||
|
#[error("unsupported overlay platform `{0}`")]
|
||||||
|
UnsupportedPlatform(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate_overlay_name(name: &str) -> Result<(), OverlayError> {
|
pub fn validate_overlay_name(name: &str) -> Result<(), OverlayError> {
|
||||||
|
|
@ -104,11 +164,173 @@ pub fn validate_overlay_secret(secret: &str) -> Result<(), OverlayError> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn validate_ipv4_packet(packet: &[u8]) -> Result<(), OverlayError> {
|
||||||
|
if packet.len() < 20 {
|
||||||
|
return Err(OverlayError::InvalidIpv4Packet(
|
||||||
|
"packet shorter than IPv4 header".to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if packet[0] >> 4 != 4 {
|
||||||
|
return Err(OverlayError::InvalidIpv4Packet(
|
||||||
|
"packet version is not IPv4".to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let header_len = usize::from(packet[0] & 0x0f) * 4;
|
||||||
|
if header_len < 20 || header_len > packet.len() {
|
||||||
|
return Err(OverlayError::InvalidIpv4Packet(
|
||||||
|
"invalid IPv4 header length".to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let total_len = u16::from_be_bytes([packet[2], packet[3]]) as usize;
|
||||||
|
if total_len < header_len || total_len > packet.len() {
|
||||||
|
return Err(OverlayError::InvalidIpv4Packet(
|
||||||
|
"invalid IPv4 total length".to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ipv4_source_destination(packet: &[u8]) -> Result<(String, String), OverlayError> {
|
||||||
|
validate_ipv4_packet(packet)?;
|
||||||
|
Ok((
|
||||||
|
Ipv4Addr::new(packet[12], packet[13], packet[14], packet[15]).to_string(),
|
||||||
|
Ipv4Addr::new(packet[16], packet[17], packet[18], packet[19]).to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn overlay_state_key(name: &str) -> String {
|
||||||
|
format!("overlay:{name}")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn overlay_packet_key(network: &str, received_at_ms: i64, packet_id: &str) -> String {
|
||||||
|
format!("overlay-packet:{network}:{received_at_ms}:{packet_id}")
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn overlay_resource_id(name: &str) -> ResourceId {
|
pub fn overlay_resource_id(name: &str) -> ResourceId {
|
||||||
ResourceId::new(format!("resource:overlay:{name}"))
|
ResourceId::new(format!("resource:overlay:{name}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn overlay_secret_fingerprint(secret: &str) -> String {
|
||||||
|
format!("blake3:{}", blake3::hash(secret.as_bytes()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn overlay_packet_id(
|
||||||
|
network: &str,
|
||||||
|
source_node: &str,
|
||||||
|
destination_node: &str,
|
||||||
|
packet: &[u8],
|
||||||
|
received_at_ms: i64,
|
||||||
|
) -> String {
|
||||||
|
format!(
|
||||||
|
"overlay-packet:{}",
|
||||||
|
blake3::hash(
|
||||||
|
format!("{network}\0{source_node}\0{destination_node}\0{received_at_ms}\0").as_bytes()
|
||||||
|
)
|
||||||
|
.to_hex()
|
||||||
|
) + &blake3::hash(packet).to_hex()[..16]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn joined_overlay_membership(
|
||||||
|
name: &str,
|
||||||
|
cidr: Option<&str>,
|
||||||
|
local_node_id: &str,
|
||||||
|
secret: &str,
|
||||||
|
joined_at_ms: i64,
|
||||||
|
) -> Result<OverlayMembership, OverlayError> {
|
||||||
|
validate_overlay_secret(secret)?;
|
||||||
|
let cidr = cidr.unwrap_or(DEFAULT_OVERLAY_CIDR);
|
||||||
|
validate_overlay_name(name)?;
|
||||||
|
validate_overlay_cidr(cidr)?;
|
||||||
|
Ok(OverlayMembership {
|
||||||
|
name: name.to_owned(),
|
||||||
|
resource: overlay_resource_id(name),
|
||||||
|
cidr: cidr.to_owned(),
|
||||||
|
state: OverlayState::Joined,
|
||||||
|
local_node_id: local_node_id.to_owned(),
|
||||||
|
virtual_ip: Some(deterministic_virtual_ip(cidr, local_node_id)?),
|
||||||
|
secret_fingerprint: overlay_secret_fingerprint(secret),
|
||||||
|
joined_at_ms,
|
||||||
|
updated_at_ms: joined_at_ms,
|
||||||
|
packet_runtime:
|
||||||
|
"joined: run `geth overlay up <name>` to start the explicit TUN/Wintun runtime"
|
||||||
|
.to_owned(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stopped_overlay_membership(
|
||||||
|
mut membership: OverlayMembership,
|
||||||
|
updated_at_ms: i64,
|
||||||
|
) -> OverlayMembership {
|
||||||
|
membership.state = OverlayState::Stopped;
|
||||||
|
membership.updated_at_ms = updated_at_ms;
|
||||||
|
membership.packet_runtime = "inactive: overlay membership stopped".to_owned();
|
||||||
|
membership
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn membership_status(membership: &OverlayMembership) -> OverlayNetworkStatus {
|
||||||
|
OverlayNetworkStatus {
|
||||||
|
name: membership.name.clone(),
|
||||||
|
resource: membership.resource.clone(),
|
||||||
|
cidr: membership.cidr.clone(),
|
||||||
|
state: membership.state.clone(),
|
||||||
|
virtual_ip: membership.virtual_ip.clone(),
|
||||||
|
peers: Vec::new(),
|
||||||
|
note: membership.packet_runtime.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interface_plan(
|
||||||
|
plan: &OverlayPlan,
|
||||||
|
platform: Option<&str>,
|
||||||
|
virtual_ip: Option<&str>,
|
||||||
|
) -> Result<OverlayInterfacePlan, OverlayError> {
|
||||||
|
let platform = platform.unwrap_or(std::env::consts::OS);
|
||||||
|
let interface_name = format!("geth-{}", interface_slug(&plan.name));
|
||||||
|
let prefix = cidr_prefix(&plan.cidr)?;
|
||||||
|
let virtual_ip = virtual_ip.map(ToOwned::to_owned);
|
||||||
|
let addr = virtual_ip
|
||||||
|
.as_ref()
|
||||||
|
.map(|ip| format!("{ip}/{prefix}"))
|
||||||
|
.unwrap_or_else(|| format!("<virtual-ip>/{prefix}"));
|
||||||
|
let commands = match platform {
|
||||||
|
"linux" => vec![
|
||||||
|
format!("ip tuntap add dev {interface_name} mode tun user <user>"),
|
||||||
|
format!("ip addr add {addr} dev {interface_name}"),
|
||||||
|
format!("ip link set {interface_name} up"),
|
||||||
|
],
|
||||||
|
"macos" | "darwin" => vec![
|
||||||
|
"open a utun device from the geth daemon process".to_owned(),
|
||||||
|
format!("ifconfig <utunN> inet {} {} up", addr, plan.cidr),
|
||||||
|
],
|
||||||
|
"windows" => vec![
|
||||||
|
"install or open a Wintun adapter for the current user context".to_owned(),
|
||||||
|
format!("assign {addr} to the Wintun adapter"),
|
||||||
|
],
|
||||||
|
other => return Err(OverlayError::UnsupportedPlatform(other.to_owned())),
|
||||||
|
};
|
||||||
|
Ok(OverlayInterfacePlan {
|
||||||
|
name: plan.name.clone(),
|
||||||
|
platform: platform.to_owned(),
|
||||||
|
interface_name,
|
||||||
|
cidr: plan.cidr.clone(),
|
||||||
|
virtual_ip,
|
||||||
|
requires_privileges: true,
|
||||||
|
commands,
|
||||||
|
notes: vec![
|
||||||
|
"generated plan only; run `geth overlay up <name>` to activate host networking"
|
||||||
|
.to_owned(),
|
||||||
|
"activation is explicit and user-scoped".to_owned(),
|
||||||
|
"all overlay packets remain carried over /geth/overlay/1".to_owned(),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn plan_overlay(
|
pub fn plan_overlay(
|
||||||
name: &str,
|
name: &str,
|
||||||
cidr: Option<&str>,
|
cidr: Option<&str>,
|
||||||
|
|
@ -128,7 +350,7 @@ pub fn plan_overlay(
|
||||||
CAPABILITY_ADMIN.to_owned(),
|
CAPABILITY_ADMIN.to_owned(),
|
||||||
],
|
],
|
||||||
discovery: "future overlay discovery may use mDNS, peer exchange, and resource metadata; discovery remains untrusted".to_owned(),
|
discovery: "future overlay discovery may use mDNS, peer exchange, and resource metadata; discovery remains untrusted".to_owned(),
|
||||||
runtime: "planned only in this prototype; no TUN/Wintun interface is created".to_owned(),
|
runtime: "explicit opt-in runtime available with `geth overlay up <name>`; creates a TUN/Wintun-style L3 interface".to_owned(),
|
||||||
security: vec![
|
security: vec![
|
||||||
"all overlay packets must be carried over daemon-owned Iroh connections".to_owned(),
|
"all overlay packets must be carried over daemon-owned Iroh connections".to_owned(),
|
||||||
"knowing an EndpointID or overlay name must not grant overlay access".to_owned(),
|
"knowing an EndpointID or overlay name must not grant overlay access".to_owned(),
|
||||||
|
|
@ -137,15 +359,73 @@ pub fn plan_overlay(
|
||||||
],
|
],
|
||||||
implementation_notes: vec![
|
implementation_notes: vec![
|
||||||
"inspired by iroh-lan's Iroh-carried packet overlay".to_owned(),
|
"inspired by iroh-lan's Iroh-carried packet overlay".to_owned(),
|
||||||
"future packet runtime should register /geth/overlay/1 on the shared geth Iroh router".to_owned(),
|
"packet runtime registers /geth/overlay/1 on the shared geth Iroh router".to_owned(),
|
||||||
"future host integration may need TUN/Wintun privileges and must remain explicitly opt-in".to_owned(),
|
"host integration may need TUN/Wintun privileges and remains explicitly opt-in".to_owned(),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn overlay_status_note() -> &'static str {
|
pub fn overlay_status_note() -> &'static str {
|
||||||
"overlay runtime is scaffolded but inactive; use `geth overlay plan <name>` to inspect the intended resource and capabilities"
|
"overlay membership is persisted locally; run `geth overlay up <name>` to start the explicit TUN/Wintun runtime"
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deterministic_virtual_ip(cidr: &str, seed: &str) -> Result<String, OverlayError> {
|
||||||
|
let (network, prefix) = parse_ipv4_cidr(cidr)?;
|
||||||
|
let host_bits = 32_u8.saturating_sub(prefix);
|
||||||
|
let address = if host_bits == 0 {
|
||||||
|
network
|
||||||
|
} else {
|
||||||
|
let host_space = 1_u64 << host_bits;
|
||||||
|
if host_space <= 2 {
|
||||||
|
return Err(OverlayError::NoUsableHostAddress(cidr.to_owned()));
|
||||||
|
}
|
||||||
|
let hash = blake3::hash(seed.as_bytes());
|
||||||
|
let mut bytes = [0_u8; 8];
|
||||||
|
bytes.copy_from_slice(&hash.as_bytes()[..8]);
|
||||||
|
let value = u64::from_le_bytes(bytes);
|
||||||
|
let host_offset = 1 + (value % (host_space - 2));
|
||||||
|
network + host_offset as u32
|
||||||
|
};
|
||||||
|
Ok(Ipv4Addr::from(address).to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cidr_prefix(cidr: &str) -> Result<u8, OverlayError> {
|
||||||
|
parse_ipv4_cidr(cidr).map(|(_, prefix)| prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_ipv4_cidr(cidr: &str) -> Result<(u32, u8), OverlayError> {
|
||||||
|
let (addr, prefix) = cidr
|
||||||
|
.split_once('/')
|
||||||
|
.ok_or_else(|| OverlayError::InvalidCidr(cidr.to_owned()))?;
|
||||||
|
let addr = addr
|
||||||
|
.parse::<Ipv4Addr>()
|
||||||
|
.map_err(|_| OverlayError::InvalidCidr(cidr.to_owned()))?;
|
||||||
|
let prefix = prefix
|
||||||
|
.parse::<u8>()
|
||||||
|
.map_err(|_| OverlayError::InvalidCidr(cidr.to_owned()))?;
|
||||||
|
if prefix > 32 {
|
||||||
|
return Err(OverlayError::InvalidCidr(cidr.to_owned()));
|
||||||
|
}
|
||||||
|
let mask = if prefix == 0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
u32::MAX << (32 - prefix)
|
||||||
|
};
|
||||||
|
Ok((u32::from(addr) & mask, prefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interface_slug(name: &str) -> String {
|
||||||
|
let slug = name
|
||||||
|
.chars()
|
||||||
|
.filter(|ch| ch.is_ascii_alphanumeric())
|
||||||
|
.take(8)
|
||||||
|
.collect::<String>();
|
||||||
|
if slug.is_empty() {
|
||||||
|
"net".to_owned()
|
||||||
|
} else {
|
||||||
|
slug
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
@ -177,6 +457,73 @@ mod tests {
|
||||||
assert_eq!(plan.cidr, DEFAULT_OVERLAY_CIDR);
|
assert_eq!(plan.cidr, DEFAULT_OVERLAY_CIDR);
|
||||||
assert!(plan.capabilities.contains(&CAPABILITY_JOIN.to_owned()));
|
assert!(plan.capabilities.contains(&CAPABILITY_JOIN.to_owned()));
|
||||||
assert!(plan.security.iter().any(|note| note.contains("Iroh")));
|
assert!(plan.security.iter().any(|note| note.contains("Iroh")));
|
||||||
assert!(plan.runtime.contains("planned only"));
|
assert!(plan.runtime.contains("overlay up"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn joined_membership_derives_virtual_ip_without_storing_secret() {
|
||||||
|
let membership = joined_overlay_membership(
|
||||||
|
"home",
|
||||||
|
Some("172.22.0.0/24"),
|
||||||
|
"node:local",
|
||||||
|
"invite-token",
|
||||||
|
42,
|
||||||
|
)
|
||||||
|
.expect("membership");
|
||||||
|
assert_eq!(
|
||||||
|
membership.resource,
|
||||||
|
ResourceId::new("resource:overlay:home")
|
||||||
|
);
|
||||||
|
assert_eq!(membership.virtual_ip, Some("172.22.0.168".to_owned()));
|
||||||
|
assert_ne!(membership.secret_fingerprint, "invite-token");
|
||||||
|
assert_eq!(membership.joined_at_ms, 42);
|
||||||
|
|
||||||
|
let status = membership_status(&membership);
|
||||||
|
assert_eq!(status.state, OverlayState::Joined);
|
||||||
|
assert_eq!(status.virtual_ip, membership.virtual_ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tiny_overlay_cidrs_do_not_assign_hosts() {
|
||||||
|
assert!(
|
||||||
|
joined_overlay_membership("tiny", Some("10.0.0.0/31"), "node:local", "secret", 1)
|
||||||
|
.is_err()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validates_ipv4_packets() {
|
||||||
|
let packet = [
|
||||||
|
0x45, 0x00, 0x00, 0x14, 0x00, 0x00, 0x40, 0x00, 64, 1, 0, 0, 172, 22, 0, 1, 172, 22, 0,
|
||||||
|
2,
|
||||||
|
];
|
||||||
|
validate_ipv4_packet(&packet).expect("valid packet");
|
||||||
|
assert!(validate_ipv4_packet(&packet[..10]).is_err());
|
||||||
|
let mut bad = packet;
|
||||||
|
bad[0] = 0x65;
|
||||||
|
assert!(validate_ipv4_packet(&bad).is_err());
|
||||||
|
let (_, destination) = ipv4_source_destination(&packet).expect("addresses");
|
||||||
|
assert_eq!(destination, "172.22.0.2");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn interface_plan_is_generated_without_mutating_host_networking() {
|
||||||
|
let plan = plan_overlay("home", None, OVERLAY_ALPN).expect("plan");
|
||||||
|
let interface =
|
||||||
|
interface_plan(&plan, Some("linux"), Some("172.22.0.10")).expect("interface plan");
|
||||||
|
assert_eq!(interface.interface_name, "geth-home");
|
||||||
|
assert!(interface.requires_privileges);
|
||||||
|
assert!(
|
||||||
|
interface
|
||||||
|
.commands
|
||||||
|
.iter()
|
||||||
|
.any(|cmd| cmd.contains("ip tuntap"))
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
interface
|
||||||
|
.notes
|
||||||
|
.iter()
|
||||||
|
.any(|note| note.contains("generated"))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -905,6 +905,14 @@ impl Store {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn delete_module_state(&self, module: &str) -> Result<(), StoreError> {
|
||||||
|
self.conn.execute(
|
||||||
|
"DELETE FROM module_state WHERE module = ?1",
|
||||||
|
params![module],
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn list_module_states_with_prefix(
|
pub fn list_module_states_with_prefix(
|
||||||
&self,
|
&self,
|
||||||
prefix: &str,
|
prefix: &str,
|
||||||
|
|
@ -1714,6 +1722,15 @@ mod tests {
|
||||||
.len(),
|
.len(),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
store
|
||||||
|
.delete_module_state("live-sync:node:laptop:ssh-certs")
|
||||||
|
.expect("delete state");
|
||||||
|
assert_eq!(
|
||||||
|
store
|
||||||
|
.get_module_state("live-sync:node:laptop:ssh-certs")
|
||||||
|
.expect("get deleted state"),
|
||||||
|
None
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ geth-discovery = { path = "../geth-discovery" }
|
||||||
geth-iroh = { path = "../geth-iroh" }
|
geth-iroh = { path = "../geth-iroh" }
|
||||||
geth-keychain = { path = "../geth-keychain" }
|
geth-keychain = { path = "../geth-keychain" }
|
||||||
geth-node = { path = "../geth-node" }
|
geth-node = { path = "../geth-node" }
|
||||||
|
geth-overlay = { path = "../geth-overlay" }
|
||||||
geth-ssh-identity = { path = "../geth-ssh-identity" }
|
geth-ssh-identity = { path = "../geth-ssh-identity" }
|
||||||
geth-store = { path = "../geth-store" }
|
geth-store = { path = "../geth-store" }
|
||||||
geth-types = { path = "../geth-types" }
|
geth-types = { path = "../geth-types" }
|
||||||
|
|
|
||||||
|
|
@ -1446,7 +1446,135 @@ fn overlay_plan_is_available_without_starting_packet_runtime() {
|
||||||
assert_eq!(plan.resource.to_string(), "resource:overlay:home-lan");
|
assert_eq!(plan.resource.to_string(), "resource:overlay:home-lan");
|
||||||
assert_eq!(plan.alpn, "/geth/overlay/1");
|
assert_eq!(plan.alpn, "/geth/overlay/1");
|
||||||
assert!(plan.capabilities.contains(&"overlay.join".to_owned()));
|
assert!(plan.capabilities.contains(&"overlay.join".to_owned()));
|
||||||
assert!(plan.runtime.contains("no TUN/Wintun"));
|
assert!(plan.runtime.contains("overlay up"));
|
||||||
|
}
|
||||||
|
other => panic!("unexpected response: {other:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn overlay_join_persists_membership_resource_and_leave_state() {
|
||||||
|
let home = tempfile::tempdir().expect("tempdir");
|
||||||
|
let paths = geth_config::GethPaths::from_home(home.path());
|
||||||
|
let node = geth_node::init_node(&paths).expect("init node");
|
||||||
|
|
||||||
|
let joined = geth_node::handle_request(
|
||||||
|
&node,
|
||||||
|
geth_control::ControlRequest::OverlayJoin {
|
||||||
|
name: "home-lan".to_owned(),
|
||||||
|
secret: "local-bootstrap-secret".to_owned(),
|
||||||
|
cidr: Some("172.22.0.0/24".to_owned()),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("overlay join");
|
||||||
|
match joined {
|
||||||
|
geth_control::ControlResponse::OverlayJoined { join } => {
|
||||||
|
assert!(join.enabled);
|
||||||
|
assert_eq!(join.plan.resource.to_string(), "resource:overlay:home-lan");
|
||||||
|
assert_eq!(join.network.state, geth_overlay::OverlayState::Joined);
|
||||||
|
assert!(join.network.virtual_ip.is_some());
|
||||||
|
assert!(join.note.contains("local bootstrap overlay"));
|
||||||
|
}
|
||||||
|
other => panic!("unexpected response: {other:?}"),
|
||||||
|
}
|
||||||
|
|
||||||
|
let resources = geth_node::handle_request(&node, geth_control::ControlRequest::ResourceList)
|
||||||
|
.expect("resource list");
|
||||||
|
match resources {
|
||||||
|
geth_control::ControlResponse::ResourceList { resources } => {
|
||||||
|
assert!(resources.iter().any(|resource| {
|
||||||
|
resource.id.to_string() == "resource:overlay:home-lan"
|
||||||
|
&& resource.kind == geth_types::ResourceKind::Overlay
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
other => panic!("unexpected response: {other:?}"),
|
||||||
|
}
|
||||||
|
|
||||||
|
let status = geth_node::handle_request(&node, geth_control::ControlRequest::OverlayStatus)
|
||||||
|
.expect("overlay status");
|
||||||
|
match status {
|
||||||
|
geth_control::ControlResponse::OverlayStatus { networks, note } => {
|
||||||
|
assert_eq!(networks.len(), 1);
|
||||||
|
assert_eq!(networks[0].state, geth_overlay::OverlayState::Joined);
|
||||||
|
assert!(note.contains("persisted locally"));
|
||||||
|
}
|
||||||
|
other => panic!("unexpected response: {other:?}"),
|
||||||
|
}
|
||||||
|
|
||||||
|
geth_node::handle_request(
|
||||||
|
&node,
|
||||||
|
geth_control::ControlRequest::OverlayLeave {
|
||||||
|
name: "home-lan".to_owned(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("overlay leave");
|
||||||
|
let status = geth_node::handle_request(&node, geth_control::ControlRequest::OverlayStatus)
|
||||||
|
.expect("overlay status after leave");
|
||||||
|
match status {
|
||||||
|
geth_control::ControlResponse::OverlayStatus { networks, .. } => {
|
||||||
|
assert_eq!(networks.len(), 1);
|
||||||
|
assert_eq!(networks[0].state, geth_overlay::OverlayState::Stopped);
|
||||||
|
}
|
||||||
|
other => panic!("unexpected response: {other:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn overlay_join_uses_bearer_invite_when_one_exists() {
|
||||||
|
let home = tempfile::tempdir().expect("tempdir");
|
||||||
|
let paths = geth_config::GethPaths::from_home(home.path());
|
||||||
|
let node = geth_node::init_node(&paths).expect("init node");
|
||||||
|
let resource = "resource:overlay:locked".to_owned();
|
||||||
|
|
||||||
|
geth_node::handle_request(
|
||||||
|
&node,
|
||||||
|
geth_control::ControlRequest::ResourceCreate {
|
||||||
|
kind: "overlay".to_owned(),
|
||||||
|
name: "locked".to_owned(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("create overlay resource");
|
||||||
|
let access = geth_node::handle_request(
|
||||||
|
&node,
|
||||||
|
geth_control::ControlRequest::SecretBearerCreate {
|
||||||
|
resource: resource.clone(),
|
||||||
|
capabilities: vec!["overlay.join".to_owned()],
|
||||||
|
expires_at_ms: None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("create bearer invite");
|
||||||
|
let token = match access {
|
||||||
|
geth_control::ControlResponse::SecretBearerCreated { access } => {
|
||||||
|
access.token.expect("private bearer token").to_string()
|
||||||
|
}
|
||||||
|
other => panic!("unexpected response: {other:?}"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let denied = geth_node::handle_request(
|
||||||
|
&node,
|
||||||
|
geth_control::ControlRequest::OverlayJoin {
|
||||||
|
name: "locked".to_owned(),
|
||||||
|
secret: "wrong-token".to_owned(),
|
||||||
|
cidr: None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect_err("bad bearer should be rejected");
|
||||||
|
assert!(denied.to_string().contains("overlay join requires"));
|
||||||
|
|
||||||
|
let joined = geth_node::handle_request(
|
||||||
|
&node,
|
||||||
|
geth_control::ControlRequest::OverlayJoin {
|
||||||
|
name: "locked".to_owned(),
|
||||||
|
secret: token,
|
||||||
|
cidr: None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("authorized overlay join");
|
||||||
|
match joined {
|
||||||
|
geth_control::ControlResponse::OverlayJoined { join } => {
|
||||||
|
assert!(join.enabled);
|
||||||
|
assert_eq!(join.network.state, geth_overlay::OverlayState::Joined);
|
||||||
|
assert!(join.note.contains("bearer overlay.join authorization"));
|
||||||
}
|
}
|
||||||
other => panic!("unexpected response: {other:?}"),
|
other => panic!("unexpected response: {other:?}"),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
Accepted for prototype scaffolding.
|
Accepted for prototype implementation.
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
|
|
@ -25,18 +25,32 @@ misconfigured.
|
||||||
|
|
||||||
## Decision
|
## Decision
|
||||||
|
|
||||||
Add an optional `overlay` resource kind and reserve `/geth/overlay/1` for a
|
Add an optional `overlay` resource kind and reserve `/geth/overlay/1` for an
|
||||||
future Iroh-carried packet overlay. The initial implementation is a typed model
|
Iroh-carried packet overlay. The prototype implementation is a typed model,
|
||||||
and CLI/control planning surface:
|
CLI/control surface, persisted local membership record, and explicit opt-in
|
||||||
|
TUN/Wintun-style runtime:
|
||||||
|
|
||||||
- `geth overlay status`
|
- `geth overlay status`
|
||||||
- `geth overlay plan <name> [--cidr <cidr>]`
|
- `geth overlay plan <name> [--cidr <cidr>]`
|
||||||
- `geth overlay join <name> --secret <resource-secret> [--cidr <cidr>]`
|
- `geth overlay join <name> --secret <resource-secret> [--cidr <cidr>]`
|
||||||
|
- `geth overlay interface-plan <name>`
|
||||||
|
- `geth overlay up <name> [--bearer-secret <route-token>] [--mtu <bytes>]`
|
||||||
|
- `geth overlay down <name>`
|
||||||
- `geth overlay leave <name>`
|
- `geth overlay leave <name>`
|
||||||
|
|
||||||
The prototype does not create TUN/Wintun interfaces, assign virtual IPs, or
|
Join creates or reuses `resource:overlay:<name>`, persists local membership in
|
||||||
route packets. Join/leave are explicit stubs that describe the intended
|
`module_state`, assigns a deterministic virtual IP from the overlay CIDR, and
|
||||||
resource, capabilities, ALPN, and security boundaries.
|
stores only a BLAKE3 fingerprint of the supplied secret. If the resource already
|
||||||
|
has bearer access with `overlay.join`, join requires a matching bearer token.
|
||||||
|
This keeps bearer access resource-scoped and does not enroll the caller as a
|
||||||
|
trusted node.
|
||||||
|
|
||||||
|
`overlay up` creates a real L3 TUN/Wintun-style device through `tun-rs`, assigns
|
||||||
|
the local virtual IP, reads IPv4 packets from the device, maps destination
|
||||||
|
overlay IPs to imported peer cards, and routes packets over `/geth/overlay/1`.
|
||||||
|
The serving daemon requires `overlay.route` before injecting received packets
|
||||||
|
into an active overlay runtime. If no runtime is active, received packets are
|
||||||
|
stored for inspection with `overlay recv`.
|
||||||
|
|
||||||
Overlay resources use capabilities:
|
Overlay resources use capabilities:
|
||||||
|
|
||||||
|
|
@ -49,18 +63,15 @@ must not create a second endpoint or non-Iroh transport.
|
||||||
|
|
||||||
## Consequences
|
## Consequences
|
||||||
|
|
||||||
The CLI can now document and test the intended overlay shape without changing
|
The CLI can now document, persist, test, and activate overlay membership.
|
||||||
host networking. This keeps the prototype safe to run on development machines
|
Activating an overlay mutates host networking and can require privileges, so it
|
||||||
while preserving a clear path toward an iroh-lan-inspired overlay.
|
is never automatic daemon startup behavior.
|
||||||
|
|
||||||
Future work must add:
|
Future work must add:
|
||||||
|
|
||||||
- persisted overlay membership/resource metadata
|
|
||||||
- resource-secret-backed join authorization
|
|
||||||
- platform-specific opt-in TUN/Wintun management
|
|
||||||
- packet routing over `/geth/overlay/1`
|
|
||||||
- peer/IP coordination through trusted resource metadata and untrusted discovery
|
- peer/IP coordination through trusted resource metadata and untrusted discovery
|
||||||
candidates
|
candidates
|
||||||
|
- stronger platform packaging for Wintun/macOS entitlements where needed
|
||||||
|
|
||||||
No current code claims VPN-grade isolation, forward secrecy, or automatic host
|
No current code claims VPN-grade isolation, forward secrecy, or automatic host
|
||||||
network security.
|
network security.
|
||||||
|
|
|
||||||
|
|
@ -253,12 +253,26 @@ TUN/Wintun interface are carried over the daemon-owned Iroh endpoint on
|
||||||
with `overlay.join`, `overlay.route`, and `overlay.admin` capabilities. The
|
with `overlay.join`, `overlay.route`, and `overlay.admin` capabilities. The
|
||||||
prototype exposes `geth overlay status`, `geth overlay plan <name>`,
|
prototype exposes `geth overlay status`, `geth overlay plan <name>`,
|
||||||
`geth overlay join <name> --secret <resource-secret>`, and `geth overlay leave
|
`geth overlay join <name> --secret <resource-secret>`, and `geth overlay leave
|
||||||
<name>` as planning/control stubs only. They validate names, CIDRs, resource
|
<name>`. Join creates or reuses `resource:overlay:<name>`, persists local
|
||||||
IDs, capabilities, and security notes, but they do not create host network
|
membership in `module_state`, assigns a deterministic virtual IP from the CIDR,
|
||||||
interfaces, assign virtual IPs, or route packets yet. Future implementation must
|
and stores only a BLAKE3 fingerprint of the supplied secret. If bearer access
|
||||||
remain explicitly opt-in because TUN/Wintun setup may need platform-specific
|
already exists for that overlay resource, join requires the supplied secret to
|
||||||
privileges. Overlay discovery can use mDNS, peer exchange, and resource
|
be a bearer token with `overlay.join`; this lets operators create explicit
|
||||||
metadata, but discovery remains untrusted and cannot grant overlay access.
|
resource-scoped overlay invites through `geth secret bearer create`. The
|
||||||
|
`geth overlay interface-plan` generates Linux, macOS, or Windows host-interface
|
||||||
|
plans for review. `geth overlay up <name>` is the explicit opt-in that creates a
|
||||||
|
real L3 TUN/Wintun-style interface through `tun-rs`, assigns the local
|
||||||
|
deterministic overlay IP, reads IPv4 packets from the interface, maps
|
||||||
|
destination overlay IPs to imported peer cards, and carries those packets over
|
||||||
|
the dedicated `/geth/overlay/1` Iroh ALPN. `geth overlay down <name>` stops the
|
||||||
|
runtime and drops the device handle. The serving daemon validates the signed
|
||||||
|
peer card against the observed EndpointID and requires `overlay.route` on
|
||||||
|
`resource:overlay:<name>` before queuing a received packet into the active
|
||||||
|
interface; if no runtime is active, the packet is persisted for `geth overlay
|
||||||
|
recv`. Interface setup may require `CAP_NET_ADMIN`, sudo, a preconfigured
|
||||||
|
`/dev/net/tun`, Wintun availability, or platform-specific network entitlements.
|
||||||
|
Overlay discovery can use mDNS, peer exchange, and resource metadata, but
|
||||||
|
discovery remains untrusted and cannot grant overlay access.
|
||||||
|
|
||||||
`geth-pipe` currently supports `pipe listen/connect/send/recv` against a
|
`geth-pipe` currently supports `pipe listen/connect/send/recv` against a
|
||||||
daemon-lifetime runtime. `geth pipe connect <name> --node <node-id>` sends an
|
daemon-lifetime runtime. `geth pipe connect <name> --node <node-id>` sends an
|
||||||
|
|
|
||||||
|
|
@ -584,19 +584,34 @@ Goal: add authorized stream-oriented management workflows over Iroh.
|
||||||
`overlay.join`, `overlay.route`, and `overlay.admin`.
|
`overlay.join`, `overlay.route`, and `overlay.admin`.
|
||||||
- `[x]` Add CLI/control commands for `geth overlay status`, `plan`, `join`,
|
- `[x]` Add CLI/control commands for `geth overlay status`, `plan`, `join`,
|
||||||
and `leave`.
|
and `leave`.
|
||||||
- `[x]` Prototype commands make clear that no TUN/Wintun interface is created
|
- `[x]` Prototype commands make clear that host networking changes require
|
||||||
and no packets are routed yet.
|
explicit `overlay up`.
|
||||||
- `[x]` Tests cover overlay validation and control serialization.
|
- `[x]` Tests cover overlay validation and control serialization.
|
||||||
- `[ ]` Persist overlay network configuration and membership state as
|
- `[x]` Persist overlay network configuration and membership state as
|
||||||
resource metadata.
|
resource metadata.
|
||||||
- `[ ]` Implement resource-authorized overlay join using resource secrets
|
- `[x]` Implement resource-authorized overlay join using resource secrets
|
||||||
without granting node identity.
|
without granting node identity.
|
||||||
- `[ ]` Add platform-specific, opt-in TUN/Wintun interface management with
|
- `[x]` Store only a secret fingerprint in overlay membership state.
|
||||||
|
- `[x]` Tests cover persisted overlay join/status/leave and bearer-token
|
||||||
|
enforcement for `overlay.join`.
|
||||||
|
- `[x]` Add platform-specific, opt-in TUN/Wintun interface plans with
|
||||||
generated-definition tests and no privileged test requirements.
|
generated-definition tests and no privileged test requirements.
|
||||||
- `[ ]` Route IPv4 packets over `/geth/overlay/1` using the shared daemon
|
- `[x]` Route IPv4 packets over `/geth/overlay/1` using the shared daemon
|
||||||
Iroh endpoint.
|
Iroh endpoint.
|
||||||
|
- `[x]` Validate overlay packet routing with two-node Iroh tests and
|
||||||
|
`overlay.route` authorization checks.
|
||||||
|
- `[x]` Add candidate peer listing from untrusted peer-card metadata.
|
||||||
|
- `[x]` Implement actual TUN/Wintun-style activation as an explicit user
|
||||||
|
opt-in through `geth overlay up/down`.
|
||||||
|
- `[x]` Runtime reads validated IPv4 packets from the TUN device, routes them
|
||||||
|
to imported peer cards by deterministic overlay IP, and injects authorized
|
||||||
|
remote packets back into the device.
|
||||||
|
- `[x]` Runtime activation surfaces privilege/setup errors clearly instead of
|
||||||
|
silently falling back to a non-overlay transport.
|
||||||
- `[ ]` Add live peer/IP coordination over trusted resource metadata and
|
- `[ ]` Add live peer/IP coordination over trusted resource metadata and
|
||||||
untrusted discovery candidates.
|
untrusted discovery candidates.
|
||||||
|
- `[ ]` Add packaged Windows Wintun deployment and macOS entitlement guidance
|
||||||
|
for release builds.
|
||||||
|
|
||||||
- `[~]` Unix socket forwarding where supported.
|
- `[~]` Unix socket forwarding where supported.
|
||||||
Acceptance criteria:
|
Acceptance criteria:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue