diff --git a/Cargo.lock b/Cargo.lock index 123bd29..e260e7f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,10 +3,19 @@ version = 4 [[package]] -name = "anstream" -version = "1.0.0" +name = "aho-corasick" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -19,44 +28,44 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.14" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" -version = "1.0.0" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.5" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.11" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys", + "windows-sys 0.60.2", ] [[package]] name = "bitflags" -version = "2.11.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "byteorder" @@ -66,9 +75,19 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.1" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "739eb0f94557554b3ca9a86d2d37bebd49c5e6d0c1d2bda35ba5bdac830befc2" +dependencies = [ + "find-msvc-tools", + "shlex", +] [[package]] name = "cfg-if" @@ -82,20 +101,11 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" -[[package]] -name = "cidr" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579504560394e388085d0c080ea587dfa5c15f7e251b4d5247d1e1a61d1d6928" -dependencies = [ - "serde", -] - [[package]] name = "clap" -version = "4.6.1" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" dependencies = [ "clap_builder", "clap_derive", @@ -103,9 +113,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.6.0" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" dependencies = [ "anstream", "anstyle", @@ -115,9 +125,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.6.1" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck", "proc-macro2", @@ -127,15 +137,25 @@ dependencies = [ [[package]] name = "clap_lex" -version = "1.1.0" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "colorchoice" -version = "1.0.5" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "cordyceps" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688d7fbb8092b8de775ef2536f36c8c31f2bc4006ece2e8d8ad2d17d00ce0a2a" +dependencies = [ + "loom", + "tracing", +] [[package]] name = "darling" @@ -203,6 +223,12 @@ dependencies = [ "syn", ] +[[package]] +name = "diatomic-waker" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab03c107fafeb3ee9f5925686dbb7a73bc76e3932abb0d2b365cb64b169cf04c" + [[package]] name = "either" version = "1.15.0" @@ -236,20 +262,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "errno" -version = "0.3.14" +name = "fastrand" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys", -] +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] -name = "fastrand" -version = "2.4.1" +name = "find-msvc-tools" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" [[package]] name = "fixedbitset" @@ -265,9 +287,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "futures" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -279,10 +301,23 @@ dependencies = [ ] [[package]] -name = "futures-channel" -version = "0.3.32" +name = "futures-buffered" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +checksum = "a8e0e1f38ec07ba4abbde21eed377082f17ccb988be9d988a5adbf4bafc118fd" +dependencies = [ + "cordyceps", + "diatomic-waker", + "futures-core", + "pin-project-lite", + "spin", +] + +[[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", @@ -290,28 +325,30 @@ dependencies = [ [[package]] name = "futures-concurrency" -version = "7.7.1" +version = "7.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175cd8cca9e1d45b87f18ffa75088f2099e3c4fe5e2f83e42de112560bea8ea6" +checksum = "0eb68017df91f2e477ed4bea586c59eaecaa47ed885a770d0444e21e62572cd2" dependencies = [ "fixedbitset", + "futures-buffered", "futures-core", "futures-lite", "pin-project", + "slab", "smallvec", ] [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -320,9 +357,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" @@ -339,9 +376,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -350,21 +387,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -374,9 +411,24 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", + "pin-utils", "slab", ] +[[package]] +name = "generator" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2" +dependencies = [ + "cc", + "cfg-if", + "libc", + "log", + "rustversion", + "windows", +] + [[package]] name = "getset" version = "0.1.6" @@ -391,9 +443,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.17.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" [[package]] name = "heck" @@ -409,9 +461,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" -version = "2.14.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", "hashbrown", @@ -442,9 +494,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.186" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "lock_api" @@ -457,9 +509,22 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] [[package]] name = "main_error" @@ -468,27 +533,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "155db5e86c6e45ee456bf32fad5a290ee1f7151c2faca27ea27097568da67d1a" [[package]] -name = "memchr" -version = "2.8.0" +name = "matchers" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +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.2.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] name = "neli" -version = "0.7.4" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22f9786d56d972959e1408b6a93be6af13b9c1392036c5c1fafa08a1b0c6ee87" +checksum = "4e328dd8a89fa4992c0628ef4ebd9c0b432a5e92522abead20b0f5a8f7bcb812" dependencies = [ "bitflags", "byteorder", @@ -502,9 +576,9 @@ dependencies = [ [[package]] name = "neli-proc-macros" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d8d08c6e98f20a62417478ebf7be8e1425ec9acecc6f63e22da633f6b71609" +checksum = "90e502fe5db321c6e0ae649ccda600675680125a8e8dee327744fe1910b19332" dependencies = [ "either", "proc-macro2", @@ -515,9 +589,8 @@ dependencies = [ [[package]] name = "netnsd" -version = "0.2.0" +version = "0.1.0" dependencies = [ - "cidr", "clap", "either", "futures", @@ -541,9 +614,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.31.2" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ "bitflags", "cfg-if", @@ -553,9 +626,9 @@ dependencies = [ [[package]] name = "ntapi" -version = "0.4.3" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" dependencies = [ "winapi", ] @@ -566,7 +639,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -590,9 +663,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.4" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" @@ -626,23 +699,23 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-link", + "windows-link 0.2.1", ] [[package]] name = "pin-project" -version = "1.1.11" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.11" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", @@ -651,9 +724,15 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.17" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "proc-macro-error-attr2" @@ -679,18 +758,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.45" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] @@ -704,6 +783,35 @@ dependencies = [ "bitflags", ] +[[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 = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -712,9 +820,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sd-notify" -version = "0.5.0" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4ef7359e694bfaf1dd27a30f9d760b54c00dfae9f19bd0c05a39bc9128fe76" +checksum = "b943eadf71d8b69e661330cb0e2656e31040acf21ee7708e2c238a0ec6af2bf4" dependencies = [ "libc", ] @@ -751,9 +859,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.1.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" dependencies = [ "serde_core", ] @@ -777,20 +885,25 @@ dependencies = [ ] [[package]] -name = "signal-hook-registry" -version = "1.4.8" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ - "errno", "libc", ] [[package]] name = "slab" -version = "0.4.12" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" @@ -800,14 +913,20 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.6.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.60.2", ] +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" + [[package]] name = "strsim" version = "0.11.1" @@ -816,9 +935,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.117" +version = "2.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" dependencies = [ "proc-macro2", "quote", @@ -827,9 +946,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.38.4" +version = "0.37.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ab6a2f8bfe508deb3c6406578252e491d299cbbf3bc0529ecc3313aee4a52f" +checksum = "16607d5caffd1c07ce073528f9ed972d88db15dd44023fa57142963be3feb11f" dependencies = [ "libc", "memchr", @@ -841,18 +960,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", @@ -870,9 +989,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.52.1" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ "bytes", "libc", @@ -881,14 +1000,14 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.7.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", @@ -897,9 +1016,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.18" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -908,9 +1027,9 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.2+spec-1.1.0" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" dependencies = [ "indexmap", "serde_core", @@ -923,33 +1042,33 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.1.1+spec-1.1.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" dependencies = [ "serde_core", ] [[package]] name = "toml_parser" -version = "1.1.2+spec-1.1.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" dependencies = [ "winnow", ] [[package]] name = "toml_writer" -version = "1.1.1+spec-1.1.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" +checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" [[package]] name = "tracing" -version = "0.1.44" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -958,9 +1077,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.31" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", @@ -969,9 +1088,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.36" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -990,23 +1109,27 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.23" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ + "matchers", "nu-ansi-term", + "once_cell", + "regex-automata", "sharded-slab", "smallvec", "thread_local", + "tracing", "tracing-core", "tracing-log", ] [[package]] name = "unicode-ident" -version = "1.0.24" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06" [[package]] name = "utf8parse" @@ -1016,9 +1139,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uzers" -version = "0.12.2" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8275fb1afee25b4111d2dc8b5c505dbbc4afd0b990cb96deb2d88bff8be18d" +checksum = "4df81ff504e7d82ad53e95ed1ad5b72103c11253f39238bcc0235b90768a97dd" dependencies = [ "libc", "log", @@ -1060,46 +1183,47 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.62.2" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ "windows-collections", "windows-core", "windows-future", + "windows-link 0.1.3", "windows-numerics", ] [[package]] name = "windows-collections" -version = "0.3.2" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ "windows-core", ] [[package]] name = "windows-core" -version = "0.62.2" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", - "windows-link", + "windows-link 0.1.3", "windows-result", "windows-strings", ] [[package]] name = "windows-future" -version = "0.3.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core", - "windows-link", + "windows-link 0.1.3", "windows-threading", ] @@ -1125,6 +1249,12 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-link" version = "0.2.1" @@ -1133,30 +1263,39 @@ checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-numerics" -version = "0.3.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ "windows-core", - "windows-link", + "windows-link 0.1.3", ] [[package]] name = "windows-result" -version = "0.4.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] name = "windows-strings" -version = "0.5.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets", ] [[package]] @@ -1165,20 +1304,85 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link", + "windows-link 0.2.1", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link 0.2.1", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows-threading" -version = "0.2.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] -name = "winnow" -version = "1.0.2" +name = "windows_aarch64_gnullvm" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[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.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[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.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[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.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" diff --git a/Cargo.toml b/Cargo.toml index 136eb34..91e02ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,29 +1,28 @@ [package] name = "netnsd" -version = "0.2.0" +version = "0.1.0" edition = "2024" rust-version = "1.88.0" [dependencies] -tokio = { version = "1.49.0", features = ["macros", "rt", "signal", "net", "io-util"] } -tokio-stream = { version = "0.1.18", features = ["signal", "net"] } -toml = "1.0.1" +tokio = { version = "1.48.0", features = ["macros", "rt", "signal", "net", "io-util"] } +tokio-stream = { version = "0.1.17", features = ["signal", "net"] } +toml = "0.9.8" serde = { version = "1.0.228", features = ["derive"] } -clap = { version = "4.5.58", features = ["derive"] } -thiserror = "2.0.18" -tracing = "0.1.44" -tracing-subscriber = "0.3.22" +clap = { version = "4.5.51", features = ["derive"] } +thiserror = "2.0.17" +tracing = "0.1.41" +tracing-subscriber = "0.3.20" main_error = "0.1.2" -nix = { version = "0.31.1", features = ["mount", "sched", "user", "signal"] } -sd-notify = "0.5.0" +nix = { version = "0.30.1", features = ["mount", "sched", "user", "signal"] } +sd-notify = "0.4.5" futures = "0.3.31" -futures-concurrency = "7.7.1" -neli = "0.7.4" +futures-concurrency = "7.6.3" +neli = "0.7.1" either = "1.15.0" -uzers = "0.12.2" -sysinfo = "0.38.1" +uzers = "0.12.1" +sysinfo = "0.37.2" landlock = "0.4.4" -cidr = { version = "0.3.2", features = ["serde"] } [dev-dependencies] -serde_test = "1.0.177" +serde_test = "1.0.177" \ No newline at end of file diff --git a/README.md b/README.md index 5aa49de..40ebe43 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,8 @@ A declarative manager for Linux network namespaces. ## Features - Fully declarative configuration -- Standalone binary with no runtime dependencies - Hot reloading of configuration -- Port forwarding into or out of the namespace -- Moving network devices to the namespace -- Setting up routing inside the namespace +- Port forwarding into the namespace ## Usage @@ -56,13 +53,6 @@ You can specify a different configuration path with the `--config` option. [[namespace]] # name of the namespace to create name = "test" -# move existing devices into the namespace -devices = ["somelink"] - -# create a route inside the namespace -[[namespace.route]] -destination = "default" # either "default" or an ip range in CIDR notation -device = "somelink" # You can define any number of port forwards to setup into the namespace [[namespace.forward]] diff --git a/config.sample.toml b/config.sample.toml index 7cfdef8..a4132ce 100644 --- a/config.sample.toml +++ b/config.sample.toml @@ -2,13 +2,6 @@ [[namespace]] # name of the namespace to create name = "test" -# move existing devices into the namespace -devices = ["somelink"] - -# create a route inside the namespace -[[namespace.route]] -destination = "default" # either "default" or an ip range in CIDR notation -device = "somelink" # You can define any number of port forwards to setup into the namespace [[namespace.forward]] diff --git a/flake.lock b/flake.lock index e7e1995..4e38b6d 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "crane": { "locked": { - "lastModified": 1774313767, - "narHash": "sha256-hy0XTQND6avzGEUFrJtYBBpFa/POiiaGBr2vpU6Y9tY=", + "lastModified": 1763938834, + "narHash": "sha256-j8iB0Yr4zAvQLueCZ5abxfk6fnG/SJ5JnGUziETjwfg=", "owner": "ipetkov", "repo": "crane", - "rev": "3d9df76e29656c679c744968b17fbaf28f0e923d", + "rev": "d9e753122e51cee64eb8d2dddfe11148f339f5a2", "type": "github" }, "original": { @@ -22,11 +22,11 @@ ] }, "locked": { - "lastModified": 1777298791, - "narHash": "sha256-MEQeYwRQcV7RvlKMVrFy07dmoY8t2s/SIK7EpCNLOu8=", + "lastModified": 1764593611, + "narHash": "sha256-6SdexcO69Dlu14YN2xuB1A6JHWSrcqMj7Na9oK7IT2M=", "owner": "nix-community", "repo": "flakelight", - "rev": "c4b125c5453559de6a228be70f71abeb017f3265", + "rev": "0d63256401341f528dd628f1a8e96d3afecade7a", "type": "github" }, "original": { @@ -44,11 +44,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1776692281, - "narHash": "sha256-DCCSbpTUDqRiHsTS/sIgCTwa1r4gsJlWSFE6difdWxk=", + "lastModified": 1764619631, + "narHash": "sha256-WojMP5S9qLmOLecEQ+7+yc33Ly1ydoRsODNG6hlLqiQ=", "ref": "refs/heads/main", - "rev": "d1d0e0bb5b0acebed06ccced9cc27f82aafab058", - "revCount": 71, + "rev": "0fae557bf52d8493840aca52d433c473ecc305ef", + "revCount": 67, "type": "git", "url": "https://codeberg.org/icewind/mill-scale" }, @@ -59,11 +59,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1777428379, - "narHash": "sha256-ypxFOeDz+CqADEQNL72haqGjvZQdBR5Vc7pyx2JDttI=", + "lastModified": 1764522689, + "narHash": "sha256-SqUuBFjhl/kpDiVaKLQBoD8TLD+/cTUzzgVFoaHrkqY=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "755f5aa91337890c432639c60b6064bb7fe67769", + "rev": "8bb5646e0bed5dbd3ab08c7a7cc15b75ab4e1d0f", "type": "github" }, "original": { @@ -88,11 +88,11 @@ ] }, "locked": { - "lastModified": 1774535687, - "narHash": "sha256-dpKS/8+uB0EoI4mCrpio+xs8Xxry6ZhLLwV8VIbbfrs=", + "lastModified": 1764557621, + "narHash": "sha256-kX5PoY8hQZ80+amMQgOO9t8Tc1JZ70gYRnzaVD4AA+o=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "75900435aa883f84b038316864b3f60956681523", + "rev": "93316876c2229460a5d6f5f052766cc4cef538ce", "type": "github" }, "original": { diff --git a/nix/module.nix b/nix/module.nix index 903d14a..855155a 100644 --- a/nix/module.nix +++ b/nix/module.nix @@ -45,37 +45,11 @@ in { type = types.oneOf [types.port types.str]; description = "target port or address inside the namespace"; }; - reverse = mkOption { - type = types.bool; - default = false; - description = "forward from inside the namespace to outside instead"; - }; }; })); description = "ports to forward into the namespace"; default = []; }; - devices = mkOption { - type = types.listOf types.str; - default = []; - description = "devices to move into the namespace"; - }; - route = mkOption { - type = types.listOf (types.submodule ({config, ...}: { - options = { - device = mkOption { - type = types.str; - description = "device to route the traffic trough"; - }; - destination = mkOption { - type = types.str; - description = "What traffic to route. Either \"default\" or an ip range in CIDR notation"; - }; - }; - })); - description = "routes to setup inside the namespace"; - default = []; - }; }; })); description = "namespaces to setup"; @@ -87,7 +61,7 @@ in { # symlink instead of passing `configFile` directly to netnsd to allow changing the config without changing the path environment.etc."netnsd/netnsd.toml".source = configFile; - environment.systemPackages = [cfg.package]; + environment.systemPackages = with pkgs; [cfg.package]; systemd.services.netnsd = { reloadTriggers = [configFile]; diff --git a/nix/package.nix b/nix/package.nix index 6afae2d..382e08e 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -12,7 +12,7 @@ rustc = rust-bin.stable.latest.minimal; }; in - rustPlatform.buildRustPackage { + rustPlatform.buildRustPackage rec { pname = cargoPackage.name; inherit (cargoPackage) version; diff --git a/src/config/mod.rs b/src/config/mod.rs index 0a31dd0..779fce1 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -2,18 +2,13 @@ mod name; mod source; mod target; -pub use crate::config::name::{DeviceName, NamespaceName}; +pub use crate::config::name::NamespaceName; pub use crate::config::source::ForwardSource; pub use crate::config::target::ForwardTarget; -use cidr::AnyIpCidr; -use serde::de::Error; -use serde::{Deserialize, Deserializer}; -use std::borrow::Cow; +use serde::Deserialize; use std::collections::HashSet; -use std::fmt::{Display, Formatter}; use std::fs::read_to_string; use std::path::{Path, PathBuf}; -use std::str::FromStr; use thiserror::Error; use toml::from_str; @@ -85,12 +80,7 @@ impl RawConfig { #[derive(Deserialize, Debug)] pub struct NamespaceConfig { pub name: NamespaceName, - #[serde(default)] pub forward: Vec, - #[serde(default)] - pub devices: Vec, - #[serde(default, rename = "route")] - pub routes: Vec, } #[derive(Deserialize, Debug)] @@ -101,27 +91,6 @@ pub struct ForwardConfig { pub reverse: bool, } -#[derive(Deserialize, Debug, PartialEq, Clone)] -pub struct RouteConfig { - #[serde(deserialize_with = "parse_cidr")] - pub destination: AnyIpCidr, - pub device: DeviceName, -} - -impl Display for RouteConfig { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{} dev {}", self.destination, self.device) - } -} - -fn parse_cidr<'de, D: Deserializer<'de>>(deserializer: D) -> Result { - let str = Cow::<'de, str>::deserialize(deserializer)?; - match str.as_ref() { - "default" => Ok(AnyIpCidr::Any), - str => AnyIpCidr::from_str(str).map_err(D::Error::custom), - } -} - #[derive(Debug, Error)] pub enum ConfigError { #[error("Error while reading config from {}: {error:#}", path.display())] diff --git a/src/config/name.rs b/src/config/name.rs index 9490fbe..32fbbd6 100644 --- a/src/config/name.rs +++ b/src/config/name.rs @@ -7,18 +7,8 @@ use std::str::FromStr; use thiserror::Error; #[derive(Debug, Clone, Eq, PartialEq, Hash)] -struct ValidatedName(String); - -#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize)] -#[serde(from = "ValidatedName")] pub struct NamespaceName(String); -impl From for NamespaceName { - fn from(value: ValidatedName) -> Self { - NamespaceName(value.0) - } -} - impl TryFrom for NamespaceName { type Error = (); @@ -62,60 +52,7 @@ impl From for String { } } -#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize)] -#[serde(from = "ValidatedName")] -pub struct DeviceName(String); - -impl From for DeviceName { - fn from(value: ValidatedName) -> Self { - DeviceName(value.0) - } -} - -impl TryFrom for DeviceName { - type Error = (); - - fn try_from(value: OsString) -> Result { - let str = value.into_string().map_err(|_| ())?; - if validate_name(&str) { - Ok(DeviceName(str)) - } else { - Err(()) - } - } -} - -impl Display for DeviceName { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } -} - -impl AsRef for DeviceName { - fn as_ref(&self) -> &str { - self.0.as_ref() - } -} - -impl AsRef for DeviceName { - fn as_ref(&self) -> &Path { - self.0.as_ref() - } -} - -impl PartialEq<&str> for DeviceName { - fn eq(&self, other: &&str) -> bool { - self.0 == *other - } -} - -impl From for String { - fn from(value: DeviceName) -> Self { - value.0 - } -} - -impl<'de> Deserialize<'de> for ValidatedName { +impl<'de> Deserialize<'de> for NamespaceName { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, @@ -123,7 +60,7 @@ impl<'de> Deserialize<'de> for ValidatedName { struct NamespaceNameVisitor; impl Visitor<'_> for NamespaceNameVisitor { - type Value = ValidatedName; + type Value = NamespaceName; fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { formatter.write_str("A valid namespace name") @@ -136,7 +73,7 @@ impl<'de> Deserialize<'de> for ValidatedName { if !validate_name(v) { return Err(E::invalid_value(Unexpected::Str(v), &self)); } - Ok(ValidatedName(v.into())) + Ok(NamespaceName(v.into())) } fn visit_string(self, v: String) -> Result @@ -146,7 +83,7 @@ impl<'de> Deserialize<'de> for ValidatedName { if !validate_name(&v) { return Err(E::invalid_value(Unexpected::Str(&v), &self)); } - Ok(ValidatedName(v)) + Ok(NamespaceName(v)) } } @@ -155,24 +92,20 @@ impl<'de> Deserialize<'de> for ValidatedName { } impl FromStr for NamespaceName { - type Err = InvalidNameError; + type Err = InvalidNamespaceNameError; fn from_str(s: &str) -> Result { if !validate_name(s) { - return Err(InvalidNameError { - name: s.into(), - kind: "namespace", - }); + return Err(InvalidNamespaceNameError { name: s.into() }); } Ok(NamespaceName(s.into())) } } #[derive(Debug, Error)] -#[error("invalid name for {kind}: '{name}'")] -pub struct InvalidNameError { +#[error("invalid name for namespace: '{name}'")] +pub struct InvalidNamespaceNameError { name: String, - kind: &'static str, } /// Check if a name follows the portable filename character set diff --git a/src/daemon.rs b/src/daemon.rs index 2367114..57d6a0b 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -1,10 +1,5 @@ -use crate::config::{ - Config, DeviceName, ForwardConfig, NamespaceConfig, NamespaceName, RouteConfig, -}; -use crate::link::{LinkError, LinkManager}; -use crate::namespace::{ - NamespaceEnterError, NamespaceError, NamespaceHandle, NamespaceHandleError, NetNs, -}; +use crate::config::{Config, ForwardConfig, NamespaceConfig, NamespaceName}; +use crate::namespace::{NamespaceError, NetNs}; use crate::proxy::{ActiveProxy, ProxyError}; use futures::FutureExt; use futures::StreamExt; @@ -30,15 +25,7 @@ async fn daemon_async(mut config: Config) -> Result<(), DaemonError> { state.update(&config)?; // now the namespaces are setup, we can tell systemd to start any service depending on them - notify(&[ - NotifyState::Ready, - NotifyState::Status(&format!( - "Started with {} namespaces", - state.namespaces.len() - )), - ]) - .map_err(DaemonError::Notify)?; - info!("ready"); + notify(false, &[NotifyState::Ready]).map_err(DaemonError::Notify)?; let reload_signal = signal(SignalKind::hangup()).map_err(DaemonError::Signal)?; let reload_signal = SignalStream::new(reload_signal).map(|_| Event::Reload); @@ -65,7 +52,7 @@ async fn daemon_async(mut config: Config) -> Result<(), DaemonError> { match NotifyState::monotonic_usec_now() { Ok(notify_time) => { - notify(&[NotifyState::Reloading, notify_time]) + notify(false, &[NotifyState::Reloading, notify_time]) .map_err(DaemonError::Notify)?; } Err(error) => { @@ -83,15 +70,7 @@ async fn daemon_async(mut config: Config) -> Result<(), DaemonError> { } } - notify(&[ - NotifyState::Ready, - NotifyState::Status(&format!( - "Reloaded with {} namespaces", - state.namespaces.len() - )), - ]) - .map_err(DaemonError::Notify)?; - info!("reloaded"); + notify(false, &[NotifyState::Ready]).map_err(DaemonError::Notify)?; } Event::Info => { for namespace in &state.namespaces { @@ -104,7 +83,7 @@ async fn daemon_async(mut config: Config) -> Result<(), DaemonError> { } } - let _ = notify(&[NotifyState::Stopping]); + let _ = notify(false, &[NotifyState::Stopping]); Ok(()) } @@ -122,7 +101,7 @@ struct State { impl State { pub fn new() -> Result { - let namespaces = NetNs::existing(false)? + let namespaces = NetNs::existing()? .map(ActiveNamespace::new) .collect::, _>>()?; Ok(State { namespaces }) @@ -145,8 +124,6 @@ impl State { for namespace in &mut self.namespaces { let config = config.get_namespace(namespace.name()).unwrap(); namespace.update_proxies(config)?; - namespace.update_devices(config)?; - namespace.update_links(config)?; } Ok(()) @@ -162,8 +139,6 @@ impl State { struct ActiveNamespace { ns: NetNs, proxies: Vec, - devices: Vec, - routes: Vec, } impl ActiveNamespace { @@ -173,8 +148,6 @@ impl ActiveNamespace { Ok(ActiveNamespace { ns, proxies: Vec::default(), - devices: Vec::default(), - routes: Vec::default(), }) } @@ -191,106 +164,10 @@ impl ActiveNamespace { Ok(()) } - pub fn update_devices(&mut self, config: &NamespaceConfig) -> Result<(), DaemonError> { - let parent_namespace = NamespaceHandle::parent()?; - - let removed: Vec<_> = self - .devices - .extract_if(.., |existing| { - !config.devices.iter().any(|new| existing == new) - }) - .collect(); - - self.ns.handle().run_in(move || { - let link_manager = LinkManager::new()?; - for link in link_manager.get_links()?.flatten() { - if removed.iter().any(|name| *name == link.name.as_str()) { - info!(namespace = %config.name, link = link.name , "moving link out of namespace"); - link_manager.move_link(&link, &parent_namespace)? - } - } - Ok::<_, LinkError>(()) - })??; - - let mut added = Vec::new(); - for new in &config.devices { - if !self.has_device(new) { - added.push(new.clone()); - } - } - - let link_manager = LinkManager::new()?; - for link in link_manager.get_links()?.flatten() { - if added.iter().any(|name| *name == link.name.as_str()) { - info!(namespace = %config.name, link = link.name , "moving link into namespace"); - link_manager.move_link(&link, self.ns.handle())? - } - } - for new in added { - self.devices.push(new); - } - - Ok(()) - } - - pub fn update_links(&mut self, config: &NamespaceConfig) -> Result<(), DaemonError> { - let removed: Vec<_> = self - .routes - .extract_if(.., |existing| { - !config.routes.iter().any(|new| existing == new) - }) - .collect(); - - let mut added = Vec::new(); - for new in &config.routes { - if !self.has_route(new) { - added.push(new.clone()); - } - } - - self.ns.handle().run_in(|| { - let link_manager = LinkManager::new()?; - for link in link_manager.get_links()?.flatten() { - if let Some(route) = removed - .iter() - .find(|route| route.device == link.name.as_str()) - { - info!(namespace = %config.name, %route, "deleting route"); - link_manager.delete_route(&link, route.destination)?; - } - } - - for link in link_manager.get_links()?.flatten() { - if let Some(route) = added - .iter() - .find(|route| route.device == link.name.as_str()) - { - info!(namespace = %config.name, %route, "adding route"); - link_manager.add_route(&link, route.destination)?; - } - } - Ok::<_, DaemonError>(()) - })??; - - for new in added { - self.routes.push(new); - } - - Ok(()) - } - fn has_forward(&self, config: &ForwardConfig) -> bool { self.proxies.iter().any(|existing| existing == config) } - fn has_device(&self, name: &DeviceName) -> bool { - self.devices.iter().any(|existing| existing == name) - } - - fn has_route(&self, route: &RouteConfig) -> bool { - self.routes.iter().any(|existing| existing == route) - } - pub fn name(&self) -> &NamespaceName { self.ns.name() } @@ -306,10 +183,4 @@ pub enum DaemonError { Signal(IoError), #[error(transparent)] Proxy(#[from] ProxyError), - #[error(transparent)] - Handle(#[from] NamespaceHandleError), - #[error(transparent)] - Enter(#[from] NamespaceEnterError), - #[error(transparent)] - Link(#[from] LinkError), } diff --git a/src/down.rs b/src/down.rs index fed2891..f29f50d 100644 --- a/src/down.rs +++ b/src/down.rs @@ -2,7 +2,7 @@ use crate::namespace::NetNs; use main_error::MainResult; pub fn down() -> MainResult { - for name in NetNs::existing(true)? { + for name in NetNs::existing()? { let ns = NetNs::new(name)?; ns.delete()? } diff --git a/src/link.rs b/src/link.rs index 98bf0cc..4543393 100644 --- a/src/link.rs +++ b/src/link.rs @@ -1,265 +1,96 @@ -use cidr::{AnyIpCidr, Family}; use neli::consts::nl::NlmF; +use neli::consts::rtnl::Ifla; +use neli::consts::rtnl::RtAddrFamily; use neli::consts::rtnl::Rtm; -use neli::consts::rtnl::{Ifla, RtScope, RtTable, Rta}; -use neli::consts::rtnl::{RtAddrFamily, Rtn, Rtprot}; use neli::consts::socket::NlFamily; use neli::err::RouterError; use neli::nl::NlPayload; use neli::router::synchronous::NlRouter; -use neli::rtnl::{Ifinfomsg, RtattrBuilder, Rtmsg}; -use neli::rtnl::{IfinfomsgBuilder, RtmsgBuilder}; -use neli::types::{Buffer, RtBuffer}; +use neli::rtnl::Ifinfomsg; +use neli::rtnl::IfinfomsgBuilder; use neli::utils::Groups; -use nix::libc::c_int; -use std::fmt::{Debug, Display, Formatter}; -use std::os::fd::AsRawFd; +use nix::errno::Errno; +use nix::sched::{setns, CloneFlags}; +use std::fs::File; +use std::io::Error as IoError; +use std::path::{Path, PathBuf}; +use std::thread::spawn; use thiserror::Error; -use tracing::{info, instrument}; #[derive(Debug, Error)] pub enum LinkError { - #[error("Failed to communicate with netlink: {0}")] - Netlink(String), - #[error("Failed to parse netlink response: {0}")] - Parse(String), - #[error("Link not found: {0}")] - NotFound(String), + #[error("failed to communicate with netlink")] + Netlink, + #[error("failed to code netlink response")] + Parse, + #[error("unexpected panic in link setup")] + Panic, + #[error("failed to enter namespace in link setup: {0}")] + Namespace(Errno), + #[error("Failed to open namespace file {}: {error:#}", path.display())] + OpenNamespace { path: PathBuf, error: IoError }, } -impl From> for LinkError -where - T: Debug, - P: Debug, -{ - fn from(value: RouterError) -> Self { - LinkError::Netlink(value.to_string()) +impl From> for LinkError { + fn from(_value: RouterError) -> Self { + LinkError::Netlink } } -pub struct LinkManager { - router: NlRouter, -} +/// Set a link to UP inside a namespace +pub fn link_up_ns(namespace: impl AsRef, link_name: &'static str) -> Result<(), LinkError> { + let namespace = namespace.as_ref(); + let ns_handle = File::open(namespace).map_err(|error| LinkError::OpenNamespace { + error, + path: namespace.into(), + })?; -pub struct Link { - family: RtAddrFamily, - index: c_int, - pub name: String, -} - -impl Display for Link { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(&self.name) - } -} - -impl Link { - fn msg_builder(&self) -> IfinfomsgBuilder { - IfinfomsgBuilder::default() - .ifi_family(self.family) - .ifi_index(self.index) - } -} - -impl LinkManager { - pub fn new() -> Result { - let (router, _) = NlRouter::connect(NlFamily::Route, None, Groups::empty())?; - router.enable_ext_ack(true)?; - router.enable_strict_checking(true)?; - Ok(LinkManager { router }) - } - - pub fn get_link(&self, name: impl AsRef) -> Result { - let name = name.as_ref(); - for link in self.get_links()? { - let link = link?; - if link.name == name { - return Ok(link); - } - } - Err(LinkError::NotFound(name.into())) - } - - pub fn get_links(&self) -> Result>, LinkError> { - let ifinfomsg = IfinfomsgBuilder::default() - .ifi_family(RtAddrFamily::Inet) - .build() - .unwrap(); - - let recv = self.router.send::<_, _, Rtm, Ifinfomsg>( - Rtm::Getlink, - NlmF::DUMP | NlmF::ACK, - NlPayload::Payload(ifinfomsg), - )?; - Ok(recv - .map(|response| { - if let Some(payload) = response?.get_payload() { - let name = payload - .rtattrs() - .get_attr_handle() - .get_attr_payload_as_with_len::(Ifla::Ifname) - .map_err(|e| LinkError::Parse(e.to_string()))?; - Ok(Some(Link { - family: *payload.ifi_family(), - index: *payload.ifi_index(), - name, - })) - } else { - Ok(None) - } - }) - .filter_map(|item| item.transpose())) - } - - /// Move a link to a namespace - pub fn move_link(&self, link: &Link, namespace: &Fd) -> Result<(), LinkError> { - let ns_handle = namespace.as_raw_fd(); - - let mut info_attrs = RtBuffer::::new(); - info_attrs.push( - RtattrBuilder::default() - .rta_type(Ifla::NetNsFd) - .rta_payload(ns_handle) - .build() - .expect("invalid rtattr"), - ); - - let msg = link.msg_builder().rtattrs(info_attrs).build().unwrap(); - self.router.send::<_, _, Rtm, Ifinfomsg>( - Rtm::Setlink, - NlmF::ACK, - NlPayload::Payload(msg), - )?; - Ok(()) - } - - pub fn up(&self, link: &Link) -> Result<(), LinkError> { - let up_msg = link.msg_builder().up().build().unwrap(); - self.router.send::<_, _, Rtm, Ifinfomsg>( - Rtm::Setlink, - NlmF::ACK, - NlPayload::Payload(up_msg), - )?; - Ok(()) - } - - #[instrument(skip_all, fields(link = %link, destination = %destination))] - pub fn add_route(&self, link: &Link, destination: AnyIpCidr) -> Result<(), LinkError> { - let rt_msg = route_message_for(link, destination); - - let res = self.router.send::<_, _, Rtm, Rtmsg>( - Rtm::Newroute, - NlmF::CREATE | NlmF::EXCL | NlmF::REQUEST | NlmF::ACK, - NlPayload::Payload(rt_msg), - )?; - - for msg in res { - match msg { - Err(RouterError::Nlmsgerr(err)) if *err.error() == -17 => { - info!("route already exists"); - // already exists - } - Err(err) => { - return Err(err.into()); - } - _ => {} - } - } - Ok(()) - } - - #[instrument(skip_all, fields(link = %link, destination = %destination))] - pub fn delete_route(&self, link: &Link, destination: AnyIpCidr) -> Result<(), LinkError> { - let rt_msg = route_message_for(link, destination); - - let res = self.router.send::<_, _, Rtm, Rtmsg>( - Rtm::Delroute, - NlmF::REQUEST | NlmF::ACK, - NlPayload::Payload(rt_msg), - )?; - - for msg in res { - msg?; - } - Ok(()) - } + spawn(move || { + setns(ns_handle, CloneFlags::CLONE_NEWNET).map_err(LinkError::Namespace)?; + link_up(link_name) + }) + .join() + .map_err(|_| LinkError::Panic)? } /// Set a link to UP pub fn link_up(link_name: &str) -> Result<(), LinkError> { - let manager = LinkManager::new()?; - let link = manager.get_link(link_name)?; - manager.up(&link) -} - -/// Move a link into a namespace -pub fn move_link_into(link_name: &str, namespace: &Fd) -> Result<(), LinkError> { - let manager = LinkManager::new()?; - // todo, might already be in target ns - let link = manager.get_link(link_name)?; - info!(name = &link.name, "moving link into namespace"); - manager.move_link(&link, namespace) -} - -/// Move all links from the current namespace (except lo) into a namespace -pub fn move_all_links(namespace: &Fd) -> Result<(), LinkError> { - let manager = LinkManager::new()?; - for link in manager.get_links()?.flatten() { - if link.name != "lo" { - info!(name = &link.name, "moving link"); - manager.move_link(&link, namespace)? - } - } - Ok::<_, LinkError>(()) -} - -fn route_message_for(link: &Link, destination: AnyIpCidr) -> Rtmsg { - let mut info_attrs = RtBuffer::::new(); - match &destination { - AnyIpCidr::V4(addr) => { - info_attrs.push( - RtattrBuilder::default() - .rta_type(Rta::Dst) - .rta_payload(addr.first_address().octets()) - .build() - .expect("invalid rtattr"), - ); - } - AnyIpCidr::V6(addr) => { - info_attrs.push( - RtattrBuilder::default() - .rta_type(Rta::Dst) - .rta_payload(addr.first_address().octets()) - .build() - .expect("invalid rtattr"), - ); - } - _ => {} - } - info_attrs.push( - RtattrBuilder::default() - .rta_type(Rta::Oif) - .rta_payload(link.index) - .build() - .expect("invalid rtattr"), - ); - - let family = match &destination.family() { - None => RtAddrFamily::Inet, - Some(Family::Ipv4) => RtAddrFamily::Inet, - Some(Family::Ipv6) => RtAddrFamily::Inet6, - }; - - RtmsgBuilder::default() - .rtm_table(RtTable::Main) - .rtm_scope(RtScope::Universe) - .rtm_family(family) - .rtattrs(info_attrs) - .rtm_src_len(0) - .rtm_tos(0) - .rtm_protocol(Rtprot::Boot) - .rtm_type(Rtn::Unicast) - .rtm_dst_len(destination.network_length().unwrap_or_default()) + // I honestly don't really know how this code works + // It's mostly a copy from one of neli's examples and seems to do what it needs to + let (rtnl, _) = NlRouter::connect(NlFamily::Route, None, Groups::empty())?; + rtnl.enable_ext_ack(true)?; + rtnl.enable_strict_checking(true)?; + let ifinfomsg = IfinfomsgBuilder::default() + .ifi_family(RtAddrFamily::Inet) .build() - .expect("rt msg") + .unwrap(); + + let recv = rtnl.send::<_, _, Rtm, Ifinfomsg>( + Rtm::Getlink, + NlmF::DUMP | NlmF::ACK, + NlPayload::Payload(ifinfomsg), + )?; + for response in recv { + if let Some(payload) = response?.get_payload() { + let name = payload + .rtattrs() + .get_attr_handle() + .get_attr_payload_as_with_len::(Ifla::Ifname) + .map_err(|_| LinkError::Parse)?; + if name == link_name { + let up_msg = IfinfomsgBuilder::default() + .ifi_family(RtAddrFamily::Inet) + .ifi_index(*payload.ifi_index()) + .up() + .build() + .unwrap(); + rtnl.send::<_, _, Rtm, Ifinfomsg>( + Rtm::Setlink, + NlmF::ACK, + NlPayload::Payload(up_msg), + )?; + } + } + } + Ok(()) } diff --git a/src/main.rs b/src/main.rs index c906f49..e5e52a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,10 +5,10 @@ use crate::proxy::proxy; use crate::up::up; use clap::{Parser, Subcommand}; use main_error::MainResult; -use nix::errno::Errno; -use nix::sys::signal::{Signal, kill}; -use nix::unistd::Pid; use std::path::PathBuf; +use nix::errno::Errno; +use nix::sys::signal::{kill, Signal}; +use nix::unistd::Pid; use sysinfo::{ProcessRefreshKind, RefreshKind, System, UpdateKind}; use tracing::{error, info, warn}; @@ -94,12 +94,10 @@ fn reload() -> MainResult { match kill(Pid::from_raw(proc.pid().as_u32() as i32), Signal::SIGHUP) { Ok(_) => { info!("Sent reload command to daemon") - } + }, Err(Errno::EPERM) => { - error!( - "Sending signal not permitted, try are you running the command as root?" - ); - } + error!("Sending signal not permitted, try are you running the command as root?"); + }, Err(error) => { error!(%error, "Unexpected error"); } diff --git a/src/namespace/handle.rs b/src/namespace/handle.rs deleted file mode 100644 index 3e73187..0000000 --- a/src/namespace/handle.rs +++ /dev/null @@ -1,76 +0,0 @@ -use crate::namespace::NamespaceEnterError; -use nix::errno::Errno; -use nix::sched::{setns, CloneFlags}; -use std::fs::File; -use std::io::Error as IoError; -use std::os::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; -use std::path::{Path, PathBuf}; -use std::thread::scope; -use thiserror::Error; - -pub struct NamespaceHandle { - path: PathBuf, - fd: OwnedFd, -} - -impl NamespaceHandle { - /// Open the namespace handle for a path - pub fn open>(path: P) -> Result { - let path = path.as_ref(); - let file = File::open(path).map_err(|error| NamespaceHandleError::Open { - error, - path: path.into(), - })?; - Ok(NamespaceHandle { - path: path.into(), - fd: file.into(), - }) - } - - /// Open the namespace handle for the namespace the current process is in - pub fn parent() -> Result { - Self::open("/proc/self/ns/net") - } - - pub fn run_in T + Send>(&self, f: F) -> Result { - scope(|scope| { - scope - .spawn(|| { - setns(&self.fd, CloneFlags::CLONE_NEWNET)?; - Ok(f()) - }) - .join() - .expect("namespace thread panicked") - }) - .map_err(|error| NamespaceEnterError { - namespace: self.path.clone(), - error, - }) - } -} - -impl AsFd for NamespaceHandle { - fn as_fd(&self) -> BorrowedFd<'_> { - self.fd.as_fd() - } -} - -impl AsRawFd for NamespaceHandle { - fn as_raw_fd(&self) -> RawFd { - self.fd.as_raw_fd() - } -} - -impl AsRawFd for &NamespaceHandle { - fn as_raw_fd(&self) -> RawFd { - self.fd.as_raw_fd() - } -} - -#[derive(Debug, Error)] -pub enum NamespaceHandleError { - #[error("Failed to open namespace handle {}: {error:#}", path.display())] - Open { path: PathBuf, error: IoError }, - #[error("Failed to enter namespace: {0:#}")] - Enter(Errno), -} diff --git a/src/namespace/mod.rs b/src/namespace/mod.rs index 747281b..b10c635 100644 --- a/src/namespace/mod.rs +++ b/src/namespace/mod.rs @@ -1,12 +1,8 @@ -mod handle; mod raw; -mod sysctl; -use crate::config::{DeviceName, NamespaceName}; -use crate::link::{link_up, move_all_links, move_link_into, LinkError}; -pub use crate::namespace::handle::{NamespaceHandle, NamespaceHandleError}; +use crate::config::NamespaceName; +use crate::link::{link_up_ns, LinkError}; use crate::namespace::raw::{create_network_namespace, NamespaceSetupError}; -use crate::namespace::sysctl::{CtlError, NamespaceCtl}; use either::Either; use nix::errno::Errno; use nix::mount::{mount, umount2, MntFlags, MsFlags}; @@ -16,19 +12,16 @@ use std::iter::empty; use std::os::unix::fs::symlink; use std::path::{Path, PathBuf}; use thiserror::Error; -use tracing::{debug, info}; +use tracing::{debug, error, info}; pub struct NetNs { name: NamespaceName, path: PathBuf, nsd_path: PathBuf, - handle: NamespaceHandle, } impl NetNs { - pub fn existing( - include_broken: bool, - ) -> Result, NamespaceError> { + pub fn existing() -> Result, NamespaceError> { let dir = match read_dir("/var/run/netnsd") { Ok(dir) => Ok(dir), Err(error) if error.kind() == ErrorKind::NotFound => { @@ -39,14 +32,12 @@ impl NetNs { error, }), }?; - Ok(Either::Right( - dir.flatten() - .filter(move |entry| include_broken || entry.path().is_symlink()) - .flat_map(|entry| NamespaceName::try_from(entry.file_name()).ok()), - )) + Ok(Either::Right(dir.flatten().flat_map(|entry| { + NamespaceName::try_from(entry.file_name()).ok() + }))) } - /// Create a new named network namespace + /// Create a new named network namespace that will be removed when dropped pub fn new(name: NamespaceName) -> Result { let parent = Path::new("/var/run/netns"); let nsd_parent = Path::new("/var/run/netnsd"); @@ -56,34 +47,22 @@ impl NetNs { let path = parent.join(&name); let nsd_path = nsd_parent.join(&name); - remove_non_mount(&path).map_err(|error| NamespaceError::Delete { - error, - path: nsd_path.clone(), - })?; - match File::create_new(&path) { Ok(_) => {} Err(e) if e.kind() == ErrorKind::AlreadyExists => { info!(%name, "using existing network namespace"); if !nsd_path.is_symlink() { - remove_file_if_exists(&nsd_path).map_err(|error| NamespaceError::Delete { - error, - path: nsd_path.clone(), - })?; - symlink(&path, &nsd_path).map_err(|error| NamespaceError::Symlink { error, path: nsd_path.clone(), })?; } - let handle = NamespaceHandle::open(&path)?; return Ok(NetNs { name, nsd_path, path, - handle, }); } Err(e) => return Err(NamespaceError::from_create(path.clone(), e)), @@ -98,12 +77,10 @@ impl NetNs { path: nsd_path.clone(), })?; } - let handle = NamespaceHandle::open(&path)?; Result::<_, NamespaceError>::Ok(NetNs { name, path, nsd_path, - handle, }) })?; @@ -156,46 +133,24 @@ impl NetNs { } fn setup_interfaces(&self) -> Result<(), NamespaceError> { - let ctl = NamespaceCtl::read()?; - self.handle.run_in(move || { - link_up("lo").map_err(NamespaceError::from)?; - dbg!(ctl).apply()?; - Ok::<_, NamespaceError>(()) - })??; + link_up_ns(&self.path, "lo")?; Ok(()) } pub fn delete(self) -> Result<(), NamespaceError> { - let parent_namespace = NamespaceHandle::parent()?; - - self.handle.run_in(|| move_all_links(&parent_namespace))??; let name = self.path.file_name().unwrap().to_str().unwrap(); info!(name, "deleting network namespace"); - match umount2(&self.path, MntFlags::MNT_DETACH) { - Err(Errno::EINVAL) => Ok(()), // not a mountpoint, namespace doesn't exist - rest => rest, - } - .map_err(NamespaceError::UnMount)?; - remove_file_if_exists(&self.path).map_err(|error| NamespaceError::Delete { + umount2(&self.path, MntFlags::MNT_DETACH).map_err(NamespaceError::UnMount)?; + remove_file(&self.path).map_err(|error| NamespaceError::Delete { error, path: self.path, })?; - remove_file_if_exists(&self.nsd_path).map_err(|error| NamespaceError::Delete { + remove_file(&self.nsd_path).map_err(|error| NamespaceError::Delete { error, path: self.nsd_path, })?; Ok(()) } - - /// Move a device into this namespace - pub fn move_device(&self, device: &DeviceName) -> Result<(), LinkError> { - move_link_into(device.as_ref(), self.handle()) - } - - /// Get the namespace handle - pub fn handle(&self) -> &NamespaceHandle { - &self.handle - } } #[derive(Debug, Error)] @@ -216,16 +171,10 @@ pub enum NamespaceError { Mount(Errno), #[error("Failed to unmount netns handle: {0:?}")] UnMount(Errno), + #[error("Failed to setup loopback inside namespace: {0:#}")] + Link(#[from] LinkError), #[error("Failed to scan {} for namespaces: {error:#}", path.display())] Scan { path: PathBuf, error: IoError }, - #[error(transparent)] - Handle(#[from] NamespaceHandleError), - #[error(transparent)] - Enter(#[from] NamespaceEnterError), - #[error(transparent)] - Link(#[from] LinkError), - #[error("Failed to setup sysctl for namespace: {0:#}")] - Sysctl(#[from] CtlError), } impl NamespaceError { @@ -233,27 +182,3 @@ impl NamespaceError { NamespaceError::Create { path, error } } } - -#[derive(Debug, Error)] -#[error("Error while entering namespace {}: {0:#}", namespace.display())] -pub struct NamespaceEnterError { - namespace: PathBuf, - error: Errno, -} - -/// `remove_file`, but ignore "file not found" errors -fn remove_file_if_exists>(path: P) -> std::io::Result<()> { - match remove_file(path) { - Err(err) if err.kind() == ErrorKind::NotFound => Ok(()), - rest => rest, - } -} - -/// `remove_file`, but ignore errors if the file doesn't exist or is a mount point -fn remove_non_mount>(path: P) -> std::io::Result<()> { - match remove_file(path) { - Err(err) if err.kind() == ErrorKind::NotFound => Ok(()), - Err(err) if err.kind() == ErrorKind::ResourceBusy => Ok(()), - rest => rest, - } -} diff --git a/src/namespace/raw.rs b/src/namespace/raw.rs index 5cffe5d..2ece09f 100644 --- a/src/namespace/raw.rs +++ b/src/namespace/raw.rs @@ -4,6 +4,7 @@ use nix::sys::signal::Signal; use nix::sys::wait::{waitpid, WaitStatus}; use std::path::PathBuf; use thiserror::Error; +use tracing::error; #[derive(Debug, Error)] pub enum NamespaceSetupError { diff --git a/src/namespace/sysctl.rs b/src/namespace/sysctl.rs deleted file mode 100644 index d2f6b6b..0000000 --- a/src/namespace/sysctl.rs +++ /dev/null @@ -1,90 +0,0 @@ -use std::fmt::Display; -use std::fs::{File, OpenOptions}; -use std::io::Write; -use std::io::{Error as IoError, ErrorKind, Read}; -use std::path::{Path, PathBuf}; -use std::str::FromStr; -use thiserror::Error; - -#[derive(Debug)] -pub struct NamespaceCtl { - use_temp_addr: u32, - temp_valid_lft: u32, - temp_preferred_lft: u32, -} - -const PATH_TEMP_ADDR: &str = "/proc/sys/net/ipv6/conf/default/use_tempaddr"; -const PATH_VALID_LFT: &str = "/proc/sys/net/ipv6/conf/default/temp_valid_lft"; -const PATH_PREFERRED_LFT: &str = "/proc/sys/net/ipv6/conf/default/temp_prefered_lft"; - -impl NamespaceCtl { - pub fn read() -> Result { - Ok(NamespaceCtl { - use_temp_addr: read_sysctl(PATH_TEMP_ADDR)?, - temp_valid_lft: read_sysctl(PATH_VALID_LFT)?, - temp_preferred_lft: read_sysctl(PATH_PREFERRED_LFT)?, - }) - } - - pub fn apply(&self) -> Result<(), CtlError> { - write_sysctl(PATH_TEMP_ADDR, self.use_temp_addr)?; - write_sysctl(PATH_VALID_LFT, self.temp_valid_lft)?; - write_sysctl(PATH_PREFERRED_LFT, self.temp_preferred_lft)?; - Ok(()) - } -} - -fn read_sysctl>(path: P) -> Result -where - ::Err: Display, -{ - let path = path.as_ref(); - let mut buff = [0; 16]; - let mut file = File::open(path).map_err(|error| CtlError::Read { - path: path.into(), - error, - })?; - let read = file.read(&mut buff).map_err(|error| CtlError::Read { - path: path.into(), - error, - })?; - let data = &buff[0..read]; - let str = str::from_utf8(data) - .map_err(|_| CtlError::Read { - path: path.into(), - error: IoError::new(ErrorKind::InvalidData, "stream did not contain valid UTF-8"), - })? - .trim(); - T::from_str(str).map_err(|error| CtlError::Parse { - path: path.into(), - error: error.to_string(), - }) -} - -fn write_sysctl>(path: P, value: T) -> Result<(), CtlError> { - let path = path.as_ref(); - - let mut file = OpenOptions::new() - .create(false) - .truncate(true) - .write(true) - .open(path) - .map_err(|error| CtlError::Write { - path: path.into(), - error, - })?; - writeln!(&mut file, "{value}").map_err(|error| CtlError::Write { - path: path.into(), - error, - }) -} - -#[derive(Debug, Error)] -pub enum CtlError { - #[error("failed to read sysctl option {}: {error:#}", path.display())] - Read { path: PathBuf, error: IoError }, - #[error("failed to read sysctl option {}: {error}", path.display())] - Parse { path: PathBuf, error: String }, - #[error("failed to write sysctl option {}: {error:#}", path.display())] - Write { path: PathBuf, error: IoError }, -} diff --git a/src/proxy/mod.rs b/src/proxy/mod.rs index ef353fa..b7de7ba 100644 --- a/src/proxy/mod.rs +++ b/src/proxy/mod.rs @@ -3,12 +3,7 @@ mod tcp; use crate::config::{ForwardConfig, ForwardSource, ForwardTarget, NamespaceName}; use crate::proxy::tcp::Proxy; use futures::future::AbortHandle; -use landlock::{ - ABI, Access, AccessFs, AccessNet, NetPort, Ruleset, RulesetAttr, RulesetCreatedAttr, - RulesetError, RulesetStatus, -}; use main_error::MainResult; -use nix::errno::Errno; use nix::sched::{CloneFlags, setns}; use nix::sys::signal::{SIGINT, kill}; use nix::unistd::{Gid, Pid, Uid, setgid, setuid}; @@ -18,6 +13,8 @@ use std::net::SocketAddr; use std::path::{Path, PathBuf}; use std::process::{Child, Command}; use std::thread::spawn; +use landlock::{Access, AccessFs, AccessNet, NetPort, Ruleset, RulesetAttr, RulesetCreatedAttr, RulesetError, RulesetStatus, ABI}; +use nix::errno::Errno; use thiserror::Error; use tokio::runtime::Builder; use tokio::signal::ctrl_c; @@ -218,4 +215,4 @@ fn landlock(port: u16) -> Result<(), RulesetError> { RulesetStatus::NotEnforced => error!("Not sandboxed! Please update your kernel."), } Ok(()) -} +} \ No newline at end of file diff --git a/src/proxy/tcp.rs b/src/proxy/tcp.rs index 159be8e..0f163c1 100644 --- a/src/proxy/tcp.rs +++ b/src/proxy/tcp.rs @@ -1,6 +1,6 @@ /// Loosely based on https://github.com/fooker/netns-proxy/blob/main/src/tcp.rs -use crate::config::{ForwardSource, ForwardTarget}; -use crate::proxy::ProxyError; +use crate::config::{ForwardTarget, ForwardSource}; +use crate::proxy::{ProxyError}; use futures::TryStreamExt; use futures::stream::{AbortRegistration, Abortable}; use std::fs::{remove_file, set_permissions}; @@ -56,18 +56,28 @@ impl Proxy { })?; debug!("Created TCP socket"); - Ok(Self { socket }) + Ok(Self { + socket, + }) } pub async fn run(self, target: ForwardTarget, abort: AbortRegistration) { match self.socket { - ProxyListener::Tcp(socket) => run_tcp(socket, target.addr, abort).await, - ProxyListener::Unix(socket) => run_unix(socket, target.addr, abort).await, + ProxyListener::Tcp(socket) => { + run_tcp(socket, target.addr, abort).await + } + ProxyListener::Unix(socket) => { + run_unix(socket, target.addr, abort).await + } } } } -async fn run_tcp(socket: TcpListener, target: SocketAddr, abort: AbortRegistration) { +async fn run_tcp( + socket: TcpListener, + target: SocketAddr, + abort: AbortRegistration, +) { let accepts = TcpListenerStream::new(socket).map_err(|error| ProxyError::Accept { error }); let mut accepts = pin!(Abortable::new(accepts, abort)); while let Some(client) = accepts.next().await { @@ -84,7 +94,11 @@ async fn run_tcp(socket: TcpListener, target: SocketAddr, abort: AbortRegistrati } } -async fn run_unix(socket: UnixListener, target: SocketAddr, abort: AbortRegistration) { +async fn run_unix( + socket: UnixListener, + target: SocketAddr, + abort: AbortRegistration, +) { let accepts = UnixListenerStream::new(socket).map_err(|error| ProxyError::Accept { error }); let mut accepts = pin!(Abortable::new(accepts, abort)); while let Some(client) = accepts.next().await { diff --git a/src/up.rs b/src/up.rs index 4e33d78..a7c65b4 100644 --- a/src/up.rs +++ b/src/up.rs @@ -1,11 +1,9 @@ use crate::config::{Config, NamespaceName}; -use crate::link::{LinkError, LinkManager}; use crate::namespace::NetNs; use main_error::MainResult; -use tracing::error; pub fn up(config: Config) -> MainResult { - let mut namespaces = NetNs::existing(false)? + let mut namespaces = NetNs::existing()? .map(NetNs::new) .collect::, _>>()?; @@ -17,21 +15,7 @@ pub fn up(config: Config) -> MainResult { for new in config.namespaces { if !has_namespace(&namespaces, &new.name) { - namespaces.push(NetNs::new(new.name.clone())?); - } - let namespace = get_namespace(&namespaces, &new.name).expect("namespace is just created"); - for device in new.devices { - if let Err(error) = namespace.move_device(&device) { - error!(%error, "failed to move device into namespace"); - } - } - for route in new.routes { - namespace.handle().run_in(|| { - let manager = LinkManager::new()?; - let link = manager.get_link(&route.device)?; - manager.add_route(&link, route.destination)?; - Ok::<_, LinkError>(()) - })??; + namespaces.push(NetNs::new(new.name)?); } } @@ -41,7 +25,3 @@ pub fn up(config: Config) -> MainResult { fn has_namespace(namespaces: &[NetNs], name: &NamespaceName) -> bool { namespaces.iter().any(|existing| existing.name() == name) } - -fn get_namespace<'a>(namespaces: &'a [NetNs], name: &NamespaceName) -> Option<&'a NetNs> { - namespaces.iter().find(|existing| existing.name() == name) -}