From 6d95bdc979039b16374e7d6809694207306e7fbf Mon Sep 17 00:00:00 2001 From: Johnathan Corgan Date: Thu, 29 Jan 2026 22:12:27 +0000 Subject: [PATCH] Add TUN interface with async netlink configuration - Implement fips.rs binary with config loading, node creation, and logging - Create src/tun.rs module (TunDevice, TunState, TunError) - Use rtnetlink crate for IPv6 address configuration - Make TunDevice::create() and Node::init_tun() async - Add IPv6 disabled check with helpful error message - Add rtnetlink, tokio, futures dependencies - Single tokio current_thread runtime for entire driver 162 tests pass. --- Cargo.lock | 825 +++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 6 + src/bin/fips.rs | 106 ++++++- src/config.rs | 49 +++ src/lib.rs | 6 +- src/node.rs | 78 ++++- src/tun.rs | 200 ++++++++++++ 7 files changed, 1258 insertions(+), 12 deletions(-) create mode 100644 src/tun.rs diff --git a/Cargo.lock b/Cargo.lock index e289966..d230e03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,51 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + [[package]] name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "bech32" version = "0.11.1" @@ -45,6 +84,51 @@ dependencies = [ "generic-array", ] +[[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]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + +[[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", +] + [[package]] name = "cc" version = "1.2.54" @@ -61,6 +145,21 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "cpufeatures" version = "0.2.17" @@ -70,6 +169,12 @@ dependencies = [ "libc", ] +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crypto-common" version = "0.1.7" @@ -108,7 +213,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -124,7 +229,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", ] [[package]] @@ -145,14 +271,119 @@ version = "0.1.0" dependencies = [ "bech32", "dirs", + "futures", "hex", "rand", + "rtnetlink", "secp256k1", "serde", "serde_yaml", "sha2", "tempfile", - "thiserror", + "thiserror 2.0.18", + "tokio", + "tracing", + "tracing-subscriber", + "tun", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", ] [[package]] @@ -219,18 +450,40 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + [[package]] name = "itoa" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + [[package]] name = "libredox" version = "0.1.12" @@ -247,6 +500,134 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "netlink-packet-core" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4" +dependencies = [ + "anyhow", + "byteorder", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-route" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74c171cd77b4ee8c7708da746ce392440cb7bcf618d122ec9ecc607b12938bf4" +dependencies = [ + "anyhow", + "byteorder", + "libc", + "log", + "netlink-packet-core", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-utils" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" +dependencies = [ + "anyhow", + "byteorder", + "paste", + "thiserror 1.0.69", +] + +[[package]] +name = "netlink-proto" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72452e012c2f8d612410d89eea01e2d9b56205274abb35d53f60200b2ec41d60" +dependencies = [ + "bytes", + "futures", + "log", + "netlink-packet-core", + "netlink-sys", + "thiserror 2.0.18", +] + +[[package]] +name = "netlink-sys" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6c30ed10fa69cc491d491b85cc971f6bdeb8e7367b7cde2ee6cc878d583fae" +dependencies = [ + "bytes", + "futures-util", + "libc", + "log", + "tokio", +] + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + +[[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", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -259,6 +640,41 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -330,7 +746,42 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.17", "libredox", - "thiserror", + "thiserror 2.0.18", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "rtnetlink" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b684475344d8df1859ddb2d395dd3dac4f8f3422a1aa0725993cb375fc5caba5" +dependencies = [ + "futures", + "log", + "netlink-packet-core", + "netlink-packet-route", + "netlink-packet-utils", + "netlink-proto", + "netlink-sys", + "nix 0.27.1", + "thiserror 1.0.69", + "tokio", ] [[package]] @@ -343,7 +794,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -426,12 +877,43 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + [[package]] name = "syn" version = "2.0.114" @@ -453,7 +935,16 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", ] [[package]] @@ -462,7 +953,18 @@ version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -476,6 +978,136 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tokio" +version = "1.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tun" +version = "0.7.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7904c94104239657089d14bccbf23fd6d363e30639ce49af21ef008a445baf97" +dependencies = [ + "bytes", + "cfg-if", + "futures", + "futures-core", + "ipnet", + "libc", + "log", + "nix 0.30.1", + "thiserror 2.0.18", + "tokio", + "tokio-util", + "windows-sys 0.59.0", + "wintun-bindings", +] + [[package]] name = "typenum" version = "1.19.0" @@ -494,6 +1126,12 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "version_check" version = "0.9.5" @@ -521,6 +1159,24 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +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.5", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -530,6 +1186,161 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[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]] +name = "wintun-bindings" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27ae04d34b8569174e849128d2e36538329a27daa79c06ed0375f2c5d6704461" +dependencies = [ + "blocking", + "c2rust-bitfields", + "futures", + "libloading", + "log", + "thiserror 2.0.18", + "windows-sys 0.61.2", + "winreg", +] + [[package]] name = "wit-bindgen" version = "0.51.0" diff --git a/Cargo.toml b/Cargo.toml index a16ac31..cd825fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,12 @@ serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.9" dirs = "6.0" hex = "0.4" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +tun = { version = "0.7", features = ["async"] } +rtnetlink = "0.14" +tokio = { version = "1", features = ["rt", "macros"] } +futures = "0.3" [dev-dependencies] tempfile = "3.15" diff --git a/src/bin/fips.rs b/src/bin/fips.rs index b63fa40..b5356bd 100644 --- a/src/bin/fips.rs +++ b/src/bin/fips.rs @@ -1,3 +1,105 @@ -fn main() { - // +//! FIPS daemon binary +//! +//! Loads configuration and creates the top-level node instance. + +use fips::{Config, Node}; +use tracing::{error, info, warn, Level}; +use tracing_subscriber::{fmt, EnvFilter}; + +#[tokio::main(flavor = "current_thread")] +async fn main() { + // Initialize logging + let filter = EnvFilter::builder() + .with_default_directive(Level::INFO.into()) + .from_env_lossy(); + + fmt() + .with_env_filter(filter) + .with_target(true) + .init(); + + info!("FIPS starting"); + + // Load configuration + info!("Loading configuration"); + let (config, loaded_paths) = match Config::load() { + Ok(result) => result, + Err(e) => { + error!("Failed to load configuration: {}", e); + std::process::exit(1); + } + }; + + if loaded_paths.is_empty() { + info!("No config files found, using defaults"); + } else { + for path in &loaded_paths { + info!(path = %path.display(), "Loaded config file"); + } + } + + // Log identity status + if config.has_identity() { + info!("Using configured identity"); + } else { + warn!("No identity configured, generating ephemeral keypair"); + } + + // Create node + info!("Creating node"); + let mut node = match Node::new(config) { + Ok(node) => node, + Err(e) => { + error!("Failed to create node: {}", e); + std::process::exit(1); + } + }; + + // Log node information + info!( + state = %node.state(), + leaf_only = node.is_leaf_only(), + "Node created" + ); + info!(" npub: {}", node.npub()); + info!(" node_id: {}", hex::encode(node.node_id().as_bytes())); + info!(" address: {}", node.identity().address()); + + // Initialize TUN interface + info!( + tun_state = %node.tun_state(), + "TUN interface" + ); + + if node.tun_state() != fips::TunState::Disabled { + info!( + name = node.config().tun.name(), + mtu = node.config().tun.mtu(), + "Initializing TUN device" + ); + + match node.init_tun().await { + Ok(true) => { + let device = node.tun_device().unwrap(); + info!( + name = device.name(), + mtu = device.mtu(), + address = %device.address(), + "TUN device active" + ); + } + Ok(false) => { + info!("TUN disabled"); + } + Err(e) => { + error!("Failed to initialize TUN: {}", e); + warn!("Continuing without TUN interface"); + } + } + } + + info!("FIPS initialized successfully"); + + // TODO: Start event loop, transports, etc. + info!("No transports configured, nothing to do"); } diff --git a/src/config.rs b/src/config.rs index d2a58b7..71340a9 100644 --- a/src/config.rs +++ b/src/config.rs @@ -66,12 +66,51 @@ pub struct NodeConfig { pub leaf_only: bool, } +/// Default TUN device name. +const DEFAULT_TUN_NAME: &str = "fips0"; + +/// Default TUN MTU (IPv6 minimum). +const DEFAULT_TUN_MTU: u16 = 1280; + +/// TUN interface configuration (`tun.*`). +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct TunConfig { + /// Enable TUN interface (`tun.enabled`). + #[serde(default, skip_serializing_if = "std::ops::Not::not")] + pub enabled: bool, + + /// TUN device name (`tun.name`). Defaults to "fips0". + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, + + /// TUN MTU (`tun.mtu`). Defaults to 1280 (IPv6 minimum). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mtu: Option, + +} + +impl TunConfig { + /// Get the TUN device name, using default if not configured. + pub fn name(&self) -> &str { + self.name.as_deref().unwrap_or(DEFAULT_TUN_NAME) + } + + /// Get the TUN MTU, using default if not configured. + pub fn mtu(&self) -> u16 { + self.mtu.unwrap_or(DEFAULT_TUN_MTU) + } +} + /// Root configuration structure. #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct Config { /// Node configuration (`node.*`). #[serde(default)] pub node: NodeConfig, + + /// TUN interface configuration (`tun.*`). + #[serde(default)] + pub tun: TunConfig, } impl Config { @@ -160,6 +199,16 @@ impl Config { if other.node.leaf_only { self.node.leaf_only = true; } + // Merge tun section + if other.tun.enabled { + self.tun.enabled = true; + } + if other.tun.name.is_some() { + self.tun.name = other.tun.name; + } + if other.tun.mtu.is_some() { + self.tun.mtu = other.tun.mtu; + } } /// Create an Identity from this configuration. diff --git a/src/lib.rs b/src/lib.rs index d043966..c2ff5c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ pub mod peer; pub mod protocol; pub mod transport; pub mod tree; +pub mod tun; // Re-export identity types pub use identity::{ @@ -20,7 +21,7 @@ pub use identity::{ }; // Re-export config types -pub use config::{Config, ConfigError, IdentityConfig}; +pub use config::{Config, ConfigError, IdentityConfig, TunConfig}; // Re-export tree types pub use tree::{ParentDeclaration, TreeCoordinate, TreeError, TreeState}; @@ -49,3 +50,6 @@ pub use peer::{Peer, PeerError, PeerState, UpstreamPeer}; // Re-export node types pub use node::{Node, NodeError, NodeState}; + +// Re-export TUN types +pub use tun::{TunDevice, TunError, TunState}; diff --git a/src/node.rs b/src/node.rs index 6815556..5f4066e 100644 --- a/src/node.rs +++ b/src/node.rs @@ -9,6 +9,7 @@ use crate::cache::CoordCache; use crate::peer::Peer; use crate::transport::{Link, LinkId, TransportId}; use crate::tree::TreeState; +use crate::tun::{TunDevice, TunError, TunState}; use crate::{Config, ConfigError, Identity, IdentityError, NodeId}; use std::collections::HashMap; use std::fmt; @@ -49,6 +50,9 @@ pub enum NodeError { #[error("identity error: {0}")] Identity(#[from] IdentityError), + + #[error("TUN error: {0}")] + Tun(#[from] TunError), } /// Node operational state. @@ -148,6 +152,12 @@ pub struct Node { next_link_id: u64, /// Next transport ID to allocate. next_transport_id: u32, + + // === TUN Interface === + /// TUN device state. + tun_state: TunState, + /// TUN device (if active). + tun_device: Option, } impl Node { @@ -155,14 +165,27 @@ impl Node { pub fn new(config: Config) -> Result { let identity = config.create_identity()?; let node_id = *identity.node_id(); + let is_leaf_only = config.is_leaf_only(); + + let bloom_state = if is_leaf_only { + BloomState::leaf_only(node_id) + } else { + BloomState::new(node_id) + }; + + let tun_state = if config.tun.enabled { + TunState::Configured + } else { + TunState::Disabled + }; Ok(Self { identity, config, state: NodeState::Created, - is_leaf_only: false, + is_leaf_only, tree_state: TreeState::new(node_id), - bloom_state: BloomState::new(node_id), + bloom_state, coord_cache: CoordCache::with_defaults(), transport_ids: Vec::new(), links: HashMap::new(), @@ -171,12 +194,19 @@ impl Node { max_links: 256, next_link_id: 1, next_transport_id: 1, + tun_state, + tun_device: None, }) } /// Create a node with a specific identity. pub fn with_identity(identity: Identity, config: Config) -> Self { let node_id = *identity.node_id(); + let tun_state = if config.tun.enabled { + TunState::Configured + } else { + TunState::Disabled + }; Self { identity, config, @@ -192,6 +222,8 @@ impl Node { max_links: 256, next_link_id: 1, next_transport_id: 1, + tun_state, + tun_device: None, } } @@ -280,6 +312,48 @@ impl Node { &mut self.coord_cache } + // === TUN Interface === + + /// Get the TUN state. + pub fn tun_state(&self) -> TunState { + self.tun_state + } + + /// Get the TUN device if active. + pub fn tun_device(&self) -> Option<&TunDevice> { + self.tun_device.as_ref() + } + + /// Get mutable TUN device if active. + pub fn tun_device_mut(&mut self) -> Option<&mut TunDevice> { + self.tun_device.as_mut() + } + + /// Initialize the TUN interface. + /// + /// Creates and configures the TUN device based on the node's configuration. + /// Requires CAP_NET_ADMIN capability (run with sudo or setcap). + /// + /// Returns Ok(true) if TUN was initialized, Ok(false) if TUN is disabled. + pub async fn init_tun(&mut self) -> Result { + if !self.config.tun.enabled { + return Ok(false); + } + + let address = *self.identity.address(); + match TunDevice::create(&self.config.tun, address).await { + Ok(device) => { + self.tun_device = Some(device); + self.tun_state = TunState::Active; + Ok(true) + } + Err(e) => { + self.tun_state = TunState::Failed; + Err(e.into()) + } + } + } + // === Resource Limits === /// Set the maximum number of peers. diff --git a/src/tun.rs b/src/tun.rs new file mode 100644 index 0000000..709f950 --- /dev/null +++ b/src/tun.rs @@ -0,0 +1,200 @@ +//! FIPS TUN Interface +//! +//! Manages the TUN device for sending and receiving IPv6 packets. +//! The TUN interface presents FIPS addresses to the local system, +//! allowing standard socket applications to communicate over the mesh. + +use crate::{FipsAddress, TunConfig}; +use futures::TryStreamExt; +use rtnetlink::{new_connection, Handle}; +use std::net::Ipv6Addr; +use thiserror::Error; +use tun::Layer; + +/// Errors that can occur with TUN operations. +#[derive(Debug, Error)] +pub enum TunError { + #[error("failed to create TUN device: {0}")] + Create(#[from] tun::Error), + + #[error("failed to configure TUN device: {0}")] + Configure(String), + + #[error("netlink error: {0}")] + Netlink(#[from] rtnetlink::Error), + + #[error("interface not found: {0}")] + InterfaceNotFound(String), + + #[error("permission denied: {0}")] + PermissionDenied(String), + + #[error("IPv6 is disabled (set net.ipv6.conf.all.disable_ipv6=0)")] + Ipv6Disabled, +} + +/// TUN device state. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TunState { + /// TUN is disabled in configuration. + Disabled, + /// TUN is configured but not yet created. + Configured, + /// TUN device is active and ready. + Active, + /// TUN device failed to initialize. + Failed, +} + +impl std::fmt::Display for TunState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TunState::Disabled => write!(f, "disabled"), + TunState::Configured => write!(f, "configured"), + TunState::Active => write!(f, "active"), + TunState::Failed => write!(f, "failed"), + } + } +} + +/// FIPS TUN device wrapper. +pub struct TunDevice { + device: tun::Device, + name: String, + mtu: u16, + address: FipsAddress, +} + +impl TunDevice { + /// Create and configure a new TUN device. + /// + /// This requires CAP_NET_ADMIN capability (run with sudo or setcap). + pub async fn create(config: &TunConfig, address: FipsAddress) -> Result { + // Check if IPv6 is enabled + if is_ipv6_disabled() { + return Err(TunError::Ipv6Disabled); + } + + let name = config.name(); + let mtu = config.mtu(); + + // Create the TUN device without address (we'll set it via netlink) + let mut tun_config = tun::Configuration::default(); + + #[allow(deprecated)] + tun_config.name(name).layer(Layer::L3).mtu(mtu); + + let device = tun::create(&tun_config)?; + + // Configure address and bring up via netlink + if let Err(e) = configure_interface(name, address.to_ipv6(), mtu).await { + // If netlink fails, the device was created but not configured. + // Drop will clean up the device. + return Err(e); + } + + Ok(Self { + device, + name: name.to_string(), + mtu, + address, + }) + } + + /// Get the device name. + pub fn name(&self) -> &str { + &self.name + } + + /// Get the configured MTU. + pub fn mtu(&self) -> u16 { + self.mtu + } + + /// Get the FIPS address assigned to this device. + pub fn address(&self) -> &FipsAddress { + &self.address + } + + /// Get a reference to the underlying tun::Device. + pub fn device(&self) -> &tun::Device { + &self.device + } + + /// Get a mutable reference to the underlying tun::Device. + pub fn device_mut(&mut self) -> &mut tun::Device { + &mut self.device + } +} + +impl std::fmt::Debug for TunDevice { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TunDevice") + .field("name", &self.name) + .field("mtu", &self.mtu) + .field("address", &self.address) + .finish() + } +} + +/// Configure a network interface with an IPv6 address via netlink. +async fn configure_interface(name: &str, addr: Ipv6Addr, mtu: u16) -> Result<(), TunError> { + let (connection, handle, _) = new_connection() + .map_err(|e| TunError::Configure(format!("netlink connection failed: {}", e)))?; + tokio::spawn(connection); + + // Get interface index + let index = get_interface_index(&handle, name).await?; + + // Add IPv6 address with /128 prefix (point-to-point) + handle + .address() + .add(index, std::net::IpAddr::V6(addr), 128) + .execute() + .await?; + + // Set MTU + handle + .link() + .set(index) + .mtu(mtu as u32) + .execute() + .await?; + + // Bring interface up + handle.link().set(index).up().execute().await?; + + Ok(()) +} + +/// Get the interface index by name. +async fn get_interface_index(handle: &Handle, name: &str) -> Result { + let mut links = handle.link().get().match_name(name.to_string()).execute(); + + if let Some(link) = links.try_next().await? { + Ok(link.header.index) + } else { + Err(TunError::InterfaceNotFound(name.to_string())) + } +} + +/// Check if IPv6 is disabled system-wide. +fn is_ipv6_disabled() -> bool { + std::fs::read_to_string("/proc/sys/net/ipv6/conf/all/disable_ipv6") + .map(|s| s.trim() == "1") + .unwrap_or(false) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_tun_state_display() { + assert_eq!(format!("{}", TunState::Disabled), "disabled"); + assert_eq!(format!("{}", TunState::Active), "active"); + } + + // Note: TUN device creation tests require elevated privileges + // and are better suited for integration tests. +}