├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── dnsd ├── main.rs └── scheme.rs ├── lib ├── error.rs ├── lib.rs └── logger.rs └── smolnetd ├── buffer_pool.rs ├── link ├── ethernet.rs ├── loopback.rs └── mod.rs ├── main.rs ├── port_set.rs ├── router ├── mod.rs └── route_table.rs └── scheme ├── icmp.rs ├── ip.rs ├── mod.rs ├── netcfg ├── mod.rs ├── nodes.rs └── notifier.rs ├── socket.rs ├── tcp.rs └── udp.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "android-tzdata" 7 | version = "0.1.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 10 | 11 | [[package]] 12 | name = "android_system_properties" 13 | version = "0.1.5" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 16 | dependencies = [ 17 | "libc", 18 | ] 19 | 20 | [[package]] 21 | name = "anyhow" 22 | version = "1.0.89" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" 25 | 26 | [[package]] 27 | name = "arg_parser" 28 | version = "0.1.0" 29 | source = "git+https://gitlab.redox-os.org/redox-os/arg-parser.git#1c434b55f3e1a0375ebcca85b3e88db7378e82fa" 30 | 31 | [[package]] 32 | name = "autocfg" 33 | version = "1.4.0" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 36 | 37 | [[package]] 38 | name = "bitflags" 39 | version = "1.3.2" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 42 | 43 | [[package]] 44 | name = "bitflags" 45 | version = "2.6.0" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 48 | 49 | [[package]] 50 | name = "bumpalo" 51 | version = "3.16.0" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 54 | 55 | [[package]] 56 | name = "byteorder" 57 | version = "0.5.3" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" 60 | 61 | [[package]] 62 | name = "byteorder" 63 | version = "1.5.0" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 66 | 67 | [[package]] 68 | name = "cc" 69 | version = "1.1.30" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" 72 | dependencies = [ 73 | "shlex", 74 | ] 75 | 76 | [[package]] 77 | name = "cfg-if" 78 | version = "0.1.10" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 81 | 82 | [[package]] 83 | name = "cfg-if" 84 | version = "1.0.0" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 87 | 88 | [[package]] 89 | name = "chrono" 90 | version = "0.4.38" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" 93 | dependencies = [ 94 | "android-tzdata", 95 | "iana-time-zone", 96 | "js-sys", 97 | "num-traits", 98 | "wasm-bindgen", 99 | "windows-targets", 100 | ] 101 | 102 | [[package]] 103 | name = "core-foundation-sys" 104 | version = "0.8.7" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 107 | 108 | [[package]] 109 | name = "crossbeam-channel" 110 | version = "0.5.13" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" 113 | dependencies = [ 114 | "crossbeam-utils", 115 | ] 116 | 117 | [[package]] 118 | name = "crossbeam-utils" 119 | version = "0.8.20" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" 122 | 123 | [[package]] 124 | name = "defmt" 125 | version = "0.3.8" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "a99dd22262668b887121d4672af5a64b238f026099f1a2a1b322066c9ecfe9e0" 128 | dependencies = [ 129 | "bitflags 1.3.2", 130 | "defmt-macros", 131 | ] 132 | 133 | [[package]] 134 | name = "defmt-macros" 135 | version = "0.3.9" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "e3a9f309eff1f79b3ebdf252954d90ae440599c26c2c553fe87a2d17195f2dcb" 138 | dependencies = [ 139 | "defmt-parser", 140 | "proc-macro-error", 141 | "proc-macro2", 142 | "quote", 143 | "syn 2.0.79", 144 | ] 145 | 146 | [[package]] 147 | name = "defmt-parser" 148 | version = "0.3.4" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "ff4a5fefe330e8d7f31b16a318f9ce81000d8e35e69b93eae154d16d2278f70f" 151 | dependencies = [ 152 | "thiserror", 153 | ] 154 | 155 | [[package]] 156 | name = "dns-parser" 157 | version = "0.7.1" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "f7020f6760aea312d43d23cb83bf6c0c49f162541db8b02bf95209ac51dc253d" 160 | dependencies = [ 161 | "byteorder 1.5.0", 162 | "quick-error", 163 | ] 164 | 165 | [[package]] 166 | name = "hash32" 167 | version = "0.3.1" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" 170 | dependencies = [ 171 | "byteorder 1.5.0", 172 | ] 173 | 174 | [[package]] 175 | name = "heapless" 176 | version = "0.8.0" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" 179 | dependencies = [ 180 | "hash32", 181 | "stable_deref_trait", 182 | ] 183 | 184 | [[package]] 185 | name = "iana-time-zone" 186 | version = "0.1.61" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" 189 | dependencies = [ 190 | "android_system_properties", 191 | "core-foundation-sys", 192 | "iana-time-zone-haiku", 193 | "js-sys", 194 | "wasm-bindgen", 195 | "windows-core", 196 | ] 197 | 198 | [[package]] 199 | name = "iana-time-zone-haiku" 200 | version = "0.1.2" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 203 | dependencies = [ 204 | "cc", 205 | ] 206 | 207 | [[package]] 208 | name = "idna" 209 | version = "0.1.5" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" 212 | dependencies = [ 213 | "matches", 214 | "unicode-bidi", 215 | "unicode-normalization", 216 | ] 217 | 218 | [[package]] 219 | name = "ioslice" 220 | version = "0.6.0" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "5e571352c8a3b89074d12e3ee5173ffe162159105352aaaf1fc5764da747e31b" 223 | dependencies = [ 224 | "libc", 225 | "winapi", 226 | ] 227 | 228 | [[package]] 229 | name = "js-sys" 230 | version = "0.3.72" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" 233 | dependencies = [ 234 | "wasm-bindgen", 235 | ] 236 | 237 | [[package]] 238 | name = "libc" 239 | version = "0.2.160" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "f0b21006cd1874ae9e650973c565615676dc4a274c965bb0a73796dac838ce4f" 242 | 243 | [[package]] 244 | name = "libredox" 245 | version = "0.1.3" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" 248 | dependencies = [ 249 | "bitflags 2.6.0", 250 | "ioslice", 251 | "libc", 252 | "redox_syscall", 253 | ] 254 | 255 | [[package]] 256 | name = "log" 257 | version = "0.4.22" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 260 | 261 | [[package]] 262 | name = "managed" 263 | version = "0.8.0" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" 266 | 267 | [[package]] 268 | name = "matches" 269 | version = "0.1.10" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" 272 | 273 | [[package]] 274 | name = "net2" 275 | version = "0.2.37" 276 | source = "git+https://gitlab.redox-os.org/redox-os/net2-rs.git?branch=master#db0604dcb0a355e6b2fa5bcaad8f175bd6aeb5aa" 277 | dependencies = [ 278 | "cfg-if 0.1.10", 279 | "libc", 280 | "winapi", 281 | ] 282 | 283 | [[package]] 284 | name = "netutils" 285 | version = "0.1.0" 286 | source = "git+https://gitlab.redox-os.org/redox-os/netutils.git#197e0ed0a0ce637653b33f6076c08afeac8cda16" 287 | dependencies = [ 288 | "anyhow", 289 | "arg_parser", 290 | "libc", 291 | "libredox", 292 | "net2", 293 | "ntpclient", 294 | "pbr", 295 | "redox-daemon", 296 | "redox_event", 297 | "redox_termios", 298 | "termion", 299 | "url", 300 | ] 301 | 302 | [[package]] 303 | name = "ntpclient" 304 | version = "0.0.1" 305 | source = "git+https://github.com/willem66745/ntpclient-rust#7e3bdf60eb940825789a8da5181025320e3050b0" 306 | dependencies = [ 307 | "byteorder 0.5.3", 308 | "time", 309 | ] 310 | 311 | [[package]] 312 | name = "num-traits" 313 | version = "0.2.19" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 316 | dependencies = [ 317 | "autocfg", 318 | ] 319 | 320 | [[package]] 321 | name = "numtoa" 322 | version = "0.2.4" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "6aa2c4e539b869820a2b82e1aef6ff40aa85e65decdd5185e83fb4b1249cd00f" 325 | 326 | [[package]] 327 | name = "once_cell" 328 | version = "1.20.2" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 331 | 332 | [[package]] 333 | name = "pbr" 334 | version = "1.1.1" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "ed5827dfa0d69b6c92493d6c38e633bbaa5937c153d0d7c28bf12313f8c6d514" 337 | dependencies = [ 338 | "crossbeam-channel", 339 | "libc", 340 | "winapi", 341 | ] 342 | 343 | [[package]] 344 | name = "percent-encoding" 345 | version = "1.0.1" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" 348 | 349 | [[package]] 350 | name = "proc-macro-error" 351 | version = "1.0.4" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 354 | dependencies = [ 355 | "proc-macro-error-attr", 356 | "proc-macro2", 357 | "quote", 358 | "syn 1.0.109", 359 | "version_check", 360 | ] 361 | 362 | [[package]] 363 | name = "proc-macro-error-attr" 364 | version = "1.0.4" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 367 | dependencies = [ 368 | "proc-macro2", 369 | "quote", 370 | "version_check", 371 | ] 372 | 373 | [[package]] 374 | name = "proc-macro2" 375 | version = "1.0.88" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" 378 | dependencies = [ 379 | "unicode-ident", 380 | ] 381 | 382 | [[package]] 383 | name = "quick-error" 384 | version = "1.2.3" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 387 | 388 | [[package]] 389 | name = "quote" 390 | version = "1.0.37" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 393 | dependencies = [ 394 | "proc-macro2", 395 | ] 396 | 397 | [[package]] 398 | name = "redox-daemon" 399 | version = "0.1.3" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "7fbdeffdb03cf2961b211a2023e683d71f2c225ea93404da5dc34b0dc94f0341" 402 | dependencies = [ 403 | "libc", 404 | "libredox", 405 | ] 406 | 407 | [[package]] 408 | name = "redox-log" 409 | version = "0.1.4" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "81460b1526438123d16f0c968dbe42ba7f61e99645109b70e57864a8b66710fb" 412 | dependencies = [ 413 | "chrono", 414 | "log", 415 | "smallvec", 416 | "termion", 417 | ] 418 | 419 | [[package]] 420 | name = "redox_event" 421 | version = "0.4.1" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "69609faa5d5992247a4ef379917bb3e39be281405d6a0ccd4f942429400b956f" 424 | dependencies = [ 425 | "bitflags 2.6.0", 426 | "libredox", 427 | ] 428 | 429 | [[package]] 430 | name = "redox_netstack" 431 | version = "0.1.0" 432 | dependencies = [ 433 | "anyhow", 434 | "dns-parser", 435 | "ioslice", 436 | "libredox", 437 | "log", 438 | "netutils", 439 | "redox-daemon", 440 | "redox-log", 441 | "redox_event", 442 | "redox_syscall", 443 | "smoltcp", 444 | ] 445 | 446 | [[package]] 447 | name = "redox_syscall" 448 | version = "0.5.7" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" 451 | dependencies = [ 452 | "bitflags 2.6.0", 453 | ] 454 | 455 | [[package]] 456 | name = "redox_termios" 457 | version = "0.1.3" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb" 460 | 461 | [[package]] 462 | name = "shlex" 463 | version = "1.3.0" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 466 | 467 | [[package]] 468 | name = "smallvec" 469 | version = "1.13.2" 470 | source = "registry+https://github.com/rust-lang/crates.io-index" 471 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 472 | 473 | [[package]] 474 | name = "smoltcp" 475 | version = "0.12.0" 476 | source = "registry+https://github.com/rust-lang/crates.io-index" 477 | checksum = "dad095989c1533c1c266d9b1e8d70a1329dd3723c3edac6d03bbd67e7bf6f4bb" 478 | dependencies = [ 479 | "bitflags 1.3.2", 480 | "byteorder 1.5.0", 481 | "cfg-if 1.0.0", 482 | "defmt", 483 | "heapless", 484 | "log", 485 | "managed", 486 | ] 487 | 488 | [[package]] 489 | name = "stable_deref_trait" 490 | version = "1.2.0" 491 | source = "registry+https://github.com/rust-lang/crates.io-index" 492 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 493 | 494 | [[package]] 495 | name = "syn" 496 | version = "1.0.109" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 499 | dependencies = [ 500 | "proc-macro2", 501 | "unicode-ident", 502 | ] 503 | 504 | [[package]] 505 | name = "syn" 506 | version = "2.0.79" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" 509 | dependencies = [ 510 | "proc-macro2", 511 | "quote", 512 | "unicode-ident", 513 | ] 514 | 515 | [[package]] 516 | name = "termion" 517 | version = "4.0.3" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "7eaa98560e51a2cf4f0bb884d8b2098a9ea11ecf3b7078e9c68242c74cc923a7" 520 | dependencies = [ 521 | "libc", 522 | "libredox", 523 | "numtoa", 524 | "redox_termios", 525 | ] 526 | 527 | [[package]] 528 | name = "thiserror" 529 | version = "1.0.64" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" 532 | dependencies = [ 533 | "thiserror-impl", 534 | ] 535 | 536 | [[package]] 537 | name = "thiserror-impl" 538 | version = "1.0.64" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" 541 | dependencies = [ 542 | "proc-macro2", 543 | "quote", 544 | "syn 2.0.79", 545 | ] 546 | 547 | [[package]] 548 | name = "time" 549 | version = "0.1.45" 550 | source = "registry+https://github.com/rust-lang/crates.io-index" 551 | checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" 552 | dependencies = [ 553 | "libc", 554 | "wasi", 555 | "winapi", 556 | ] 557 | 558 | [[package]] 559 | name = "tinyvec" 560 | version = "1.8.0" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" 563 | dependencies = [ 564 | "tinyvec_macros", 565 | ] 566 | 567 | [[package]] 568 | name = "tinyvec_macros" 569 | version = "0.1.1" 570 | source = "registry+https://github.com/rust-lang/crates.io-index" 571 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 572 | 573 | [[package]] 574 | name = "unicode-bidi" 575 | version = "0.3.17" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" 578 | 579 | [[package]] 580 | name = "unicode-ident" 581 | version = "1.0.13" 582 | source = "registry+https://github.com/rust-lang/crates.io-index" 583 | checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" 584 | 585 | [[package]] 586 | name = "unicode-normalization" 587 | version = "0.1.24" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" 590 | dependencies = [ 591 | "tinyvec", 592 | ] 593 | 594 | [[package]] 595 | name = "url" 596 | version = "1.7.2" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" 599 | dependencies = [ 600 | "idna", 601 | "matches", 602 | "percent-encoding", 603 | ] 604 | 605 | [[package]] 606 | name = "version_check" 607 | version = "0.9.5" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 610 | 611 | [[package]] 612 | name = "wasi" 613 | version = "0.10.0+wasi-snapshot-preview1" 614 | source = "registry+https://github.com/rust-lang/crates.io-index" 615 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 616 | 617 | [[package]] 618 | name = "wasm-bindgen" 619 | version = "0.2.95" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" 622 | dependencies = [ 623 | "cfg-if 1.0.0", 624 | "once_cell", 625 | "wasm-bindgen-macro", 626 | ] 627 | 628 | [[package]] 629 | name = "wasm-bindgen-backend" 630 | version = "0.2.95" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" 633 | dependencies = [ 634 | "bumpalo", 635 | "log", 636 | "once_cell", 637 | "proc-macro2", 638 | "quote", 639 | "syn 2.0.79", 640 | "wasm-bindgen-shared", 641 | ] 642 | 643 | [[package]] 644 | name = "wasm-bindgen-macro" 645 | version = "0.2.95" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" 648 | dependencies = [ 649 | "quote", 650 | "wasm-bindgen-macro-support", 651 | ] 652 | 653 | [[package]] 654 | name = "wasm-bindgen-macro-support" 655 | version = "0.2.95" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" 658 | dependencies = [ 659 | "proc-macro2", 660 | "quote", 661 | "syn 2.0.79", 662 | "wasm-bindgen-backend", 663 | "wasm-bindgen-shared", 664 | ] 665 | 666 | [[package]] 667 | name = "wasm-bindgen-shared" 668 | version = "0.2.95" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" 671 | 672 | [[package]] 673 | name = "winapi" 674 | version = "0.3.9" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 677 | dependencies = [ 678 | "winapi-i686-pc-windows-gnu", 679 | "winapi-x86_64-pc-windows-gnu", 680 | ] 681 | 682 | [[package]] 683 | name = "winapi-i686-pc-windows-gnu" 684 | version = "0.4.0" 685 | source = "registry+https://github.com/rust-lang/crates.io-index" 686 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 687 | 688 | [[package]] 689 | name = "winapi-x86_64-pc-windows-gnu" 690 | version = "0.4.0" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 693 | 694 | [[package]] 695 | name = "windows-core" 696 | version = "0.52.0" 697 | source = "registry+https://github.com/rust-lang/crates.io-index" 698 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 699 | dependencies = [ 700 | "windows-targets", 701 | ] 702 | 703 | [[package]] 704 | name = "windows-targets" 705 | version = "0.52.6" 706 | source = "registry+https://github.com/rust-lang/crates.io-index" 707 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 708 | dependencies = [ 709 | "windows_aarch64_gnullvm", 710 | "windows_aarch64_msvc", 711 | "windows_i686_gnu", 712 | "windows_i686_gnullvm", 713 | "windows_i686_msvc", 714 | "windows_x86_64_gnu", 715 | "windows_x86_64_gnullvm", 716 | "windows_x86_64_msvc", 717 | ] 718 | 719 | [[package]] 720 | name = "windows_aarch64_gnullvm" 721 | version = "0.52.6" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 724 | 725 | [[package]] 726 | name = "windows_aarch64_msvc" 727 | version = "0.52.6" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 730 | 731 | [[package]] 732 | name = "windows_i686_gnu" 733 | version = "0.52.6" 734 | source = "registry+https://github.com/rust-lang/crates.io-index" 735 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 736 | 737 | [[package]] 738 | name = "windows_i686_gnullvm" 739 | version = "0.52.6" 740 | source = "registry+https://github.com/rust-lang/crates.io-index" 741 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 742 | 743 | [[package]] 744 | name = "windows_i686_msvc" 745 | version = "0.52.6" 746 | source = "registry+https://github.com/rust-lang/crates.io-index" 747 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 748 | 749 | [[package]] 750 | name = "windows_x86_64_gnu" 751 | version = "0.52.6" 752 | source = "registry+https://github.com/rust-lang/crates.io-index" 753 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 754 | 755 | [[package]] 756 | name = "windows_x86_64_gnullvm" 757 | version = "0.52.6" 758 | source = "registry+https://github.com/rust-lang/crates.io-index" 759 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 760 | 761 | [[package]] 762 | name = "windows_x86_64_msvc" 763 | version = "0.52.6" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 766 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "redox_netstack" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "dnsd" 8 | path = "src/dnsd/main.rs" 9 | 10 | [[bin]] 11 | name = "smolnetd" 12 | path = "src/smolnetd/main.rs" 13 | 14 | [lib] 15 | name = "redox_netstack" 16 | path = "src/lib/lib.rs" 17 | 18 | [dependencies] 19 | netutils = { git = "https://gitlab.redox-os.org/redox-os/netutils.git" } 20 | redox_event = "0.4.1" 21 | redox-daemon = "0.1.2" 22 | redox_syscall = "0.5" 23 | redox-log = "0.1" 24 | dns-parser = "0.7.1" 25 | libredox = { version = "0.1.3", features = ["mkns"] } 26 | anyhow = "1.0.81" 27 | ioslice = "0.6.0" 28 | 29 | [dependencies.log] 30 | version = "0.4" 31 | default-features = false 32 | features = ["release_max_level_warn"] 33 | 34 | [dependencies.smoltcp] 35 | version = "0.12.0" 36 | default-features = false 37 | features = [ 38 | "std", 39 | "medium-ethernet", "medium-ip", 40 | "proto-ipv4", 41 | "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "socket-tcp-cubic", 42 | "iface-max-addr-count-8", 43 | "log" 44 | ] 45 | #For debugging: "log", "verbose" 46 | 47 | [profile.release] 48 | lto = true 49 | 50 | [patch.crates-io] 51 | net2 = { git = "https://gitlab.redox-os.org/redox-os/net2-rs.git", branch = "master" } 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Redox OS Developers 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Redox OS userspace networking stack 2 | 3 | This repository contains the networking stack for Redox OS. It makes use of [smoltcp](https://github.com/m-labs/smoltcp). 4 | 5 | ## How To Contribute 6 | 7 | To learn how to contribute to this system component you need to read the following document: 8 | 9 | - [CONTRIBUTING.md](https://gitlab.redox-os.org/redox-os/redox/-/blob/master/CONTRIBUTING.md) 10 | 11 | ## Development 12 | 13 | To learn how to do development with this system component inside the Redox build system you need to read the [Build System](https://doc.redox-os.org/book/build-system-reference.html) and [Coding and Building](https://doc.redox-os.org/book/coding-and-building.html) pages. 14 | 15 | ### How To Build 16 | 17 | To build this system component you need to download the Redox build system, you can learn how to do it on the [Building Redox](https://doc.redox-os.org/book/podman-build.html) page. 18 | 19 | This is necessary because they only work with cross-compilation to a Redox virtual machine, but you can do some testing from Linux. 20 | -------------------------------------------------------------------------------- /src/dnsd/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | 4 | use anyhow::{Context, Result}; 5 | use event::EventQueue; 6 | use ioslice::IoSlice; 7 | use libredox::Fd; 8 | use redox_netstack::logger; 9 | use scheme::Dnsd; 10 | use std::fs::File; 11 | use std::os::unix::io::{FromRawFd, RawFd}; 12 | use std::process; 13 | 14 | mod scheme; 15 | 16 | fn run(daemon: redox_daemon::Daemon) -> Result<()> { 17 | use libredox::flag::*; 18 | 19 | let dns_fd = 20 | Fd::open(":dns", O_RDWR | O_CREAT | O_NONBLOCK, 0).context("failed to open :dns")?; 21 | 22 | let time_path = format!("/scheme/time/{}", CLOCK_MONOTONIC); 23 | let time_fd = Fd::open(&time_path, O_RDWR, 0).context("failed to open time")?; 24 | 25 | let nameserver_fd = Fd::open( 26 | "/scheme/netcfg/resolv/nameserver", 27 | O_RDWR | O_CREAT | O_NONBLOCK, 28 | 0, 29 | ) 30 | .context("failed to open nameserver")?; 31 | 32 | let event_queue = EventQueue::::new().context("failed to create event queue")?; 33 | 34 | event_queue 35 | .subscribe( 36 | dns_fd.raw(), 37 | EventSource::DnsScheme, 38 | event::EventFlags::READ, 39 | ) 40 | .context("failed to listen to time events")?; 41 | event_queue 42 | .subscribe( 43 | nameserver_fd.raw(), 44 | EventSource::NameserverScheme, 45 | event::EventFlags::READ, 46 | ) 47 | .context("failed to listen to nameserver socket events")?; 48 | event_queue 49 | .subscribe(time_fd.raw(), EventSource::Timer, event::EventFlags::READ) 50 | .context("failed to listen to timer events")?; 51 | 52 | let (dns_file, time_file) = unsafe { 53 | ( 54 | File::from_raw_fd(dns_fd.into_raw() as RawFd), 55 | File::from_raw_fd(time_fd.into_raw() as RawFd), 56 | ) 57 | }; 58 | 59 | let mut dnsd = Dnsd::new(dns_file, time_file, &event_queue); 60 | 61 | let new_ns = 62 | libredox::call::mkns(&[IoSlice::new(b"dns")]).expect("dnsd: failed to create namespace"); 63 | libredox::call::setrens(new_ns, new_ns).expect("dnsd: failed to enter namespace"); 64 | 65 | daemon.ready().expect("dnsd: failed to notify parent"); 66 | 67 | for event_res in event_queue.iter() { 68 | let event = event_res.context("failed to read from event queue")?; 69 | match event.user_data { 70 | EventSource::DnsScheme => { 71 | if !dnsd.on_dns_file_event()? { 72 | break; 73 | } 74 | } 75 | EventSource::NameserverScheme => dnsd.on_nameserver_event()?, 76 | EventSource::Timer => dnsd.on_time_event()?, 77 | EventSource::Other => dnsd.on_unknown_fd_event(event.fd as RawFd)?, 78 | } 79 | } 80 | Ok(()) 81 | } 82 | 83 | fn main() { 84 | redox_daemon::Daemon::new(move |daemon| { 85 | logger::init_logger("dnsd"); 86 | if let Err(err) = run(daemon) { 87 | error!("dnsd: {}", err); 88 | process::exit(1); 89 | } 90 | process::exit(0); 91 | }) 92 | .expect("dnsd: failed to daemonize"); 93 | } 94 | 95 | event::user_data! { 96 | enum EventSource { 97 | DnsScheme, 98 | NameserverScheme, 99 | Timer, 100 | Other, 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/dnsd/scheme.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::ToOwned; 2 | use std::collections::btree_map::Entry; 3 | use std::collections::VecDeque; 4 | use std::collections::{BTreeMap, BTreeSet}; 5 | use std::fs::File; 6 | use std::io::{ErrorKind, Read, Write}; 7 | use std::mem; 8 | use std::net::Ipv4Addr; 9 | use std::os::unix::io::RawFd; 10 | use std::rc::Rc; 11 | use std::str; 12 | use std::str::FromStr; 13 | 14 | use libredox::flag; 15 | use syscall; 16 | use syscall::data::TimeSpec; 17 | use syscall::{ 18 | Error as SyscallError, EventFlags as SyscallEventFlags, Packet as SyscallPacket, 19 | Result as SyscallResult, SchemeMut, 20 | }; 21 | 22 | use event::EventQueue; 23 | use redox_netstack::error::{Error, Result}; 24 | 25 | use dns_parser::{Builder, Packet as DNSPacket, RRData, ResponseCode}; 26 | use dns_parser::{QueryClass, QueryType}; 27 | 28 | use crate::EventSource; 29 | 30 | enum DnsFile { 31 | Resolved { data: Rc<[u8]>, pos: usize }, 32 | Waiting { domain: String }, 33 | Timeout, 34 | Failed, 35 | } 36 | 37 | enum Domain { 38 | Resolved { 39 | data: Rc<[u8]>, 40 | }, 41 | Requested { 42 | waiting_fds: BTreeSet, 43 | socket_fd: RawFd, 44 | }, 45 | } 46 | 47 | enum DnsParsingResult { 48 | WakeUpFiles(BTreeSet), 49 | FailFiles(BTreeSet), 50 | } 51 | 52 | struct Domains { 53 | nameserver: Ipv4Addr, 54 | domains: BTreeMap, Domain>, 55 | requests: BTreeMap>, 56 | resolved_timeouts: VecDeque<(TimeSpec, Rc)>, 57 | requested_timeouts: VecDeque<(TimeSpec, Rc)>, 58 | } 59 | 60 | impl Domains { 61 | fn new() -> Domains { 62 | let mut domains = Domains { 63 | nameserver: Ipv4Addr::new(8, 8, 8, 8), 64 | domains: BTreeMap::new(), 65 | requests: BTreeMap::new(), 66 | resolved_timeouts: VecDeque::new(), 67 | requested_timeouts: VecDeque::new(), 68 | }; 69 | domains.update_nameserver(); 70 | domains 71 | } 72 | 73 | pub fn update_nameserver(&mut self) { 74 | if let Ok(mut file) = File::open("/scheme/netcfg/resolv/nameserver") { 75 | let mut nameserver = String::new(); 76 | if file.read_to_string(&mut nameserver).is_ok() { 77 | if let Some(line) = nameserver.lines().next() { 78 | if let Ok(ip) = Ipv4Addr::from_str(line) { 79 | trace!("Changing nameserver to {}", ip); 80 | self.nameserver = ip; 81 | } 82 | } 83 | } 84 | } 85 | } 86 | 87 | fn request_domain(&mut self, domain: &str, queue: &EventQueue) -> Option { 88 | trace!("Requesting domain {}", domain); 89 | let mut builder = Builder::new_query(1, true); 90 | builder.add_question(domain, QueryType::A, QueryClass::IN); 91 | let packet = builder.build().ok()?; 92 | let udp_fd = libredox::call::open( 93 | &format!("udp:{}:53", self.nameserver), 94 | libredox::flag::O_RDWR | libredox::flag::O_CREAT | libredox::flag::O_NONBLOCK, 95 | 0, 96 | ) 97 | .ok()?; 98 | if libredox::call::write(udp_fd, &packet) != Ok(packet.len()) { 99 | libredox::call::close(udp_fd).ok()?; 100 | return None; 101 | } 102 | queue 103 | .subscribe(udp_fd, EventSource::Other, event::EventFlags::READ) 104 | .ok()?; 105 | self.requests 106 | .insert(udp_fd as RawFd, domain.to_owned().into()); 107 | Some(udp_fd as RawFd) 108 | } 109 | 110 | fn on_time_event( 111 | &mut self, 112 | cur_time: &TimeSpec, 113 | queue: &EventQueue, 114 | ) -> Result> { 115 | while let Some((timeout, domain)) = self.resolved_timeouts.pop_front() { 116 | if timeout.tv_sec > cur_time.tv_sec 117 | || (timeout.tv_sec == cur_time.tv_sec && timeout.tv_nsec > cur_time.tv_nsec) 118 | { 119 | self.resolved_timeouts.push_front((timeout, domain)); 120 | break; 121 | } 122 | trace!("Timing out resolved domain {:?}", domain); 123 | match self.domains.entry(domain) { 124 | Entry::Vacant(_) => {} 125 | Entry::Occupied(e) => { 126 | if let Domain::Resolved { .. } = *e.get() { 127 | e.remove(); 128 | } 129 | } 130 | } 131 | } 132 | 133 | let mut fds_to_wakeup = BTreeSet::new(); 134 | 135 | while let Some((timeout, domain)) = self.requested_timeouts.pop_front() { 136 | if timeout.tv_sec > cur_time.tv_sec 137 | || (timeout.tv_sec == cur_time.tv_sec && timeout.tv_nsec > cur_time.tv_nsec) 138 | { 139 | self.requested_timeouts.push_front((timeout, domain)); 140 | break; 141 | } 142 | trace!("Timing out requested domain {:?}", domain); 143 | match self.domains.entry(domain) { 144 | Entry::Vacant(_) => {} 145 | Entry::Occupied(e) => { 146 | if let Domain::Requested { .. } = *e.get() { 147 | if let Domain::Requested { 148 | mut waiting_fds, 149 | socket_fd, 150 | } = e.remove() 151 | { 152 | fds_to_wakeup.append(&mut waiting_fds); 153 | queue.unsubscribe(socket_fd as usize).map_err(|e| { 154 | Error::from_syscall_error(e.into(), "unsubscribe failure") 155 | })?; 156 | let _ = libredox::call::close(socket_fd as usize); 157 | } 158 | } 159 | } 160 | } 161 | } 162 | 163 | Ok(fds_to_wakeup) 164 | } 165 | 166 | fn on_fd_event( 167 | &mut self, 168 | fd: RawFd, 169 | cur_time: &TimeSpec, 170 | queue: &EventQueue, 171 | ) -> Option { 172 | let e = match self.requests.entry(fd) { 173 | Entry::Vacant(_) => { 174 | return None; 175 | } 176 | Entry::Occupied(e) => e, 177 | }; 178 | let mut buf = [0u8; 0x1000]; 179 | let readed = libredox::call::read(fd as usize, &mut buf).ok()?; 180 | if readed == 0 { 181 | return None; 182 | } 183 | let pkt = DNSPacket::parse(&buf).ok()?; 184 | if pkt.header.response_code != ResponseCode::NoError || pkt.answers.is_empty() { 185 | if let Some(query) = pkt.questions.iter().next() { 186 | if query.qname.to_string().to_lowercase() == e.get().as_ref() { 187 | queue.unsubscribe(fd as usize).ok()?; 188 | libredox::call::close(fd as usize).ok()?; 189 | let domain = e.remove(); 190 | self.requested_timeouts 191 | .retain(|&(_, ref d)| d.as_ref() != domain.as_ref()); 192 | if let Entry::Occupied(e) = self.domains.entry(domain) { 193 | let domain_data = e.remove(); 194 | return if let Domain::Requested { waiting_fds, .. } = domain_data { 195 | Some(DnsParsingResult::FailFiles(waiting_fds)) 196 | } else { 197 | None 198 | }; 199 | } 200 | } 201 | } 202 | return None; 203 | } 204 | let mut result = String::new(); 205 | for answer in pkt.answers { 206 | if answer.name.to_string().to_lowercase() != e.get().as_ref() { 207 | continue; 208 | } 209 | if let RRData::A(ip) = answer.data { 210 | result += &format!("{}\n", ip); 211 | } 212 | } 213 | if result.is_empty() { 214 | return None; 215 | } 216 | let data = Rc::from(result.into_bytes()); 217 | queue.unsubscribe(fd as usize).ok()?; 218 | libredox::call::close(fd as usize).ok()?; 219 | let domain = e.remove(); 220 | let mut domain_data = Domain::Resolved { data }; 221 | trace!("On FD event {} {} resolved", fd, domain); 222 | 223 | let mut resolved_timeout = *cur_time; 224 | resolved_timeout.tv_sec += Dnsd::RESOLVED_TIMEOUT_S; 225 | 226 | self.resolved_timeouts 227 | .push_back((resolved_timeout, Rc::clone(&domain))); 228 | 229 | self.requested_timeouts 230 | .retain(|&(_, ref d)| d.as_ref() != domain.as_ref()); 231 | 232 | match self.domains.entry(domain) { 233 | Entry::Vacant(e) => { 234 | e.insert(domain_data); 235 | None 236 | } 237 | Entry::Occupied(mut e) => { 238 | mem::swap(e.get_mut(), &mut domain_data); 239 | if let Domain::Requested { waiting_fds, .. } = domain_data { 240 | Some(DnsParsingResult::WakeUpFiles(waiting_fds)) 241 | } else { 242 | None 243 | } 244 | } 245 | } 246 | } 247 | 248 | fn file_from_domain( 249 | &mut self, 250 | domain: &str, 251 | fd: usize, 252 | cur_time: &TimeSpec, 253 | queue: &EventQueue, 254 | ) -> DnsFile { 255 | if let Some(domain_data) = self.domains.get_mut(domain) { 256 | match *domain_data { 257 | Domain::Resolved { ref data } => DnsFile::Resolved { 258 | data: Rc::clone(data), 259 | pos: 0, 260 | }, 261 | Domain::Requested { 262 | ref mut waiting_fds, 263 | .. 264 | } => { 265 | waiting_fds.insert(fd); 266 | DnsFile::Waiting { 267 | domain: domain.to_owned(), 268 | } 269 | } 270 | } 271 | } else { 272 | if let Some(socket_fd) = self.request_domain(domain, queue) { 273 | let mut waiting_fds = BTreeSet::new(); 274 | let domain = domain.to_owned().into(); 275 | waiting_fds.insert(fd); 276 | self.domains.insert( 277 | Rc::clone(&domain), 278 | Domain::Requested { 279 | waiting_fds, 280 | socket_fd, 281 | }, 282 | ); 283 | let mut timeout = *cur_time; 284 | timeout.tv_sec += Dnsd::REQUEST_TIMEOUT_S; 285 | self.requested_timeouts.push_back((timeout, domain)); 286 | } 287 | DnsFile::Waiting { 288 | domain: domain.to_owned(), 289 | } 290 | } 291 | } 292 | 293 | fn unwait_fd(&mut self, domain: &str, fd: usize) { 294 | if let Some(domain_data) = self.domains.get_mut(domain) { 295 | if let Domain::Requested { 296 | ref mut waiting_fds, 297 | .. 298 | } = *domain_data 299 | { 300 | waiting_fds.remove(&fd); 301 | } 302 | } 303 | } 304 | } 305 | 306 | pub struct Dnsd<'q> { 307 | dns_file: File, 308 | time_file: File, 309 | queue: &'q EventQueue, 310 | files: BTreeMap, 311 | domains: Domains, 312 | wait_map: BTreeMap, 313 | next_fd: usize, 314 | } 315 | 316 | impl<'q> Dnsd<'q> { 317 | const RESOLVED_TIMEOUT_S: i64 = 5 * 60; 318 | const REQUEST_TIMEOUT_S: i64 = 30; 319 | const TIME_EVENT_TIMEOUT_S: i64 = 5; 320 | 321 | pub fn new(dns_file: File, time_file: File, queue: &'q EventQueue) -> Self { 322 | Dnsd { 323 | dns_file, 324 | time_file, 325 | queue, 326 | files: BTreeMap::new(), 327 | domains: Domains::new(), 328 | wait_map: BTreeMap::new(), 329 | next_fd: 1, 330 | } 331 | } 332 | 333 | pub fn on_time_event(&mut self) -> Result<()> { 334 | let mut time = TimeSpec::default(); 335 | if self.time_file.read(&mut time)? < mem::size_of::() { 336 | return Err(Error::from_syscall_error( 337 | syscall::Error::new(syscall::EBADF), 338 | "Can't read current time", 339 | )); 340 | } 341 | 342 | let fds_to_wakeup = self.domains.on_time_event(&time, self.queue)?; 343 | if !fds_to_wakeup.is_empty() { 344 | for fd in &fds_to_wakeup { 345 | if let Some(file) = self.files.get_mut(fd) { 346 | *file = DnsFile::Timeout; 347 | } 348 | } 349 | self.wakeup_fds(&fds_to_wakeup); 350 | } 351 | 352 | time.tv_sec += Dnsd::TIME_EVENT_TIMEOUT_S; 353 | self.time_file 354 | .write_all(&time) 355 | .map_err(|e| Error::from_io_error(e, "Failed to write to time file"))?; 356 | Ok(()) 357 | } 358 | 359 | pub fn on_dns_file_event(&mut self) -> Result { 360 | loop { 361 | let mut packet = SyscallPacket::default(); 362 | match self.dns_file.read(&mut packet) { 363 | Ok(0) => { 364 | //TODO: Cleanup must occur 365 | return Ok(false); 366 | } 367 | Ok(_) => (), 368 | Err(err) => { 369 | if err.kind() == ErrorKind::WouldBlock { 370 | return Ok(true); 371 | } else { 372 | return Err(Error::from(err)); 373 | } 374 | } 375 | } 376 | // TODO: implement cancellation 377 | let a = packet.a; 378 | self.handle(&mut packet); 379 | if packet.a != (-syscall::EWOULDBLOCK) as usize { 380 | self.dns_file.write_all(&packet)?; 381 | } else { 382 | packet.a = a; 383 | self.handle_block(packet)?; 384 | } 385 | } 386 | } 387 | 388 | pub fn on_unknown_fd_event(&mut self, fd: RawFd) -> Result<()> { 389 | trace!("Unknown fd event {}", fd); 390 | let cur_time = libredox::call::clock_gettime(libredox::flag::CLOCK_MONOTONIC) 391 | .map_err(|e| Error::from_syscall_error(e.into(), "Can't get time"))?; 392 | // TODO 393 | let cur_time = TimeSpec { 394 | tv_sec: cur_time.tv_sec, 395 | tv_nsec: cur_time.tv_nsec as _, 396 | }; 397 | 398 | match self.domains.on_fd_event(fd, &cur_time, self.queue) { 399 | Some(DnsParsingResult::FailFiles(fds_to_fail)) => { 400 | for fd in &fds_to_fail { 401 | if let Some(file) = self.files.get_mut(fd) { 402 | *file = DnsFile::Failed; 403 | } 404 | } 405 | self.wakeup_fds(&fds_to_fail); 406 | } 407 | Some(DnsParsingResult::WakeUpFiles(fds_to_wakeup)) => { 408 | self.wakeup_fds(&fds_to_wakeup); 409 | } 410 | None => {} 411 | } 412 | Ok(()) 413 | } 414 | 415 | pub fn on_nameserver_event(&mut self) -> Result<()> { 416 | self.domains.update_nameserver(); 417 | Ok(()) 418 | } 419 | 420 | fn wakeup_fds(&mut self, fds_to_wakeup: &BTreeSet) { 421 | let mut syscall_packets = vec![]; 422 | for fd in fds_to_wakeup { 423 | if let Some(packet) = self.wait_map.remove(fd) { 424 | syscall_packets.push(packet); 425 | } 426 | } 427 | 428 | for mut packet in syscall_packets.drain(..) { 429 | self.handle(&mut packet); 430 | let _ = self.dns_file.write_all(&packet); 431 | } 432 | } 433 | 434 | fn handle_block(&mut self, packet: SyscallPacket) -> Result<()> { 435 | let fd = packet.b; 436 | self.wait_map.insert(fd, packet); 437 | Ok(()) 438 | } 439 | 440 | fn validate_domain(domain: &str) -> bool { 441 | if domain.len() > 256 { 442 | return false; 443 | } 444 | 445 | for part in domain.split('.') { 446 | if part.len() >= 63 { 447 | return false; 448 | } 449 | } 450 | 451 | true 452 | } 453 | } 454 | 455 | impl SchemeMut for Dnsd<'_> { 456 | fn open(&mut self, url: &str, _flags: usize, _uid: u32, _gid: u32) -> SyscallResult { 457 | let domain = url.to_lowercase(); 458 | if domain.is_empty() || !Dnsd::validate_domain(&domain) { 459 | return Err(SyscallError::new(syscall::EINVAL)); 460 | } 461 | let fd = self.next_fd; 462 | self.next_fd += 1; 463 | let cur_time = libredox::call::clock_gettime(flag::CLOCK_MONOTONIC)?; 464 | let dns_file = self.domains.file_from_domain( 465 | &domain, 466 | fd, 467 | &TimeSpec { 468 | tv_sec: cur_time.tv_sec, 469 | tv_nsec: cur_time.tv_nsec as i32, 470 | }, 471 | self.queue, 472 | ); 473 | self.files.insert(fd, dns_file); 474 | trace!("Open {} {}", &domain, fd); 475 | Ok(fd) 476 | } 477 | 478 | fn close(&mut self, fd: usize) -> SyscallResult { 479 | trace!("Close {}", fd); 480 | let file = self 481 | .files 482 | .get_mut(&fd) 483 | .ok_or_else(|| SyscallError::new(syscall::EBADF))?; 484 | 485 | if let DnsFile::Waiting { ref domain } = *file { 486 | self.domains.unwait_fd(domain, fd); 487 | } 488 | 489 | self.files.remove(&fd); 490 | Ok(0) 491 | } 492 | 493 | fn write(&mut self, _fd: usize, _buf: &[u8]) -> SyscallResult { 494 | Err(SyscallError::new(syscall::EINVAL)) 495 | } 496 | 497 | fn read(&mut self, fd: usize, buf: &mut [u8]) -> SyscallResult { 498 | trace!("Read {}", fd); 499 | let file = self 500 | .files 501 | .get_mut(&fd) 502 | .ok_or_else(|| SyscallError::new(syscall::EBADF))?; 503 | 504 | let cur_time = libredox::call::clock_gettime(flag::CLOCK_MONOTONIC)?; 505 | 506 | if let DnsFile::Waiting { ref domain } = *file { 507 | *file = self.domains.file_from_domain( 508 | domain, 509 | fd, 510 | &TimeSpec { 511 | tv_sec: cur_time.tv_sec, 512 | tv_nsec: cur_time.tv_nsec as i32, 513 | }, 514 | self.queue, 515 | ); 516 | } 517 | 518 | match *file { 519 | DnsFile::Resolved { 520 | ref data, 521 | ref mut pos, 522 | } => { 523 | let mut i = 0; 524 | while i < buf.len() && *pos < data.len() { 525 | buf[i] = data[*pos]; 526 | i += 1; 527 | *pos += 1; 528 | } 529 | Ok(i) 530 | } 531 | DnsFile::Waiting { .. } => Err(SyscallError::new(syscall::EWOULDBLOCK)), 532 | DnsFile::Timeout => Err(SyscallError::new(syscall::ETIMEDOUT)), 533 | DnsFile::Failed => Err(SyscallError::new(syscall::ENODATA)), 534 | } 535 | } 536 | 537 | fn fevent( 538 | &mut self, 539 | _fd: usize, 540 | _events: SyscallEventFlags, 541 | ) -> SyscallResult { 542 | Ok(SyscallEventFlags::empty()) 543 | } 544 | 545 | fn fsync(&mut self, _fd: usize) -> SyscallResult { 546 | Ok(0) 547 | } 548 | } 549 | -------------------------------------------------------------------------------- /src/lib/error.rs: -------------------------------------------------------------------------------- 1 | use std::convert; 2 | use std::fmt; 3 | use std::io::Error as IOError; 4 | use std::result; 5 | use syscall::error::Error as SyscallError; 6 | 7 | #[derive(Debug)] 8 | enum ErrorType { 9 | Syscall(SyscallError), 10 | IOError(IOError), 11 | Other, 12 | } 13 | 14 | #[derive(Debug)] 15 | pub struct Error { 16 | error_type: ErrorType, 17 | descr: String, 18 | } 19 | 20 | impl Error { 21 | pub fn from_syscall_error>(syscall_error: SyscallError, descr: S) -> Error { 22 | Error { 23 | error_type: ErrorType::Syscall(syscall_error), 24 | descr: descr.into(), 25 | } 26 | } 27 | 28 | pub fn from_io_error>(io_error: IOError, descr: S) -> Error { 29 | Error { 30 | error_type: ErrorType::IOError(io_error), 31 | descr: descr.into(), 32 | } 33 | } 34 | 35 | pub fn other_error>(descr: S) -> Error { 36 | Error { 37 | error_type: ErrorType::Other, 38 | descr: descr.into(), 39 | } 40 | } 41 | } 42 | 43 | impl fmt::Display for Error { 44 | fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { 45 | match self.error_type { 46 | ErrorType::Syscall(ref syscall_error) => { 47 | write!(f, "{}: syscall error: {}", self.descr, syscall_error) 48 | } 49 | ErrorType::IOError(ref io_error) => { 50 | write!(f, "{} : io error : {}", self.descr, io_error) 51 | } 52 | ErrorType::Other => { 53 | write!(f, "{}", self.descr) 54 | } 55 | } 56 | } 57 | } 58 | impl std::error::Error for Error {} 59 | 60 | impl convert::From for Error { 61 | fn from(e: IOError) -> Self { 62 | Error::from_io_error(e, "") 63 | } 64 | } 65 | 66 | pub type Result = result::Result; 67 | -------------------------------------------------------------------------------- /src/lib/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod error; 2 | pub mod logger; 3 | -------------------------------------------------------------------------------- /src/lib/logger.rs: -------------------------------------------------------------------------------- 1 | use redox_log::{OutputBuilder, RedoxLogger}; 2 | 3 | pub fn init_logger(process_name: &str) { 4 | if let Err(_) = RedoxLogger::new() 5 | .with_output( 6 | OutputBuilder::stdout() 7 | .with_ansi_escape_codes() 8 | .flush_on_newline(true) 9 | .with_filter(log::LevelFilter::Trace) 10 | .build(), 11 | ) 12 | .with_process_name(process_name.into()) 13 | .enable() 14 | { 15 | eprintln!("{process_name}: Failed to init logger") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/smolnetd/buffer_pool.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::mem::{replace, swap}; 3 | use std::ops::{Deref, DerefMut, Drop}; 4 | use std::rc::Rc; 5 | 6 | type BufferStack = Rc>>>; 7 | 8 | pub struct Buffer { 9 | buffer: Vec, 10 | stack: BufferStack, 11 | } 12 | 13 | impl Buffer { 14 | pub fn resize(&mut self, new_len: usize) { 15 | self.buffer.resize(new_len, 0u8); 16 | } 17 | 18 | pub fn move_out(&mut self) -> Buffer { 19 | Buffer { 20 | buffer: replace(&mut self.buffer, vec![]), 21 | stack: Rc::clone(&self.stack), 22 | } 23 | } 24 | } 25 | 26 | impl AsRef<[u8]> for Buffer { 27 | fn as_ref(&self) -> &[u8] { 28 | &self.buffer 29 | } 30 | } 31 | 32 | impl AsMut<[u8]> for Buffer { 33 | fn as_mut(&mut self) -> &mut [u8] { 34 | &mut self.buffer 35 | } 36 | } 37 | 38 | impl Deref for Buffer { 39 | type Target = [u8]; 40 | 41 | fn deref(&self) -> &Self::Target { 42 | &self.buffer 43 | } 44 | } 45 | 46 | impl DerefMut for Buffer { 47 | fn deref_mut(&mut self) -> &mut Self::Target { 48 | &mut self.buffer 49 | } 50 | } 51 | 52 | impl Drop for Buffer { 53 | fn drop(&mut self) { 54 | if self.buffer.capacity() > 0 { 55 | let mut tmp = vec![]; 56 | swap(&mut tmp, &mut self.buffer); 57 | { 58 | let mut stack = self.stack.borrow_mut(); 59 | stack.push(tmp); 60 | } 61 | } 62 | } 63 | } 64 | 65 | pub struct BufferPool { 66 | buffers_size: usize, 67 | stack: BufferStack, 68 | } 69 | 70 | impl BufferPool { 71 | pub fn new(buffers_size: usize) -> BufferPool { 72 | BufferPool { 73 | buffers_size, 74 | stack: Rc::new(RefCell::new(vec![])), 75 | } 76 | } 77 | 78 | pub fn get_buffer(&mut self) -> Buffer { 79 | let buffer = match self.stack.borrow_mut().pop() { 80 | None => vec![0u8; self.buffers_size], 81 | Some(mut v) => { 82 | // memsetting the buffer with `resize` would be a waste of time 83 | let capacity = v.capacity(); 84 | unsafe { 85 | v.set_len(capacity); 86 | } 87 | v 88 | } 89 | }; 90 | 91 | Buffer { 92 | buffer, 93 | stack: Rc::clone(&self.stack), 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/smolnetd/link/ethernet.rs: -------------------------------------------------------------------------------- 1 | use std::collections::btree_map::Entry; 2 | use std::collections::BTreeMap; 3 | use std::fs::File; 4 | use std::io::{ErrorKind, Read, Write}; 5 | use std::rc::Rc; 6 | 7 | use smoltcp::storage::PacketMetadata; 8 | use smoltcp::time::{Duration, Instant}; 9 | use smoltcp::wire::{ 10 | ArpOperation, ArpPacket, ArpRepr, EthernetAddress, EthernetFrame, EthernetProtocol, 11 | EthernetRepr, IpAddress, IpCidr, Ipv4Address, Ipv4Cidr, 12 | }; 13 | 14 | use super::LinkDevice; 15 | 16 | struct Neighbor { 17 | hardware_address: EthernetAddress, 18 | expires_at: Instant, 19 | } 20 | 21 | #[derive(Debug, Default)] 22 | enum ArpState { 23 | #[default] 24 | Discovered, 25 | Discovering { 26 | target: Ipv4Address, 27 | tries: u32, 28 | silent_until: Instant, 29 | }, 30 | } 31 | 32 | type PacketBuffer = smoltcp::storage::PacketBuffer<'static, IpAddress>; 33 | 34 | const EMPTY_MAC: EthernetAddress = EthernetAddress([0; 6]); 35 | 36 | pub struct EthernetLink { 37 | name: Rc, 38 | neighbor_cache: BTreeMap, 39 | arp_state: ArpState, 40 | waiting_packets: PacketBuffer, 41 | input_buffer: Vec, 42 | output_buffer: Vec, 43 | network_file: File, 44 | hardware_address: Option, 45 | ip_address: Option, 46 | } 47 | 48 | impl EthernetLink { 49 | // TODO: Review these constants 50 | const MAX_WAITING_PACKET_COUNT: usize = 10; 51 | const MTU: usize = 1500; 52 | const WAITING_PACKET_BUFFER_SIZE: usize = Self::MTU * Self::MAX_WAITING_PACKET_COUNT; 53 | 54 | const NEIGHBOR_LIVE_TIME: Duration = Duration::from_secs(60); 55 | const ARP_SILENCE_TIME: Duration = Duration::from_secs(1); 56 | 57 | pub fn new(name: &str, network_file: File) -> Self { 58 | let waiting_packets = PacketBuffer::new( 59 | vec![PacketMetadata::EMPTY; Self::MAX_WAITING_PACKET_COUNT], 60 | vec![0u8; Self::WAITING_PACKET_BUFFER_SIZE], 61 | ); 62 | 63 | Self { 64 | name: name.into(), 65 | network_file, 66 | waiting_packets, 67 | hardware_address: None, 68 | ip_address: None, 69 | input_buffer: vec![0u8; Self::MTU], 70 | output_buffer: Vec::with_capacity(Self::MTU), 71 | arp_state: Default::default(), 72 | neighbor_cache: Default::default(), 73 | } 74 | } 75 | 76 | fn send_to(&mut self, dst: EthernetAddress, size: usize, f: F, proto: EthernetProtocol) 77 | where 78 | F: FnOnce(&mut [u8]), 79 | { 80 | let Some(hardware_address) = self.hardware_address else { 81 | return; 82 | }; 83 | 84 | let repr = EthernetRepr { 85 | src_addr: hardware_address, 86 | dst_addr: dst, 87 | ethertype: proto, 88 | }; 89 | 90 | self.output_buffer.clear(); 91 | self.output_buffer.resize(repr.buffer_len() + size, 0); 92 | let mut frame = EthernetFrame::new_unchecked(&mut self.output_buffer); 93 | repr.emit(&mut frame); 94 | 95 | f(frame.payload_mut()); 96 | 97 | if let Err(_) = self.network_file.write_all(&self.output_buffer) { 98 | error!( 99 | "Dropped outboud packet on {} (failed to write to network file)", 100 | self.name 101 | ) 102 | } 103 | } 104 | 105 | fn process_arp(&mut self, packet: &[u8], now: Instant) { 106 | let Some(hardware_address) = self.hardware_address else { 107 | return; 108 | }; 109 | 110 | let Some(ip_addr) = self.ip_address else { 111 | return; 112 | }; 113 | 114 | let Ok(repr) = ArpPacket::new_checked(packet).and_then(|packet| ArpRepr::parse(&packet)) 115 | else { 116 | debug!("Dropped incomming arp packet on {} (Malformed)", self.name); 117 | return; 118 | }; 119 | 120 | match repr { 121 | ArpRepr::EthernetIpv4 { 122 | operation, 123 | source_hardware_addr, 124 | source_protocol_addr, 125 | target_hardware_addr, 126 | target_protocol_addr, 127 | } => { 128 | let is_unicast_mac = 129 | target_hardware_addr != EMPTY_MAC && !target_hardware_addr.is_broadcast(); 130 | 131 | if is_unicast_mac && hardware_address != target_hardware_addr { 132 | // Only process packet that are for us 133 | return; 134 | } 135 | 136 | if let ArpOperation::Unknown(_) = operation { 137 | return; 138 | } 139 | 140 | if !source_hardware_addr.is_unicast() 141 | || source_protocol_addr.is_broadcast() 142 | || source_protocol_addr.is_multicast() 143 | || source_protocol_addr.is_unspecified() 144 | { 145 | return; 146 | } 147 | 148 | if ip_addr.address() != target_protocol_addr { 149 | return; 150 | } 151 | 152 | self.neighbor_cache.insert( 153 | IpAddress::Ipv4(source_protocol_addr), 154 | Neighbor { 155 | hardware_address: source_hardware_addr, 156 | expires_at: now + Self::NEIGHBOR_LIVE_TIME, 157 | }, 158 | ); 159 | 160 | if let ArpOperation::Request = operation { 161 | let response = ArpRepr::EthernetIpv4 { 162 | operation: ArpOperation::Reply, 163 | source_hardware_addr: hardware_address, 164 | source_protocol_addr: ip_addr.address(), 165 | target_hardware_addr: source_hardware_addr, 166 | target_protocol_addr: source_protocol_addr, 167 | }; 168 | 169 | self.send_to( 170 | source_hardware_addr, 171 | response.buffer_len(), 172 | |buf| response.emit(&mut ArpPacket::new_unchecked(buf)), 173 | EthernetProtocol::Arp, 174 | ); 175 | } 176 | self.check_waiting_packets(source_protocol_addr, source_hardware_addr, now); 177 | } 178 | _ => {} 179 | } 180 | } 181 | 182 | fn check_waiting_packets(&mut self, ip: Ipv4Address, mac: EthernetAddress, now: Instant) { 183 | let mut waiting_packets = 184 | std::mem::replace(&mut self.waiting_packets, PacketBuffer::new(vec![], vec![])); 185 | loop { 186 | match waiting_packets.peek() { 187 | Ok((IpAddress::Ipv4(dst), _)) if dst == &ip => {} 188 | Ok((IpAddress::Ipv4(dst), _)) => { 189 | self.arp_state = ArpState::Discovering { 190 | target: *dst, 191 | tries: 0, 192 | silent_until: Instant::ZERO, 193 | }; 194 | self.send_arp(now); 195 | break; 196 | } 197 | Err(_) => { 198 | self.arp_state = ArpState::Discovered; 199 | break; 200 | } 201 | } 202 | 203 | let (_, packet) = waiting_packets.dequeue().unwrap(); 204 | self.send_to( 205 | mac, 206 | packet.len(), 207 | |buf| buf.copy_from_slice(packet), 208 | EthernetProtocol::Ipv4, 209 | ); 210 | } 211 | 212 | self.waiting_packets = waiting_packets; 213 | } 214 | 215 | fn drop_waiting_packets(&mut self, ip: Ipv4Address, now: Instant) { 216 | loop { 217 | match self.waiting_packets.peek() { 218 | Ok((IpAddress::Ipv4(dst), _)) if dst == &ip => {} 219 | Ok((IpAddress::Ipv4(dst), _)) => { 220 | self.arp_state = ArpState::Discovering { 221 | target: *dst, 222 | tries: 0, 223 | silent_until: Instant::ZERO, 224 | }; 225 | 226 | self.send_arp(now); 227 | 228 | return; 229 | } 230 | Err(_) => { 231 | self.arp_state = ArpState::Discovered; 232 | return; 233 | } 234 | } 235 | 236 | let _ = self.waiting_packets.dequeue(); 237 | debug!( 238 | "Dropped packet on {} because neighbor was not found", 239 | self.name 240 | ) 241 | } 242 | } 243 | 244 | fn handle_missing_neighbor(&mut self, next_hop: IpAddress, packet: &[u8], now: Instant) { 245 | let Ok(buf) = self.waiting_packets.enqueue(packet.len(), next_hop) else { 246 | warn!( 247 | "Dropped packet on {} because waiting queue was full", 248 | self.name 249 | ); 250 | return; 251 | }; 252 | buf.copy_from_slice(packet); 253 | 254 | let IpAddress::Ipv4(next_hop) = next_hop; 255 | if let ArpState::Discovered = self.arp_state { 256 | self.arp_state = ArpState::Discovering { 257 | target: next_hop, 258 | tries: 0, 259 | silent_until: Instant::ZERO, 260 | }; 261 | 262 | self.send_arp(now) 263 | } 264 | } 265 | 266 | fn send_arp(&mut self, now: Instant) { 267 | let Some(hardware_address) = self.hardware_address else { 268 | return; 269 | }; 270 | 271 | let Some(ip_address) = self.ip_address else { 272 | return; 273 | }; 274 | 275 | match self.arp_state { 276 | ArpState::Discovered => {} 277 | ArpState::Discovering { silent_until, .. } if silent_until > now => {} 278 | ArpState::Discovering { target, tries, .. } if tries >= 3 => { 279 | self.drop_waiting_packets(target, now) 280 | } 281 | ArpState::Discovering { 282 | target, 283 | ref mut tries, 284 | ref mut silent_until, 285 | } => { 286 | let arp_repr = ArpRepr::EthernetIpv4 { 287 | operation: ArpOperation::Request, 288 | source_hardware_addr: hardware_address, 289 | source_protocol_addr: ip_address.address(), 290 | target_hardware_addr: EthernetAddress::BROADCAST, 291 | target_protocol_addr: target, 292 | }; 293 | 294 | *tries += 1; 295 | *silent_until = now + Self::ARP_SILENCE_TIME; 296 | 297 | self.send_to( 298 | EthernetAddress::BROADCAST, 299 | arp_repr.buffer_len(), 300 | |buf| arp_repr.emit(&mut ArpPacket::new_unchecked(buf)), 301 | EthernetProtocol::Arp, 302 | ); 303 | } 304 | } 305 | } 306 | } 307 | 308 | impl LinkDevice for EthernetLink { 309 | fn send(&mut self, next_hop: IpAddress, packet: &[u8], now: Instant) { 310 | let local_broadcast = match self.ip_address.and_then(|cidr| cidr.broadcast()) { 311 | Some(addr) => IpAddress::Ipv4(addr) == next_hop, 312 | None => false, 313 | }; 314 | 315 | if local_broadcast || next_hop.is_broadcast() { 316 | self.send_to( 317 | EthernetAddress::BROADCAST, 318 | packet.len(), 319 | |buf| buf.copy_from_slice(packet), 320 | EthernetProtocol::Ipv4, 321 | ); 322 | return; 323 | } 324 | 325 | match self.neighbor_cache.entry(next_hop) { 326 | Entry::Vacant(_) => self.handle_missing_neighbor(next_hop, packet, now), 327 | Entry::Occupied(e) => { 328 | if e.get().expires_at < now { 329 | e.remove(); 330 | self.handle_missing_neighbor(next_hop, packet, now) 331 | } else { 332 | let mac = e.get().hardware_address; 333 | self.send_to( 334 | mac, 335 | packet.len(), 336 | |buf| buf.copy_from_slice(packet), 337 | EthernetProtocol::Ipv4, 338 | ) 339 | } 340 | } 341 | } 342 | } 343 | 344 | fn recv(&mut self, now: Instant) -> Option<&[u8]> { 345 | let Some(hardware_address) = self.hardware_address else { 346 | return None; 347 | }; 348 | 349 | let mut input_buffer = std::mem::replace(&mut self.input_buffer, Vec::new()); 350 | loop { 351 | if let Err(e) = self.network_file.read(&mut input_buffer) { 352 | if e.kind() != ErrorKind::WouldBlock { 353 | error!("Failed to read ethernet device on link {}", self.name); 354 | } else { 355 | // No packet to read but we check if we have arp to send 356 | self.send_arp(now); 357 | } 358 | self.input_buffer = input_buffer; 359 | return None; 360 | } 361 | let packet = EthernetFrame::new_unchecked(&input_buffer[..]); 362 | let Ok(repr) = EthernetRepr::parse(&packet) else { 363 | debug!("Dropped incomming frame on {} (Malformed)", self.name); 364 | continue; 365 | }; 366 | 367 | // We let EMPTY_MAC pass because somehow this is the mac used when net=redir is used 368 | if !repr.dst_addr.is_broadcast() 369 | && repr.dst_addr != EMPTY_MAC 370 | && repr.dst_addr != hardware_address 371 | { 372 | // Drop packets which are not for us 373 | continue; 374 | } 375 | 376 | match repr.ethertype { 377 | EthernetProtocol::Ipv4 => { 378 | self.input_buffer = input_buffer; 379 | return Some(EthernetFrame::new_unchecked(&self.input_buffer[..]).payload()); 380 | } 381 | EthernetProtocol::Arp => self.process_arp(packet.payload(), now), 382 | _ => continue, 383 | } 384 | } 385 | } 386 | 387 | fn name(&self) -> &Rc { 388 | &self.name 389 | } 390 | 391 | fn can_recv(&self) -> bool { 392 | // We don't buffer any packets so we can't receive immediatly 393 | false 394 | } 395 | 396 | fn mac_address(&self) -> Option { 397 | self.hardware_address 398 | } 399 | 400 | fn set_mac_address(&mut self, addr: EthernetAddress) { 401 | self.hardware_address = Some(addr) 402 | } 403 | 404 | fn ip_address(&self) -> Option { 405 | Some(IpCidr::Ipv4(self.ip_address?)) 406 | } 407 | 408 | fn set_ip_address(&mut self, addr: IpCidr) { 409 | let IpCidr::Ipv4(addr) = addr; 410 | self.ip_address = Some(addr); 411 | } 412 | } 413 | -------------------------------------------------------------------------------- /src/smolnetd/link/loopback.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use smoltcp::storage::PacketMetadata; 4 | use smoltcp::time::Instant; 5 | 6 | use crate::scheme::Smolnetd; 7 | 8 | use super::LinkDevice; 9 | 10 | pub type PacketBuffer = smoltcp::storage::PacketBuffer<'static, ()>; 11 | 12 | pub struct LoopbackDevice { 13 | name: Rc, 14 | buffer: PacketBuffer, 15 | } 16 | 17 | impl Default for LoopbackDevice { 18 | fn default() -> Self { 19 | let buffer = PacketBuffer::new( 20 | vec![PacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], 21 | vec![0u8; 1500 * Smolnetd::SOCKET_BUFFER_SIZE], 22 | ); 23 | LoopbackDevice { 24 | name: "loopback".into(), 25 | buffer, 26 | } 27 | } 28 | } 29 | 30 | impl LinkDevice for LoopbackDevice { 31 | fn send(&mut self, _next_hop: smoltcp::wire::IpAddress, packet: &[u8], _now: Instant) { 32 | match self.buffer.enqueue(packet.len(), ()) { 33 | Err(_) => warn!("loopback dropped packet because buffer was full"), 34 | Ok(buf) => buf.copy_from_slice(packet), 35 | } 36 | } 37 | 38 | fn recv(&mut self, _now: Instant) -> Option<&[u8]> { 39 | self.buffer.dequeue().ok().map(|((), buf)| &*buf) 40 | } 41 | 42 | fn name(&self) -> &std::rc::Rc { 43 | &self.name 44 | } 45 | 46 | fn can_recv(&self) -> bool { 47 | !self.buffer.is_empty() 48 | } 49 | 50 | fn mac_address(&self) -> Option { 51 | None 52 | } 53 | 54 | fn set_mac_address(&mut self, _addr: smoltcp::wire::EthernetAddress) {} 55 | 56 | fn ip_address(&self) -> Option { 57 | Some("127.0.0.1/8".parse().unwrap()) 58 | } 59 | 60 | fn set_ip_address(&mut self, _addr: smoltcp::wire::IpCidr) { 61 | todo!() 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/smolnetd/link/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ethernet; 2 | pub mod loopback; 3 | 4 | use std::rc::Rc; 5 | 6 | use smoltcp::time::Instant; 7 | use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; 8 | 9 | /// Represent a link layer device (eth0, loopback...) 10 | pub trait LinkDevice { 11 | /// Send the given packet to the machine with the `next_hop` ip address 12 | /// This method cannot fail so it's the implementor responsability 13 | /// to buffer packets which can't be sent immediatly or decide to 14 | /// drop them if necessary 15 | fn send(&mut self, next_hop: IpAddress, packet: &[u8], now: Instant); 16 | 17 | /// Returns None if nothing is received. 18 | /// Returns an Ip packet otherwise 19 | fn recv(&mut self, now: Instant) -> Option<&[u8]>; 20 | 21 | /// Returns the LinkDevice display name used to refer to it and for lookups 22 | fn name(&self) -> &Rc; 23 | 24 | /// Returns wether this device have packets pending 25 | fn can_recv(&self) -> bool; 26 | 27 | fn mac_address(&self) -> Option; 28 | fn set_mac_address(&mut self, addr: EthernetAddress); 29 | 30 | fn ip_address(&self) -> Option; 31 | fn set_ip_address(&mut self, addr: IpCidr); 32 | } 33 | 34 | #[derive(Default)] 35 | pub struct DeviceList { 36 | inner: Vec>, 37 | } 38 | 39 | impl DeviceList { 40 | pub fn push(&mut self, dev: T) { 41 | self.inner.push(Box::new(dev)) 42 | } 43 | 44 | pub fn get(&self, device_name: &str) -> Option<&dyn LinkDevice> { 45 | self.inner 46 | .iter() 47 | .find(|dev| dev.name().as_ref() == device_name) 48 | .map(|device| device.as_ref()) 49 | } 50 | 51 | pub fn get_mut(&mut self, device_name: &str) -> Option<&mut (dyn LinkDevice + 'static)> { 52 | self.inner 53 | .iter_mut() 54 | .find(|dev| dev.name().as_ref() == device_name) 55 | .map(|device| device.as_mut()) 56 | } 57 | 58 | pub fn iter(&self) -> impl Iterator { 59 | self.inner.iter().map(|b| b.as_ref()) 60 | } 61 | 62 | pub fn iter_mut(&mut self) -> impl Iterator { 63 | self.inner.iter_mut().map(|b| b.as_mut()) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/smolnetd/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | use std::process; 4 | 5 | use anyhow::{anyhow, bail, Context, Result}; 6 | use event::{EventFlags, EventQueue}; 7 | use libredox::flag::{O_CREAT, O_NONBLOCK, O_RDWR}; 8 | use libredox::Fd; 9 | 10 | use redox_netstack::logger; 11 | use scheme::Smolnetd; 12 | use smoltcp::wire::EthernetAddress; 13 | 14 | mod buffer_pool; 15 | mod link; 16 | mod port_set; 17 | mod router; 18 | mod scheme; 19 | 20 | fn get_network_adapter() -> Result { 21 | use std::fs; 22 | 23 | let mut adapters = vec![]; 24 | 25 | for entry_res in fs::read_dir("/scheme")? { 26 | let Ok(entry) = entry_res else { 27 | continue; 28 | }; 29 | 30 | let Ok(scheme) = entry.file_name().into_string() else { 31 | continue; 32 | }; 33 | 34 | if !scheme.starts_with("network") { 35 | continue; 36 | } 37 | 38 | adapters.push(scheme); 39 | } 40 | 41 | if adapters.is_empty() { 42 | bail!("no network adapter found"); 43 | } else { 44 | let adapter = adapters.remove(0); 45 | if !adapters.is_empty() { 46 | // FIXME allow using multiple network adapters at the same time 47 | warn!("Multiple network adapters found. Only {adapter} will be used"); 48 | } 49 | Ok(adapter) 50 | } 51 | } 52 | 53 | fn run(daemon: redox_daemon::Daemon) -> Result<()> { 54 | let adapter = get_network_adapter()?; 55 | trace!("opening {adapter}:"); 56 | let network_fd = Fd::open(&format!("/scheme/{adapter}"), O_RDWR | O_NONBLOCK, 0) 57 | .map_err(|e| anyhow!("failed to open {adapter}: {e}"))?; 58 | 59 | let hardware_addr = std::fs::read(format!("/scheme/{adapter}/mac")) 60 | .map(|mac_address| EthernetAddress::from_bytes(&mac_address)) 61 | .context("failed to get mac address from network adapter")?; 62 | 63 | trace!("opening :ip"); 64 | let ip_fd = Fd::open(":ip", O_RDWR | O_CREAT | O_NONBLOCK, 0).context("failed to open :ip")?; 65 | 66 | trace!("opening :udp"); 67 | let udp_fd = 68 | Fd::open(":udp", O_RDWR | O_CREAT | O_NONBLOCK, 0).context("failed to open :udp")?; 69 | 70 | trace!("opening :tcp"); 71 | let tcp_fd = 72 | Fd::open(":tcp", O_RDWR | O_CREAT | O_NONBLOCK, 0).context("failed to open :tcp")?; 73 | 74 | trace!("opening :icmp"); 75 | let icmp_fd = 76 | Fd::open(":icmp", O_RDWR | O_CREAT | O_NONBLOCK, 0).context("failed to open :icmp")?; 77 | 78 | trace!("opening :netcfg"); 79 | let netcfg_fd = 80 | Fd::open(":netcfg", O_RDWR | O_CREAT | O_NONBLOCK, 0).context("failed to open :netcfg")?; 81 | 82 | let time_path = format!("/scheme/time/{}", syscall::CLOCK_MONOTONIC); 83 | let time_fd = Fd::open(&time_path, O_RDWR, 0).context("failed to open /scheme/time")?; 84 | 85 | event::user_data! { 86 | enum EventSource { 87 | Network, 88 | Time, 89 | IpScheme, 90 | UdpScheme, 91 | TcpScheme, 92 | IcmpScheme, 93 | NetcfgScheme, 94 | } 95 | } 96 | 97 | let event_queue = EventQueue::::new().context("failed to create event queue")?; 98 | 99 | daemon.ready().expect("smolnetd: failed to notify parent"); 100 | 101 | event_queue 102 | .subscribe(network_fd.raw(), EventSource::Network, EventFlags::READ) 103 | .context("failed to listen to network events")?; 104 | 105 | event_queue 106 | .subscribe(time_fd.raw(), EventSource::Time, EventFlags::READ) 107 | .context("failed to listen to timer events")?; 108 | 109 | event_queue 110 | .subscribe(ip_fd.raw(), EventSource::IpScheme, EventFlags::READ) 111 | .context("failed to listen to ip scheme events")?; 112 | 113 | event_queue 114 | .subscribe(udp_fd.raw(), EventSource::UdpScheme, EventFlags::READ) 115 | .context("failed to listen to udp scheme events")?; 116 | 117 | event_queue 118 | .subscribe(tcp_fd.raw(), EventSource::TcpScheme, EventFlags::READ) 119 | .context("failed to listen to tcp scheme events")?; 120 | 121 | event_queue 122 | .subscribe(icmp_fd.raw(), EventSource::IcmpScheme, EventFlags::READ) 123 | .context("failed to listen to icmp scheme events")?; 124 | 125 | event_queue 126 | .subscribe(netcfg_fd.raw(), EventSource::NetcfgScheme, EventFlags::READ) 127 | .context("failed to listen to netcfg scheme events")?; 128 | 129 | let mut smolnetd = Smolnetd::new( 130 | network_fd, 131 | hardware_addr, 132 | ip_fd, 133 | udp_fd, 134 | tcp_fd, 135 | icmp_fd, 136 | time_fd, 137 | netcfg_fd, 138 | ); 139 | 140 | libredox::call::setrens(0, 0).context("smolnetd: failed to enter null namespace")?; 141 | 142 | let all = { 143 | use EventSource::*; 144 | [Network, Time, IpScheme, UdpScheme, IcmpScheme, NetcfgScheme].map(Ok) 145 | }; 146 | 147 | for event_res in all 148 | .into_iter() 149 | .chain(event_queue.map(|r| r.map(|e| e.user_data))) 150 | { 151 | match event_res? { 152 | EventSource::Network => smolnetd.on_network_scheme_event()?, 153 | EventSource::Time => smolnetd.on_time_event()?, 154 | EventSource::IpScheme => smolnetd.on_ip_scheme_event()?, 155 | EventSource::UdpScheme => smolnetd.on_udp_scheme_event()?, 156 | EventSource::TcpScheme => smolnetd.on_tcp_scheme_event()?, 157 | EventSource::IcmpScheme => smolnetd.on_icmp_scheme_event()?, 158 | EventSource::NetcfgScheme => smolnetd.on_netcfg_scheme_event()?, 159 | } 160 | } 161 | Ok(()) 162 | } 163 | 164 | fn main() { 165 | redox_daemon::Daemon::new(move |daemon| { 166 | logger::init_logger("smolnetd"); 167 | 168 | if let Err(err) = run(daemon) { 169 | error!("smoltcpd: {}", err); 170 | process::exit(1); 171 | } 172 | process::exit(0); 173 | }) 174 | .expect("smoltcp: failed to daemonize"); 175 | } 176 | -------------------------------------------------------------------------------- /src/smolnetd/port_set.rs: -------------------------------------------------------------------------------- 1 | use std::collections::btree_map::{BTreeMap, Entry}; 2 | 3 | pub struct PortSet { 4 | from: u16, 5 | range: u16, 6 | next: u16, 7 | ports: BTreeMap, 8 | } 9 | 10 | impl PortSet { 11 | pub fn new(from: u16, to: u16) -> Option { 12 | if from > to { 13 | return None; 14 | } 15 | Some(PortSet { 16 | from, 17 | range: to - from + 1, 18 | next: 0, 19 | ports: BTreeMap::new(), 20 | }) 21 | } 22 | 23 | pub fn get_port(&mut self) -> Option { 24 | if self.ports.len() >= self.range as usize { 25 | return None; 26 | } 27 | 28 | let port = loop { 29 | if let Entry::Vacant(entry) = self.ports.entry(self.next) { 30 | entry.insert(1); 31 | let port = self.from + self.next; 32 | self.next = self.next.wrapping_add(1); 33 | break port; 34 | } 35 | self.next = self.next.wrapping_add(1); 36 | }; 37 | 38 | Some(port) 39 | } 40 | 41 | pub fn claim_port(&mut self, port: u16) -> bool { 42 | if let Entry::Vacant(entry) = self.ports.entry(port) { 43 | entry.insert(1); 44 | true 45 | } else { 46 | false 47 | } 48 | } 49 | 50 | pub fn acquire_port(&mut self, port: u16) { 51 | *self.ports.entry(port).or_insert(0) += 1; 52 | } 53 | 54 | pub fn release_port(&mut self, port: u16) { 55 | if let Entry::Occupied(mut entry) = self.ports.entry(port) { 56 | *entry.get_mut() -= 1; 57 | if *entry.get() == 0 { 58 | entry.remove(); 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/smolnetd/router/mod.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::rc::Rc; 3 | 4 | use smoltcp::phy::{Device, DeviceCapabilities, Medium}; 5 | use smoltcp::storage::PacketMetadata; 6 | use smoltcp::time::Instant; 7 | use smoltcp::wire::IpAddress; 8 | 9 | use self::route_table::RouteTable; 10 | use crate::link::DeviceList; 11 | use crate::scheme::Smolnetd; 12 | 13 | pub mod route_table; 14 | 15 | pub type PacketBuffer = smoltcp::storage::PacketBuffer<'static, ()>; 16 | 17 | pub struct Router { 18 | rx_buffer: PacketBuffer, 19 | tx_buffer: PacketBuffer, 20 | devices: Rc>, 21 | route_table: Rc>, 22 | } 23 | 24 | impl Router { 25 | pub fn new(devices: Rc>, route_table: Rc>) -> Self { 26 | let rx_buffer = PacketBuffer::new( 27 | vec![PacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], 28 | vec![0u8; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE], 29 | ); 30 | let tx_buffer = PacketBuffer::new( 31 | vec![PacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], 32 | vec![0u8; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE], 33 | ); 34 | Self { 35 | rx_buffer, 36 | tx_buffer, 37 | devices, 38 | route_table, 39 | } 40 | } 41 | 42 | pub const MTU: usize = 1486; 43 | 44 | pub fn can_recv(&self) -> bool { 45 | let mut can_recv = false; 46 | for dev in self.devices.borrow().iter() { 47 | can_recv |= dev.can_recv(); 48 | } 49 | can_recv 50 | } 51 | 52 | pub fn poll(&mut self, now: Instant) { 53 | for dev in self.devices.borrow_mut().iter_mut() { 54 | if self.rx_buffer.is_full() { 55 | break; 56 | } 57 | 58 | loop { 59 | if self.rx_buffer.is_full() { 60 | break; 61 | } 62 | 63 | let Some(buf) = dev.recv(now) else { 64 | break; 65 | }; 66 | 67 | self.rx_buffer 68 | .enqueue(buf.len(), ()) 69 | .expect("We checked if it was full") 70 | .copy_from_slice(buf); 71 | } 72 | } 73 | } 74 | 75 | pub fn dispatch(&mut self, now: Instant) { 76 | while let Ok(((), packet)) = self.tx_buffer.dequeue() { 77 | if let Ok(mut packet) = smoltcp::wire::Ipv4Packet::new_checked(packet) { 78 | let dst_addr = IpAddress::Ipv4(packet.dst_addr()); 79 | if packet.dst_addr().is_broadcast() { 80 | let buf = packet.into_inner(); 81 | for dev in self.devices.borrow_mut().iter_mut() { 82 | dev.send(dst_addr, buf, now) 83 | } 84 | } else { 85 | let route_table = self.route_table.borrow(); 86 | let Some(rule) = route_table.lookup_rule(&dst_addr) else { 87 | warn!("No route found for destination: {}", dst_addr); 88 | continue; 89 | }; 90 | 91 | let next_hop = match rule.via { 92 | Some(via) => via, 93 | None => dst_addr, 94 | }; 95 | 96 | let mut devices = self.devices.borrow_mut(); 97 | let Some(dev) = devices.get_mut(&rule.dev) else { 98 | warn!("Device {} not found", rule.dev); 99 | // TODO: Remove route if device doesn't exist anymore ? 100 | continue; 101 | }; 102 | 103 | let IpAddress::Ipv4(src) = rule.src; 104 | if src != packet.src_addr() { 105 | packet.set_src_addr(src); 106 | packet.fill_checksum() 107 | } 108 | 109 | dev.send(next_hop, packet.into_inner(), now); 110 | } 111 | } 112 | } 113 | } 114 | } 115 | 116 | impl Device for Router { 117 | type RxToken<'a> = RxToken<'a>; 118 | 119 | type TxToken<'a> = TxToken<'a>; 120 | 121 | fn receive( 122 | &mut self, 123 | _timestamp: smoltcp::time::Instant, 124 | ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { 125 | if self.rx_buffer.is_empty() || self.tx_buffer.is_full() { 126 | None 127 | } else { 128 | Some(( 129 | RxToken { 130 | rx_buffer: &mut self.rx_buffer, 131 | }, 132 | TxToken { 133 | tx_buffer: &mut self.tx_buffer, 134 | }, 135 | )) 136 | } 137 | } 138 | 139 | fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option> { 140 | if self.tx_buffer.is_full() { 141 | None 142 | } else { 143 | Some(TxToken { 144 | tx_buffer: &mut self.tx_buffer, 145 | }) 146 | } 147 | } 148 | 149 | fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities { 150 | let mut caps = DeviceCapabilities::default(); 151 | caps.medium = Medium::Ip; 152 | caps.max_transmission_unit = Router::MTU; 153 | caps.max_burst_size = Some(Smolnetd::SOCKET_BUFFER_SIZE); 154 | caps 155 | } 156 | } 157 | 158 | pub struct TxToken<'a> { 159 | tx_buffer: &'a mut PacketBuffer, 160 | } 161 | 162 | impl smoltcp::phy::TxToken for TxToken<'_> { 163 | fn consume(self, len: usize, f: F) -> R 164 | where 165 | F: FnOnce(&mut [u8]) -> R, 166 | { 167 | f(self 168 | .tx_buffer 169 | .enqueue(len, ()) 170 | .expect("This was checked before creating the TxToken")) 171 | } 172 | } 173 | 174 | pub struct RxToken<'a> { 175 | rx_buffer: &'a mut PacketBuffer, 176 | } 177 | 178 | impl<'a> smoltcp::phy::RxToken for RxToken<'a> { 179 | fn consume(self, f: F) -> R 180 | where 181 | F: FnOnce(&[u8]) -> R, 182 | { 183 | let ((), buf) = self 184 | .rx_buffer 185 | .dequeue() 186 | .expect("This was checked before creating the RxToken"); 187 | 188 | f(buf) 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/smolnetd/router/route_table.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Display; 2 | use std::rc::Rc; 3 | 4 | use smoltcp::wire::{IpAddress, IpCidr}; 5 | 6 | #[derive(Debug)] 7 | pub struct Rule { 8 | pub filter: IpCidr, 9 | pub via: Option, 10 | pub dev: Rc, 11 | pub src: IpAddress, 12 | } 13 | 14 | impl Rule { 15 | pub fn new(filter: IpCidr, via: Option, dev: Rc, src: IpAddress) -> Self { 16 | Self { 17 | filter, 18 | via, 19 | dev, 20 | src, 21 | } 22 | } 23 | } 24 | 25 | impl Display for Rule { 26 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 27 | if self.filter.prefix_len() == 0 { 28 | write!(f, "default")?; 29 | } else { 30 | write!(f, "{} ", self.filter)?; 31 | } 32 | 33 | if let Some(via) = self.via { 34 | write!(f, " via {}", via)?; 35 | } 36 | 37 | write!(f, " dev {}", self.dev)?; 38 | write!(f, " src {}", self.src)?; 39 | 40 | Ok(()) 41 | } 42 | } 43 | 44 | #[derive(Debug, Default)] 45 | pub struct RouteTable { 46 | rules: Vec, 47 | } 48 | 49 | impl RouteTable { 50 | pub fn lookup_rule(&self, dst: &IpAddress) -> Option<&Rule> { 51 | self.rules 52 | .iter() 53 | .rev() 54 | .find(|rule| rule.filter.contains_addr(dst)) 55 | } 56 | 57 | pub fn lookup_src_addr(&self, dst: &IpAddress) -> Option { 58 | Some(self.lookup_rule(dst)?.src) 59 | } 60 | 61 | pub fn lookup_gateway(&self, dst: &IpAddress) -> Option { 62 | self.lookup_rule(dst)?.via 63 | } 64 | 65 | pub fn lookup_device(&self, dst: &IpAddress) -> Option> { 66 | Some(self.lookup_rule(dst)?.dev.clone()) 67 | } 68 | 69 | pub fn insert_rule(&mut self, new_rule: Rule) { 70 | let i = match self 71 | .rules 72 | .binary_search_by_key(&new_rule.filter.prefix_len(), |rule| { 73 | rule.filter.prefix_len() 74 | }) { 75 | Ok(i) | Err(i) => i, 76 | }; 77 | self.rules.insert(i, new_rule); 78 | } 79 | 80 | pub fn remove_rule(&mut self, filter: IpCidr) { 81 | self.rules.retain(|rule| rule.filter != filter); 82 | } 83 | 84 | pub fn change_src(&mut self, old_src: IpAddress, new_src: IpAddress) { 85 | for rule in self.rules.iter_mut().filter(|rule| rule.src == old_src) { 86 | rule.src = new_src; 87 | } 88 | } 89 | } 90 | 91 | impl Display for RouteTable { 92 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 93 | for rule in self.rules.iter() { 94 | writeln!(f, "{}", rule)?; 95 | } 96 | 97 | Ok(()) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/smolnetd/scheme/icmp.rs: -------------------------------------------------------------------------------- 1 | use smoltcp::iface::SocketHandle; 2 | use smoltcp::socket::icmp::{ 3 | Endpoint as IcmpEndpoint, PacketBuffer as IcmpSocketBuffer, 4 | PacketMetadata as IcmpPacketMetadata, Socket as IcmpSocket, 5 | }; 6 | use smoltcp::wire::{Icmpv4Packet, Icmpv4Repr, IpAddress, IpListenEndpoint}; 7 | use std::mem; 8 | use std::str; 9 | use syscall; 10 | use syscall::{Error as SyscallError, Result as SyscallResult}; 11 | 12 | use super::socket::{Context, DupResult, SchemeFile, SchemeSocket, SocketFile, SocketScheme}; 13 | use super::{Smolnetd, SocketSet}; 14 | use crate::port_set::PortSet; 15 | use crate::router::Router; 16 | 17 | pub type IcmpScheme = SocketScheme>; 18 | 19 | enum IcmpSocketType { 20 | Echo, 21 | Udp, 22 | } 23 | 24 | pub struct IcmpData { 25 | socket_type: IcmpSocketType, 26 | ip: IpAddress, 27 | ident: u16, 28 | } 29 | 30 | impl<'a> SchemeSocket for IcmpSocket<'a> { 31 | type SchemeDataT = PortSet; 32 | type DataT = IcmpData; 33 | type SettingT = (); 34 | 35 | fn new_scheme_data() -> Self::SchemeDataT { 36 | PortSet::new(1u16, 0xffffu16).expect("Wrong ICMP ident values") 37 | } 38 | 39 | fn can_send(&self) -> bool { 40 | self.can_send() 41 | } 42 | 43 | fn can_recv(&self) -> bool { 44 | self.can_recv() 45 | } 46 | 47 | fn may_recv(&self) -> bool { 48 | true 49 | } 50 | 51 | fn get_setting( 52 | _file: &SocketFile, 53 | _setting: Self::SettingT, 54 | _buf: &mut [u8], 55 | ) -> SyscallResult { 56 | Ok(0) 57 | } 58 | 59 | fn set_setting( 60 | _file: &mut SocketFile, 61 | _setting: Self::SettingT, 62 | _buf: &[u8], 63 | ) -> SyscallResult { 64 | Ok(0) 65 | } 66 | 67 | fn hop_limit(&self) -> u8 { 68 | self.hop_limit().unwrap_or(64) 69 | } 70 | 71 | fn set_hop_limit(&mut self, hop_limit: u8) { 72 | self.set_hop_limit(Some(hop_limit)); 73 | } 74 | 75 | fn new_socket( 76 | socket_set: &mut SocketSet, 77 | path: &str, 78 | _uid: u32, 79 | ident_set: &mut Self::SchemeDataT, 80 | _context: &Context, 81 | ) -> SyscallResult<(SocketHandle, Self::DataT)> { 82 | use std::str::FromStr; 83 | 84 | let mut parts = path.split('/'); 85 | let method = parts 86 | .next() 87 | .ok_or_else(|| syscall::Error::new(syscall::EINVAL))?; 88 | 89 | match method { 90 | "echo" => { 91 | let addr = parts 92 | .next() 93 | .ok_or_else(|| syscall::Error::new(syscall::EINVAL))?; 94 | let ip = 95 | IpAddress::from_str(addr).map_err(|_| syscall::Error::new(syscall::EINVAL))?; 96 | 97 | let socket = IcmpSocket::new( 98 | IcmpSocketBuffer::new( 99 | vec![IcmpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], 100 | vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE], 101 | ), 102 | IcmpSocketBuffer::new( 103 | vec![IcmpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], 104 | vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE], 105 | ), 106 | ); 107 | let handle = socket_set.add(socket); 108 | let icmp_socket = socket_set.get_mut::(handle); 109 | let ident = ident_set 110 | .get_port() 111 | .ok_or_else(|| SyscallError::new(syscall::EINVAL))?; 112 | icmp_socket 113 | .bind(IcmpEndpoint::Ident(ident)) 114 | .map_err(|_| syscall::Error::new(syscall::EINVAL))?; 115 | let socket_data = IcmpData { 116 | socket_type: IcmpSocketType::Echo, 117 | ident, 118 | ip, 119 | }; 120 | Ok((handle, socket_data)) 121 | } 122 | "udp" => { 123 | let addr = parts 124 | .next() 125 | .ok_or_else(|| syscall::Error::new(syscall::EINVAL))?; 126 | let ip = 127 | IpAddress::from_str(addr).map_err(|_| syscall::Error::new(syscall::EINVAL))?; 128 | 129 | let socket = IcmpSocket::new( 130 | IcmpSocketBuffer::new( 131 | vec![IcmpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], 132 | vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE], 133 | ), 134 | IcmpSocketBuffer::new( 135 | vec![IcmpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], 136 | vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE], 137 | ), 138 | ); 139 | let handle = socket_set.add(socket); 140 | let icmp_socket = socket_set.get_mut::(handle); 141 | let ident = ident_set 142 | .get_port() 143 | .ok_or_else(|| SyscallError::new(syscall::EINVAL))?; 144 | icmp_socket 145 | .bind(IcmpEndpoint::Udp(IpListenEndpoint::from(ident))) 146 | .map_err(|_| syscall::Error::new(syscall::EINVAL))?; 147 | let socket_data = IcmpData { 148 | socket_type: IcmpSocketType::Udp, 149 | ident, 150 | ip, 151 | }; 152 | Ok((handle, socket_data)) 153 | } 154 | _ => Err(syscall::Error::new(syscall::EINVAL)), 155 | } 156 | } 157 | 158 | fn close_file( 159 | &self, 160 | file: &SchemeFile, 161 | ident_set: &mut Self::SchemeDataT, 162 | ) -> SyscallResult<()> { 163 | if let SchemeFile::Socket(ref file) = *file { 164 | ident_set.release_port(file.data.ident); 165 | } 166 | Ok(()) 167 | } 168 | 169 | fn write_buf( 170 | &mut self, 171 | file: &mut SocketFile, 172 | buf: &[u8], 173 | ) -> SyscallResult> { 174 | if self.can_send() { 175 | match file.data.socket_type { 176 | IcmpSocketType::Echo => { 177 | if buf.len() < mem::size_of::() { 178 | return Err(SyscallError::new(syscall::EINVAL)); 179 | } 180 | let (&seq_buf, payload) = buf.split_first_chunk::<2>().unwrap(); 181 | let seq_no = u16::from_be_bytes(seq_buf); 182 | let icmp_repr = Icmpv4Repr::EchoRequest { 183 | ident: file.data.ident, 184 | seq_no, 185 | data: payload, 186 | }; 187 | 188 | let icmp_payload = self 189 | .send(icmp_repr.buffer_len(), file.data.ip) 190 | .map_err(|_| syscall::Error::new(syscall::EINVAL))?; 191 | let mut icmp_packet = Icmpv4Packet::new_unchecked(icmp_payload); 192 | //TODO: replace Default with actual caps 193 | icmp_repr.emit(&mut icmp_packet, &Default::default()); 194 | Ok(Some(buf.len())) 195 | } 196 | IcmpSocketType::Udp => Err(SyscallError::new(syscall::EINVAL)), 197 | } 198 | } else if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK { 199 | Err(SyscallError::new(syscall::EAGAIN)) 200 | } else { 201 | Ok(None) // internally scheduled to re-read 202 | } 203 | } 204 | 205 | fn read_buf( 206 | &mut self, 207 | file: &mut SocketFile, 208 | buf: &mut [u8], 209 | ) -> SyscallResult> { 210 | while self.can_recv() { 211 | let (payload, _) = self.recv().expect("Can't recv icmp packet"); 212 | let icmp_packet = Icmpv4Packet::new_unchecked(&payload); 213 | //TODO: replace default with actual caps 214 | let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &Default::default()).unwrap(); 215 | 216 | if let Icmpv4Repr::EchoReply { seq_no, data, .. } = icmp_repr { 217 | if buf.len() < mem::size_of::() + data.len() { 218 | return Err(SyscallError::new(syscall::EINVAL)); 219 | } 220 | buf[0..2].copy_from_slice(&seq_no.to_be_bytes()); 221 | 222 | for i in 0..data.len() { 223 | buf[mem::size_of::() + i] = data[i]; 224 | } 225 | 226 | return Ok(Some(mem::size_of::() + data.len())); 227 | } 228 | } 229 | 230 | if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK { 231 | Err(SyscallError::new(syscall::EAGAIN)) 232 | } else { 233 | Ok(None) // internally scheduled to re-read 234 | } 235 | } 236 | 237 | fn dup( 238 | _socket_set: &mut SocketSet, 239 | _file: &mut SchemeFile, 240 | _path: &str, 241 | _: &mut Self::SchemeDataT, 242 | ) -> SyscallResult> { 243 | Err(SyscallError::new(syscall::EBADF)) 244 | } 245 | 246 | fn fpath(&self, file: &SchemeFile, buf: &mut [u8]) -> SyscallResult { 247 | if let SchemeFile::Socket(ref socket_file) = *file { 248 | match socket_file.data.socket_type { 249 | IcmpSocketType::Echo => { 250 | let path = format!("icmp:echo/{}", socket_file.data.ip); 251 | let path = path.as_bytes(); 252 | 253 | let mut i = 0; 254 | while i < buf.len() && i < path.len() { 255 | buf[i] = path[i]; 256 | i += 1; 257 | } 258 | 259 | Ok(i) 260 | } 261 | IcmpSocketType::Udp => { 262 | let path = format!("icmp:udp/{}", socket_file.data.ip); 263 | let path = path.as_bytes(); 264 | 265 | let mut i = 0; 266 | while i < buf.len() && i < path.len() { 267 | buf[i] = path[i]; 268 | i += 1; 269 | } 270 | 271 | Ok(i) 272 | } 273 | } 274 | } else { 275 | Err(SyscallError::new(syscall::EBADF)) 276 | } 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /src/smolnetd/scheme/ip.rs: -------------------------------------------------------------------------------- 1 | use smoltcp::iface::SocketHandle; 2 | use smoltcp::socket::raw::{ 3 | PacketBuffer as RawSocketBuffer, PacketMetadata as RawPacketMetadata, Socket as RawSocket, 4 | }; 5 | use smoltcp::wire::{IpProtocol, IpVersion}; 6 | use std::str; 7 | use syscall; 8 | use syscall::{Error as SyscallError, Result as SyscallResult}; 9 | 10 | use crate::router::Router; 11 | 12 | use super::socket::{Context, DupResult, SchemeFile, SchemeSocket, SocketFile, SocketScheme}; 13 | use super::{Smolnetd, SocketSet}; 14 | 15 | pub type IpScheme = SocketScheme>; 16 | 17 | impl<'a> SchemeSocket for RawSocket<'a> { 18 | type SchemeDataT = (); 19 | type DataT = (); 20 | type SettingT = (); 21 | 22 | fn new_scheme_data() -> Self::SchemeDataT { 23 | () 24 | } 25 | 26 | fn can_send(&self) -> bool { 27 | self.can_send() 28 | } 29 | 30 | fn can_recv(&self) -> bool { 31 | self.can_recv() 32 | } 33 | 34 | fn may_recv(&self) -> bool { 35 | true 36 | } 37 | 38 | fn get_setting( 39 | _file: &SocketFile, 40 | _setting: Self::SettingT, 41 | _buf: &mut [u8], 42 | ) -> SyscallResult { 43 | Ok(0) 44 | } 45 | 46 | fn set_setting( 47 | _file: &mut SocketFile, 48 | _setting: Self::SettingT, 49 | _buf: &[u8], 50 | ) -> SyscallResult { 51 | Ok(0) 52 | } 53 | 54 | fn hop_limit(&self) -> u8 { 55 | 0 56 | } 57 | 58 | fn set_hop_limit(&mut self, _hop_limit: u8) {} 59 | 60 | fn new_socket( 61 | socket_set: &mut SocketSet, 62 | path: &str, 63 | uid: u32, 64 | _: &mut Self::SchemeDataT, 65 | _context: &Context, 66 | ) -> SyscallResult<(SocketHandle, Self::DataT)> { 67 | if uid != 0 { 68 | return Err(SyscallError::new(syscall::EACCES)); 69 | } 70 | let proto = 71 | u8::from_str_radix(path, 16).or_else(|_| Err(SyscallError::new(syscall::ENOENT)))?; 72 | 73 | let rx_buffer = RawSocketBuffer::new( 74 | vec![RawPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], 75 | vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE], 76 | ); 77 | let tx_buffer = RawSocketBuffer::new( 78 | vec![RawPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], 79 | vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE], 80 | ); 81 | let ip_socket = RawSocket::new( 82 | IpVersion::Ipv4, 83 | IpProtocol::from(proto), 84 | rx_buffer, 85 | tx_buffer, 86 | ); 87 | 88 | let socket_handle = socket_set.add(ip_socket); 89 | Ok((socket_handle, ())) 90 | } 91 | 92 | fn close_file(&self, _: &SchemeFile, _: &mut Self::SchemeDataT) -> SyscallResult<()> { 93 | Ok(()) 94 | } 95 | 96 | fn write_buf( 97 | &mut self, 98 | file: &mut SocketFile, 99 | buf: &[u8], 100 | ) -> SyscallResult> { 101 | if self.can_send() { 102 | self.send_slice(buf).expect("Can't send slice"); 103 | Ok(Some(buf.len())) 104 | } else if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK { 105 | Err(SyscallError::new(syscall::EAGAIN)) 106 | } else { 107 | Ok(None) // internally scheduled to re-read 108 | } 109 | } 110 | 111 | fn read_buf( 112 | &mut self, 113 | file: &mut SocketFile, 114 | buf: &mut [u8], 115 | ) -> SyscallResult> { 116 | if self.can_recv() { 117 | let length = self.recv_slice(buf).expect("Can't receive slice"); 118 | Ok(Some(length)) 119 | } else if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK { 120 | Err(SyscallError::new(syscall::EAGAIN)) 121 | } else { 122 | Ok(None) // internally scheduled to re-read 123 | } 124 | } 125 | 126 | fn dup( 127 | _socket_set: &mut SocketSet, 128 | _file: &mut SchemeFile, 129 | _path: &str, 130 | _: &mut Self::SchemeDataT, 131 | ) -> SyscallResult> { 132 | Err(SyscallError::new(syscall::EBADF)) 133 | } 134 | 135 | fn fpath(&self, _file: &SchemeFile, buf: &mut [u8]) -> SyscallResult { 136 | let path = format!("ip:{}", self.ip_protocol()); 137 | let path = path.as_bytes(); 138 | 139 | let mut i = 0; 140 | while i < buf.len() && i < path.len() { 141 | buf[i] = path[i]; 142 | i += 1; 143 | } 144 | 145 | Ok(i) 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/smolnetd/scheme/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::link::ethernet::EthernetLink; 2 | use crate::link::LinkDevice; 3 | use crate::link::{loopback::LoopbackDevice, DeviceList}; 4 | use crate::router::route_table::{RouteTable, Rule}; 5 | use crate::router::Router; 6 | use crate::scheme::smoltcp::iface::SocketSet as SmoltcpSocketSet; 7 | use libredox::Fd; 8 | use netutils::getcfg; 9 | use smoltcp; 10 | use smoltcp::iface::{Config, Interface as SmoltcpInterface}; 11 | use smoltcp::phy::Tracer; 12 | use smoltcp::time::{Duration, Instant}; 13 | use smoltcp::wire::{ 14 | EthernetAddress, HardwareAddress, IpAddress, IpCidr, IpListenEndpoint, Ipv4Address, 15 | }; 16 | use std::cell::RefCell; 17 | use std::fs::File; 18 | use std::io::{Read, Write}; 19 | use std::mem::size_of; 20 | use std::os::fd::{FromRawFd, RawFd}; 21 | use std::rc::Rc; 22 | use std::str::FromStr; 23 | use syscall; 24 | use syscall::data::TimeSpec; 25 | 26 | use self::icmp::IcmpScheme; 27 | use self::ip::IpScheme; 28 | use self::netcfg::NetCfgScheme; 29 | use self::tcp::TcpScheme; 30 | use self::udp::UdpScheme; 31 | use redox_netstack::error::{Error, Result}; 32 | 33 | mod icmp; 34 | mod ip; 35 | mod netcfg; 36 | mod socket; 37 | mod tcp; 38 | mod udp; 39 | 40 | type SocketSet = SmoltcpSocketSet<'static>; 41 | type Interface = Rc>; 42 | 43 | const MAX_DURATION: Duration = Duration::from_micros(u64::MAX); 44 | const MIN_DURATION: Duration = Duration::from_micros(0); 45 | 46 | pub struct Smolnetd { 47 | router_device: Tracer, 48 | iface: Interface, 49 | time_file: File, 50 | 51 | socket_set: Rc>, 52 | timer: ::std::time::Instant, 53 | 54 | ip_scheme: IpScheme, 55 | udp_scheme: UdpScheme, 56 | tcp_scheme: TcpScheme, 57 | icmp_scheme: IcmpScheme, 58 | netcfg_scheme: NetCfgScheme, 59 | } 60 | 61 | impl Smolnetd { 62 | pub const MAX_PACKET_SIZE: usize = 2048; 63 | pub const SOCKET_BUFFER_SIZE: usize = 128; //packets 64 | pub const MIN_CHECK_TIMEOUT: Duration = Duration::from_millis(10); 65 | pub const MAX_CHECK_TIMEOUT: Duration = Duration::from_millis(500); 66 | 67 | pub fn new( 68 | network_file: Fd, 69 | hardware_addr: EthernetAddress, 70 | ip_file: Fd, 71 | udp_file: Fd, 72 | tcp_file: Fd, 73 | icmp_file: Fd, 74 | time_file: Fd, 75 | netcfg_file: Fd, 76 | ) -> Smolnetd { 77 | let protocol_addrs = vec![ 78 | //This is a placeholder IP for DHCP 79 | IpCidr::new(IpAddress::v4(0, 0, 0, 0), 8), 80 | ]; 81 | 82 | let default_gw = Ipv4Address::from_str(getcfg("ip_router").unwrap().trim()) 83 | .expect("Can't parse the 'ip_router' cfg."); 84 | 85 | let devices = Rc::new(RefCell::new(DeviceList::default())); 86 | let route_table = Rc::new(RefCell::new(RouteTable::default())); 87 | let mut network_device = Tracer::new( 88 | Router::new(Rc::clone(&devices), Rc::clone(&route_table)), 89 | |_timestamp, printer| trace!("{}", printer), 90 | ); 91 | 92 | let config = Config::new(HardwareAddress::Ip); 93 | let mut iface = SmoltcpInterface::new(config, &mut network_device, Instant::now()); 94 | iface.update_ip_addrs(|ip_addrs| ip_addrs.extend(protocol_addrs)); 95 | iface 96 | .routes_mut() 97 | .add_default_ipv4_route(default_gw) 98 | .expect("Failed to add default gateway"); 99 | 100 | let iface = Rc::new(RefCell::new(iface)); 101 | let socket_set = Rc::new(RefCell::new(SocketSet::new(vec![]))); 102 | 103 | let loopback = LoopbackDevice::default(); 104 | route_table.borrow_mut().insert_rule(Rule::new( 105 | "127.0.0.0/8".parse().unwrap(), 106 | None, 107 | Rc::clone(loopback.name()), 108 | "127.0.0.1".parse().unwrap(), 109 | )); 110 | 111 | let mut eth0 = EthernetLink::new("eth0", unsafe { 112 | File::from_raw_fd(network_file.into_raw() as RawFd) 113 | }); 114 | eth0.set_mac_address(hardware_addr); 115 | 116 | devices.borrow_mut().push(loopback); 117 | devices.borrow_mut().push(eth0); 118 | 119 | Smolnetd { 120 | iface: Rc::clone(&iface), 121 | router_device: network_device, 122 | socket_set: Rc::clone(&socket_set), 123 | timer: ::std::time::Instant::now(), 124 | time_file: unsafe { File::from_raw_fd(time_file.into_raw() as RawFd) }, 125 | ip_scheme: IpScheme::new( 126 | Rc::clone(&iface), 127 | Rc::clone(&route_table), 128 | Rc::clone(&socket_set), 129 | unsafe { File::from_raw_fd(ip_file.into_raw() as RawFd) }, 130 | ), 131 | udp_scheme: UdpScheme::new( 132 | Rc::clone(&iface), 133 | Rc::clone(&route_table), 134 | Rc::clone(&socket_set), 135 | unsafe { File::from_raw_fd(udp_file.into_raw() as RawFd) }, 136 | ), 137 | tcp_scheme: TcpScheme::new( 138 | Rc::clone(&iface), 139 | Rc::clone(&route_table), 140 | Rc::clone(&socket_set), 141 | unsafe { File::from_raw_fd(tcp_file.into_raw() as RawFd) }, 142 | ), 143 | icmp_scheme: IcmpScheme::new( 144 | Rc::clone(&iface), 145 | Rc::clone(&route_table), 146 | Rc::clone(&socket_set), 147 | unsafe { File::from_raw_fd(icmp_file.into_raw() as RawFd) }, 148 | ), 149 | netcfg_scheme: NetCfgScheme::new( 150 | Rc::clone(&iface), 151 | unsafe { File::from_raw_fd(netcfg_file.into_raw() as RawFd) }, 152 | Rc::clone(&route_table), 153 | Rc::clone(&devices), 154 | ), 155 | } 156 | } 157 | 158 | pub fn on_network_scheme_event(&mut self) -> Result<()> { 159 | self.poll()?; 160 | Ok(()) 161 | } 162 | 163 | pub fn on_ip_scheme_event(&mut self) -> Result<()> { 164 | self.ip_scheme.on_scheme_event()?; 165 | let _ = self.poll()?; 166 | Ok(()) 167 | } 168 | 169 | pub fn on_udp_scheme_event(&mut self) -> Result<()> { 170 | self.udp_scheme.on_scheme_event()?; 171 | let _ = self.poll()?; 172 | Ok(()) 173 | } 174 | 175 | pub fn on_tcp_scheme_event(&mut self) -> Result<()> { 176 | self.tcp_scheme.on_scheme_event()?; 177 | let _ = self.poll()?; 178 | Ok(()) 179 | } 180 | 181 | pub fn on_icmp_scheme_event(&mut self) -> Result<()> { 182 | self.icmp_scheme.on_scheme_event()?; 183 | let _ = self.poll()?; 184 | Ok(()) 185 | } 186 | 187 | pub fn on_time_event(&mut self) -> Result<()> { 188 | let timeout = self.poll()?; 189 | self.schedule_time_event(timeout)?; 190 | //TODO: Fix network scheme to ensure events are not missed 191 | self.on_network_scheme_event() 192 | } 193 | 194 | pub fn on_netcfg_scheme_event(&mut self) -> Result<()> { 195 | self.netcfg_scheme.on_scheme_event()?; 196 | Ok(()) 197 | } 198 | 199 | fn schedule_time_event(&mut self, timeout: Duration) -> Result<()> { 200 | let mut time = TimeSpec::default(); 201 | if self.time_file.read(&mut time)? < size_of::() { 202 | return Err(Error::from_syscall_error( 203 | syscall::Error::new(syscall::EBADF), 204 | "Can't read current time", 205 | )); 206 | } 207 | let mut time_ms = time.tv_sec * 1000i64 + i64::from(time.tv_nsec) / 1_000_000i64; 208 | time_ms += timeout.total_millis() as i64; 209 | time.tv_sec = time_ms / 1000; 210 | time.tv_nsec = ((time_ms % 1000) * 1_000_000) as i32; 211 | self.time_file 212 | .write_all(&time) 213 | .map_err(|e| Error::from_io_error(e, "Failed to write to time file"))?; 214 | Ok(()) 215 | } 216 | 217 | fn poll(&mut self) -> Result { 218 | let timeout = { 219 | let mut iter_limit = 10usize; 220 | let mut iface = self.iface.borrow_mut(); 221 | let mut socket_set = self.socket_set.borrow_mut(); 222 | 223 | loop { 224 | let timestamp = Instant::from(self.timer); 225 | if iter_limit == 0 { 226 | break MIN_DURATION; 227 | } 228 | iter_limit -= 1; 229 | 230 | self.router_device.get_mut().poll(timestamp); 231 | 232 | // TODO: Check what if the bool returned by poll can be useful 233 | iface.poll(timestamp, &mut self.router_device, &mut socket_set); 234 | 235 | self.router_device.get_mut().dispatch(timestamp); 236 | 237 | if !self.router_device.get_ref().can_recv() { 238 | match iface.poll_delay(timestamp, &socket_set) { 239 | Some(delay) if delay == Duration::ZERO => {} 240 | Some(delay) => break ::std::cmp::min(MAX_DURATION, delay), 241 | None => break MAX_DURATION, 242 | }; 243 | } 244 | } 245 | }; 246 | 247 | self.notify_sockets()?; 248 | 249 | Ok(::std::cmp::min( 250 | ::std::cmp::max(Smolnetd::MIN_CHECK_TIMEOUT, timeout), 251 | Smolnetd::MAX_CHECK_TIMEOUT, 252 | )) 253 | } 254 | 255 | fn notify_sockets(&mut self) -> Result<()> { 256 | self.ip_scheme.notify_sockets()?; 257 | self.udp_scheme.notify_sockets()?; 258 | self.tcp_scheme.notify_sockets()?; 259 | self.icmp_scheme.notify_sockets() 260 | } 261 | } 262 | 263 | fn post_fevent(scheme_file: &mut File, fd: usize, event: usize, data_len: usize) -> Result<()> { 264 | scheme_file 265 | .write(&syscall::Packet { 266 | id: 0, 267 | pid: 0, 268 | uid: 0, 269 | gid: 0, 270 | a: syscall::number::SYS_FEVENT, 271 | b: fd, 272 | c: event, 273 | d: data_len, 274 | }) 275 | .map(|_| ()) 276 | .map_err(|e| Error::from_io_error(e, "failed to post fevent")) 277 | } 278 | 279 | fn parse_endpoint(socket: &str) -> IpListenEndpoint { 280 | let mut socket_parts = socket.split(':'); 281 | let host = Ipv4Address::from_str(socket_parts.next().unwrap_or("")) 282 | .ok() 283 | .filter(|addr| !addr.is_unspecified()) 284 | .map(IpAddress::Ipv4); 285 | 286 | let port = socket_parts 287 | .next() 288 | .unwrap_or("") 289 | .parse::() 290 | .unwrap_or(0); 291 | IpListenEndpoint { addr: host, port } 292 | } 293 | -------------------------------------------------------------------------------- /src/smolnetd/scheme/netcfg/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod nodes; 3 | mod notifier; 4 | 5 | use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address}; 6 | use std::cell::RefCell; 7 | use std::collections::BTreeMap; 8 | use std::fs::File; 9 | use std::io::{ErrorKind, Read, Write}; 10 | use std::mem; 11 | use std::rc::Rc; 12 | use std::str; 13 | use std::str::FromStr; 14 | use syscall; 15 | use syscall::data::Stat; 16 | use syscall::flag::{MODE_DIR, MODE_FILE}; 17 | use syscall::{ 18 | Error as SyscallError, EventFlags as SyscallEventFlags, Packet as SyscallPacket, 19 | Result as SyscallResult, SchemeMut, 20 | }; 21 | 22 | use crate::link::DeviceList; 23 | use crate::router::route_table::{RouteTable, Rule}; 24 | 25 | use self::nodes::*; 26 | use self::notifier::*; 27 | use super::{post_fevent, Interface}; 28 | use redox_netstack::error::{Error, Result}; 29 | 30 | const WRITE_BUFFER_MAX_SIZE: usize = 0xffff; 31 | 32 | fn gateway_cidr() -> IpCidr { 33 | // TODO: const fn 34 | IpCidr::new(IpAddress::v4(0, 0, 0, 0), 0) 35 | } 36 | 37 | fn parse_route(value: &str, route_table: &RouteTable) -> SyscallResult { 38 | let mut parts = value.split_whitespace(); 39 | let cidr_str = parts.next().ok_or(SyscallError::new(syscall::EINVAL))?; 40 | let cidr = match cidr_str { 41 | "default" => gateway_cidr(), 42 | cidr_str => cidr_str 43 | .parse() 44 | .map_err(|_| SyscallError::new(syscall::EINVAL))?, 45 | }; 46 | 47 | let via: IpAddress = match parts.next().ok_or(SyscallError::new(syscall::EINVAL))? { 48 | "via" => parts 49 | .next() 50 | .ok_or(SyscallError::new(syscall::EINVAL))? 51 | .parse() 52 | .map_err(|_| SyscallError::new(syscall::EINVAL))?, 53 | _ => return Err(SyscallError::new(syscall::EINVAL)), 54 | }; 55 | 56 | if !via.is_unicast() { 57 | return Err(SyscallError::new(syscall::EINVAL)); 58 | } 59 | 60 | let rule = route_table 61 | .lookup_rule(&via) 62 | .ok_or(SyscallError::new(syscall::EINVAL))?; 63 | 64 | Ok(Rule::new(cidr, Some(via), rule.dev.clone(), rule.src)) 65 | } 66 | 67 | fn mk_root_node( 68 | iface: Interface, 69 | notifier: NotifierRef, 70 | dns_config: DNSConfigRef, 71 | route_table: Rc>, 72 | devices: Rc>, 73 | ) -> CfgNodeRef { 74 | cfg_node! { 75 | "resolv" => { 76 | "nameserver" => { 77 | rw [dns_config, notifier] (Option, None) 78 | || { 79 | format!("{}\n", dns_config.borrow().name_server) 80 | } 81 | |cur_value, line| { 82 | if cur_value.is_none() { 83 | let ip = Ipv4Address::from_str(line.trim()) 84 | .map_err(|_| SyscallError::new(syscall::EINVAL))?; 85 | if ip.is_broadcast() || ip.is_multicast() || ip.is_unspecified() { 86 | return Err(SyscallError::new(syscall::EINVAL)); 87 | } 88 | *cur_value = Some(ip); 89 | Ok(()) 90 | } else { 91 | Err(SyscallError::new(syscall::EINVAL)) 92 | } 93 | } 94 | |cur_value| { 95 | if let Some(ip) = *cur_value { 96 | dns_config.borrow_mut().name_server = ip; 97 | notifier.borrow_mut().schedule_notify("resolv/nameserver"); 98 | } 99 | Ok(()) 100 | } 101 | } 102 | }, 103 | "route" => { 104 | "list" => { 105 | ro [route_table] || { 106 | format!("{}", route_table.borrow()) 107 | } 108 | }, 109 | "add" => { 110 | wo [iface, notifier, route_table] (Option, None) 111 | |cur_value, line| { 112 | if cur_value.is_none() { 113 | let route = parse_route(line, &route_table.borrow())?; 114 | *cur_value = Some(route); 115 | Ok(()) 116 | } else { 117 | Err(SyscallError::new(syscall::EINVAL)) 118 | } 119 | } 120 | |cur_value| { 121 | if let Some(route) = cur_value.take() { 122 | route_table.borrow_mut().insert_rule(route); 123 | notifier.borrow_mut().schedule_notify("route/list"); 124 | Ok(()) 125 | } else { 126 | Err(SyscallError::new(syscall::EINVAL)) 127 | } 128 | } 129 | }, 130 | "rm" => { 131 | wo [iface, notifier, route_table] (Option, None) 132 | |cur_value, line| { 133 | if cur_value.is_none() { 134 | match line.parse() { 135 | Ok(cidr) => { 136 | *cur_value = Some(cidr); 137 | Ok(()) 138 | } 139 | Err(_) => Err(SyscallError::new(syscall::EINVAL)) 140 | } 141 | } else { 142 | Err(SyscallError::new(syscall::EINVAL)) 143 | } 144 | } 145 | |cur_value| { 146 | if let Some(cidr) = *cur_value { 147 | route_table.borrow_mut().remove_rule(cidr); 148 | notifier.borrow_mut().schedule_notify("route/list"); 149 | Ok(()) 150 | } else { 151 | Err(SyscallError::new(syscall::EINVAL)) 152 | } 153 | } 154 | }, 155 | }, 156 | "ifaces" => { 157 | "eth0" => { 158 | "mac" => { 159 | rw [iface, notifier, devices] (Option, None) 160 | || { 161 | match devices.borrow().get("eth0") { 162 | Some(dev) => { 163 | match dev.mac_address() { 164 | Some(addr) => format!("{addr}\n"), 165 | None => "Not configured\n".into(), 166 | } 167 | } 168 | None => "Device not found\n".into(), 169 | } 170 | } 171 | |cur_value, line| { 172 | if cur_value.is_none() { 173 | let mac = EthernetAddress::from_str(line). 174 | map_err(|_| SyscallError::new(syscall::EINVAL))?; 175 | if !mac.is_unicast() { 176 | return Err(SyscallError::new(syscall::EINVAL)); 177 | } 178 | *cur_value = Some(mac); 179 | Ok(()) 180 | } else { 181 | Err(SyscallError::new(syscall::EINVAL)) 182 | } 183 | } 184 | |cur_value| { 185 | if let Some(mac) = *cur_value { 186 | if let Some(dev) = devices.borrow_mut().get_mut("eth0") { 187 | dev.set_mac_address(mac); 188 | notifier.borrow_mut().schedule_notify("ifaces/eth0/mac"); 189 | } 190 | } 191 | Ok(()) 192 | } 193 | }, 194 | "addr" => { 195 | "list" => { 196 | ro [devices] 197 | || { 198 | let res = match devices.borrow().get("eth0") { 199 | Some(dev) => { 200 | match dev.ip_address() { 201 | Some(addr) => format!("{addr}\n"), 202 | None => "Not configured\n".into(), 203 | } 204 | } 205 | None => "Device not found\n".into(), 206 | }; 207 | res 208 | } 209 | }, 210 | "set" => { 211 | wo [iface, notifier, devices, route_table] (Option, None) 212 | |cur_value, line| { 213 | if cur_value.is_none() { 214 | let cidr = IpCidr::from_str(line) 215 | .map_err(|_| SyscallError::new(syscall::EINVAL))?; 216 | if !cidr.address().is_unicast() { 217 | return Err(SyscallError::new(syscall::EINVAL)); 218 | } 219 | *cur_value = Some(cidr); 220 | Ok(()) 221 | } else { 222 | Err(SyscallError::new(syscall::EINVAL)) 223 | } 224 | } 225 | |cur_value| { 226 | // TODO: Multiple IPs 227 | if let Some(cidr) = cur_value.take() { 228 | if let Some(dev) = devices.borrow_mut().get_mut("eth0") { 229 | 230 | let mut route_table = route_table.borrow_mut(); 231 | if let Some(old_addr) = dev.ip_address() { 232 | let IpCidr::Ipv4(old_v4_cidr) = old_addr; 233 | let old_network = IpCidr::Ipv4(old_v4_cidr.network()); 234 | 235 | route_table.remove_rule(old_network); 236 | route_table.change_src(old_addr.address(), cidr.address()); 237 | iface.borrow_mut().update_ip_addrs(|addrs| addrs.retain(|addr| *addr != old_addr)) 238 | } 239 | 240 | dev.set_ip_address(cidr); 241 | // FIXME: Here, the insert 0 is a workaround to let UDP sockets 242 | // work with this interface only. 243 | // Smoltcp takes the first ip address when looking for a source 244 | // ip address when sending UDP packets. 245 | // This behavior will have to be fixed as it's our route table 246 | // job to find give this source. 247 | iface.borrow_mut().update_ip_addrs(|addrs| addrs.insert(0, cidr).unwrap()); 248 | 249 | let IpCidr::Ipv4(v4_cidr) = cidr; 250 | let network_cidr = IpCidr::Ipv4(v4_cidr.network()); 251 | route_table.insert_rule(Rule::new(network_cidr, None, dev.name().clone(), cidr.address())) 252 | } 253 | notifier.borrow_mut().schedule_notify("ifaces/eth0/addr/list"); 254 | notifier.borrow_mut().schedule_notify("route/list"); 255 | } 256 | Ok(()) 257 | } 258 | }, 259 | } 260 | } 261 | } 262 | } 263 | } 264 | 265 | struct DNSConfig { 266 | name_server: Ipv4Address, 267 | } 268 | 269 | type DNSConfigRef = Rc>; 270 | 271 | struct NetCfgFile { 272 | path: String, 273 | is_dir: bool, 274 | is_writable: bool, 275 | is_readable: bool, 276 | node_writer: Option>, 277 | read_buf: Vec, 278 | write_buf: Vec, 279 | pos: usize, 280 | uid: u32, 281 | done: bool, 282 | } 283 | 284 | impl NetCfgFile { 285 | fn commit(&mut self) -> SyscallResult<()> { 286 | if let Some(ref mut node_writer) = self.node_writer { 287 | if !self.write_buf.is_empty() { 288 | let line = str::from_utf8(&self.write_buf) 289 | .or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?; 290 | node_writer.write_line(line)?; 291 | } 292 | node_writer.commit()?; 293 | self.write_buf.clear(); 294 | } 295 | Ok(()) 296 | } 297 | 298 | fn consume_lines(&mut self) -> SyscallResult<()> { 299 | if let Some(ref mut node_writer) = self.node_writer { 300 | let mut swap_with = None; 301 | { 302 | let mut lines = self.write_buf.split(|&c| c == b'\n'); 303 | if let Some(mut cur_line) = lines.next() { 304 | let mut consumed = false; 305 | for next_line in lines { 306 | let line = str::from_utf8(cur_line) 307 | .or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?; 308 | trace!("writing line {}", line); 309 | node_writer.write_line(line)?; 310 | cur_line = next_line; 311 | consumed = true; 312 | } 313 | if consumed { 314 | swap_with = Some(From::from(cur_line)) 315 | } 316 | } 317 | } 318 | if let Some(ref mut new_vec) = swap_with { 319 | mem::swap(&mut self.write_buf, new_vec); 320 | } 321 | Ok(()) 322 | } else { 323 | Err(SyscallError::new(syscall::EBADF)) 324 | } 325 | } 326 | } 327 | 328 | pub struct NetCfgScheme { 329 | scheme_file: File, 330 | next_fd: usize, 331 | files: BTreeMap, 332 | root_node: CfgNodeRef, 333 | notifier: NotifierRef, 334 | } 335 | 336 | impl NetCfgScheme { 337 | pub fn new( 338 | iface: Interface, 339 | scheme_file: File, 340 | route_table: Rc>, 341 | devices: Rc>, 342 | ) -> NetCfgScheme { 343 | let notifier = Notifier::new_ref(); 344 | let dns_config = Rc::new(RefCell::new(DNSConfig { 345 | name_server: Ipv4Address::new(8, 8, 8, 8), 346 | })); 347 | NetCfgScheme { 348 | scheme_file, 349 | next_fd: 1, 350 | files: BTreeMap::new(), 351 | root_node: mk_root_node( 352 | iface, 353 | Rc::clone(¬ifier), 354 | dns_config, 355 | route_table, 356 | devices, 357 | ), 358 | notifier, 359 | } 360 | } 361 | 362 | pub fn on_scheme_event(&mut self) -> Result> { 363 | let result = loop { 364 | let mut packet = SyscallPacket::default(); 365 | match self.scheme_file.read(&mut packet) { 366 | Ok(0) => { 367 | //TODO: Cleanup must occur 368 | break Some(()); 369 | } 370 | Ok(_) => (), 371 | Err(err) => { 372 | if err.kind() == ErrorKind::WouldBlock { 373 | break None; 374 | } else { 375 | return Err(Error::from(err)); 376 | } 377 | } 378 | } 379 | self.handle(&mut packet); 380 | self.scheme_file.write_all(&packet)?; 381 | }; 382 | self.notify_scheduled_fds(); 383 | Ok(result) 384 | } 385 | 386 | fn notify_scheduled_fds(&mut self) { 387 | let fds_to_notify = self.notifier.borrow_mut().get_notified_fds(); 388 | for fd in fds_to_notify { 389 | let _ = post_fevent(&mut self.scheme_file, fd, syscall::EVENT_READ.bits(), 1); 390 | } 391 | } 392 | } 393 | 394 | impl SchemeMut for NetCfgScheme { 395 | fn open(&mut self, path: &str, _flags: usize, uid: u32, _gid: u32) -> SyscallResult { 396 | let mut current_node = Rc::clone(&self.root_node); 397 | for part in path.split('/') { 398 | if part.is_empty() { 399 | continue; 400 | } 401 | let next_node = current_node 402 | .borrow_mut() 403 | .open(part) 404 | .ok_or_else(|| SyscallError::new(syscall::EINVAL))?; 405 | current_node = next_node; 406 | } 407 | let current_node = current_node.borrow(); 408 | let read_buf = Vec::from(current_node.read()); 409 | let fd = self.next_fd; 410 | trace!("open {} {}", fd, path); 411 | self.next_fd += 1; 412 | self.files.insert( 413 | fd, 414 | NetCfgFile { 415 | path: path.to_owned(), 416 | is_dir: current_node.is_dir(), 417 | is_writable: current_node.is_writable(), 418 | is_readable: current_node.is_readable(), 419 | node_writer: if current_node.is_writable() { 420 | current_node.new_writer() 421 | } else { 422 | None 423 | }, 424 | uid, 425 | pos: 0, 426 | read_buf, 427 | write_buf: vec![], 428 | done: false, 429 | }, 430 | ); 431 | Ok(fd) 432 | } 433 | 434 | fn close(&mut self, fd: usize) -> SyscallResult { 435 | trace!("close {}", fd); 436 | if let Some(mut file) = self.files.remove(&fd) { 437 | self.notifier.borrow_mut().unsubscribe(&file.path, fd); 438 | if !file.done { 439 | file.commit().map(|_| 0) 440 | } else { 441 | Ok(0) 442 | } 443 | } else { 444 | Err(SyscallError::new(syscall::EBADF)) 445 | } 446 | } 447 | 448 | fn write(&mut self, fd: usize, buf: &[u8]) -> SyscallResult { 449 | let file = self 450 | .files 451 | .get_mut(&fd) 452 | .ok_or_else(|| SyscallError::new(syscall::EBADF))?; 453 | 454 | if file.done { 455 | return Err(SyscallError::new(syscall::EBADF)); 456 | } 457 | 458 | if file.uid != 0 { 459 | return Err(SyscallError::new(syscall::EACCES)); 460 | } 461 | 462 | if (WRITE_BUFFER_MAX_SIZE - file.write_buf.len()) < buf.len() { 463 | return Err(SyscallError::new(syscall::EMSGSIZE)); 464 | } 465 | 466 | file.write_buf.extend_from_slice(buf); 467 | 468 | if let Err(e) = file.consume_lines() { 469 | trace!("Failed write {} {}", fd, e); 470 | file.done = true; 471 | return Err(e); 472 | } 473 | 474 | Ok(buf.len()) 475 | } 476 | 477 | fn read(&mut self, fd: usize, buf: &mut [u8]) -> SyscallResult { 478 | let file = self 479 | .files 480 | .get_mut(&fd) 481 | .ok_or_else(|| SyscallError::new(syscall::EBADF))?; 482 | 483 | let mut i = 0; 484 | while i < buf.len() && file.pos < file.read_buf.len() { 485 | buf[i] = file.read_buf[file.pos]; 486 | i += 1; 487 | file.pos += 1; 488 | } 489 | Ok(i) 490 | } 491 | 492 | fn fstat(&mut self, fd: usize, stat: &mut Stat) -> SyscallResult { 493 | let file = self 494 | .files 495 | .get_mut(&fd) 496 | .ok_or_else(|| SyscallError::new(syscall::EBADF))?; 497 | 498 | stat.st_mode = if file.is_dir { MODE_DIR } else { MODE_FILE }; 499 | if file.is_writable { 500 | stat.st_mode |= 0o222; 501 | } 502 | if file.is_readable { 503 | stat.st_mode |= 0o444; 504 | } 505 | stat.st_uid = 0; 506 | stat.st_gid = 0; 507 | stat.st_size = file.read_buf.len() as u64; 508 | 509 | Ok(0) 510 | } 511 | 512 | fn fevent(&mut self, fd: usize, events: SyscallEventFlags) -> SyscallResult { 513 | let file = self 514 | .files 515 | .get_mut(&fd) 516 | .ok_or_else(|| SyscallError::new(syscall::EBADF))?; 517 | if events.contains(syscall::EVENT_READ) { 518 | self.notifier.borrow_mut().subscribe(&file.path, fd); 519 | } else { 520 | self.notifier.borrow_mut().unsubscribe(&file.path, fd); 521 | } 522 | Ok(SyscallEventFlags::empty()) 523 | } 524 | 525 | fn fsync(&mut self, fd: usize) -> SyscallResult { 526 | let file = self 527 | .files 528 | .get_mut(&fd) 529 | .ok_or_else(|| SyscallError::new(syscall::EBADF))?; 530 | 531 | if !file.done { 532 | let res = file.commit().map(|_| 0); 533 | file.done = true; 534 | res 535 | } else { 536 | Err(SyscallError::new(syscall::EBADF)) 537 | } 538 | } 539 | } 540 | -------------------------------------------------------------------------------- /src/smolnetd/scheme/netcfg/nodes.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::collections::BTreeMap; 3 | use std::rc::Rc; 4 | use syscall::Result as SyscallResult; 5 | 6 | pub type CfgNodeRef = Rc>; 7 | 8 | pub trait NodeWriter { 9 | fn write_line(&mut self, _: &str) -> SyscallResult<()> { 10 | Ok(()) 11 | } 12 | 13 | fn commit(&mut self) -> SyscallResult<()> { 14 | Ok(()) 15 | } 16 | } 17 | 18 | pub struct SimpleWriter 19 | where 20 | WL: 'static + Fn(&mut T, &str) -> SyscallResult<()>, 21 | C: 'static + Fn(&mut T) -> SyscallResult<()>, 22 | { 23 | data: T, 24 | write_line: WL, 25 | commit: C, 26 | } 27 | 28 | impl NodeWriter for SimpleWriter 29 | where 30 | WL: 'static + Fn(&mut T, &str) -> SyscallResult<()>, 31 | C: 'static + Fn(&mut T) -> SyscallResult<()>, 32 | { 33 | fn write_line(&mut self, line: &str) -> SyscallResult<()> { 34 | (self.write_line)(&mut self.data, line) 35 | } 36 | 37 | fn commit(&mut self) -> SyscallResult<()> { 38 | (self.commit)(&mut self.data) 39 | } 40 | } 41 | 42 | impl SimpleWriter 43 | where 44 | WL: 'static + Fn(&mut T, &str) -> SyscallResult<()>, 45 | C: 'static + Fn(&mut T) -> SyscallResult<()>, 46 | { 47 | pub fn new_boxed(data: T, write_line: WL, commit: C) -> Box { 48 | Box::new(SimpleWriter { 49 | data, 50 | write_line, 51 | commit, 52 | }) 53 | } 54 | } 55 | 56 | pub trait CfgNode { 57 | fn is_dir(&self) -> bool { 58 | false 59 | } 60 | 61 | fn is_writable(&self) -> bool { 62 | false 63 | } 64 | 65 | fn is_readable(&self) -> bool { 66 | true 67 | } 68 | 69 | fn read(&self) -> String { 70 | String::new() 71 | } 72 | 73 | fn open(&self, _file: &str) -> Option { 74 | None 75 | } 76 | 77 | fn new_writer(&self) -> Option> { 78 | None 79 | } 80 | } 81 | 82 | pub struct RONode 83 | where 84 | F: Fn() -> String, 85 | { 86 | read_fun: F, 87 | } 88 | 89 | impl CfgNode for RONode 90 | where 91 | F: Fn() -> String, 92 | { 93 | fn read(&self) -> String { 94 | (self.read_fun)() 95 | } 96 | } 97 | 98 | impl RONode 99 | where 100 | F: 'static + Fn() -> String, 101 | { 102 | pub fn new_ref(read_fun: F) -> CfgNodeRef { 103 | Rc::new(RefCell::new(RONode { read_fun })) 104 | } 105 | } 106 | 107 | pub struct WONode 108 | where 109 | W: 'static + Fn() -> Box, 110 | { 111 | new_writer: W, 112 | } 113 | 114 | impl CfgNode for WONode 115 | where 116 | W: 'static + Fn() -> Box, 117 | { 118 | fn is_readable(&self) -> bool { 119 | false 120 | } 121 | 122 | fn is_writable(&self) -> bool { 123 | true 124 | } 125 | 126 | fn new_writer(&self) -> Option> { 127 | Some((self.new_writer)()) 128 | } 129 | } 130 | 131 | impl WONode 132 | where 133 | W: 'static + Fn() -> Box, 134 | { 135 | pub fn new_ref(new_writer: W) -> CfgNodeRef { 136 | Rc::new(RefCell::new(WONode { new_writer })) 137 | } 138 | } 139 | 140 | pub struct RWNode 141 | where 142 | F: Fn() -> String, 143 | W: 'static + Fn() -> Box, 144 | { 145 | read_fun: F, 146 | new_writer: W, 147 | } 148 | 149 | impl CfgNode for RWNode 150 | where 151 | F: Fn() -> String, 152 | W: 'static + Fn() -> Box, 153 | { 154 | fn read(&self) -> String { 155 | (self.read_fun)() 156 | } 157 | 158 | fn is_writable(&self) -> bool { 159 | true 160 | } 161 | 162 | fn new_writer(&self) -> Option> { 163 | Some((self.new_writer)()) 164 | } 165 | } 166 | 167 | impl RWNode 168 | where 169 | F: 'static + Fn() -> String, 170 | W: 'static + Fn() -> Box, 171 | { 172 | pub fn new_ref(read_fun: F, new_writer: W) -> CfgNodeRef { 173 | Rc::new(RefCell::new(RWNode { 174 | read_fun, 175 | new_writer, 176 | })) 177 | } 178 | } 179 | 180 | pub struct StaticDirNode { 181 | child_nodes: BTreeMap, 182 | } 183 | 184 | impl CfgNode for StaticDirNode { 185 | fn is_dir(&self) -> bool { 186 | true 187 | } 188 | 189 | fn read(&self) -> String { 190 | let mut files = String::new(); 191 | for child in self.child_nodes.keys() { 192 | if !files.is_empty() { 193 | files.push('\n'); 194 | } 195 | files += child; 196 | } 197 | files 198 | } 199 | 200 | fn open(&self, file: &str) -> Option { 201 | self.child_nodes.get(file).map(|node| Rc::clone(node)) 202 | } 203 | } 204 | 205 | impl StaticDirNode { 206 | pub fn new_ref(child_nodes: BTreeMap) -> CfgNodeRef { 207 | Rc::new(RefCell::new(StaticDirNode { child_nodes })) 208 | } 209 | } 210 | 211 | macro_rules! cfg_node { 212 | (val $e:expr) => { 213 | $e 214 | }; 215 | (ro [ $($c:ident),* ] || $b:block ) => { 216 | { 217 | $(let $c = $c.clone();)* 218 | RONode::new_ref(move|| $b) 219 | } 220 | }; 221 | (wo [ $($c:ident),* ] ( $et:ty , $e:expr ) |$data_i:ident, $line_i:ident| 222 | $write_line:block |$data_i2:ident| $commit:block) => { 223 | { 224 | $(#[allow(unused_variables)] let $c = $c.clone();)* 225 | let new_writer = move || -> Box { 226 | let write_line = { 227 | $(#[allow(unused_variables)] let $c = $c.clone();)* 228 | move |$data_i: &mut $et, $line_i: &str| $write_line 229 | }; 230 | let commit = { 231 | $(#[allow(unused_variables)] let $c = $c.clone();)* 232 | move |$data_i2: &mut $et| $commit 233 | }; 234 | let data: $et = $e; 235 | SimpleWriter::new_boxed(data, write_line, commit) 236 | }; 237 | WONode::new_ref(new_writer) 238 | } 239 | }; 240 | (rw [ $($c:ident),* ] ( $et:ty , $e:expr ) || $read_fun:block |$data_i:ident, $line_i:ident| 241 | $write_line:block |$data_i2:ident| $commit:block) => { 242 | { 243 | let read_fun = { 244 | $(#[allow(unused_variables)] let $c = $c.clone();)* 245 | move || $read_fun 246 | }; 247 | $(#[allow(unused_variables)] let $c = $c.clone();)* 248 | let new_writer = move || -> Box { 249 | let write_line = { 250 | $(#[allow(unused_variables)] let $c = $c.clone();)* 251 | move |$data_i: &mut $et, $line_i: &str| $write_line 252 | }; 253 | let commit = { 254 | $(#[allow(unused_variables)] let $c = $c.clone();)* 255 | move |$data_i2: &mut $et| $commit 256 | }; 257 | let data: $et = $e; 258 | SimpleWriter::new_boxed(data, write_line, commit) 259 | }; 260 | RWNode::new_ref(read_fun, new_writer) 261 | } 262 | }; 263 | ($($e:expr => { $($t:tt)* }),* $(,)*) => { 264 | { 265 | let mut children = BTreeMap::new(); 266 | $(children.insert($e.into(), cfg_node!($($t)*));)* 267 | StaticDirNode::new_ref(children) 268 | } 269 | }; 270 | } 271 | -------------------------------------------------------------------------------- /src/smolnetd/scheme/netcfg/notifier.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::collections::btree_map::Entry; 3 | use std::collections::{BTreeMap, BTreeSet}; 4 | use std::rc::Rc; 5 | 6 | pub struct Notifier { 7 | listeners: BTreeMap>, 8 | notified: BTreeSet, 9 | } 10 | 11 | pub type NotifierRef = Rc>; 12 | 13 | impl Notifier { 14 | pub fn new_ref() -> NotifierRef { 15 | Rc::new(RefCell::new(Notifier { 16 | listeners: BTreeMap::new(), 17 | notified: BTreeSet::new(), 18 | })) 19 | } 20 | 21 | pub fn subscribe(&mut self, path: &str, fd: usize) { 22 | trace!("Sub fd {} to {}", fd, path); 23 | match self.listeners.entry(path.to_owned()) { 24 | Entry::Occupied(mut e) => { 25 | e.get_mut().insert(fd); 26 | } 27 | Entry::Vacant(e) => { 28 | let mut fds = BTreeSet::new(); 29 | fds.insert(fd); 30 | e.insert(fds); 31 | } 32 | } 33 | } 34 | 35 | pub fn unsubscribe(&mut self, path: &str, fd: usize) { 36 | let empty = if let Some(fds) = self.listeners.get_mut(path) { 37 | if fds.remove(&fd) { 38 | trace!("Unsub fd {} from {}", fd, path); 39 | } 40 | fds.is_empty() 41 | } else { 42 | false 43 | }; 44 | if empty { 45 | self.listeners.remove(path); 46 | } 47 | } 48 | 49 | pub fn schedule_notify(&mut self, path: &str) { 50 | trace!("Notifying {}", path); 51 | if let Some(fds) = self.listeners.get(path) { 52 | self.notified.extend(fds); 53 | } 54 | } 55 | 56 | pub fn get_notified_fds(&mut self) -> BTreeSet { 57 | use std::mem::swap; 58 | let mut notified = BTreeSet::new(); 59 | swap(&mut self.notified, &mut notified); 60 | notified 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/smolnetd/scheme/socket.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::collections::btree_map::Entry; 3 | use std::collections::BTreeMap; 4 | use std::fs::File; 5 | use std::io::{ErrorKind, Read, Write}; 6 | use std::marker::PhantomData; 7 | use std::mem; 8 | use std::ops::Deref; 9 | use std::ops::DerefMut; 10 | use std::rc::Rc; 11 | use std::str; 12 | 13 | use libredox::flag::{self, CLOCK_MONOTONIC}; 14 | use syscall::data::TimeSpec; 15 | use syscall::flag::{EVENT_READ, EVENT_WRITE}; 16 | use syscall::{self, KSMSG_CANCEL}; 17 | use syscall::{ 18 | Error as SyscallError, EventFlags as SyscallEventFlags, Packet as SyscallPacket, 19 | Result as SyscallResult, SchemeBlockMut, 20 | }; 21 | 22 | use super::Interface; 23 | use crate::router::route_table::RouteTable; 24 | use crate::scheme::smoltcp::iface::SocketHandle; 25 | use redox_netstack::error::{Error, Result}; 26 | use smoltcp::socket::AnySocket; 27 | 28 | use super::{post_fevent, SocketSet}; 29 | 30 | pub struct Context { 31 | pub iface: Interface, 32 | pub route_table: Rc>, 33 | } 34 | 35 | pub struct NullFile { 36 | pub flags: usize, 37 | pub uid: u32, 38 | pub gid: u32, 39 | } 40 | 41 | pub struct SocketFile { 42 | pub flags: usize, 43 | pub data: DataT, 44 | 45 | events: usize, 46 | socket_handle: SocketHandle, 47 | read_notified: bool, 48 | write_notified: bool, 49 | read_timeout: Option, 50 | write_timeout: Option, 51 | } 52 | 53 | impl SocketFile { 54 | pub fn clone_with_data(&self, data: DataT) -> SocketFile { 55 | SocketFile { 56 | flags: self.flags, 57 | events: self.events, 58 | read_notified: false, // we still want to notify about this new socket 59 | write_notified: false, 60 | read_timeout: self.read_timeout, 61 | write_timeout: self.write_timeout, 62 | socket_handle: self.socket_handle, 63 | data, 64 | } 65 | } 66 | 67 | pub fn new_with_data(socket_handle: SocketHandle, data: DataT) -> SocketFile { 68 | SocketFile { 69 | flags: 0, 70 | events: 0, 71 | read_notified: false, 72 | write_notified: false, 73 | read_timeout: None, 74 | write_timeout: None, 75 | socket_handle, 76 | data, 77 | } 78 | } 79 | } 80 | 81 | #[derive(Copy, Clone)] 82 | enum Setting { 83 | Ttl, 84 | ReadTimeout, 85 | WriteTimeout, 86 | #[allow(dead_code)] 87 | Other(SettingT), 88 | } 89 | 90 | pub struct SettingFile { 91 | fd: usize, 92 | socket_handle: SocketHandle, 93 | setting: Setting, 94 | } 95 | 96 | pub enum SchemeFile 97 | where 98 | SocketT: SchemeSocket, 99 | { 100 | Setting(SettingFile), 101 | Socket(SocketFile), 102 | } 103 | 104 | impl SchemeFile 105 | where 106 | SocketT: SchemeSocket, 107 | { 108 | pub fn socket_handle(&self) -> SocketHandle { 109 | match *self { 110 | SchemeFile::Socket(SocketFile { socket_handle, .. }) 111 | | SchemeFile::Setting(SettingFile { socket_handle, .. }) => socket_handle, 112 | } 113 | } 114 | 115 | pub fn events(&mut self, socket_set: &mut SocketSet) -> usize 116 | where 117 | SocketT: AnySocket<'static>, 118 | { 119 | let mut revents = 0; 120 | if let &mut SchemeFile::Socket(SocketFile { 121 | socket_handle, 122 | events, 123 | ref mut read_notified, 124 | ref mut write_notified, 125 | .. 126 | }) = self 127 | { 128 | let socket = socket_set.get::(socket_handle); 129 | 130 | if events & syscall::EVENT_READ.bits() == syscall::EVENT_READ.bits() 131 | && (socket.can_recv() || !socket.may_recv()) 132 | { 133 | if !*read_notified { 134 | *read_notified = true; 135 | revents |= EVENT_READ.bits(); 136 | } 137 | } else { 138 | *read_notified = false; 139 | } 140 | 141 | if events & syscall::EVENT_WRITE.bits() == syscall::EVENT_WRITE.bits() 142 | && socket.can_send() 143 | { 144 | if !*write_notified { 145 | *write_notified = true; 146 | revents |= EVENT_WRITE.bits(); 147 | } 148 | } else { 149 | *write_notified = false; 150 | } 151 | } 152 | revents 153 | } 154 | } 155 | 156 | #[derive(Default, Clone)] 157 | struct WaitHandle { 158 | until: Option, 159 | packet: SyscallPacket, 160 | } 161 | 162 | type WaitQueue = Vec; 163 | 164 | pub type DupResult = Option<( 165 | SchemeFile, 166 | Option<(SocketHandle, ::DataT)>, 167 | )>; 168 | 169 | pub trait SchemeSocket 170 | where 171 | Self: ::std::marker::Sized, 172 | { 173 | type SchemeDataT; 174 | type DataT; 175 | type SettingT: Copy; 176 | 177 | fn new_scheme_data() -> Self::SchemeDataT; 178 | 179 | fn can_send(&self) -> bool; 180 | fn can_recv(&self) -> bool; 181 | fn may_recv(&self) -> bool; 182 | 183 | fn hop_limit(&self) -> u8; 184 | fn set_hop_limit(&mut self, hop_limit: u8); 185 | 186 | fn get_setting( 187 | file: &SocketFile, 188 | setting: Self::SettingT, 189 | buf: &mut [u8], 190 | ) -> SyscallResult; 191 | fn set_setting( 192 | file: &mut SocketFile, 193 | setting: Self::SettingT, 194 | buf: &[u8], 195 | ) -> SyscallResult; 196 | 197 | fn new_socket( 198 | sockets: &mut SocketSet, 199 | path: &str, 200 | uid: u32, 201 | data: &mut Self::SchemeDataT, 202 | context: &Context, 203 | ) -> SyscallResult<(SocketHandle, Self::DataT)>; 204 | 205 | fn close_file( 206 | &self, 207 | file: &SchemeFile, 208 | data: &mut Self::SchemeDataT, 209 | ) -> SyscallResult<()>; 210 | 211 | fn write_buf( 212 | &mut self, 213 | file: &mut SocketFile, 214 | buf: &[u8], 215 | ) -> SyscallResult>; 216 | 217 | fn read_buf( 218 | &mut self, 219 | file: &mut SocketFile, 220 | buf: &mut [u8], 221 | ) -> SyscallResult>; 222 | 223 | fn fpath(&self, file: &SchemeFile, buf: &mut [u8]) -> SyscallResult; 224 | 225 | fn dup( 226 | sockets: &mut SocketSet, 227 | file: &mut SchemeFile, 228 | path: &str, 229 | data: &mut Self::SchemeDataT, 230 | ) -> SyscallResult>; 231 | } 232 | 233 | pub struct SocketScheme 234 | where 235 | SocketT: SchemeSocket + AnySocket<'static>, 236 | { 237 | next_fd: usize, 238 | nulls: BTreeMap, 239 | files: BTreeMap>, 240 | ref_counts: BTreeMap, 241 | context: Context, 242 | socket_set: Rc>, 243 | scheme_file: File, 244 | wait_queue: WaitQueue, 245 | scheme_data: SocketT::SchemeDataT, 246 | _phantom_socket: PhantomData, 247 | } 248 | 249 | impl SocketScheme 250 | where 251 | SocketT: SchemeSocket + AnySocket<'static>, 252 | { 253 | pub fn new( 254 | iface: Interface, 255 | route_table: Rc>, 256 | socket_set: Rc>, 257 | scheme_file: File, 258 | ) -> SocketScheme { 259 | SocketScheme { 260 | next_fd: 1, 261 | nulls: BTreeMap::new(), 262 | files: BTreeMap::new(), 263 | ref_counts: BTreeMap::new(), 264 | socket_set, 265 | scheme_data: SocketT::new_scheme_data(), 266 | scheme_file, 267 | wait_queue: Vec::new(), 268 | _phantom_socket: PhantomData, 269 | context: Context { iface, route_table }, 270 | } 271 | } 272 | 273 | pub fn on_scheme_event(&mut self) -> Result> { 274 | let result = loop { 275 | let mut packet = SyscallPacket::default(); 276 | match self.scheme_file.read(&mut packet) { 277 | Ok(0) => { 278 | //TODO: Cleanup must occur 279 | break Some(()); 280 | } 281 | Ok(_) => (), 282 | Err(err) => { 283 | if err.kind() == ErrorKind::WouldBlock { 284 | break None; 285 | } else { 286 | return Err(Error::from(err)); 287 | } 288 | } 289 | } 290 | if packet.a == KSMSG_CANCEL { 291 | println!("smolnetd: todo: handle cancellation"); 292 | continue; 293 | } 294 | if let Some(a) = self.handle(&mut packet) { 295 | packet.a = a; 296 | self.scheme_file.write_all(&packet)?; 297 | } else { 298 | match self.handle_block(&mut packet) { 299 | Ok(timeout) => { 300 | self.wait_queue.push(WaitHandle { 301 | until: timeout, 302 | packet, 303 | }); 304 | } 305 | Err(err) => { 306 | packet.a = (-err.errno) as usize; 307 | self.scheme_file.write_all(&packet)?; 308 | return Err(Error::from_syscall_error( 309 | err, 310 | "Can't handle blocked socket", 311 | )); 312 | } 313 | } 314 | } 315 | }; 316 | Ok(result) 317 | } 318 | 319 | pub fn notify_sockets(&mut self) -> Result<()> { 320 | let cur_time = libredox::call::clock_gettime(flag::CLOCK_MONOTONIC) 321 | .map_err(|e| Error::from_syscall_error(e.into(), "Can't get time"))?; 322 | 323 | // Notify non-blocking sockets 324 | for (&fd, ref mut file) in &mut self.files { 325 | let events = { 326 | let mut socket_set = self.socket_set.borrow_mut(); 327 | file.events(&mut socket_set) 328 | }; 329 | if events > 0 { 330 | post_fevent(&mut self.scheme_file, fd, events, 1)?; 331 | } 332 | } 333 | 334 | // Wake up blocking queue 335 | let mut i = 0; 336 | while i < self.wait_queue.len() { 337 | let mut packet = self.wait_queue[i].packet; 338 | if let Some(a) = self.handle(&packet) { 339 | self.wait_queue.remove(i); 340 | packet.a = a; 341 | self.scheme_file.write_all(&packet)?; 342 | } else { 343 | match self.wait_queue[i].until { 344 | Some(until) 345 | if (until.tv_sec < cur_time.tv_sec 346 | || (until.tv_sec == cur_time.tv_sec 347 | && i64::from(until.tv_nsec) < i64::from(cur_time.tv_nsec))) => 348 | { 349 | self.wait_queue.remove(i); 350 | packet.a = (-syscall::ETIMEDOUT) as usize; 351 | self.scheme_file.write_all(&packet)?; 352 | } 353 | _ => { 354 | i += 1; 355 | } 356 | } 357 | } 358 | } 359 | 360 | Ok(()) 361 | } 362 | 363 | fn handle_block(&mut self, packet: &mut SyscallPacket) -> SyscallResult> { 364 | let fd = packet.b; 365 | let (read_timeout, write_timeout) = { 366 | let file = self 367 | .files 368 | .get(&fd) 369 | .ok_or_else(|| SyscallError::new(syscall::EBADF))?; 370 | 371 | if let SchemeFile::Socket(ref scheme_file) = *file { 372 | Ok((scheme_file.read_timeout, scheme_file.write_timeout)) 373 | } else { 374 | Err(SyscallError::new(syscall::EBADF)) 375 | } 376 | }?; 377 | 378 | let mut timeout = match packet.a { 379 | syscall::SYS_WRITE => write_timeout, 380 | syscall::SYS_READ => read_timeout, 381 | _ => None, 382 | }; 383 | 384 | if let Some(ref mut timeout) = timeout { 385 | let cur_time = libredox::call::clock_gettime(CLOCK_MONOTONIC)?; 386 | *timeout = add_time( 387 | timeout, 388 | &TimeSpec { 389 | tv_sec: cur_time.tv_sec, 390 | tv_nsec: cur_time.tv_nsec as i32, 391 | }, 392 | ) 393 | } 394 | 395 | Ok(timeout) 396 | } 397 | 398 | fn get_setting( 399 | &mut self, 400 | fd: usize, 401 | setting: Setting, 402 | buf: &mut [u8], 403 | ) -> SyscallResult { 404 | let file = self 405 | .files 406 | .get_mut(&fd) 407 | .ok_or_else(|| SyscallError::new(syscall::EBADF))?; 408 | let file = match *file { 409 | SchemeFile::Socket(ref mut file) => file, 410 | _ => { 411 | return Err(SyscallError::new(syscall::EBADF)); 412 | } 413 | }; 414 | 415 | match setting { 416 | Setting::Other(setting) => SocketT::get_setting(file, setting, buf), 417 | Setting::Ttl => { 418 | if let Some(hop_limit) = buf.get_mut(0) { 419 | let socket_set = self.socket_set.borrow(); 420 | let socket = socket_set.get::(file.socket_handle); 421 | *hop_limit = socket.hop_limit(); 422 | Ok(1) 423 | } else { 424 | Err(SyscallError::new(syscall::EIO)) 425 | } 426 | } 427 | Setting::ReadTimeout | Setting::WriteTimeout => { 428 | let timespec = match (setting, file.read_timeout, file.write_timeout) { 429 | (Setting::ReadTimeout, Some(read_timeout), _) => read_timeout, 430 | (Setting::WriteTimeout, _, Some(write_timeout)) => write_timeout, 431 | _ => { 432 | return Ok(0); 433 | } 434 | }; 435 | 436 | if buf.len() < mem::size_of::() { 437 | Ok(0) 438 | } else { 439 | let count = timespec.deref().read(buf).map_err(|err| { 440 | SyscallError::new(err.raw_os_error().unwrap_or(syscall::EIO)) 441 | })?; 442 | Ok(count) 443 | } 444 | } 445 | } 446 | } 447 | 448 | fn update_setting( 449 | &mut self, 450 | fd: usize, 451 | setting: Setting, 452 | buf: &[u8], 453 | ) -> SyscallResult { 454 | let file = self 455 | .files 456 | .get_mut(&fd) 457 | .ok_or_else(|| SyscallError::new(syscall::EBADF))?; 458 | let file = match *file { 459 | SchemeFile::Socket(ref mut file) => file, 460 | _ => { 461 | return Err(SyscallError::new(syscall::EBADF)); 462 | } 463 | }; 464 | match setting { 465 | Setting::ReadTimeout | Setting::WriteTimeout => { 466 | let (timeout, count) = { 467 | if buf.len() < mem::size_of::() { 468 | (None, 0) 469 | } else { 470 | let mut timespec = TimeSpec::default(); 471 | let count = timespec.deref_mut().write(buf).map_err(|err| { 472 | SyscallError::new(err.raw_os_error().unwrap_or(syscall::EIO)) 473 | })?; 474 | (Some(timespec), count) 475 | } 476 | }; 477 | match setting { 478 | Setting::ReadTimeout => { 479 | file.read_timeout = timeout; 480 | } 481 | Setting::WriteTimeout => { 482 | file.write_timeout = timeout; 483 | } 484 | _ => {} 485 | }; 486 | Ok(count) 487 | } 488 | Setting::Ttl => { 489 | if let Some(hop_limit) = buf.get(0) { 490 | let mut socket_set = self.socket_set.borrow_mut(); 491 | let socket = socket_set.get_mut::(file.socket_handle); 492 | socket.set_hop_limit(*hop_limit); 493 | Ok(1) 494 | } else { 495 | Err(SyscallError::new(syscall::EIO)) 496 | } 497 | } 498 | Setting::Other(setting) => SocketT::set_setting(file, setting, buf), 499 | } 500 | } 501 | } 502 | 503 | impl syscall::SchemeBlockMut for SocketScheme 504 | where 505 | SocketT: SchemeSocket + AnySocket<'static>, 506 | { 507 | fn open( 508 | &mut self, 509 | path: &str, 510 | flags: usize, 511 | uid: u32, 512 | _gid: u32, 513 | ) -> SyscallResult> { 514 | if path.is_empty() { 515 | let null = NullFile { 516 | flags, 517 | uid, 518 | gid: _gid, 519 | }; 520 | 521 | let id = self.next_fd; 522 | self.next_fd += 1; 523 | 524 | self.nulls.insert(id, null); 525 | 526 | Ok(Some(id)) 527 | } else { 528 | let (socket_handle, data) = SocketT::new_socket( 529 | &mut self.socket_set.borrow_mut(), 530 | path, 531 | uid, 532 | &mut self.scheme_data, 533 | &self.context, 534 | )?; 535 | 536 | let file = SchemeFile::Socket(SocketFile { 537 | flags, 538 | events: 0, 539 | socket_handle, 540 | read_notified: false, 541 | write_notified: false, 542 | write_timeout: None, 543 | read_timeout: None, 544 | data, 545 | }); 546 | 547 | let id = self.next_fd; 548 | self.next_fd += 1; 549 | 550 | self.ref_counts.insert(socket_handle, 1); 551 | self.files.insert(id, file); 552 | 553 | Ok(Some(id)) 554 | } 555 | } 556 | 557 | fn close(&mut self, fd: usize) -> SyscallResult> { 558 | if let Some(_null) = self.nulls.remove(&fd) { 559 | return Ok(Some(0)); 560 | } 561 | 562 | let socket_handle = { 563 | let file = self 564 | .files 565 | .get(&fd) 566 | .ok_or_else(|| SyscallError::new(syscall::EBADF))?; 567 | file.socket_handle() 568 | }; 569 | let scheme_file = self.files.remove(&fd); 570 | let mut socket_set = self.socket_set.borrow_mut(); 571 | if let Some(scheme_file) = scheme_file { 572 | let socket = socket_set.get::(socket_handle); 573 | socket.close_file(&scheme_file, &mut self.scheme_data)?; 574 | } 575 | 576 | self.wait_queue.retain( 577 | |&WaitHandle { 578 | packet: SyscallPacket { a, .. }, 579 | .. 580 | }| a != fd, 581 | ); 582 | 583 | let remove = match self.ref_counts.entry(socket_handle) { 584 | Entry::Vacant(_) => { 585 | warn!("Closing a socket_handle with no ref"); 586 | true 587 | } 588 | Entry::Occupied(mut e) => { 589 | if *e.get() == 0 { 590 | warn!("Closing a socket_handle with no ref"); 591 | e.remove(); 592 | true 593 | } else { 594 | *e.get_mut() -= 1; 595 | if *e.get() == 0 { 596 | e.remove(); 597 | true 598 | } else { 599 | false 600 | } 601 | } 602 | } 603 | }; 604 | 605 | if remove { 606 | socket_set.remove(socket_handle); 607 | } 608 | Ok(Some(0)) 609 | } 610 | 611 | fn write(&mut self, fd: usize, buf: &[u8]) -> SyscallResult> { 612 | let (fd, setting) = { 613 | let file = self 614 | .files 615 | .get_mut(&fd) 616 | .ok_or_else(|| SyscallError::new(syscall::EBADF))?; 617 | 618 | match *file { 619 | SchemeFile::Setting(ref setting_handle) => { 620 | (setting_handle.fd, setting_handle.setting) 621 | } 622 | SchemeFile::Socket(ref mut file) => { 623 | let mut socket_set = self.socket_set.borrow_mut(); 624 | let socket = socket_set.get_mut::(file.socket_handle); 625 | let ret = SocketT::write_buf(socket, file, buf); 626 | match ret { 627 | Ok(None) => {} 628 | _ => file.write_notified = false, 629 | } 630 | return ret; 631 | } 632 | } 633 | }; 634 | self.update_setting(fd, setting, buf).map(Some) 635 | } 636 | 637 | fn read(&mut self, fd: usize, buf: &mut [u8]) -> SyscallResult> { 638 | let (fd, setting) = { 639 | let file = self 640 | .files 641 | .get_mut(&fd) 642 | .ok_or_else(|| SyscallError::new(syscall::EBADF))?; 643 | match *file { 644 | SchemeFile::Setting(ref setting_handle) => { 645 | (setting_handle.fd, setting_handle.setting) 646 | } 647 | SchemeFile::Socket(ref mut file) => { 648 | let mut socket_set = self.socket_set.borrow_mut(); 649 | let socket = socket_set.get_mut::(file.socket_handle); 650 | 651 | let ret = SocketT::read_buf(socket, file, buf); 652 | match ret { 653 | Ok(None) => {} 654 | _ => file.read_notified = false, 655 | } 656 | 657 | return ret; 658 | } 659 | } 660 | }; 661 | self.get_setting(fd, setting, buf).map(Some) 662 | } 663 | 664 | fn dup(&mut self, fd: usize, buf: &[u8]) -> SyscallResult> { 665 | let path = str::from_utf8(buf).or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?; 666 | 667 | if let Some((flags, uid, gid)) = self 668 | .nulls 669 | .get(&fd) 670 | .map(|null| (null.flags, null.uid, null.gid)) 671 | { 672 | return self.open(path, flags, uid, gid); 673 | } 674 | 675 | let new_file = { 676 | let file = self 677 | .files 678 | .get_mut(&fd) 679 | .ok_or_else(|| SyscallError::new(syscall::EBADF))?; 680 | 681 | let socket_handle = file.socket_handle(); 682 | 683 | let (new_handle, update_with) = match path { 684 | "hop_limit" => ( 685 | SchemeFile::Setting(SettingFile { 686 | socket_handle, 687 | fd, 688 | setting: Setting::Ttl, 689 | }), 690 | None, 691 | ), 692 | "read_timeout" => ( 693 | SchemeFile::Setting(SettingFile { 694 | socket_handle, 695 | fd, 696 | setting: Setting::ReadTimeout, 697 | }), 698 | None, 699 | ), 700 | "write_timeout" => ( 701 | SchemeFile::Setting(SettingFile { 702 | socket_handle, 703 | fd, 704 | setting: Setting::WriteTimeout, 705 | }), 706 | None, 707 | ), 708 | _ => match SocketT::dup( 709 | &mut self.socket_set.borrow_mut(), 710 | file, 711 | path, 712 | &mut self.scheme_data, 713 | )? { 714 | Some(some) => some, 715 | None => return Ok(None), 716 | }, 717 | }; 718 | 719 | if let Some((socket_handle, data)) = update_with { 720 | if let SchemeFile::Socket(ref mut file) = *file { 721 | // We replace the socket_handle pointed by file so update the ref_counts 722 | // accordingly 723 | self.ref_counts 724 | .entry(file.socket_handle) 725 | .and_modify(|e| *e = e.saturating_sub(1)) 726 | .or_insert(0); 727 | 728 | *self.ref_counts.entry(socket_handle).or_insert(0) += 1; 729 | 730 | file.socket_handle = socket_handle; 731 | file.data = data; 732 | } 733 | } 734 | *self 735 | .ref_counts 736 | .entry(new_handle.socket_handle()) 737 | .or_insert(0) += 1; 738 | new_handle 739 | }; 740 | 741 | let id = self.next_fd; 742 | self.files.insert(id, new_file); 743 | self.next_fd += 1; 744 | 745 | Ok(Some(id)) 746 | } 747 | 748 | fn fevent( 749 | &mut self, 750 | fd: usize, 751 | events: SyscallEventFlags, 752 | ) -> SyscallResult> { 753 | let file = self 754 | .files 755 | .get_mut(&fd) 756 | .ok_or_else(|| SyscallError::new(syscall::EBADF))?; 757 | match *file { 758 | SchemeFile::Setting(_) => return Err(SyscallError::new(syscall::EBADF)), 759 | SchemeFile::Socket(ref mut file) => { 760 | file.events = events.bits(); 761 | file.read_notified = false; // resend missed events 762 | file.write_notified = false; 763 | } 764 | } 765 | let mut socket_set = self.socket_set.borrow_mut(); 766 | let revents = SyscallEventFlags::from_bits_truncate(file.events(&mut socket_set)); 767 | Ok(Some(revents)) 768 | } 769 | 770 | fn fsync(&mut self, fd: usize) -> SyscallResult> { 771 | { 772 | let _file = self 773 | .files 774 | .get_mut(&fd) 775 | .ok_or_else(|| SyscallError::new(syscall::EBADF))?; 776 | } 777 | Ok(Some(0)) 778 | // TODO Implement fsyncing 779 | // self.0.network_fsync() 780 | } 781 | 782 | fn fpath(&mut self, fd: usize, buf: &mut [u8]) -> SyscallResult> { 783 | let file = self 784 | .files 785 | .get_mut(&fd) 786 | .ok_or_else(|| SyscallError::new(syscall::EBADF))?; 787 | 788 | let socket_set = self.socket_set.borrow(); 789 | let socket = socket_set.get::(file.socket_handle()); 790 | 791 | socket.fpath(file, buf).map(Some) 792 | } 793 | 794 | fn fcntl(&mut self, fd: usize, cmd: usize, arg: usize) -> SyscallResult> { 795 | if let Some(ref mut null) = self.nulls.get_mut(&fd) { 796 | match cmd { 797 | syscall::F_GETFL => Ok(Some(null.flags)), 798 | syscall::F_SETFL => { 799 | null.flags = arg & !syscall::O_ACCMODE; 800 | Ok(Some(0)) 801 | } 802 | _ => Err(SyscallError::new(syscall::EINVAL)), 803 | } 804 | } else { 805 | let file = self 806 | .files 807 | .get_mut(&fd) 808 | .ok_or_else(|| SyscallError::new(syscall::EBADF))?; 809 | 810 | if let SchemeFile::Socket(ref mut socket_file) = *file { 811 | match cmd { 812 | syscall::F_GETFL => Ok(Some(socket_file.flags)), 813 | syscall::F_SETFL => { 814 | socket_file.flags = arg & !syscall::O_ACCMODE; 815 | Ok(Some(0)) 816 | } 817 | _ => Err(SyscallError::new(syscall::EINVAL)), 818 | } 819 | } else { 820 | Err(SyscallError::new(syscall::EBADF)) 821 | } 822 | } 823 | } 824 | } 825 | 826 | fn add_time(a: &TimeSpec, b: &TimeSpec) -> TimeSpec { 827 | let mut secs = a.tv_sec + b.tv_sec; 828 | let mut nsecs = a.tv_nsec + b.tv_nsec; 829 | 830 | secs += i64::from(nsecs) / 1_000_000_000; 831 | nsecs %= 1_000_000_000; 832 | 833 | TimeSpec { 834 | tv_sec: secs, 835 | tv_nsec: nsecs, 836 | } 837 | } 838 | -------------------------------------------------------------------------------- /src/smolnetd/scheme/tcp.rs: -------------------------------------------------------------------------------- 1 | use smoltcp::iface::SocketHandle; 2 | use smoltcp::socket::tcp::{Socket as TcpSocket, SocketBuffer as TcpSocketBuffer}; 3 | use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; 4 | use std::fmt::Write; 5 | use std::str; 6 | use syscall; 7 | use syscall::{Error as SyscallError, Result as SyscallResult}; 8 | 9 | use super::socket::{Context, DupResult, SchemeFile, SchemeSocket, SocketFile, SocketScheme}; 10 | use super::{parse_endpoint, SocketSet}; 11 | use crate::port_set::PortSet; 12 | 13 | pub type TcpScheme = SocketScheme>; 14 | 15 | impl<'a> SchemeSocket for TcpSocket<'a> { 16 | type SchemeDataT = PortSet; 17 | type DataT = Option; 18 | type SettingT = (); 19 | 20 | fn new_scheme_data() -> Self::SchemeDataT { 21 | PortSet::new(49_152u16, 65_535u16).expect("Wrong TCP port numbers") 22 | } 23 | 24 | fn can_send(&self) -> bool { 25 | self.can_send() 26 | } 27 | 28 | fn can_recv(&self) -> bool { 29 | self.can_recv() 30 | } 31 | 32 | fn may_recv(&self) -> bool { 33 | self.may_recv() 34 | } 35 | 36 | fn hop_limit(&self) -> u8 { 37 | self.hop_limit().unwrap_or(64) 38 | } 39 | 40 | fn set_hop_limit(&mut self, hop_limit: u8) { 41 | self.set_hop_limit(Some(hop_limit)); 42 | } 43 | 44 | fn get_setting( 45 | _file: &SocketFile, 46 | _setting: Self::SettingT, 47 | _buf: &mut [u8], 48 | ) -> SyscallResult { 49 | Ok(0) 50 | } 51 | 52 | fn set_setting( 53 | _file: &mut SocketFile, 54 | _setting: Self::SettingT, 55 | _buf: &[u8], 56 | ) -> SyscallResult { 57 | Ok(0) 58 | } 59 | 60 | fn new_socket( 61 | socket_set: &mut SocketSet, 62 | path: &str, 63 | uid: u32, 64 | port_set: &mut Self::SchemeDataT, 65 | context: &Context, 66 | ) -> SyscallResult<(SocketHandle, Self::DataT)> { 67 | trace!("TCP open {}", path); 68 | let mut parts = path.split('/'); 69 | let remote_endpoint = parse_endpoint(parts.next().unwrap_or("")); 70 | let mut local_endpoint = parse_endpoint(parts.next().unwrap_or("")); 71 | 72 | if local_endpoint.port > 0 && local_endpoint.port <= 1024 && uid != 0 { 73 | return Err(SyscallError::new(syscall::EACCES)); 74 | } 75 | 76 | let rx_packets = vec![0; 0xffff]; 77 | let tx_packets = vec![0; 0xffff]; 78 | let rx_buffer = TcpSocketBuffer::new(rx_packets); 79 | let tx_buffer = TcpSocketBuffer::new(tx_packets); 80 | let socket = TcpSocket::new(rx_buffer, tx_buffer); 81 | 82 | if local_endpoint.port == 0 { 83 | local_endpoint.port = port_set 84 | .get_port() 85 | .ok_or_else(|| SyscallError::new(syscall::EINVAL))?; 86 | } else if !port_set.claim_port(local_endpoint.port) { 87 | return Err(SyscallError::new(syscall::EADDRINUSE)); 88 | } 89 | 90 | let socket_handle = socket_set.add(socket); 91 | 92 | let tcp_socket = socket_set.get_mut::(socket_handle); 93 | 94 | let listen_enpoint = if remote_endpoint.is_specified() { 95 | let local_endpoint_addr = match local_endpoint.addr { 96 | Some(addr) if !addr.is_unspecified() => Some(addr), 97 | _ => { 98 | let route_table = context.route_table.borrow(); 99 | let addr = route_table 100 | .lookup_src_addr(&remote_endpoint.addr.expect("Checked in is_specified")); 101 | if matches!(addr, None) { 102 | error!("Opening a TCP connection with a probably invalid source IP as no route have been found for destination: {}", remote_endpoint); 103 | } 104 | addr 105 | } 106 | }; 107 | let local_endpoint = IpListenEndpoint { 108 | addr: local_endpoint_addr, 109 | port: local_endpoint.port, 110 | }; 111 | 112 | trace!("Connecting tcp {} {}", local_endpoint, remote_endpoint); 113 | tcp_socket 114 | .connect( 115 | context.iface.borrow_mut().context(), 116 | IpEndpoint::new(remote_endpoint.addr.unwrap(), remote_endpoint.port), 117 | local_endpoint, 118 | ) 119 | .expect("Can't connect tcp socket "); 120 | None 121 | } else { 122 | trace!("Listening tcp {}", local_endpoint); 123 | tcp_socket 124 | .listen(local_endpoint) 125 | .expect("Can't listen on local endpoint"); 126 | Some(local_endpoint) 127 | }; 128 | 129 | Ok((socket_handle, listen_enpoint)) 130 | } 131 | 132 | fn close_file( 133 | &self, 134 | file: &SchemeFile, 135 | port_set: &mut Self::SchemeDataT, 136 | ) -> SyscallResult<()> { 137 | if let SchemeFile::Socket(SocketFile { data, .. }) = *file { 138 | if let Some(endpoint) = self.local_endpoint() { 139 | // Socket was connected on some port 140 | port_set.release_port(endpoint.port); 141 | } else if let Some(endpoint) = data { 142 | // Socket was listening on some port 143 | port_set.release_port(endpoint.port); 144 | } 145 | } 146 | Ok(()) 147 | } 148 | 149 | fn write_buf( 150 | &mut self, 151 | file: &mut SocketFile, 152 | buf: &[u8], 153 | ) -> SyscallResult> { 154 | if !self.is_active() { 155 | Err(SyscallError::new(syscall::ENOTCONN)) 156 | } else if self.can_send() { 157 | self.send_slice(buf).expect("Can't send slice"); 158 | Ok(Some(buf.len())) 159 | } else if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK { 160 | Err(SyscallError::new(syscall::EAGAIN)) 161 | } else { 162 | Ok(None) // internally scheduled to re-write 163 | } 164 | } 165 | 166 | fn read_buf( 167 | &mut self, 168 | file: &mut SocketFile, 169 | buf: &mut [u8], 170 | ) -> SyscallResult> { 171 | if !self.is_active() { 172 | Err(SyscallError::new(syscall::ENOTCONN)) 173 | } else if self.can_recv() { 174 | let length = self.recv_slice(buf).expect("Can't receive slice"); 175 | Ok(Some(length)) 176 | } else if !self.may_recv() { 177 | Ok(Some(0)) 178 | } else if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK { 179 | Err(SyscallError::new(syscall::EAGAIN)) 180 | } else { 181 | Ok(None) // internally scheduled to re-read 182 | } 183 | } 184 | 185 | fn dup( 186 | socket_set: &mut SocketSet, 187 | file: &mut SchemeFile, 188 | path: &str, 189 | port_set: &mut Self::SchemeDataT, 190 | ) -> SyscallResult> { 191 | let socket_handle = file.socket_handle(); 192 | 193 | let (is_active, local_endpoint) = { 194 | let socket = socket_set.get::(socket_handle); 195 | (socket.is_active(), socket.local_endpoint()) 196 | }; 197 | 198 | let file = match path { 199 | "listen" => { 200 | if let SchemeFile::Socket(ref tcp_handle) = *file { 201 | let Some(listen_enpoint) = tcp_handle.data else { 202 | // This socket is not listening so we can't accept a connection 203 | return Err(SyscallError::new(syscall::EINVAL)); 204 | }; 205 | 206 | if !is_active { 207 | // Socket listening but no connection received 208 | if tcp_handle.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK { 209 | return Err(SyscallError::new(syscall::EAGAIN)); 210 | } else { 211 | return Ok(None); 212 | } 213 | } 214 | trace!("TCP creating new listening socket"); 215 | // We pass None as data because this new handle is to the active connection so 216 | // not a listening socket 217 | let new_handle = SchemeFile::Socket(tcp_handle.clone_with_data(None)); 218 | 219 | // Creating a socket to continue listening 220 | let rx_packets = vec![0; 0xffff]; 221 | let tx_packets = vec![0; 0xffff]; 222 | let rx_buffer = TcpSocketBuffer::new(rx_packets); 223 | let tx_buffer = TcpSocketBuffer::new(tx_packets); 224 | let socket = TcpSocket::new(rx_buffer, tx_buffer); 225 | let new_socket_handle = socket_set.add(socket); 226 | { 227 | let tcp_socket = socket_set.get_mut::(new_socket_handle); 228 | tcp_socket 229 | .listen(listen_enpoint) 230 | .expect("Can't listen on local endpoint"); 231 | } 232 | // We got a new connection to the socket so acquire the port 233 | port_set.acquire_port( 234 | local_endpoint 235 | .expect("Socket was active so local endpoint must be set") 236 | .port, 237 | ); 238 | return Ok(Some(( 239 | new_handle, 240 | Some((new_socket_handle, Some(listen_enpoint))), 241 | ))); 242 | } else { 243 | return Err(SyscallError::new(syscall::EBADF)); 244 | } 245 | } 246 | _ => { 247 | trace!("TCP dup unknown {}", path); 248 | if let SchemeFile::Socket(ref tcp_handle) = *file { 249 | SchemeFile::Socket(tcp_handle.clone_with_data(tcp_handle.data)) 250 | } else { 251 | SchemeFile::Socket(SocketFile::new_with_data(socket_handle, None)) 252 | } 253 | } 254 | }; 255 | 256 | if let SchemeFile::Socket(_) = file { 257 | if let Some(local_endpoint) = local_endpoint { 258 | port_set.acquire_port(local_endpoint.port); 259 | } 260 | } 261 | 262 | Ok(Some((file, None))) 263 | } 264 | 265 | fn fpath(&self, file: &SchemeFile, buf: &mut [u8]) -> SyscallResult { 266 | let unspecified = "0.0.0.0:0"; 267 | let mut path = String::from("tcp:"); 268 | match self.remote_endpoint() { 269 | Some(endpoint) => write!(&mut path, "{}", endpoint).unwrap(), 270 | None => path.push_str(unspecified), 271 | } 272 | path.push('/'); 273 | match (self.local_endpoint(), file) { 274 | (Some(endpoint), _) => write!(&mut path, "{}", endpoint).unwrap(), 275 | ( 276 | None, 277 | SchemeFile::Socket(SocketFile { 278 | data: Some(endpoint), 279 | .. 280 | }), 281 | ) => { 282 | if endpoint.is_specified() { 283 | write!(&mut path, "{}", endpoint).unwrap() 284 | } else { 285 | write!(&mut path, "0.0.0.0:{}", endpoint.port).unwrap() 286 | } 287 | } 288 | _ => path.push_str(unspecified), 289 | } 290 | trace!("fpath: {}", path); 291 | let path = path.as_bytes(); 292 | 293 | let mut i = 0; 294 | while i < buf.len() && i < path.len() { 295 | buf[i] = path[i]; 296 | i += 1; 297 | } 298 | 299 | Ok(i) 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /src/smolnetd/scheme/udp.rs: -------------------------------------------------------------------------------- 1 | use smoltcp::iface::SocketHandle; 2 | use smoltcp::socket::udp::{ 3 | PacketBuffer as UdpSocketBuffer, PacketMetadata as UdpPacketMetadata, Socket as UdpSocket, 4 | }; 5 | use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; 6 | use std::str; 7 | use syscall; 8 | use syscall::{Error as SyscallError, Result as SyscallResult}; 9 | 10 | use super::socket::{Context, DupResult, SchemeFile, SchemeSocket, SocketFile, SocketScheme}; 11 | use super::{parse_endpoint, Smolnetd, SocketSet}; 12 | use crate::port_set::PortSet; 13 | use crate::router::Router; 14 | 15 | pub type UdpScheme = SocketScheme>; 16 | 17 | impl<'a> SchemeSocket for UdpSocket<'a> { 18 | type SchemeDataT = PortSet; 19 | type DataT = IpListenEndpoint; 20 | type SettingT = (); 21 | 22 | fn new_scheme_data() -> Self::SchemeDataT { 23 | PortSet::new(49_152u16, 65_535u16).expect("Wrong UDP port numbers") 24 | } 25 | 26 | fn can_send(&self) -> bool { 27 | self.can_send() 28 | } 29 | 30 | fn can_recv(&self) -> bool { 31 | self.can_recv() 32 | } 33 | 34 | fn may_recv(&self) -> bool { 35 | true 36 | } 37 | 38 | fn hop_limit(&self) -> u8 { 39 | self.hop_limit().unwrap_or(64) 40 | } 41 | 42 | fn set_hop_limit(&mut self, hop_limit: u8) { 43 | self.set_hop_limit(Some(hop_limit)); 44 | } 45 | 46 | fn get_setting( 47 | _file: &SocketFile, 48 | _setting: Self::SettingT, 49 | _buf: &mut [u8], 50 | ) -> SyscallResult { 51 | Ok(0) 52 | } 53 | 54 | fn set_setting( 55 | _file: &mut SocketFile, 56 | _setting: Self::SettingT, 57 | _buf: &[u8], 58 | ) -> SyscallResult { 59 | Ok(0) 60 | } 61 | 62 | fn new_socket( 63 | socket_set: &mut SocketSet, 64 | path: &str, 65 | uid: u32, 66 | port_set: &mut Self::SchemeDataT, 67 | _context: &Context, 68 | ) -> SyscallResult<(SocketHandle, Self::DataT)> { 69 | let mut parts = path.split('/'); 70 | let remote_endpoint = parse_endpoint(parts.next().unwrap_or("")); 71 | let mut local_endpoint = parse_endpoint(parts.next().unwrap_or("")); 72 | 73 | if local_endpoint.port > 0 && local_endpoint.port <= 1024 && uid != 0 { 74 | return Err(SyscallError::new(syscall::EACCES)); 75 | } 76 | 77 | let rx_buffer = UdpSocketBuffer::new( 78 | vec![UdpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], 79 | vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE], 80 | ); 81 | let tx_buffer = UdpSocketBuffer::new( 82 | vec![UdpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], 83 | vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE], 84 | ); 85 | let udp_socket = UdpSocket::new(rx_buffer, tx_buffer); 86 | 87 | if local_endpoint.port == 0 { 88 | local_endpoint.port = port_set 89 | .get_port() 90 | .ok_or_else(|| SyscallError::new(syscall::EINVAL))?; 91 | } else if !port_set.claim_port(local_endpoint.port) { 92 | return Err(SyscallError::new(syscall::EADDRINUSE)); 93 | } 94 | 95 | let socket_handle = socket_set.add(udp_socket); 96 | 97 | let udp_socket = socket_set.get_mut::(socket_handle); 98 | udp_socket 99 | .bind(local_endpoint) 100 | .expect("Can't bind udp socket to local endpoint"); 101 | 102 | Ok((socket_handle, remote_endpoint)) 103 | } 104 | 105 | fn close_file( 106 | &self, 107 | file: &SchemeFile, 108 | port_set: &mut Self::SchemeDataT, 109 | ) -> SyscallResult<()> { 110 | if let SchemeFile::Socket(_) = *file { 111 | port_set.release_port(self.endpoint().port); 112 | } 113 | Ok(()) 114 | } 115 | 116 | fn write_buf( 117 | &mut self, 118 | file: &mut SocketFile, 119 | buf: &[u8], 120 | ) -> SyscallResult> { 121 | if !file.data.is_specified() { 122 | return Err(SyscallError::new(syscall::EADDRNOTAVAIL)); 123 | } 124 | if self.can_send() { 125 | let endpoint = file.data; 126 | let endpoint = IpEndpoint::new( 127 | endpoint 128 | .addr 129 | .expect("If we can send, this should be specified"), 130 | endpoint.port, 131 | ); 132 | self.send_slice(buf, endpoint).expect("Can't send slice"); 133 | Ok(Some(buf.len())) 134 | } else if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK { 135 | Err(SyscallError::new(syscall::EAGAIN)) 136 | } else { 137 | Ok(None) // internally scheduled to re-read 138 | } 139 | } 140 | 141 | fn read_buf( 142 | &mut self, 143 | file: &mut SocketFile, 144 | buf: &mut [u8], 145 | ) -> SyscallResult> { 146 | if self.can_recv() { 147 | let (length, _) = self.recv_slice(buf).expect("Can't receive slice"); 148 | Ok(Some(length)) 149 | } else if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK { 150 | Err(SyscallError::new(syscall::EAGAIN)) 151 | } else { 152 | Ok(None) // internally scheduled to re-read 153 | } 154 | } 155 | 156 | fn dup( 157 | socket_set: &mut SocketSet, 158 | file: &mut SchemeFile, 159 | path: &str, 160 | port_set: &mut Self::SchemeDataT, 161 | ) -> SyscallResult> { 162 | let socket_handle = file.socket_handle(); 163 | let file = match path { 164 | _ => { 165 | let remote_endpoint = parse_endpoint(path); 166 | if let SchemeFile::Socket(ref udp_handle) = *file { 167 | SchemeFile::Socket(udp_handle.clone_with_data( 168 | if remote_endpoint.is_specified() { 169 | remote_endpoint 170 | } else { 171 | udp_handle.data 172 | }, 173 | )) 174 | } else { 175 | SchemeFile::Socket(SocketFile::new_with_data(socket_handle, remote_endpoint)) 176 | } 177 | } 178 | }; 179 | 180 | let endpoint = { 181 | let socket = socket_set.get::(socket_handle); 182 | socket.endpoint() 183 | }; 184 | 185 | if let SchemeFile::Socket(_) = file { 186 | port_set.acquire_port(endpoint.port); 187 | } 188 | 189 | Ok(Some((file, None))) 190 | } 191 | 192 | fn fpath(&self, file: &SchemeFile, buf: &mut [u8]) -> SyscallResult { 193 | if let SchemeFile::Socket(ref socket_file) = *file { 194 | let path = format!("udp:{}/{}", socket_file.data, self.endpoint()); 195 | let path = path.as_bytes(); 196 | 197 | let mut i = 0; 198 | while i < buf.len() && i < path.len() { 199 | buf[i] = path[i]; 200 | i += 1; 201 | } 202 | 203 | Ok(i) 204 | } else { 205 | Err(SyscallError::new(syscall::EBADF)) 206 | } 207 | } 208 | } 209 | --------------------------------------------------------------------------------