diff --git a/Cargo.lock b/Cargo.lock index ee733098ef..bbeab0af36 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,11 +2,35 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "arg_parser" version = "0.1.0" source = "git+https://gitlab.redox-os.org/redox-os/arg-parser.git#1c434b55f3e1a0375ebcca85b3e88db7378e82fa" +[[package]] +name = "atomic-polyfill" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ff7eb3f316534d83a8a2c3d1674ace8a5a71198eba31e2e2b597833f699b28" +dependencies = [ + "critical-section", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -19,6 +43,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + [[package]] name = "byteorder" version = "0.5.3" @@ -31,6 +61,15 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + [[package]] name = "cfg-if" version = "0.1.10" @@ -43,6 +82,33 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ed24df0632f708f5f6d8082675bef2596f7084dee3dd55f632290bf35bfe0f" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "time", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + [[package]] name = "crossbeam-channel" version = "0.5.8" @@ -62,6 +128,38 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "defmt" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2d011b2fee29fb7d659b83c43fce9a2cb4df453e16d441a51448e448f3f98" +dependencies = [ + "bitflags", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54f0216f6c5acb5ae1a47050a6645024e6edafc2ee32d421955eccfef12ef92e" +dependencies = [ + "defmt-parser", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.29", +] + +[[package]] +name = "defmt-parser" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "269924c02afd7f94bc4cecbfa5c379f6ffcf9766b3408fe63d22c728654eccd0" +dependencies = [ + "thiserror", +] + [[package]] name = "dns-parser" version = "0.7.1" @@ -73,25 +171,49 @@ dependencies = [ ] [[package]] -name = "extra" -version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/libextra.git#cf213969493db8667052a591e32a1e26d43c4234" - -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" +name = "hash32" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" dependencies = [ - "bitflags", - "fuchsia-zircon-sys", + "byteorder 1.4.3", ] [[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" +name = "heapless" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "spin", + "stable_deref_trait", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] [[package]] name = "idna" @@ -105,30 +227,14 @@ dependencies = [ ] [[package]] -name = "iovec" -version = "0.1.4" +name = "js-sys" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ - "libc", + "wasm-bindgen", ] -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - -[[package]] -name = "lazycell" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f08839bc70ef4a3fe1d566d5350f519c5912ea86be0df1740a7d247c7fc0ef" - [[package]] name = "libc" version = "0.2.147" @@ -136,12 +242,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] -name = "log" -version = "0.3.9" +name = "lock_api" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ - "log 0.4.20", + "autocfg", + "scopeguard", ] [[package]] @@ -152,9 +259,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "managed" -version = "0.7.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577" +checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" [[package]] name = "matches" @@ -162,36 +269,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" -[[package]] -name = "mio" -version = "0.6.14" -source = "git+https://gitlab.redox-os.org/redox-os/mio.git?branch=redox-unix#c9a70849ced97387e2607c9c466d23b130ec8901" -dependencies = [ - "fuchsia-zircon", - "fuchsia-zircon-sys", - "iovec", - "kernel32-sys", - "lazycell", - "libc", - "log 0.4.20", - "miow", - "net2", - "slab", - "winapi 0.2.8", -] - -[[package]] -name = "miow" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" -dependencies = [ - "kernel32-sys", - "net2", - "winapi 0.2.8", - "ws2_32-sys", -] - [[package]] name = "net2" version = "0.2.37" @@ -199,24 +276,22 @@ source = "git+https://gitlab.redox-os.org/redox-os/net2-rs.git?branch=master#db0 dependencies = [ "cfg-if 0.1.10", "libc", - "winapi 0.3.9", + "winapi", ] [[package]] name = "netutils" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/netutils.git?branch=redox-unix#105ed1ea43413a91152b289fbe76e7efc996e933" +source = "git+https://gitlab.redox-os.org/redox-os/netutils.git#fc11b9bb3c1f5eefccb93ae670e4ec8b3d6fad4f" dependencies = [ "arg_parser", - "extra", "libc", - "mio", "net2", "ntpclient", "pbr", "redox-daemon", "redox_event", - "redox_syscall 0.2.16", + "redox_syscall 0.3.5", "redox_termios", "termion", "url", @@ -231,12 +306,27 @@ dependencies = [ "time", ] +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + [[package]] name = "numtoa" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + [[package]] name = "pbr" version = "1.1.1" @@ -245,7 +335,7 @@ checksum = "ed5827dfa0d69b6c92493d6c38e633bbaa5937c153d0d7c28bf12313f8c6d514" dependencies = [ "crossbeam-channel", "libc", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -254,12 +344,54 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + [[package]] name = "quick-error" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + [[package]] name = "redox-daemon" version = "0.1.1" @@ -270,6 +402,18 @@ dependencies = [ "redox_syscall 0.3.5", ] +[[package]] +name = "redox-log" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf6d33a003a5c0b94ec11f10c7c797303f236592964ddb1bfb93e1b438a1557" +dependencies = [ + "chrono", + "log", + "smallvec", + "termion", +] + [[package]] name = "redox_event" version = "0.1.0" @@ -284,9 +428,10 @@ version = "0.1.0" dependencies = [ "byteorder 1.4.3", "dns-parser", - "log 0.3.9", + "log", "netutils", "redox-daemon", + "redox-log", "redox_event", "redox_syscall 0.3.5", "smoltcp", @@ -320,23 +465,83 @@ dependencies = [ ] [[package]] -name = "slab" -version = "0.4.9" +name = "rustc_version" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "autocfg", + "semver", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" + +[[package]] +name = "smallvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" + [[package]] name = "smoltcp" -version = "0.5.0" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d2e3a36ac8fea7b94e666dfa3871063d6e0a5c9d5d4fec9a1a6b7b6760f0229" dependencies = [ "bitflags", "byteorder 1.4.3", + "cfg-if 1.0.0", + "defmt", + "heapless", + "log", "managed", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "termion" version = "1.5.6" @@ -349,6 +554,26 @@ dependencies = [ "redox_termios", ] +[[package]] +name = "thiserror" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", +] + [[package]] name = "time" version = "0.1.45" @@ -357,7 +582,7 @@ checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" dependencies = [ "libc", "wasi", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -381,6 +606,12 @@ version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + [[package]] name = "unicode-normalization" version = "0.1.22" @@ -401,6 +632,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" @@ -408,10 +645,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] -name = "winapi" -version = "0.2.8" +name = "wasm-bindgen" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.29", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "winapi" @@ -423,12 +708,6 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" - [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -442,11 +721,72 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "ws2_32-sys" -version = "0.2.1" +name = "windows" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "winapi 0.2.8", - "winapi-build", + "windows-targets", ] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[patch.unused]] +name = "mio" +version = "0.6.14" +source = "git+https://gitlab.redox-os.org/redox-os/mio.git?branch=redox-unix#c9a70849ced97387e2607c9c466d23b130ec8901" diff --git a/Cargo.toml b/Cargo.toml index 2b69b83462..4f4ef1b4b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,22 +20,25 @@ netutils = { git = "https://gitlab.redox-os.org/redox-os/netutils.git" } redox_event = { git = "https://gitlab.redox-os.org/redox-os/event.git" } redox-daemon = "0.1.1" redox_syscall = "0.3" +redox-log = "0.1" byteorder = { version = "1.0", default-features = false } dns-parser = "0.7.1" [dependencies.log] -version = "0.3" +version = "0.4" default-features = false features = ["release_max_level_warn"] [dependencies.smoltcp] -path = "smoltcp" +version = "0.10.0" default-features = false features = [ "std", - "ethernet", + "medium-ethernet", "medium-ip", "proto-ipv4", - "socket-raw", "socket-icmp", "socket-udp", "socket-tcp" + "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", + "iface-max-addr-count-8", + "log" ] #For debugging: "log", "verbose" diff --git a/src/dnsd/main.rs b/src/dnsd/main.rs index 0fb01f491b..3ba52906f8 100644 --- a/src/dnsd/main.rs +++ b/src/dnsd/main.rs @@ -81,7 +81,7 @@ fn run(daemon: redox_daemon::Daemon) -> Result<()> { fn main() { redox_daemon::Daemon::new(move |daemon| { - logger::init_logger(); + logger::init_logger("dnsd"); if let Err(err) = run(daemon) { error!("dnsd: {}", err); process::exit(1); diff --git a/src/lib/logger.rs b/src/lib/logger.rs index 90ed1f8306..e2952e98db 100644 --- a/src/lib/logger.rs +++ b/src/lib/logger.rs @@ -1,22 +1,16 @@ -use log::{set_logger_raw, Log, LogLevelFilter, LogMetadata, LogRecord}; +use redox_log::{OutputBuilder, RedoxLogger}; -struct Logger; - -impl Log for Logger { - fn enabled(&self, _: &LogMetadata) -> bool { - true - } - - fn log(&self, record: &LogRecord) { - println!("{}: {}", record.target(), record.args()); - } -} - -pub fn init_logger() { - unsafe { - set_logger_raw(|max_log_level| { - max_log_level.set(LogLevelFilter::Trace); - &Logger - }).expect("Can't initialize logger"); +pub fn init_logger(process_name: &str) { + if let Err(_) = RedoxLogger::new() + .with_output( + OutputBuilder::stdout() + .with_ansi_escape_codes() + .flush_on_newline(true) + .with_filter(log::LevelFilter::Trace) + .build(), + ) + .with_process_name(process_name.into()) + .enable() { + eprintln!("{process_name}: Failed to init logger") } } diff --git a/src/smolnetd/device.rs b/src/smolnetd/device.rs deleted file mode 100644 index 63919c2be4..0000000000 --- a/src/smolnetd/device.rs +++ /dev/null @@ -1,123 +0,0 @@ -use smoltcp; -use std::cell::RefCell; -use std::collections::VecDeque; -use std::fs::File; -use std::io::Write; -use std::rc::Rc; - -use smoltcp::time::Instant; -use smoltcp::wire::EthernetAddress; -use crate::buffer_pool::{Buffer, BufferPool}; - -struct NetworkDeviceData { - network_file: Rc>, - input_queue: Rc>>, - local_hwaddr: smoltcp::wire::EthernetAddress, - buffer_pool: Rc>, -} - -pub struct NetworkDevice { - data: Rc>, -} - -impl NetworkDevice { - pub const MTU: usize = 1520; - - pub fn new( - network_file: Rc>, - input_queue: Rc>>, - local_hwaddr: smoltcp::wire::EthernetAddress, - buffer_pool: Rc>, - ) -> NetworkDevice { - NetworkDevice { - data: Rc::new(RefCell::new(NetworkDeviceData { - network_file, - input_queue, - local_hwaddr, - buffer_pool, - })), - } - } -} - -pub struct RxToken { - buffer: Buffer, -} - -impl smoltcp::phy::RxToken for RxToken { - fn consume(mut self, _timestamp: Instant, f: F) -> smoltcp::Result - where - F: FnOnce(&mut [u8]) -> smoltcp::Result, - { - f(&mut self.buffer) - } -} - -pub struct TxToken { - data: Rc>, -} - -impl smoltcp::phy::TxToken for TxToken { - fn consume(self, _timestamp: Instant, len: usize, f: F) -> smoltcp::Result - where - F: FnOnce(&mut [u8]) -> smoltcp::Result, - { - let data = self.data.borrow_mut(); - let mut buffer = data.buffer_pool.borrow_mut().get_buffer(); - buffer.resize(len); - let res = f(&mut buffer)?; - - let mut loopback = false; - if let Ok(mut frame) = smoltcp::wire::EthernetFrame::new_checked(&mut buffer) { - if frame.dst_addr() == EthernetAddress::default() { - frame.set_dst_addr(data.local_hwaddr); - loopback = true; - } - } - - if loopback { - data.input_queue.borrow_mut().push_back(buffer.move_out()); - } else { - data.network_file - .borrow_mut() - .write(&buffer) - .map_err(|_| smoltcp::Error::Dropped)?; - } - - Ok(res) - } -} - -impl<'a> smoltcp::phy::Device<'a> for NetworkDevice { - type RxToken = RxToken; - type TxToken = TxToken; - - fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities { - let mut limits = smoltcp::phy::DeviceCapabilities::default(); - limits.max_transmission_unit = Self::MTU; - limits.max_burst_size = Some(20); - limits - } - - fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { - let data = self.data.borrow_mut(); - let buffer = data.input_queue.borrow_mut().pop_front(); - - if let Some(buffer) = buffer { - Some(( - RxToken { buffer }, - TxToken { - data: Rc::clone(&self.data), - }, - )) - } else { - None - } - } - - fn transmit(&'a mut self) -> Option { - Some(TxToken { - data: Rc::clone(&self.data), - }) - } -} diff --git a/src/smolnetd/link/ethernet.rs b/src/smolnetd/link/ethernet.rs new file mode 100644 index 0000000000..111569aee9 --- /dev/null +++ b/src/smolnetd/link/ethernet.rs @@ -0,0 +1,401 @@ +use std::collections::btree_map::Entry; +use std::collections::BTreeMap; +use std::fs::File; +use std::io::{ErrorKind, Read, Write}; +use std::rc::Rc; + +use smoltcp::storage::PacketMetadata; +use smoltcp::time::{Duration, Instant}; +use smoltcp::wire::{ + ArpOperation, ArpPacket, ArpRepr, EthernetAddress, EthernetFrame, EthernetProtocol, + EthernetRepr, IpAddress, IpCidr, Ipv4Address, Ipv4Cidr, +}; + +use super::LinkDevice; + +struct Neighbor { + hardware_address: EthernetAddress, + expires_at: Instant, +} + +#[derive(Debug, Default)] +enum ArpState { + #[default] + Discovered, + Discovering { + target: Ipv4Address, + tries: u32, + silent_until: Instant, + }, +} + +type PacketBuffer = smoltcp::storage::PacketBuffer<'static, IpAddress>; + +const EMPTY_MAC: EthernetAddress = EthernetAddress([0; 6]); + +pub struct EthernetLink { + name: Rc, + neighbor_cache: BTreeMap, + arp_state: ArpState, + waiting_packets: PacketBuffer, + input_buffer: Vec, + output_buffer: Vec, + network_file: File, + hardware_address: Option, + ip_address: Option, +} + +impl EthernetLink { + // TODO: Review these constants + const MAX_WAITING_PACKET_COUNT: usize = 10; + const MTU: usize = 1500; + const WAITING_PACKET_BUFFER_SIZE: usize = Self::MTU * Self::MAX_WAITING_PACKET_COUNT; + + const NEIGHBOR_LIVE_TIME: Duration = Duration::from_secs(60); + const ARP_SILENCE_TIME: Duration = Duration::from_secs(1); + + pub fn new(name: &str, network_file: File) -> Self { + let waiting_packets = PacketBuffer::new( + vec![PacketMetadata::EMPTY; Self::MAX_WAITING_PACKET_COUNT], + vec![0u8; Self::WAITING_PACKET_BUFFER_SIZE], + ); + + Self { + name: name.into(), + network_file, + waiting_packets, + hardware_address: None, + ip_address: None, + input_buffer: vec![0u8; Self::MTU], + output_buffer: Vec::with_capacity(Self::MTU), + arp_state: Default::default(), + neighbor_cache: Default::default(), + } + } + + fn send_to(&mut self, dst: EthernetAddress, size: usize, f: F, proto: EthernetProtocol) + where + F: FnOnce(&mut [u8]), + { + let Some(hardware_address) = self.hardware_address else { + return; + }; + + let repr = EthernetRepr { + src_addr: hardware_address, + dst_addr: dst, + ethertype: proto, + }; + + self.output_buffer.clear(); + self.output_buffer.resize(repr.buffer_len() + size, 0); + let mut frame = EthernetFrame::new_unchecked(&mut self.output_buffer); + repr.emit(&mut frame); + + f(frame.payload_mut()); + + if let Err(_) = self.network_file.write_all(&self.output_buffer) { + error!( + "Dropped outboud packet on {} (failed to write to network file)", + self.name + ) + } + } + + fn process_arp(&mut self, packet: &[u8], now: Instant) { + let Some(hardware_address) = self.hardware_address else { + return; + }; + + let Some(ip_addr) = self.ip_address else { + return; + }; + + let Ok(repr) = ArpPacket::new_checked(packet).and_then(|packet| ArpRepr::parse(&packet)) else { + debug!("Dropped incomming arp packet on {} (Malformed)", self.name); + return; + }; + + match repr { + ArpRepr::EthernetIpv4 { + operation, + source_hardware_addr, + source_protocol_addr, + target_hardware_addr, + target_protocol_addr, + } => { + if hardware_address != target_hardware_addr { + // Only process packet that are for us + return; + } + + if let ArpOperation::Unknown(_) = operation { + return; + } + + if !source_hardware_addr.is_unicast() || !source_protocol_addr.is_unicast() { + return; + } + + if !ip_addr.contains_addr(&target_protocol_addr) { + return; + } + + self.neighbor_cache.insert( + IpAddress::Ipv4(source_protocol_addr), + Neighbor { + hardware_address: source_hardware_addr, + expires_at: now + Self::NEIGHBOR_LIVE_TIME, + }, + ); + + if let ArpOperation::Request = operation { + let response = ArpRepr::EthernetIpv4 { + operation: ArpOperation::Reply, + source_hardware_addr: hardware_address, + source_protocol_addr: ip_addr.address(), + target_hardware_addr: source_hardware_addr, + target_protocol_addr: source_protocol_addr, + }; + + self.send_to( + source_hardware_addr, + response.buffer_len(), + |buf| response.emit(&mut ArpPacket::new_unchecked(buf)), + EthernetProtocol::Arp, + ); + } + self.check_waiting_packets(source_protocol_addr, source_hardware_addr, now); + } + _ => {} + } + } + + fn check_waiting_packets(&mut self, ip: Ipv4Address, mac: EthernetAddress, now: Instant) { + let mut waiting_packets = + std::mem::replace(&mut self.waiting_packets, PacketBuffer::new(vec![], vec![])); + loop { + match waiting_packets.peek() { + Ok((IpAddress::Ipv4(dst), _)) if dst == &ip => {} + Ok((IpAddress::Ipv4(dst), _)) => { + self.arp_state = ArpState::Discovering { + target: *dst, + tries: 0, + silent_until: Instant::ZERO, + }; + self.send_arp(now); + break; + } + Err(_) => { + self.arp_state = ArpState::Discovered; + break; + } + } + + let (_, packet) = waiting_packets.dequeue().unwrap(); + self.send_to( + mac, + packet.len(), + |buf| buf.copy_from_slice(packet), + EthernetProtocol::Ipv4, + ); + } + + self.waiting_packets = waiting_packets; + } + + fn drop_waiting_packets(&mut self, ip: Ipv4Address, now: Instant) { + loop { + match self.waiting_packets.peek() { + Ok((IpAddress::Ipv4(dst), _)) if dst == &ip => {} + Ok((IpAddress::Ipv4(dst), _)) => { + self.arp_state = ArpState::Discovering { + target: *dst, + tries: 0, + silent_until: Instant::ZERO, + }; + + self.send_arp(now); + + return; + } + Err(_) => { + self.arp_state = ArpState::Discovered; + return; + } + } + + let _ = self.waiting_packets.dequeue(); + debug!( + "Dropped packet on {} because neighbor was not found", + self.name + ) + } + } + + fn handle_missing_neighbor(&mut self, next_hop: IpAddress, packet: &[u8], now: Instant) { + let Ok(buf) = self.waiting_packets.enqueue(packet.len(), next_hop) else { + warn!("Dropped packet on {} because waiting queue was full", self.name); + return; + }; + buf.copy_from_slice(packet); + + let IpAddress::Ipv4(next_hop) = next_hop; + if let ArpState::Discovered = self.arp_state { + self.arp_state = ArpState::Discovering { + target: next_hop, + tries: 0, + silent_until: Instant::ZERO, + }; + + self.send_arp(now) + } + } + + fn send_arp(&mut self, now: Instant) { + let Some(hardware_address) = self.hardware_address else { + return; + }; + + let Some(ip_address) = self.ip_address else { + return; + }; + + match self.arp_state { + ArpState::Discovered => {} + ArpState::Discovering { silent_until, .. } if silent_until > now => {} + ArpState::Discovering { target, tries, .. } if tries >= 3 => { + self.drop_waiting_packets(target, now) + } + ArpState::Discovering { + target, + ref mut tries, + ref mut silent_until, + } => { + let arp_repr = ArpRepr::EthernetIpv4 { + operation: ArpOperation::Request, + source_hardware_addr: hardware_address, + source_protocol_addr: ip_address.address(), + target_hardware_addr: EthernetAddress::BROADCAST, + target_protocol_addr: target, + }; + + *tries += 1; + *silent_until = now + Self::ARP_SILENCE_TIME; + + self.send_to( + EthernetAddress::BROADCAST, + arp_repr.buffer_len(), + |buf| arp_repr.emit(&mut ArpPacket::new_unchecked(buf)), + EthernetProtocol::Arp, + ); + } + } + } +} + +impl LinkDevice for EthernetLink { + fn send(&mut self, next_hop: IpAddress, packet: &[u8], now: Instant) { + let local_broadcast = match self.ip_address.and_then(|cidr| cidr.broadcast()) { + Some(addr) => IpAddress::Ipv4(addr) == next_hop, + None => false, + }; + + if local_broadcast || next_hop.is_broadcast() { + self.send_to( + EthernetAddress::BROADCAST, + packet.len(), + |buf| buf.copy_from_slice(packet), + EthernetProtocol::Ipv4, + ); + return; + } + + match self.neighbor_cache.entry(next_hop) { + Entry::Vacant(_) => self.handle_missing_neighbor(next_hop, packet, now), + Entry::Occupied(e) => { + if e.get().expires_at < now { + e.remove(); + self.handle_missing_neighbor(next_hop, packet, now) + } else { + let mac = e.get().hardware_address; + self.send_to( + mac, + packet.len(), + |buf| buf.copy_from_slice(packet), + EthernetProtocol::Ipv4, + ) + } + } + } + } + + fn recv(&mut self, now: Instant) -> Option<&[u8]> { + let Some(hardware_address) = self.hardware_address else { + return None; + }; + + let mut input_buffer = std::mem::replace(&mut self.input_buffer, Vec::new()); + loop { + if let Err(e) = self.network_file.read(&mut input_buffer) { + if e.kind() != ErrorKind::WouldBlock { + error!("Failed to read ethernet device on link {}", self.name); + } else { + // No packet to read but we check if we have arp to send + self.send_arp(now); + } + self.input_buffer = input_buffer; + return None; + } + let packet = EthernetFrame::new_unchecked(&input_buffer[..]); + let Ok(repr) = EthernetRepr::parse(&packet) else { + debug!("Dropped incomming frame on {} (Malformed)", self.name); + continue; + }; + + // We let EMPTY_MAC pass because somehow this is the mac used when net=redir is used + if !repr.dst_addr.is_broadcast() + && repr.dst_addr != EMPTY_MAC + && repr.dst_addr != hardware_address + { + // Drop packets which are not for us + continue; + } + + match repr.ethertype { + EthernetProtocol::Ipv4 => { + self.input_buffer = input_buffer; + return Some(EthernetFrame::new_unchecked(&self.input_buffer[..]).payload()); + } + EthernetProtocol::Arp => self.process_arp(packet.payload(), now), + _ => continue, + } + } + } + + fn name(&self) -> &Rc { + &self.name + } + + fn can_recv(&self) -> bool { + // We don't buffer any packets so we can't receive immediatly + false + } + + fn mac_address(&self) -> Option { + self.hardware_address + } + + fn set_mac_address(&mut self, addr: EthernetAddress) { + self.hardware_address = Some(addr) + } + + fn ip_address(&self) -> Option { + Some(IpCidr::Ipv4(self.ip_address?)) + } + + fn set_ip_address(&mut self, addr: IpCidr) { + let IpCidr::Ipv4(addr) = addr; + self.ip_address = Some(addr); + } +} diff --git a/src/smolnetd/link/loopback.rs b/src/smolnetd/link/loopback.rs new file mode 100644 index 0000000000..e0cc8c0aff --- /dev/null +++ b/src/smolnetd/link/loopback.rs @@ -0,0 +1,66 @@ +use std::rc::Rc; + +use smoltcp::storage::PacketMetadata; +use smoltcp::time::Instant; + +use crate::scheme::Smolnetd; + +use super::LinkDevice; + +pub type PacketBuffer = smoltcp::storage::PacketBuffer<'static, ()>; + +pub struct LoopbackDevice { + name: Rc, + buffer: PacketBuffer, +} + +impl Default for LoopbackDevice { + fn default() -> Self { + let buffer = PacketBuffer::new( + vec![PacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], + vec![0u8; 1500 * Smolnetd::SOCKET_BUFFER_SIZE], + ); + LoopbackDevice { + name: "loopback".into(), + buffer, + } + } +} + +impl LinkDevice for LoopbackDevice { + fn send(&mut self, _next_hop: smoltcp::wire::IpAddress, packet: &[u8], _now: Instant) { + match self.buffer.enqueue(packet.len(), ()) { + Err(_) => warn!("loopback dropped packet because buffer was full"), + Ok(buf) => buf.copy_from_slice(packet), + } + } + + fn recv(&mut self, _now: Instant) -> Option<&[u8]> { + self.buffer.dequeue().ok().map(|((), buf)| &*buf) + } + + fn name(&self) -> &std::rc::Rc { + &self.name + } + + fn can_recv(&self) -> bool { + !self.buffer.is_empty() + } + + fn mac_address(&self) -> Option { + None + } + + fn set_mac_address(&mut self, _addr: smoltcp::wire::EthernetAddress) { + } + + fn ip_address(&self) -> Option { + Some("127.0.0.1/8".parse().unwrap()) + } + + fn set_ip_address(&mut self, _addr: smoltcp::wire::IpCidr) { + todo!() + } + + +} diff --git a/src/smolnetd/link/mod.rs b/src/smolnetd/link/mod.rs new file mode 100644 index 0000000000..bf135a36a3 --- /dev/null +++ b/src/smolnetd/link/mod.rs @@ -0,0 +1,66 @@ +pub mod loopback; +pub mod ethernet; + +use std::rc::Rc; + +use smoltcp::time::Instant; +use smoltcp::wire::{IpAddress, EthernetAddress, IpCidr}; + +/// Represent a link layer device (eth0, loopback...) +pub trait LinkDevice { + + /// Send the given packet to the machine with the `next_hop` ip address + /// This method cannot fail so it's the implementor responsability + /// to buffer packets which can't be sent immediatly or decide to + /// drop them if necessary + fn send(&mut self, next_hop: IpAddress, packet: &[u8], now: Instant); + + /// Returns None if nothing is received. + /// Returns an Ip packet otherwise + fn recv(&mut self, now: Instant) -> Option<&[u8]>; + + /// Returns the LinkDevice display name used to refer to it and for lookups + fn name(&self) -> &Rc; + + /// Returns wether this device have packets pending + fn can_recv(&self) -> bool; + + fn mac_address(&self) -> Option; + fn set_mac_address(&mut self, addr: EthernetAddress); + + fn ip_address(&self) -> Option; + fn set_ip_address(&mut self, addr: IpCidr); +} + +#[derive(Default)] +pub struct DeviceList { + inner: Vec>, +} + +impl DeviceList { + pub fn push(&mut self, dev: T) { + self.inner.push(Box::new(dev)) + } + + pub fn get(&self, device_name: &str) -> Option<&dyn LinkDevice> { + self.inner + .iter() + .find(|dev| dev.name().as_ref() == device_name) + .map(|device| device.as_ref()) + } + + pub fn get_mut(&mut self, device_name: &str) -> Option<&mut (dyn LinkDevice + 'static)> { + self.inner + .iter_mut() + .find(|dev| dev.name().as_ref() == device_name) + .map(|device| device.as_mut()) + } + + pub fn iter(&self) -> impl Iterator { + self.inner.iter().map(|b| b.as_ref()) + } + + pub fn iter_mut(&mut self) -> impl Iterator { + self.inner.iter_mut().map(|b| b.as_mut()) + } +} diff --git a/src/smolnetd/main.rs b/src/smolnetd/main.rs index b359245ec3..3f1ac9c2d3 100644 --- a/src/smolnetd/main.rs +++ b/src/smolnetd/main.rs @@ -3,11 +3,11 @@ extern crate event; #[macro_use] extern crate log; +extern crate byteorder; extern crate netutils; extern crate redox_netstack; extern crate smoltcp; extern crate syscall; -extern crate byteorder; use std::cell::RefCell; use std::fs::File; @@ -15,14 +15,15 @@ use std::os::unix::io::{FromRawFd, RawFd}; use std::process; use std::rc::Rc; +use event::EventQueue; use redox_netstack::error::{Error, Result}; use redox_netstack::logger; -use event::EventQueue; use scheme::Smolnetd; mod buffer_pool; -mod device; +mod link; mod port_set; +mod router; mod scheme; fn run(daemon: redox_daemon::Daemon) -> Result<()> { @@ -143,7 +144,7 @@ fn run(daemon: redox_daemon::Daemon) -> Result<()> { event_queue.trigger_all(event::Event { fd: 0, - flags: EventFlags::empty() + flags: EventFlags::empty(), })?; event_queue.run() @@ -151,12 +152,13 @@ fn run(daemon: redox_daemon::Daemon) -> Result<()> { fn main() { redox_daemon::Daemon::new(move |daemon| { - logger::init_logger(); + logger::init_logger("smolnetd"); if let Err(err) = run(daemon) { error!("smoltcpd: {}", err); process::exit(1); } process::exit(0); - }).expect("smoltcp: failed to daemonize"); + }) + .expect("smoltcp: failed to daemonize"); } diff --git a/src/smolnetd/router/mod.rs b/src/smolnetd/router/mod.rs new file mode 100644 index 0000000000..d639edbe4e --- /dev/null +++ b/src/smolnetd/router/mod.rs @@ -0,0 +1,191 @@ +use std::cell::RefCell; +use std::rc::Rc; + +use smoltcp::phy::{Device, DeviceCapabilities, Medium}; +use smoltcp::storage::PacketMetadata; +use smoltcp::time::Instant; +use smoltcp::wire::IpAddress; + +use self::route_table::RouteTable; +use crate::link::DeviceList; +use crate::scheme::Smolnetd; + +pub mod route_table; + +pub type PacketBuffer = smoltcp::storage::PacketBuffer<'static, ()>; + +pub struct Router { + rx_buffer: PacketBuffer, + tx_buffer: PacketBuffer, + devices: Rc>, + route_table: Rc>, +} + +impl Router { + pub fn new(devices: Rc>, route_table: Rc>) -> Self { + let rx_buffer = PacketBuffer::new( + vec![PacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], + vec![0u8; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE], + ); + let tx_buffer = PacketBuffer::new( + vec![PacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], + vec![0u8; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE], + ); + Self { + rx_buffer, + tx_buffer, + devices, + route_table, + } + } + + pub const MTU: usize = 1486; + + pub fn can_recv(&self) -> bool { + let mut can_recv = false; + for dev in self.devices.borrow().iter() { + can_recv |= dev.can_recv(); + } + can_recv + } + + pub fn poll(&mut self, now: Instant) { + for dev in self.devices.borrow_mut().iter_mut() { + if self.rx_buffer.is_full() { + break; + } + + loop { + if self.rx_buffer.is_full() { + break; + } + + let Some(buf) = dev.recv(now) else { + break; + }; + + self.rx_buffer + .enqueue(buf.len(), ()) + .expect("We checked if it was full") + .copy_from_slice(buf); + } + } + } + + pub fn dispatch(&mut self, now: Instant) { + while let Ok(((), packet)) = self.tx_buffer.dequeue() { + if let Ok(mut packet) = smoltcp::wire::Ipv4Packet::new_checked(packet) { + let dst_addr = IpAddress::Ipv4(packet.dst_addr()); + if packet.dst_addr().is_broadcast() { + let buf = packet.into_inner(); + for dev in self.devices.borrow_mut().iter_mut() { + dev.send(dst_addr, buf, now) + } + } else { + let route_table = self.route_table.borrow(); + let Some(rule) = route_table.lookup_rule(&dst_addr) else { + warn!("No route found for destination: {}", dst_addr); + continue; + }; + + let next_hop = match rule.via { + Some(via) => via, + None => dst_addr, + }; + + let mut devices = self.devices.borrow_mut(); + let Some(dev) = devices.get_mut(&rule.dev) else { + warn!("Device {} not found", rule.dev); + // TODO: Remove route if device doesn't exist anymore ? + continue; + }; + + let IpAddress::Ipv4(src) = rule.src; + if src != packet.src_addr() { + error!("Changed packet source {} -> {}", packet.src_addr(), src); + packet.set_src_addr(src); + packet.fill_checksum() + } + + dev.send(next_hop, packet.into_inner(), now); + } + } + } + } +} + +impl Device for Router { + type RxToken<'a> = RxToken<'a>; + + type TxToken<'a> = TxToken<'a>; + + fn receive( + &mut self, + _timestamp: smoltcp::time::Instant, + ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + if self.rx_buffer.is_empty() || self.tx_buffer.is_full() { + None + } else { + Some(( + RxToken { + rx_buffer: &mut self.rx_buffer, + }, + TxToken { + tx_buffer: &mut self.tx_buffer, + }, + )) + } + } + + fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option> { + if self.tx_buffer.is_full() { + None + } else { + Some(TxToken { + tx_buffer: &mut self.tx_buffer, + }) + } + } + + fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities { + let mut caps = DeviceCapabilities::default(); + caps.medium = Medium::Ip; + caps.max_transmission_unit = Router::MTU; + caps.max_burst_size = Some(Smolnetd::SOCKET_BUFFER_SIZE); + caps + } +} + +pub struct TxToken<'a> { + tx_buffer: &'a mut PacketBuffer, +} + +impl smoltcp::phy::TxToken for TxToken<'_> { + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + f(self + .tx_buffer + .enqueue(len, ()) + .expect("This was checked before creating the TxToken")) + } +} + +pub struct RxToken<'a> { + rx_buffer: &'a mut PacketBuffer, +} + +impl<'a> smoltcp::phy::RxToken for RxToken<'a> { + fn consume(self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + let ((), buf) = self + .rx_buffer + .dequeue() + .expect("This was checked before creating the RxToken"); + + f(buf) + } +} diff --git a/src/smolnetd/router/route_table.rs b/src/smolnetd/router/route_table.rs new file mode 100644 index 0000000000..51943c932f --- /dev/null +++ b/src/smolnetd/router/route_table.rs @@ -0,0 +1,99 @@ +use std::fmt::Display; +use std::rc::Rc; + +use smoltcp::wire::{IpAddress, IpCidr}; + +#[derive(Debug)] +pub struct Rule { + pub filter: IpCidr, + pub via: Option, + pub dev: Rc, + pub src: IpAddress, +} + +impl Rule { + pub fn new(filter: IpCidr, via: Option, dev: Rc, src: IpAddress) -> Self { + Self { + filter, + via, + dev, + src, + } + } +} + +impl Display for Rule { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.filter.prefix_len() == 0 { + write!(f, "default")?; + } else { + write!(f, "{} ", self.filter)?; + } + + if let Some(via) = self.via { + write!(f, " via {}", via)?; + } + + write!(f, " dev {}", self.dev)?; + write!(f, " src {}", self.src)?; + + Ok(()) + } +} + +#[derive(Debug, Default)] +pub struct RouteTable { + rules: Vec, +} + +impl RouteTable { + pub fn lookup_rule(&self, dst: &IpAddress) -> Option<&Rule> { + self.rules + .iter() + .rev() + .find(|rule| rule.filter.contains_addr(dst)) + } + + pub fn lookup_src_addr(&self, dst: &IpAddress) -> Option { + Some(self.lookup_rule(dst)?.src) + } + + pub fn lookup_gateway(&self, dst: &IpAddress) -> Option { + self.lookup_rule(dst)?.via + } + + pub fn lookup_device(&self, dst: &IpAddress) -> Option> { + Some(self.lookup_rule(dst)?.dev.clone()) + } + + pub fn insert_rule(&mut self, new_rule: Rule) { + let i = match self + .rules + .binary_search_by_key(&new_rule.filter.prefix_len(), |rule| { + rule.filter.prefix_len() + }) { + Ok(i) | Err(i) => i, + }; + self.rules.insert(i, new_rule); + } + + pub fn remove_rule(&mut self, filter: IpCidr) { + self.rules.retain(|rule| rule.filter != filter); + } + + pub fn change_src(&mut self, old_src: IpAddress, new_src: IpAddress) { + for rule in self.rules.iter_mut().filter(|rule| rule.src == old_src) { + rule.src = new_src; + } + } +} + +impl Display for RouteTable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for rule in self.rules.iter() { + writeln!(f, "{}", rule)?; + } + + Ok(()) + } +} diff --git a/src/smolnetd/scheme/icmp.rs b/src/smolnetd/scheme/icmp.rs index cd65926f81..2f2fc680fe 100644 --- a/src/smolnetd/scheme/icmp.rs +++ b/src/smolnetd/scheme/icmp.rs @@ -1,17 +1,18 @@ -use smoltcp::socket::{IcmpEndpoint, IcmpPacketMetadata, IcmpSocket, IcmpSocketBuffer, SocketHandle}; -use smoltcp::wire::{Icmpv4Packet, Icmpv4Repr, IpAddress, IpEndpoint}; +use smoltcp::socket::icmp::{Endpoint as IcmpEndpoint, PacketMetadata as IcmpPacketMetadata, Socket as IcmpSocket, PacketBuffer as IcmpSocketBuffer}; +use smoltcp::iface::SocketHandle; +use smoltcp::wire::{Icmpv4Packet, Icmpv4Repr, IpAddress, IpListenEndpoint}; use std::mem; use std::str; use syscall::{Error as SyscallError, Result as SyscallResult}; use syscall; use byteorder::{ByteOrder, NetworkEndian}; -use crate::device::NetworkDevice; use crate::port_set::PortSet; -use super::socket::{DupResult, SchemeFile, SchemeSocket, SocketFile, SocketScheme}; +use crate::router::Router; +use super::socket::{DupResult, SchemeFile, SchemeSocket, SocketFile, SocketScheme, Context}; use super::{Smolnetd, SocketSet}; -pub type IcmpScheme = SocketScheme>; +pub type IcmpScheme = SocketScheme>; enum IcmpSocketType { Echo, @@ -24,7 +25,7 @@ pub struct IcmpData { ident: u16, } -impl<'a, 'b> SchemeSocket for IcmpSocket<'a, 'b> { +impl<'a> SchemeSocket for IcmpSocket<'a> { type SchemeDataT = PortSet; type DataT = IcmpData; type SettingT = (); @@ -74,6 +75,7 @@ impl<'a, 'b> SchemeSocket for IcmpSocket<'a, 'b> { path: &str, _uid: u32, ident_set: &mut Self::SchemeDataT, + _context: &Context ) -> SyscallResult<(SocketHandle, Self::DataT)> { use std::str::FromStr; @@ -93,15 +95,15 @@ impl<'a, 'b> SchemeSocket for IcmpSocket<'a, 'b> { let socket = IcmpSocket::new( IcmpSocketBuffer::new( vec![IcmpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], - vec![0; NetworkDevice::MTU * Smolnetd::SOCKET_BUFFER_SIZE] + vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE] ), IcmpSocketBuffer::new( vec![IcmpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], - vec![0; NetworkDevice::MTU * Smolnetd::SOCKET_BUFFER_SIZE] + vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE] ) ); let handle = socket_set.add(socket); - let mut icmp_socket = socket_set.get::(handle); + let icmp_socket = socket_set.get_mut::(handle); let ident = ident_set .get_port() .ok_or_else(|| SyscallError::new(syscall::EINVAL))?; @@ -125,20 +127,20 @@ impl<'a, 'b> SchemeSocket for IcmpSocket<'a, 'b> { let socket = IcmpSocket::new( IcmpSocketBuffer::new( vec![IcmpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], - vec![0; NetworkDevice::MTU * Smolnetd::SOCKET_BUFFER_SIZE] + vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE] ), IcmpSocketBuffer::new( vec![IcmpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], - vec![0; NetworkDevice::MTU * Smolnetd::SOCKET_BUFFER_SIZE] + vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE] ) ); let handle = socket_set.add(socket); - let mut icmp_socket = socket_set.get::(handle); + let icmp_socket = socket_set.get_mut::(handle); let ident = ident_set .get_port() .ok_or_else(|| SyscallError::new(syscall::EINVAL))?; icmp_socket - .bind(IcmpEndpoint::Udp(IpEndpoint::from(ident))) + .bind(IcmpEndpoint::Udp(IpListenEndpoint::from(ident))) .map_err(|_| syscall::Error::new(syscall::EINVAL))?; let socket_data = IcmpData { socket_type: IcmpSocketType::Udp, diff --git a/src/smolnetd/scheme/ip.rs b/src/smolnetd/scheme/ip.rs index b13cbc2720..149b173599 100644 --- a/src/smolnetd/scheme/ip.rs +++ b/src/smolnetd/scheme/ip.rs @@ -1,16 +1,18 @@ -use smoltcp::socket::{RawPacketMetadata, RawSocket, RawSocketBuffer, SocketHandle}; +use smoltcp::socket::raw::{PacketMetadata as RawPacketMetadata, Socket as RawSocket, PacketBuffer as RawSocketBuffer}; +use smoltcp::iface::SocketHandle; use smoltcp::wire::{IpProtocol, IpVersion}; use std::str; use syscall::{Error as SyscallError, Result as SyscallResult}; use syscall; -use crate::device::NetworkDevice; +use crate::router::Router; + use super::{Smolnetd, SocketSet}; -use super::socket::{DupResult, SchemeFile, SchemeSocket, SocketFile, SocketScheme}; +use super::socket::{DupResult, SchemeFile, SchemeSocket, SocketFile, SocketScheme, Context}; -pub type IpScheme = SocketScheme>; +pub type IpScheme = SocketScheme>; -impl<'a, 'b> SchemeSocket for RawSocket<'a, 'b> { +impl<'a> SchemeSocket for RawSocket<'a> { type SchemeDataT = (); type DataT = (); type SettingT = (); @@ -58,6 +60,7 @@ impl<'a, 'b> SchemeSocket for RawSocket<'a, 'b> { path: &str, uid: u32, _: &mut Self::SchemeDataT, + _context: &Context ) -> SyscallResult<(SocketHandle, Self::DataT)> { if uid != 0 { return Err(SyscallError::new(syscall::EACCES)); @@ -67,11 +70,11 @@ impl<'a, 'b> SchemeSocket for RawSocket<'a, 'b> { let rx_buffer = RawSocketBuffer::new( vec![RawPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], - vec![0; NetworkDevice::MTU * Smolnetd::SOCKET_BUFFER_SIZE] + vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE] ); let tx_buffer = RawSocketBuffer::new( vec![RawPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], - vec![0; NetworkDevice::MTU * Smolnetd::SOCKET_BUFFER_SIZE] + vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE] ); let ip_socket = RawSocket::new( IpVersion::Ipv4, diff --git a/src/smolnetd/scheme/mod.rs b/src/smolnetd/scheme/mod.rs index eab5942b29..d27f611fef 100644 --- a/src/smolnetd/scheme/mod.rs +++ b/src/smolnetd/scheme/mod.rs @@ -1,47 +1,51 @@ +use crate::link::ethernet::EthernetLink; +use crate::link::LinkDevice; +use crate::link::{loopback::LoopbackDevice, DeviceList}; +use crate::router::route_table::{RouteTable, Rule}; +use crate::router::Router; +use crate::scheme::smoltcp::iface::SocketSet as SmoltcpSocketSet; use netutils::getcfg; use smoltcp; -use smoltcp::iface::{EthernetInterface, EthernetInterfaceBuilder, NeighborCache, Routes}; -use smoltcp::phy::EthernetTracer; -use smoltcp::socket::SocketSet as SmoltcpSocketSet; +use smoltcp::iface::{Config, Interface as SmoltcpInterface}; +use smoltcp::phy::Tracer; use smoltcp::time::{Duration, Instant}; -use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, IpEndpoint, Ipv4Address}; +use smoltcp::wire::{ + EthernetAddress, HardwareAddress, IpAddress, IpCidr, IpListenEndpoint, Ipv4Address, +}; use std::cell::RefCell; -use std::collections::{BTreeMap, VecDeque}; use std::fs::File; -use std::io::{self, Read, Write}; +use std::io::{Read, Write}; use std::mem::size_of; use std::rc::Rc; use std::str::FromStr; -use syscall::data::TimeSpec; use syscall; +use syscall::data::TimeSpec; -use crate::buffer_pool::{Buffer, BufferPool}; -use crate::device::NetworkDevice; -use redox_netstack::error::{Error, Result}; +use self::icmp::IcmpScheme; use self::ip::IpScheme; +use self::netcfg::NetCfgScheme; use self::tcp::TcpScheme; use self::udp::UdpScheme; -use self::icmp::IcmpScheme; -use self::netcfg::NetCfgScheme; +use redox_netstack::error::{Error, Result}; +mod icmp; mod ip; +mod netcfg; mod socket; mod tcp; mod udp; -mod icmp; -mod netcfg; -type SocketSet = SmoltcpSocketSet<'static, 'static, 'static>; -type Interface = Rc>>>; +type SocketSet = SmoltcpSocketSet<'static>; +type Interface = Rc>; -const MAX_DURATION: Duration = Duration { millis: ::std::u64::MAX }; -const MIN_DURATION: Duration = Duration { millis: 0 }; +const MAX_DURATION: Duration = Duration::from_millis(u64::MAX); +const MIN_DURATION: Duration = Duration::from_millis(0); pub struct Smolnetd { - network_file: Rc>, + router_device: Tracer, + iface: Interface, time_file: File, - iface: Interface, socket_set: Rc>, timer: ::std::time::Instant, @@ -50,16 +54,13 @@ pub struct Smolnetd { tcp_scheme: TcpScheme, icmp_scheme: IcmpScheme, netcfg_scheme: NetCfgScheme, - - input_queue: Rc>>, - buffer_pool: Rc>, } impl Smolnetd { - const MAX_PACKET_SIZE: usize = 2048; - const SOCKET_BUFFER_SIZE: usize = 128; //packets - const MIN_CHECK_TIMEOUT: Duration = Duration { millis: 10 }; - const MAX_CHECK_TIMEOUT: Duration = Duration { millis: 500 }; + pub const MAX_PACKET_SIZE: usize = 2048; + pub const SOCKET_BUFFER_SIZE: usize = 128; //packets + pub const MIN_CHECK_TIMEOUT: Duration = Duration::from_millis(10); + pub const MAX_CHECK_TIMEOUT: Duration = Duration::from_millis(500); pub fn new( network_file: File, @@ -72,56 +73,90 @@ impl Smolnetd { ) -> Smolnetd { let hardware_addr = EthernetAddress::from_str(getcfg("mac").unwrap().trim()) .expect("Can't parse the 'mac' cfg"); - let local_ip = - IpAddress::from_str(getcfg("ip").unwrap().trim()).expect("Can't parse the 'ip' cfg."); let protocol_addrs = vec![ - IpCidr::new(local_ip, 24), IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8), ]; + let default_gw = Ipv4Address::from_str(getcfg("ip_router").unwrap().trim()) .expect("Can't parse the 'ip_router' cfg."); - let buffer_pool = Rc::new(RefCell::new(BufferPool::new(Self::MAX_PACKET_SIZE))); - let input_queue = Rc::new(RefCell::new(VecDeque::new())); - let network_file = Rc::new(RefCell::new(network_file)); - let network_device = EthernetTracer::new(NetworkDevice::new( - Rc::clone(&network_file), - Rc::clone(&input_queue), - hardware_addr, - Rc::clone(&buffer_pool), - ), |_timestamp, printer| { - trace!("{}", printer) - }); - let mut routes = Routes::new(BTreeMap::new()); - routes.add_default_ipv4_route(default_gw).expect("Failed to add default gateway"); - let iface = EthernetInterfaceBuilder::new(network_device) - .neighbor_cache(NeighborCache::new(BTreeMap::new())) - .ethernet_addr(hardware_addr) - .ip_addrs(protocol_addrs) - .routes(routes) - .finalize(); + let devices = Rc::new(RefCell::new(DeviceList::default())); + let route_table = Rc::new(RefCell::new(RouteTable::default())); + let mut network_device = Tracer::new( + Router::new(Rc::clone(&devices), Rc::clone(&route_table)), + |_timestamp, printer| trace!("{}", printer), + ); + + let config = Config::new(HardwareAddress::Ip); + let mut iface = SmoltcpInterface::new(config, &mut network_device, Instant::now()); + iface.update_ip_addrs(|ip_addrs| ip_addrs.extend(protocol_addrs)); + iface + .routes_mut() + .add_default_ipv4_route(default_gw) + .expect("Failed to add default gateway"); + let iface = Rc::new(RefCell::new(iface)); let socket_set = Rc::new(RefCell::new(SocketSet::new(vec![]))); + + let loopback = LoopbackDevice::default(); + route_table.borrow_mut().insert_rule(Rule::new( + "127.0.0.0/8".parse().unwrap(), + None, + Rc::clone(loopback.name()), + "127.0.0.1".parse().unwrap(), + )); + + + let mut eth0 = EthernetLink::new( + "eth0", + network_file, + ); + eth0.set_mac_address(hardware_addr); + + devices.borrow_mut().push(loopback); + devices.borrow_mut().push(eth0); + Smolnetd { iface: Rc::clone(&iface), + router_device: network_device, socket_set: Rc::clone(&socket_set), timer: ::std::time::Instant::now(), time_file, - ip_scheme: IpScheme::new(Rc::clone(&socket_set), ip_file), - udp_scheme: UdpScheme::new(Rc::clone(&socket_set), udp_file), - tcp_scheme: TcpScheme::new(Rc::clone(&socket_set), tcp_file), - icmp_scheme: IcmpScheme::new(Rc::clone(&socket_set), icmp_file), - netcfg_scheme: NetCfgScheme::new(Rc::clone(&iface), netcfg_file), - input_queue, - network_file, - buffer_pool, + ip_scheme: IpScheme::new( + Rc::clone(&iface), + Rc::clone(&route_table), + Rc::clone(&socket_set), + ip_file, + ), + udp_scheme: UdpScheme::new( + Rc::clone(&iface), + Rc::clone(&route_table), + Rc::clone(&socket_set), + udp_file, + ), + tcp_scheme: TcpScheme::new( + Rc::clone(&iface), + Rc::clone(&route_table), + Rc::clone(&socket_set), + tcp_file, + ), + icmp_scheme: IcmpScheme::new( + Rc::clone(&iface), + Rc::clone(&route_table), + Rc::clone(&socket_set), + icmp_file, + ), + netcfg_scheme: NetCfgScheme::new( + Rc::clone(&iface), + netcfg_file, + Rc::clone(&route_table), + Rc::clone(&devices), + ), } } pub fn on_network_scheme_event(&mut self) -> Result> { - if self.read_frames()? > 0 { - self.poll()?; - } + self.poll()?; Ok(None) } @@ -184,55 +219,39 @@ impl Smolnetd { let mut iter_limit = 10usize; let mut iface = self.iface.borrow_mut(); let mut socket_set = self.socket_set.borrow_mut(); - let timestamp = Instant::from(self.timer); + loop { + let timestamp = Instant::from(self.timer); if iter_limit == 0 { break MIN_DURATION; } iter_limit -= 1; - match iface.poll(&mut socket_set, timestamp) { - Ok(_) | Err(smoltcp::Error::Unrecognized) => (), - Err(e) => { - error!("poll error: {}", e); - break MIN_DURATION; - } - } - match iface.poll_delay(&socket_set, timestamp) { - Some(Duration { millis: 0 }) => { } - Some(delay) => { - break ::std::cmp::min(MAX_DURATION, delay) - } - None => break MAX_DURATION + + self.router_device.get_mut().poll(timestamp); + + // TODO: Check what if the bool returned by poll can be useful + iface.poll(timestamp, &mut self.router_device, &mut socket_set); + + self.router_device.get_mut().dispatch(timestamp); + + if !self.router_device.get_ref().can_recv() { + match iface.poll_delay(timestamp, &socket_set) { + Some(delay) if delay == Duration::ZERO => {} + Some(delay) => break ::std::cmp::min(MAX_DURATION, delay), + None => break MAX_DURATION, + }; } } }; + self.notify_sockets()?; + Ok(::std::cmp::min( ::std::cmp::max(Smolnetd::MIN_CHECK_TIMEOUT, timeout), Smolnetd::MAX_CHECK_TIMEOUT, )) } - fn read_frames(&mut self) -> Result { - let mut total_frames = 0; - loop { - let mut buffer = self.buffer_pool.borrow_mut().get_buffer(); - let count = match self.network_file.borrow_mut().read(&mut buffer) { - Ok(count) => count, - Err(err) => match err.kind() { - io::ErrorKind::WouldBlock => break, - _ => return Err( - Error::from_io_error(err, "Failed to read from network file") - ) - } - }; - buffer.resize(count); - self.input_queue.borrow_mut().push_back(buffer); - total_frames += 1; - } - Ok(total_frames) - } - fn notify_sockets(&mut self) -> Result<()> { self.ip_scheme.notify_sockets()?; self.udp_scheme.notify_sockets()?; @@ -257,17 +276,17 @@ fn post_fevent(scheme_file: &mut File, fd: usize, event: usize, data_len: usize) .map_err(|e| Error::from_io_error(e, "failed to post fevent")) } -fn parse_endpoint(socket: &str) -> IpEndpoint { +fn parse_endpoint(socket: &str) -> IpListenEndpoint { let mut socket_parts = socket.split(':'); - let host = IpAddress::Ipv4( - Ipv4Address::from_str(socket_parts.next().unwrap_or("")) - .unwrap_or_else(|_| Ipv4Address::new(0, 0, 0, 0)), - ); + let host = Ipv4Address::from_str(socket_parts.next().unwrap_or("")) + .ok() + .filter(|addr| !addr.is_unspecified()) + .map(IpAddress::Ipv4); let port = socket_parts .next() .unwrap_or("") .parse::() .unwrap_or(0); - IpEndpoint::new(host, port) + IpListenEndpoint { addr: host, port } } diff --git a/src/smolnetd/scheme/netcfg/mod.rs b/src/smolnetd/scheme/netcfg/mod.rs index c64b310092..76cb8f5b80 100644 --- a/src/smolnetd/scheme/netcfg/mod.rs +++ b/src/smolnetd/scheme/netcfg/mod.rs @@ -2,24 +2,30 @@ mod nodes; mod notifier; -use smoltcp::wire::{IpAddress, EthernetAddress, IpCidr, Ipv4Address}; +use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address}; use std::cell::RefCell; use std::collections::BTreeMap; use std::fs::File; use std::io::{ErrorKind, Read, Write}; -use std::rc::Rc; use std::mem; -use std::str::FromStr; +use std::rc::Rc; use std::str; +use std::str::FromStr; +use syscall; use syscall::data::Stat; use syscall::flag::{MODE_DIR, MODE_FILE}; -use syscall::{Error as SyscallError, EventFlags as SyscallEventFlags, Packet as SyscallPacket, Result as SyscallResult, SchemeMut}; -use syscall; +use syscall::{ + Error as SyscallError, EventFlags as SyscallEventFlags, Packet as SyscallPacket, + Result as SyscallResult, SchemeMut, +}; + +use crate::link::DeviceList; +use crate::router::route_table::{RouteTable, Rule}; use self::nodes::*; use self::notifier::*; -use redox_netstack::error::{Error, Result}; use super::{post_fevent, Interface}; +use redox_netstack::error::{Error, Result}; const WRITE_BUFFER_MAX_SIZE: usize = 0xffff; @@ -28,27 +34,44 @@ fn gateway_cidr() -> IpCidr { IpCidr::new(IpAddress::v4(0, 0, 0, 0), 0) } -fn parse_default_gw(value: &str) -> SyscallResult { - let mut routes = value.lines(); - if let Some(route) = routes.next() { - if !routes.next().is_none() { - return Err(SyscallError::new(syscall::EINVAL)); - } - let mut words = route.split_whitespace(); - if let Some("default") = words.next() { - if let Some("via") = words.next() { - if let Some(ip) = words.next() { - return Ipv4Address::from_str(ip) - .map_err(|_| SyscallError::new(syscall::EINVAL)); - } - } - } +fn parse_route(value: &str, route_table: &RouteTable) -> SyscallResult { + let mut parts = value.split_whitespace(); + let cidr_str = parts.next().ok_or(SyscallError::new(syscall::EINVAL))?; + let cidr = match cidr_str { + "default" => gateway_cidr(), + cidr_str => cidr_str + .parse() + .map_err(|_| SyscallError::new(syscall::EINVAL))?, + }; + + let via: IpAddress = match parts.next().ok_or(SyscallError::new(syscall::EINVAL))? { + "via" => parts + .next() + .ok_or(SyscallError::new(syscall::EINVAL))? + .parse() + .map_err(|_| SyscallError::new(syscall::EINVAL))?, + _ => return Err(SyscallError::new(syscall::EINVAL)), + }; + + if !via.is_unicast() { + return Err(SyscallError::new(syscall::EINVAL)); } - Err(SyscallError::new(syscall::EINVAL)) + + let rule = route_table + .lookup_rule(&via) + .ok_or(SyscallError::new(syscall::EINVAL))?; + + Ok(Rule::new(cidr, Some(via), rule.dev.clone(), rule.src)) } -fn mk_root_node(iface: Interface, notifier: NotifierRef, dns_config: DNSConfigRef) -> CfgNodeRef { - cfg_node!{ +fn mk_root_node( + iface: Interface, + notifier: NotifierRef, + dns_config: DNSConfigRef, + route_table: Rc>, + devices: Rc>, +) -> CfgNodeRef { + cfg_node! { "resolv" => { "nameserver" => { rw [dns_config, notifier] (Option, None) @@ -79,37 +102,24 @@ fn mk_root_node(iface: Interface, notifier: NotifierRef, dns_config: DNSConfigRe }, "route" => { "list" => { - ro [iface] || { - let mut gateway = None; - iface.borrow_mut().routes_mut().update(|map| { - gateway = map.get(&gateway_cidr()).map(|route| route.via_router); - }); - if let Some(ip) = gateway { - format!("default via {}\n", ip) - } else { - String::new() - } + ro [route_table] || { + format!("{}", route_table.borrow()) } }, "add" => { - wo [iface, notifier] (Option, None) + wo [iface, notifier, route_table] (Option, None) |cur_value, line| { if cur_value.is_none() { - let default_gw = parse_default_gw(line)?; - if !default_gw.is_unicast() { - return Err(SyscallError::new(syscall::EINVAL)); - } - *cur_value = Some(default_gw); + let route = parse_route(line, &route_table.borrow())?; + *cur_value = Some(route); Ok(()) } else { Err(SyscallError::new(syscall::EINVAL)) } } |cur_value| { - if let Some(default_gw) = *cur_value { - if iface.borrow_mut().routes_mut().add_default_ipv4_route(default_gw).is_err() { - return Err(SyscallError::new(syscall::EINVAL)); - } + if let Some(route) = cur_value.take() { + route_table.borrow_mut().insert_rule(route); notifier.borrow_mut().schedule_notify("route/list"); Ok(()) } else { @@ -118,32 +128,23 @@ fn mk_root_node(iface: Interface, notifier: NotifierRef, dns_config: DNSConfigRe } }, "rm" => { - wo [iface, notifier] (Option, None) + wo [iface, notifier, route_table] (Option, None) |cur_value, line| { if cur_value.is_none() { - let default_gw = parse_default_gw(line)?; - if !default_gw.is_unicast() { - return Err(SyscallError::new(syscall::EINVAL)); + match line.parse() { + Ok(cidr) => { + *cur_value = Some(cidr); + Ok(()) + } + Err(_) => Err(SyscallError::new(syscall::EINVAL)) } - *cur_value = Some(default_gw); - Ok(()) } else { Err(SyscallError::new(syscall::EINVAL)) } } |cur_value| { - if let Some(default_gw) = *cur_value { - let mut iface = iface.borrow_mut(); - let mut gateway = None; - iface.routes_mut().update(|map| { - gateway = map.get(&gateway_cidr()).map(|route| route.via_router); - }); - if gateway != Some(IpAddress::Ipv4(default_gw)) { - return Err(SyscallError::new(syscall::EINVAL)); - } - iface.routes_mut().update(|map| { - map.remove(&gateway_cidr()); - }); + if let Some(cidr) = *cur_value { + route_table.borrow_mut().remove_rule(cidr); notifier.borrow_mut().schedule_notify("route/list"); Ok(()) } else { @@ -155,9 +156,17 @@ fn mk_root_node(iface: Interface, notifier: NotifierRef, dns_config: DNSConfigRe "ifaces" => { "eth0" => { "mac" => { - rw [iface, notifier] (Option, None) + rw [iface, notifier, devices] (Option, None) || { - format!("{}\n", iface.borrow().ethernet_addr()) + match devices.borrow().get("eth0") { + Some(dev) => { + match dev.mac_address() { + Some(addr) => format!("{addr}\n"), + None => "Not configured\n".into(), + } + } + None => "Device not found\n".into(), + } } |cur_value, line| { if cur_value.is_none() { @@ -174,96 +183,76 @@ fn mk_root_node(iface: Interface, notifier: NotifierRef, dns_config: DNSConfigRe } |cur_value| { if let Some(mac) = *cur_value { - iface.borrow_mut().set_ethernet_addr(mac); - notifier.borrow_mut().schedule_notify("ifaces/eth0/mac"); + if let Some(dev) = devices.borrow_mut().get_mut("eth0") { + dev.set_mac_address(mac); + notifier.borrow_mut().schedule_notify("ifaces/eth0/mac"); + } } Ok(()) } }, "addr" => { "list" => { - ro [iface] + ro [devices] || { - let mut ips = String::new(); - for cidr in iface.borrow().ip_addrs() { - ips += &format!("{}\n", cidr); - } - ips + let res = match devices.borrow().get("eth0") { + Some(dev) => { + match dev.ip_address() { + Some(addr) => format!("{addr}\n"), + None => "Not configured\n".into(), + } + } + None => "Device not found\n".into(), + }; + res } }, "set" => { - wo [iface, notifier] (Vec, Vec::new()) + wo [iface, notifier, devices, route_table] (Option, None) |cur_value, line| { - let cidr = IpCidr::from_str(line) - .map_err(|_| SyscallError::new(syscall::EINVAL))?; - if !cidr.address().is_unicast() { - return Err(SyscallError::new(syscall::EINVAL)); - } - cur_value.push(cidr); - Ok(()) - } - |cur_value| { - if !cur_value.is_empty() { - let mut iface = iface.borrow_mut(); - let mut cidrs = vec![]; - mem::swap(cur_value, &mut cidrs); - iface.update_ip_addrs(|s| { - *s = From::from(cidrs); - }); - notifier.borrow_mut().schedule_notify("ifaces/eth0/addr/list"); - } - Ok(()) - } - }, - "add" => { - wo [iface, notifier] (Vec, Vec::new()) - |cur_value, line| { - let cidr = IpCidr::from_str(line) - .map_err(|_| SyscallError::new(syscall::EINVAL))?; - if !cidr.address().is_unicast() { - return Err(SyscallError::new(syscall::EINVAL)); - } - cur_value.push(cidr); - Ok(()) - } - |cur_value| { - let mut iface = iface.borrow_mut(); - let mut cidrs = iface.ip_addrs().to_vec(); - for cidr in cur_value { - cidrs.insert(0, *cidr); - } - iface.update_ip_addrs(|s| { - *s = From::from(cidrs); - }); - notifier.borrow_mut().schedule_notify("ifaces/eth0/addr/list"); - Ok(()) - } - }, - "rm" => { - wo [iface, notifier] (Vec, Vec::new()) - |cur_value, line| { - let cidr = IpCidr::from_str(line) - .map_err(|_| SyscallError::new(syscall::EINVAL))?; - if !cidr.address().is_unicast() { - return Err(SyscallError::new(syscall::EINVAL)); - } - cur_value.push(cidr); - Ok(()) - } - |cur_value| { - let mut iface = iface.borrow_mut(); - let mut cidrs = iface.ip_addrs().to_vec(); - for cidr in cur_value { - let pre_retain_len = cidrs.len(); - cidrs.retain(|&c| c != *cidr); - if pre_retain_len == cidrs.len() { + if cur_value.is_none() { + let cidr = IpCidr::from_str(line) + .map_err(|_| SyscallError::new(syscall::EINVAL))?; + if !cidr.address().is_unicast() { return Err(SyscallError::new(syscall::EINVAL)); } + *cur_value = Some(cidr); + Ok(()) + } else { + Err(SyscallError::new(syscall::EINVAL)) + } + } + |cur_value| { + // TODO: Multiple IPs + if let Some(cidr) = cur_value.take() { + if let Some(dev) = devices.borrow_mut().get_mut("eth0") { + + let mut route_table = route_table.borrow_mut(); + if let Some(old_addr) = dev.ip_address() { + let IpCidr::Ipv4(old_v4_cidr) = old_addr; + let old_network = IpCidr::Ipv4(old_v4_cidr.network()); + + route_table.remove_rule(old_network); + route_table.change_src(old_addr.address(), cidr.address()); + iface.borrow_mut().update_ip_addrs(|addrs| addrs.retain(|addr| *addr != old_addr)) + } + + dev.set_ip_address(cidr); + // FIXME: Here, the insert 0 is a workaround to let UDP sockets + // work with this interface only. + // Smoltcp takes the first ip address when looking for a source + // ip address when sending UDP packets. + // This behavior will have to be fixed as it's our route table + // job to find give this source. + iface.borrow_mut().update_ip_addrs(|addrs| addrs.insert(0, cidr).unwrap()); + + let IpCidr::Ipv4(v4_cidr) = cidr; + let network_cidr = IpCidr::Ipv4(v4_cidr.network()); + route_table.insert_rule(Rule::new(network_cidr, None, dev.name().clone(), cidr.address())) + } + notifier.borrow_mut().schedule_notify("ifaces/eth0/addr/list"); + notifier.borrow_mut().schedule_notify("route/list"); } - iface.update_ip_addrs(|s| { - *s = From::from(cidrs); - }); - notifier.borrow_mut().schedule_notify("ifaces/eth0/addr/list"); Ok(()) } }, @@ -345,7 +334,12 @@ pub struct NetCfgScheme { } impl NetCfgScheme { - pub fn new(iface: Interface, scheme_file: File) -> NetCfgScheme { + pub fn new( + iface: Interface, + scheme_file: File, + route_table: Rc>, + devices: Rc>, + ) -> NetCfgScheme { let notifier = Notifier::new_ref(); let dns_config = Rc::new(RefCell::new(DNSConfig { name_server: Ipv4Address::new(8, 8, 8, 8), @@ -354,7 +348,13 @@ impl NetCfgScheme { scheme_file, next_fd: 1, files: BTreeMap::new(), - root_node: mk_root_node(iface, Rc::clone(¬ifier), dns_config), + root_node: mk_root_node( + iface, + Rc::clone(¬ifier), + dns_config, + route_table, + devices, + ), notifier, } } @@ -366,12 +366,14 @@ impl NetCfgScheme { Ok(0) => { //TODO: Cleanup must occur break Some(()); - }, + } Ok(_) => (), - Err(err) => if err.kind() == ErrorKind::WouldBlock { - break None; - } else { - return Err(Error::from(err)); + Err(err) => { + if err.kind() == ErrorKind::WouldBlock { + break None; + } else { + return Err(Error::from(err)); + } } } self.handle(&mut packet); @@ -414,7 +416,11 @@ impl SchemeMut for NetCfgScheme { is_dir: current_node.is_dir(), is_writable: current_node.is_writable(), is_readable: current_node.is_readable(), - node_writer: if current_node.is_writable() { current_node.new_writer() } else { None }, + node_writer: if current_node.is_writable() { + current_node.new_writer() + } else { + None + }, uid, pos: 0, read_buf, @@ -440,7 +446,8 @@ impl SchemeMut for NetCfgScheme { } fn write(&mut self, fd: usize, buf: &[u8]) -> SyscallResult { - let file = self.files + let file = self + .files .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; @@ -468,7 +475,8 @@ impl SchemeMut for NetCfgScheme { } fn read(&mut self, fd: usize, buf: &mut [u8]) -> SyscallResult { - let file = self.files + let file = self + .files .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; @@ -482,15 +490,12 @@ impl SchemeMut for NetCfgScheme { } fn fstat(&mut self, fd: usize, stat: &mut Stat) -> SyscallResult { - let file = self.files + let file = self + .files .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; - stat.st_mode = if file.is_dir { - MODE_DIR - } else { - MODE_FILE - }; + stat.st_mode = if file.is_dir { MODE_DIR } else { MODE_FILE }; if file.is_writable { stat.st_mode |= 0o222; } @@ -505,7 +510,8 @@ impl SchemeMut for NetCfgScheme { } fn fevent(&mut self, fd: usize, events: SyscallEventFlags) -> SyscallResult { - let file = self.files + let file = self + .files .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; if events.contains(syscall::EVENT_READ) { @@ -517,7 +523,8 @@ impl SchemeMut for NetCfgScheme { } fn fsync(&mut self, fd: usize) -> SyscallResult { - let file = self.files + let file = self + .files .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; diff --git a/src/smolnetd/scheme/socket.rs b/src/smolnetd/scheme/socket.rs index b61ee1ce23..45c64cfeeb 100644 --- a/src/smolnetd/scheme/socket.rs +++ b/src/smolnetd/scheme/socket.rs @@ -1,4 +1,5 @@ use std::cell::RefCell; +use std::collections::btree_map::Entry; use std::collections::BTreeMap; use std::fs::File; use std::io::{ErrorKind, Read, Write}; @@ -10,15 +11,26 @@ use std::rc::Rc; use std::str; use syscall; -use syscall::{Error as SyscallError, EventFlags as SyscallEventFlags, Packet as SyscallPacket, Result as SyscallResult, SchemeBlockMut}; use syscall::data::TimeSpec; use syscall::flag::{EVENT_READ, EVENT_WRITE}; +use syscall::{ + Error as SyscallError, EventFlags as SyscallEventFlags, Packet as SyscallPacket, + Result as SyscallResult, SchemeBlockMut, +}; +use super::Interface; +use crate::router::route_table::RouteTable; +use crate::scheme::smoltcp::iface::SocketHandle; use redox_netstack::error::{Error, Result}; -use smoltcp::socket::{AnySocket, SocketHandle}; +use smoltcp::socket::AnySocket; use super::{post_fevent, SocketSet}; +pub struct Context { + pub iface: Interface, + pub route_table: Rc>, +} + pub struct NullFile { pub flags: usize, pub uid: u32, @@ -70,7 +82,8 @@ enum Setting { Ttl, ReadTimeout, WriteTimeout, - #[allow(dead_code)] Other(SettingT), + #[allow(dead_code)] + Other(SettingT), } pub struct SettingFile { @@ -98,7 +111,10 @@ where } } - pub fn events(&mut self, socket_set: &mut SocketSet) -> usize where SocketT: AnySocket<'static, 'static> { + pub fn events(&mut self, socket_set: &mut SocketSet) -> usize + where + SocketT: AnySocket<'static>, + { let mut revents = 0; if let &mut SchemeFile::Socket(SocketFile { socket_handle, @@ -110,7 +126,9 @@ where { let socket = socket_set.get::(socket_handle); - if events & syscall::EVENT_READ.bits() == syscall::EVENT_READ.bits() && (socket.can_recv() || !socket.may_recv()) { + if events & syscall::EVENT_READ.bits() == syscall::EVENT_READ.bits() + && (socket.can_recv() || !socket.may_recv()) + { if !*read_notified { *read_notified = true; revents |= EVENT_READ.bits(); @@ -119,7 +137,9 @@ where *read_notified = false; } - if events & syscall::EVENT_WRITE.bits() == syscall::EVENT_WRITE.bits() && socket.can_send() { + if events & syscall::EVENT_WRITE.bits() == syscall::EVENT_WRITE.bits() + && socket.can_send() + { if !*write_notified { *write_notified = true; revents |= EVENT_WRITE.bits(); @@ -162,21 +182,42 @@ where fn hop_limit(&self) -> u8; fn set_hop_limit(&mut self, hop_limit: u8); - fn get_setting(file: &SocketFile, setting: Self::SettingT, buf: &mut [u8]) -> SyscallResult; - fn set_setting(file: &mut SocketFile, setting: Self::SettingT, buf: &[u8]) -> SyscallResult; + fn get_setting( + file: &SocketFile, + setting: Self::SettingT, + buf: &mut [u8], + ) -> SyscallResult; + fn set_setting( + file: &mut SocketFile, + setting: Self::SettingT, + buf: &[u8], + ) -> SyscallResult; fn new_socket( sockets: &mut SocketSet, path: &str, uid: u32, data: &mut Self::SchemeDataT, + context: &Context, ) -> SyscallResult<(SocketHandle, Self::DataT)>; - fn close_file(&self, file: &SchemeFile, data: &mut Self::SchemeDataT) -> SyscallResult<()>; + fn close_file( + &self, + file: &SchemeFile, + data: &mut Self::SchemeDataT, + ) -> SyscallResult<()>; - fn write_buf(&mut self, file: &mut SocketFile, buf: &[u8]) -> SyscallResult>; + fn write_buf( + &mut self, + file: &mut SocketFile, + buf: &[u8], + ) -> SyscallResult>; - fn read_buf(&mut self, file: &mut SocketFile, buf: &mut [u8]) -> SyscallResult>; + fn read_buf( + &mut self, + file: &mut SocketFile, + buf: &mut [u8], + ) -> SyscallResult>; fn fpath(&self, file: &SchemeFile, buf: &mut [u8]) -> SyscallResult; @@ -190,11 +231,13 @@ where pub struct SocketScheme where - SocketT: SchemeSocket + AnySocket<'static, 'static>, + SocketT: SchemeSocket + AnySocket<'static>, { next_fd: usize, nulls: BTreeMap, files: BTreeMap>, + ref_counts: BTreeMap, + context: Context, socket_set: Rc>, scheme_file: File, wait_queue: WaitQueue, @@ -204,18 +247,25 @@ where impl SocketScheme where - SocketT: SchemeSocket + AnySocket<'static, 'static>, + SocketT: SchemeSocket + AnySocket<'static>, { - pub fn new(socket_set: Rc>, scheme_file: File) -> SocketScheme { + pub fn new( + iface: Interface, + route_table: Rc>, + socket_set: Rc>, + scheme_file: File, + ) -> SocketScheme { SocketScheme { next_fd: 1, nulls: BTreeMap::new(), files: BTreeMap::new(), + ref_counts: BTreeMap::new(), socket_set, scheme_data: SocketT::new_scheme_data(), scheme_file, wait_queue: Vec::new(), _phantom_socket: PhantomData, + context: Context { iface, route_table }, } } @@ -226,12 +276,14 @@ where Ok(0) => { //TODO: Cleanup must occur break Some(()); - }, + } Ok(_) => (), - Err(err) => if err.kind() == ErrorKind::WouldBlock { - break None; - } else { - return Err(Error::from(err)); + Err(err) => { + if err.kind() == ErrorKind::WouldBlock { + break None; + } else { + return Err(Error::from(err)); + } } } if let Some(a) = self.handle(&mut packet) { @@ -244,7 +296,7 @@ where until: timeout, packet, }); - }, + } Err(err) => { packet.a = (-err.errno) as usize; self.scheme_file.write_all(&packet)?; @@ -293,7 +345,7 @@ where self.wait_queue.remove(i); packet.a = (-syscall::ETIMEDOUT) as usize; self.scheme_file.write_all(&packet)?; - }, + } _ => { i += 1; } @@ -307,15 +359,13 @@ where fn handle_block(&mut self, packet: &mut SyscallPacket) -> SyscallResult> { let fd = packet.b; let (read_timeout, write_timeout) = { - let file = self.files + let file = self + .files .get(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; if let SchemeFile::Socket(ref scheme_file) = *file { - Ok(( - scheme_file.read_timeout, - scheme_file.write_timeout, - )) + Ok((scheme_file.read_timeout, scheme_file.write_timeout)) } else { Err(SyscallError::new(syscall::EBADF)) } @@ -342,7 +392,8 @@ where setting: Setting, buf: &mut [u8], ) -> SyscallResult { - let file = self.files + let file = self + .files .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; let file = match *file { @@ -354,14 +405,16 @@ where match setting { Setting::Other(setting) => SocketT::get_setting(file, setting, buf), - Setting::Ttl => if let Some(hop_limit) = buf.get_mut(0) { - let mut socket_set = self.socket_set.borrow_mut(); - let socket = socket_set.get::(file.socket_handle); - *hop_limit = socket.hop_limit(); - Ok(1) - } else { - Err(SyscallError::new(syscall::EIO)) - }, + Setting::Ttl => { + if let Some(hop_limit) = buf.get_mut(0) { + let socket_set = self.socket_set.borrow(); + let socket = socket_set.get::(file.socket_handle); + *hop_limit = socket.hop_limit(); + Ok(1) + } else { + Err(SyscallError::new(syscall::EIO)) + } + } Setting::ReadTimeout | Setting::WriteTimeout => { let timespec = match (setting, file.read_timeout, file.write_timeout) { (Setting::ReadTimeout, Some(read_timeout), _) => read_timeout, @@ -389,7 +442,8 @@ where setting: Setting, buf: &[u8], ) -> SyscallResult { - let file = self.files + let file = self + .files .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; let file = match *file { @@ -422,14 +476,16 @@ where }; Ok(count) } - Setting::Ttl => if let Some(hop_limit) = buf.get(0) { - let mut socket_set = self.socket_set.borrow_mut(); - let mut socket = socket_set.get::(file.socket_handle); - socket.set_hop_limit(*hop_limit); - Ok(1) - } else { - Err(SyscallError::new(syscall::EIO)) - }, + Setting::Ttl => { + if let Some(hop_limit) = buf.get(0) { + let mut socket_set = self.socket_set.borrow_mut(); + let socket = socket_set.get_mut::(file.socket_handle); + socket.set_hop_limit(*hop_limit); + Ok(1) + } else { + Err(SyscallError::new(syscall::EIO)) + } + } Setting::Other(setting) => SocketT::set_setting(file, setting, buf), } } @@ -437,9 +493,15 @@ where impl syscall::SchemeBlockMut for SocketScheme where - SocketT: SchemeSocket + AnySocket<'static, 'static>, + SocketT: SchemeSocket + AnySocket<'static>, { - fn open(&mut self, path: &str, flags: usize, uid: u32, _gid: u32) -> SyscallResult> { + fn open( + &mut self, + path: &str, + flags: usize, + uid: u32, + _gid: u32, + ) -> SyscallResult> { if path.is_empty() { let null = NullFile { flags, @@ -459,6 +521,7 @@ where path, uid, &mut self.scheme_data, + &self.context, )?; let file = SchemeFile::Socket(SocketFile { @@ -475,6 +538,7 @@ where let id = self.next_fd; self.next_fd += 1; + self.ref_counts.insert(socket_handle, 1); self.files.insert(id, file); Ok(Some(id)) @@ -487,7 +551,8 @@ where } let socket_handle = { - let file = self.files + let file = self + .files .get(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; file.socket_handle() @@ -506,15 +571,38 @@ where }| a != fd, ); - socket_set.release(socket_handle); - //TODO: removing sockets in release should make prune unnecessary - socket_set.prune(); + let remove = match self.ref_counts.entry(socket_handle) { + Entry::Vacant(_) => { + warn!("Closing a socket_handle with no ref"); + true + } + Entry::Occupied(mut e) => { + if *e.get() == 0 { + warn!("Closing a socket_handle with no ref"); + e.remove(); + true + } else { + *e.get_mut() -= 1; + if *e.get() == 0 { + e.remove(); + true + } else { + false + } + } + } + }; + + if remove { + socket_set.remove(socket_handle); + } Ok(Some(0)) } fn write(&mut self, fd: usize, buf: &[u8]) -> SyscallResult> { let (fd, setting) = { - let file = self.files + let file = self + .files .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; @@ -524,8 +612,13 @@ where } SchemeFile::Socket(ref mut file) => { let mut socket_set = self.socket_set.borrow_mut(); - let mut socket = socket_set.get::(file.socket_handle); - return SocketT::write_buf(&mut socket, file, buf); + let socket = socket_set.get_mut::(file.socket_handle); + let ret = SocketT::write_buf(socket, file, buf); + match ret { + Ok(None) => {} + _ => file.write_notified = false, + } + return ret; } } }; @@ -534,7 +627,8 @@ where fn read(&mut self, fd: usize, buf: &mut [u8]) -> SyscallResult> { let (fd, setting) = { - let file = self.files + let file = self + .files .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; match *file { @@ -543,8 +637,15 @@ where } SchemeFile::Socket(ref mut file) => { let mut socket_set = self.socket_set.borrow_mut(); - let mut socket = socket_set.get::(file.socket_handle); - return SocketT::read_buf(&mut socket, file, buf); + let socket = socket_set.get_mut::(file.socket_handle); + + let ret = SocketT::read_buf(socket, file, buf); + match ret { + Ok(None) => {} + _ => file.read_notified = false + } + + return ret; } } }; @@ -554,7 +655,8 @@ where fn dup(&mut self, fd: usize, buf: &[u8]) -> SyscallResult> { let path = str::from_utf8(buf).or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?; - if let Some((flags, uid, gid)) = self.nulls + if let Some((flags, uid, gid)) = self + .nulls .get(&fd) .map(|null| (null.flags, null.uid, null.gid)) { @@ -562,7 +664,8 @@ where } let new_file = { - let file = self.files + let file = self + .files .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; @@ -606,14 +709,23 @@ where if let Some((socket_handle, data)) = update_with { if let SchemeFile::Socket(ref mut file) = *file { + // We replace the socket_handle pointed by file so update the ref_counts + // accordingly + self.ref_counts + .entry(file.socket_handle) + .and_modify(|e| *e = e.saturating_sub(1)) + .or_insert(0); + + *self.ref_counts.entry(socket_handle).or_insert(0) += 1; + file.socket_handle = socket_handle; file.data = data; - } else { - self.socket_set.borrow_mut().retain(file.socket_handle()); } - } else { - self.socket_set.borrow_mut().retain(file.socket_handle()); } + *self + .ref_counts + .entry(new_handle.socket_handle()) + .or_insert(0) += 1; new_handle }; @@ -624,8 +736,13 @@ where Ok(Some(id)) } - fn fevent(&mut self, fd: usize, events: SyscallEventFlags) -> SyscallResult> { - let file = self.files + fn fevent( + &mut self, + fd: usize, + events: SyscallEventFlags, + ) -> SyscallResult> { + let file = self + .files .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; match *file { @@ -643,7 +760,8 @@ where fn fsync(&mut self, fd: usize) -> SyscallResult> { { - let _file = self.files + let _file = self + .files .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; } @@ -653,11 +771,12 @@ where } fn fpath(&mut self, fd: usize, buf: &mut [u8]) -> SyscallResult> { - let file = self.files + let file = self + .files .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; - let mut socket_set = self.socket_set.borrow_mut(); + let socket_set = self.socket_set.borrow(); let socket = socket_set.get::(file.socket_handle()); socket.fpath(file, buf).map(Some) @@ -674,7 +793,8 @@ where _ => Err(SyscallError::new(syscall::EINVAL)), } } else { - let file = self.files + let file = self + .files .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; diff --git a/src/smolnetd/scheme/tcp.rs b/src/smolnetd/scheme/tcp.rs index 200ba42aa4..9d3dbb0425 100644 --- a/src/smolnetd/scheme/tcp.rs +++ b/src/smolnetd/scheme/tcp.rs @@ -1,17 +1,20 @@ -use smoltcp::socket::{SocketHandle, TcpSocket, TcpSocketBuffer}; +use smoltcp::iface::SocketHandle; +use smoltcp::socket::tcp::{Socket as TcpSocket, SocketBuffer as TcpSocketBuffer}; +use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; +use std::fmt::Write; use std::str; -use syscall::{Error as SyscallError, Result as SyscallResult}; use syscall; +use syscall::{Error as SyscallError, Result as SyscallResult}; -use crate::port_set::PortSet; -use super::socket::{DupResult, SchemeFile, SchemeSocket, SocketFile, SocketScheme}; +use super::socket::{Context, DupResult, SchemeFile, SchemeSocket, SocketFile, SocketScheme}; use super::{parse_endpoint, SocketSet}; +use crate::port_set::PortSet; pub type TcpScheme = SocketScheme>; impl<'a> SchemeSocket for TcpSocket<'a> { type SchemeDataT = PortSet; - type DataT = (); + type DataT = Option; type SettingT = (); fn new_scheme_data() -> Self::SchemeDataT { @@ -59,6 +62,7 @@ impl<'a> SchemeSocket for TcpSocket<'a> { path: &str, uid: u32, port_set: &mut Self::SchemeDataT, + context: &Context, ) -> SyscallResult<(SocketHandle, Self::DataT)> { trace!("TCP open {}", path); let mut parts = path.split('/'); @@ -85,21 +89,44 @@ impl<'a> SchemeSocket for TcpSocket<'a> { let socket_handle = socket_set.add(socket); - let mut tcp_socket = socket_set.get::(socket_handle); + let tcp_socket = socket_set.get_mut::(socket_handle); + + let listen_enpoint = if remote_endpoint.is_specified() { + let local_endpoint_addr = match local_endpoint.addr { + Some(addr) if !addr.is_unspecified() => Some(addr), + _ => { + let route_table = context.route_table.borrow(); + let addr = route_table + .lookup_src_addr(&remote_endpoint.addr.expect("Checked in is_specified")); + if matches!(addr, None) { + error!("Opening a TCP connection with a probably invalid source IP as no route have been found for destination: {}", remote_endpoint); + } + addr + } + }; + let local_endpoint = IpListenEndpoint { + addr: local_endpoint_addr, + port: local_endpoint.port, + }; - if remote_endpoint.is_specified() { trace!("Connecting tcp {} {}", local_endpoint, remote_endpoint); tcp_socket - .connect(remote_endpoint, local_endpoint) + .connect( + context.iface.borrow_mut().context(), + IpEndpoint::new(remote_endpoint.addr.unwrap(), remote_endpoint.port), + local_endpoint, + ) .expect("Can't connect tcp socket "); + None } else { trace!("Listening tcp {}", local_endpoint); tcp_socket .listen(local_endpoint) .expect("Can't listen on local endpoint"); - } + Some(local_endpoint) + }; - Ok((socket_handle, ())) + Ok((socket_handle, listen_enpoint)) } fn close_file( @@ -107,8 +134,14 @@ impl<'a> SchemeSocket for TcpSocket<'a> { file: &SchemeFile, port_set: &mut Self::SchemeDataT, ) -> SyscallResult<()> { - if let SchemeFile::Socket(_) = *file { - port_set.release_port(self.local_endpoint().port); + if let SchemeFile::Socket(SocketFile { data, .. }) = *file { + if let Some(endpoint) = self.local_endpoint() { + // Socket was connected on some port + port_set.release_port(endpoint.port); + } else if let Some(endpoint) = data { + // Socket was listening on some port + port_set.release_port(endpoint.port); + } } Ok(()) } @@ -163,53 +196,98 @@ impl<'a> SchemeSocket for TcpSocket<'a> { }; let file = match path { - "listen" => if let SchemeFile::Socket(ref tcp_handle) = *file { - if !is_active { - if tcp_handle.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK { - return Err(SyscallError::new(syscall::EAGAIN)); - } else { - return Ok(None); - } - } - trace!("TCP creating new listening socket"); - let new_handle = SchemeFile::Socket(tcp_handle.clone_with_data(())); + "listen" => { + if let SchemeFile::Socket(ref tcp_handle) = *file { + let Some(listen_enpoint) = tcp_handle.data else { + // This socket is not listening so we can't accept a connection + return Err(SyscallError::new(syscall::EINVAL)); + }; - let rx_packets = vec![0; 0xffff]; - let tx_packets = vec![0; 0xffff]; - let rx_buffer = TcpSocketBuffer::new(rx_packets); - let tx_buffer = TcpSocketBuffer::new(tx_packets); - let socket = TcpSocket::new(rx_buffer, tx_buffer); - let new_socket_handle = socket_set.add(socket); - { - let mut tcp_socket = socket_set.get::(new_socket_handle); - tcp_socket - .listen(local_endpoint) - .expect("Can't listen on local endpoint"); + if !is_active { + // Socket listening but no connection received + if tcp_handle.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK { + return Err(SyscallError::new(syscall::EAGAIN)); + } else { + return Ok(None); + } + } + trace!("TCP creating new listening socket"); + // We pass None as data because this new handle is to the active connection so + // not a listening socket + let new_handle = SchemeFile::Socket(tcp_handle.clone_with_data(None)); + + // Creating a socket to continue listening + let rx_packets = vec![0; 0xffff]; + let tx_packets = vec![0; 0xffff]; + let rx_buffer = TcpSocketBuffer::new(rx_packets); + let tx_buffer = TcpSocketBuffer::new(tx_packets); + let socket = TcpSocket::new(rx_buffer, tx_buffer); + let new_socket_handle = socket_set.add(socket); + { + let tcp_socket = socket_set.get_mut::(new_socket_handle); + tcp_socket + .listen(listen_enpoint) + .expect("Can't listen on local endpoint"); + } + // We got a new connection to the socket so acquire the port + port_set.acquire_port( + local_endpoint + .expect("Socket was active so local endpoint must be set") + .port, + ); + return Ok(Some(( + new_handle, + Some((new_socket_handle, Some(listen_enpoint))), + ))); + } else { + return Err(SyscallError::new(syscall::EBADF)); } - port_set.acquire_port(local_endpoint.port); - return Ok(Some((new_handle, Some((new_socket_handle, ()))))); - } else { - return Err(SyscallError::new(syscall::EBADF)); - }, + } _ => { trace!("TCP dup unknown {}", path); if let SchemeFile::Socket(ref tcp_handle) = *file { - SchemeFile::Socket(tcp_handle.clone_with_data(())) + SchemeFile::Socket(tcp_handle.clone_with_data(tcp_handle.data)) } else { - SchemeFile::Socket(SocketFile::new_with_data(socket_handle, ())) + SchemeFile::Socket(SocketFile::new_with_data(socket_handle, None)) } } }; if let SchemeFile::Socket(_) = file { - port_set.acquire_port(local_endpoint.port); + if let Some(local_endpoint) = local_endpoint { + port_set.acquire_port(local_endpoint.port); + } } Ok(Some((file, None))) } - fn fpath(&self, _: &SchemeFile, buf: &mut [u8]) -> SyscallResult { - let path = format!("tcp:{}/{}", self.remote_endpoint(), self.local_endpoint()); + fn fpath(&self, file: &SchemeFile, buf: &mut [u8]) -> SyscallResult { + let unspecified = "0.0.0.0:0"; + let mut path = String::from("tcp:"); + match self.remote_endpoint() { + Some(endpoint) => write!(&mut path, "{}", endpoint).unwrap(), + None => path.push_str(unspecified), + } + path.push('/'); + match (self.local_endpoint(), file) { + (Some(endpoint), _) => write!(&mut path, "{}", endpoint).unwrap(), + ( + None, + SchemeFile::Socket(SocketFile { + data: Some(endpoint), + .. + }), + ) => { + if endpoint.is_specified() { + write!(&mut path, "{}", endpoint).unwrap() + } else { + write!(&mut path, "0.0.0.0:{}", endpoint.port).unwrap() + } + } + _ => path.push_str(unspecified), + } + trace!("fpath: {}", path); let path = path.as_bytes(); let mut i = 0; diff --git a/src/smolnetd/scheme/udp.rs b/src/smolnetd/scheme/udp.rs index 646821f625..0ed4209446 100644 --- a/src/smolnetd/scheme/udp.rs +++ b/src/smolnetd/scheme/udp.rs @@ -1,19 +1,20 @@ -use smoltcp::socket::{SocketHandle, UdpPacketMetadata, UdpSocket, UdpSocketBuffer}; -use smoltcp::wire::IpEndpoint; +use smoltcp::socket::udp::{PacketMetadata as UdpPacketMetadata, Socket as UdpSocket, PacketBuffer as UdpSocketBuffer}; +use smoltcp::iface::SocketHandle; +use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; use std::str; use syscall::{Error as SyscallError, Result as SyscallResult}; use syscall; -use crate::device::NetworkDevice; use crate::port_set::PortSet; -use super::socket::{DupResult, SchemeFile, SchemeSocket, SocketFile, SocketScheme}; +use crate::router::Router; +use super::socket::{DupResult, SchemeFile, SchemeSocket, SocketFile, SocketScheme, Context}; use super::{parse_endpoint, Smolnetd, SocketSet}; -pub type UdpScheme = SocketScheme>; +pub type UdpScheme = SocketScheme>; -impl<'a, 'b> SchemeSocket for UdpSocket<'a, 'b> { +impl<'a> SchemeSocket for UdpSocket<'a> { type SchemeDataT = PortSet; - type DataT = IpEndpoint; + type DataT = IpListenEndpoint; type SettingT = (); fn new_scheme_data() -> Self::SchemeDataT { @@ -61,6 +62,7 @@ impl<'a, 'b> SchemeSocket for UdpSocket<'a, 'b> { path: &str, uid: u32, port_set: &mut Self::SchemeDataT, + _context: &Context ) -> SyscallResult<(SocketHandle, Self::DataT)> { let mut parts = path.split('/'); let remote_endpoint = parse_endpoint(parts.next().unwrap_or("")); @@ -72,11 +74,11 @@ impl<'a, 'b> SchemeSocket for UdpSocket<'a, 'b> { let rx_buffer = UdpSocketBuffer::new( vec![UdpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], - vec![0; NetworkDevice::MTU * Smolnetd::SOCKET_BUFFER_SIZE] + vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE] ); let tx_buffer = UdpSocketBuffer::new( vec![UdpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], - vec![0; NetworkDevice::MTU * Smolnetd::SOCKET_BUFFER_SIZE] + vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE] ); let udp_socket = UdpSocket::new(rx_buffer, tx_buffer); @@ -90,11 +92,12 @@ impl<'a, 'b> SchemeSocket for UdpSocket<'a, 'b> { let socket_handle = socket_set.add(udp_socket); - let mut udp_socket = socket_set.get::(socket_handle); + let udp_socket = socket_set.get_mut::(socket_handle); udp_socket .bind(local_endpoint) .expect("Can't bind udp socket to local endpoint"); + Ok((socket_handle, remote_endpoint)) } @@ -118,7 +121,9 @@ impl<'a, 'b> SchemeSocket for UdpSocket<'a, 'b> { return Err(SyscallError::new(syscall::EADDRNOTAVAIL)); } if self.can_send() { - self.send_slice(buf, file.data).expect("Can't send slice"); + let endpoint = file.data; + let endpoint = IpEndpoint::new(endpoint.addr.expect("If we can send, this should be specified"), endpoint.port); + self.send_slice(buf, endpoint).expect("Can't send slice"); Ok(Some(buf.len())) } else if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK { Err(SyscallError::new(syscall::EAGAIN))