├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── examples ├── capture_packet.rs ├── isolate.rs ├── nat.rs └── ping_pong.rs ├── netsim-macros ├── Cargo.toml └── src │ └── lib.rs └── src ├── adapter ├── delay.rs ├── loss.rs └── mod.rs ├── connect.rs ├── device ├── channel.rs ├── hub.rs ├── mod.rs └── nat │ ├── mod.rs │ ├── port_map.rs │ └── restrictions.rs ├── iface ├── configure.rs ├── create.rs ├── mod.rs └── stream.rs ├── ioctl.rs ├── lib.rs ├── machine.rs ├── namespace.rs ├── network.rs ├── packet.rs ├── priv_prelude.rs ├── stream_ext.rs ├── sys.rs └── tests ├── delay.rs ├── loss.rs ├── mod.rs └── nat.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | /netsim-macros/Cargo.lock 4 | /netsim-macros/target 5 | -------------------------------------------------------------------------------- /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 = "addr2line" 7 | version = "0.24.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "autocfg" 22 | version = "1.4.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 25 | 26 | [[package]] 27 | name = "backtrace" 28 | version = "0.3.74" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 31 | dependencies = [ 32 | "addr2line", 33 | "cfg-if", 34 | "libc", 35 | "miniz_oxide", 36 | "object", 37 | "rustc-demangle", 38 | "windows-targets", 39 | ] 40 | 41 | [[package]] 42 | name = "byteorder" 43 | version = "1.5.0" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 46 | 47 | [[package]] 48 | name = "bytes" 49 | version = "1.9.0" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" 52 | 53 | [[package]] 54 | name = "cfg-if" 55 | version = "1.0.0" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 58 | 59 | [[package]] 60 | name = "futures" 61 | version = "0.3.31" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 64 | dependencies = [ 65 | "futures-channel", 66 | "futures-core", 67 | "futures-executor", 68 | "futures-io", 69 | "futures-sink", 70 | "futures-task", 71 | "futures-util", 72 | ] 73 | 74 | [[package]] 75 | name = "futures-channel" 76 | version = "0.3.31" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 79 | dependencies = [ 80 | "futures-core", 81 | "futures-sink", 82 | ] 83 | 84 | [[package]] 85 | name = "futures-core" 86 | version = "0.3.31" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 89 | 90 | [[package]] 91 | name = "futures-executor" 92 | version = "0.3.31" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 95 | dependencies = [ 96 | "futures-core", 97 | "futures-task", 98 | "futures-util", 99 | ] 100 | 101 | [[package]] 102 | name = "futures-io" 103 | version = "0.3.31" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 106 | 107 | [[package]] 108 | name = "futures-macro" 109 | version = "0.3.31" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 112 | dependencies = [ 113 | "proc-macro2", 114 | "quote", 115 | "syn 2.0.90", 116 | ] 117 | 118 | [[package]] 119 | name = "futures-sink" 120 | version = "0.3.31" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 123 | 124 | [[package]] 125 | name = "futures-task" 126 | version = "0.3.31" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 129 | 130 | [[package]] 131 | name = "futures-util" 132 | version = "0.3.31" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 135 | dependencies = [ 136 | "futures-channel", 137 | "futures-core", 138 | "futures-io", 139 | "futures-macro", 140 | "futures-sink", 141 | "futures-task", 142 | "memchr", 143 | "pin-project-lite", 144 | "pin-utils", 145 | "slab", 146 | ] 147 | 148 | [[package]] 149 | name = "getrandom" 150 | version = "0.2.15" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 153 | dependencies = [ 154 | "cfg-if", 155 | "libc", 156 | "wasi", 157 | ] 158 | 159 | [[package]] 160 | name = "gimli" 161 | version = "0.31.1" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 164 | 165 | [[package]] 166 | name = "ioctl-sys" 167 | version = "0.8.0" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "8bd11f3a29434026f5ff98c730b668ba74b1033637b8817940b54d040696133c" 170 | 171 | [[package]] 172 | name = "libc" 173 | version = "0.2.168" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" 176 | 177 | [[package]] 178 | name = "log" 179 | version = "0.4.22" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 182 | 183 | [[package]] 184 | name = "memchr" 185 | version = "2.7.4" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 188 | 189 | [[package]] 190 | name = "miniz_oxide" 191 | version = "0.8.2" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" 194 | dependencies = [ 195 | "adler2", 196 | ] 197 | 198 | [[package]] 199 | name = "mio" 200 | version = "1.0.3" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 203 | dependencies = [ 204 | "libc", 205 | "wasi", 206 | "windows-sys", 207 | ] 208 | 209 | [[package]] 210 | name = "net-literals" 211 | version = "0.2.0" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "da93265ae87c37493239e475c4cef98ff7a6b9f3f845bbd8957c714250614c6d" 214 | dependencies = [ 215 | "proc-macro-error", 216 | "proc-macro2", 217 | "quote", 218 | "syn 1.0.109", 219 | ] 220 | 221 | [[package]] 222 | name = "netsim" 223 | version = "0.3.0" 224 | dependencies = [ 225 | "bytes", 226 | "futures", 227 | "ioctl-sys", 228 | "libc", 229 | "log", 230 | "net-literals", 231 | "netsim-macros", 232 | "oneshot", 233 | "pin-project", 234 | "rand", 235 | "tokio", 236 | ] 237 | 238 | [[package]] 239 | name = "netsim-macros" 240 | version = "0.3.0" 241 | dependencies = [ 242 | "proc-macro2", 243 | "quote", 244 | "syn 2.0.90", 245 | ] 246 | 247 | [[package]] 248 | name = "object" 249 | version = "0.36.5" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" 252 | dependencies = [ 253 | "memchr", 254 | ] 255 | 256 | [[package]] 257 | name = "oneshot" 258 | version = "0.1.8" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "e296cf87e61c9cfc1a61c3c63a0f7f286ed4554e0e22be84e8a38e1d264a2a29" 261 | 262 | [[package]] 263 | name = "pin-project" 264 | version = "1.1.7" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" 267 | dependencies = [ 268 | "pin-project-internal", 269 | ] 270 | 271 | [[package]] 272 | name = "pin-project-internal" 273 | version = "1.1.7" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" 276 | dependencies = [ 277 | "proc-macro2", 278 | "quote", 279 | "syn 2.0.90", 280 | ] 281 | 282 | [[package]] 283 | name = "pin-project-lite" 284 | version = "0.2.15" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" 287 | 288 | [[package]] 289 | name = "pin-utils" 290 | version = "0.1.0" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 293 | 294 | [[package]] 295 | name = "ppv-lite86" 296 | version = "0.2.20" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" 299 | dependencies = [ 300 | "zerocopy", 301 | ] 302 | 303 | [[package]] 304 | name = "proc-macro-error" 305 | version = "1.0.4" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 308 | dependencies = [ 309 | "proc-macro-error-attr", 310 | "proc-macro2", 311 | "quote", 312 | "syn 1.0.109", 313 | "version_check", 314 | ] 315 | 316 | [[package]] 317 | name = "proc-macro-error-attr" 318 | version = "1.0.4" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 321 | dependencies = [ 322 | "proc-macro2", 323 | "quote", 324 | "version_check", 325 | ] 326 | 327 | [[package]] 328 | name = "proc-macro2" 329 | version = "1.0.92" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" 332 | dependencies = [ 333 | "unicode-ident", 334 | ] 335 | 336 | [[package]] 337 | name = "quote" 338 | version = "1.0.37" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 341 | dependencies = [ 342 | "proc-macro2", 343 | ] 344 | 345 | [[package]] 346 | name = "rand" 347 | version = "0.8.5" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 350 | dependencies = [ 351 | "libc", 352 | "rand_chacha", 353 | "rand_core", 354 | ] 355 | 356 | [[package]] 357 | name = "rand_chacha" 358 | version = "0.3.1" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 361 | dependencies = [ 362 | "ppv-lite86", 363 | "rand_core", 364 | ] 365 | 366 | [[package]] 367 | name = "rand_core" 368 | version = "0.6.4" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 371 | dependencies = [ 372 | "getrandom", 373 | ] 374 | 375 | [[package]] 376 | name = "rustc-demangle" 377 | version = "0.1.24" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 380 | 381 | [[package]] 382 | name = "slab" 383 | version = "0.4.9" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 386 | dependencies = [ 387 | "autocfg", 388 | ] 389 | 390 | [[package]] 391 | name = "socket2" 392 | version = "0.5.8" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" 395 | dependencies = [ 396 | "libc", 397 | "windows-sys", 398 | ] 399 | 400 | [[package]] 401 | name = "syn" 402 | version = "1.0.109" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 405 | dependencies = [ 406 | "proc-macro2", 407 | "quote", 408 | "unicode-ident", 409 | ] 410 | 411 | [[package]] 412 | name = "syn" 413 | version = "2.0.90" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" 416 | dependencies = [ 417 | "proc-macro2", 418 | "quote", 419 | "unicode-ident", 420 | ] 421 | 422 | [[package]] 423 | name = "tokio" 424 | version = "1.42.0" 425 | source = "registry+https://github.com/rust-lang/crates.io-index" 426 | checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" 427 | dependencies = [ 428 | "backtrace", 429 | "bytes", 430 | "libc", 431 | "mio", 432 | "pin-project-lite", 433 | "socket2", 434 | "tokio-macros", 435 | "windows-sys", 436 | ] 437 | 438 | [[package]] 439 | name = "tokio-macros" 440 | version = "2.4.0" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" 443 | dependencies = [ 444 | "proc-macro2", 445 | "quote", 446 | "syn 2.0.90", 447 | ] 448 | 449 | [[package]] 450 | name = "unicode-ident" 451 | version = "1.0.14" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" 454 | 455 | [[package]] 456 | name = "version_check" 457 | version = "0.9.5" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 460 | 461 | [[package]] 462 | name = "wasi" 463 | version = "0.11.0+wasi-snapshot-preview1" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 466 | 467 | [[package]] 468 | name = "windows-sys" 469 | version = "0.52.0" 470 | source = "registry+https://github.com/rust-lang/crates.io-index" 471 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 472 | dependencies = [ 473 | "windows-targets", 474 | ] 475 | 476 | [[package]] 477 | name = "windows-targets" 478 | version = "0.52.6" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 481 | dependencies = [ 482 | "windows_aarch64_gnullvm", 483 | "windows_aarch64_msvc", 484 | "windows_i686_gnu", 485 | "windows_i686_gnullvm", 486 | "windows_i686_msvc", 487 | "windows_x86_64_gnu", 488 | "windows_x86_64_gnullvm", 489 | "windows_x86_64_msvc", 490 | ] 491 | 492 | [[package]] 493 | name = "windows_aarch64_gnullvm" 494 | version = "0.52.6" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 497 | 498 | [[package]] 499 | name = "windows_aarch64_msvc" 500 | version = "0.52.6" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 503 | 504 | [[package]] 505 | name = "windows_i686_gnu" 506 | version = "0.52.6" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 509 | 510 | [[package]] 511 | name = "windows_i686_gnullvm" 512 | version = "0.52.6" 513 | source = "registry+https://github.com/rust-lang/crates.io-index" 514 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 515 | 516 | [[package]] 517 | name = "windows_i686_msvc" 518 | version = "0.52.6" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 521 | 522 | [[package]] 523 | name = "windows_x86_64_gnu" 524 | version = "0.52.6" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 527 | 528 | [[package]] 529 | name = "windows_x86_64_gnullvm" 530 | version = "0.52.6" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 533 | 534 | [[package]] 535 | name = "windows_x86_64_msvc" 536 | version = "0.52.6" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 539 | 540 | [[package]] 541 | name = "zerocopy" 542 | version = "0.7.35" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 545 | dependencies = [ 546 | "byteorder", 547 | "zerocopy-derive", 548 | ] 549 | 550 | [[package]] 551 | name = "zerocopy-derive" 552 | version = "0.7.35" 553 | source = "registry+https://github.com/rust-lang/crates.io-index" 554 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 555 | dependencies = [ 556 | "proc-macro2", 557 | "quote", 558 | "syn 2.0.90", 559 | ] 560 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "netsim" 3 | version = "0.3.0" 4 | authors = ["Andrew Cann "] 5 | description = "Run tests in network-isolated threads. Intercept and meddle with their packets." 6 | repository = "https://github.com/canndrew/netsim-ng" 7 | documentation = "https://docs.rs/netsim" 8 | license = "MIT OR BSD-3-Clause" 9 | keywords = ["network", "ip", "testing"] 10 | categories = ["asynchronous", "development-tools::testing", "network-programming", "simulation", "virtualization"] 11 | edition = "2021" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | libc = "0.2.168" 17 | tokio = { version = "1.42.0", features = ["sync", "rt-multi-thread", "macros", "net", "time", "io-util"] } 18 | futures = "0.3.31" 19 | net-literals = "0.2.0" 20 | ioctl-sys = "0.8.0" 21 | oneshot = { version = "0.1.8", features = ["std"] } 22 | rand = "0.8.5" 23 | pin-project = "1.1.7" 24 | bytes = "1.9.0" 25 | log = "0.4.22" 26 | netsim-macros = { path = "netsim-macros", version = "0.3.0" } 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # netsim 2 | 3 | `netsim` is a Rust library which allows you to: 4 | 5 | * Run tests in network-isolated threads. 6 | * Test networking code on simulated networks. 7 | * Capture and inspect packets produced by your code. 8 | * Inject and meddle with network packets. 9 | 10 | [Documentation](https://docs.rs/netsim/) 11 | 12 | ## Examples 13 | 14 | ### Example 1: Isolating tests. 15 | 16 | Suppose you have multiple tests that need to bind to the same port for some reason. By using 17 | `#[netsim::isolate]` you can run your test suite without having to use `--test-threads=1` and 18 | without having to stop any daemons running on your dev machine. 19 | 20 | ```no_run 21 | #[test] 22 | #[netsim::isolate] 23 | fn a_test() { 24 | let _listener = std::net::TcpListener::bind("0.0.0.0:80").unwrap(); 25 | } 26 | 27 | #[test] 28 | #[netsim::isolate] 29 | fn another_test_that_runs_in_parallel() { 30 | let _listener = std::net::TcpListener::bind("0.0.0.0:80").unwrap(); 31 | } 32 | ``` 33 | 34 | ### Example 2: Capturing a packet. 35 | 36 | The `#[netsim::isolate]` attribute showcased above is just a convenient way to setup a `Machine` 37 | and spawn a task onto it. To capture a packet you'll need to do these steps yourself. You'll also 38 | need to give the machine a network interface to send the packet on. 39 | 40 | In this example we create a UDP socket and use it to send the message "hello" towards an arbitary 41 | address. The packet then arrives on our `IpIface`, hoping to be routed somewhere, and we can check 42 | that it still contains the correct message. 43 | 44 | ```rust 45 | let local_addr: SocketAddrV4 = "10.1.2.3:5555".parse().unwrap(); 46 | let remote_addr: SocketAddrV4 = "1.1.1.1:53".parse().unwrap(); 47 | let machine = Machine::new().unwrap(); 48 | let mut iface = { 49 | machine 50 | .add_ip_iface() 51 | .ipv4_addr(*local_addr.ip()) 52 | .ipv4_default_route() 53 | .build() 54 | .unwrap() 55 | }; 56 | machine.spawn(async move { 57 | let socket = UdpSocket::bind(local_addr).await.unwrap(); 58 | socket.send_to(b"hello", remote_addr).await.unwrap(); 59 | }).await.unwrap(); 60 | 61 | let packet = loop { 62 | let packet = iface.next().await.unwrap().unwrap(); 63 | let IpPacketVersion::V4(packet) = packet.version_box() else { continue }; 64 | let Ipv4PacketProtocol::Udp(packet) = packet.protocol_box() else { continue }; 65 | break packet; 66 | }; 67 | assert_eq!(packet.data(), b"hello"); 68 | ``` 69 | 70 | ### More, longer examples. 71 | 72 | Check out the [examples](https://github.com/canndrew/netsim/tree/master/examples) directory in 73 | this repo. 74 | 75 | ## Limitations 76 | 77 | `netsim` currently only supports Linux since it makes use of the Linux containerization APIs. 78 | 79 | ## License 80 | 81 | MIT or BSD-3-Clause at your option. 82 | 83 | -------------------------------------------------------------------------------- /examples/capture_packet.rs: -------------------------------------------------------------------------------- 1 | use { 2 | tokio::net::UdpSocket, 3 | netsim::{ 4 | Machine, 5 | packet::{IpPacketVersion, Ipv4PacketProtocol}, 6 | }, 7 | net_literals::addrv4, 8 | futures::prelude::stream::StreamExt, 9 | }; 10 | 11 | #[tokio::main] 12 | async fn main() { 13 | let addr = addrv4!("10.1.2.3:5555"); 14 | 15 | let machine = Machine::new().unwrap(); 16 | let mut iface = { 17 | machine 18 | .add_ip_iface() 19 | .ipv4_addr(*addr.ip()) 20 | .ipv4_default_route() 21 | .build() 22 | .unwrap() 23 | }; 24 | machine.spawn(async move { 25 | let socket = UdpSocket::bind(addr).await.unwrap(); 26 | socket.send_to(b"hello", addrv4!("1.1.1.1:80")).await.unwrap(); 27 | }).await.unwrap(); 28 | 29 | let packet = loop { 30 | let packet = iface.next().await.unwrap().unwrap(); 31 | let IpPacketVersion::V4(packet) = packet.version_box() else { continue }; 32 | let Ipv4PacketProtocol::Udp(packet) = packet.protocol_box() else { continue }; 33 | break packet; 34 | }; 35 | assert_eq!(packet.data(), b"hello"); 36 | } 37 | 38 | -------------------------------------------------------------------------------- /examples/isolate.rs: -------------------------------------------------------------------------------- 1 | use { 2 | std::{ 3 | thread, 4 | net::{TcpStream, TcpListener}, 5 | }, 6 | net_literals::addrv4, 7 | }; 8 | 9 | #[netsim::isolate] 10 | fn main() { 11 | let addr = addrv4!("127.0.0.1:80"); 12 | 13 | let listener = TcpListener::bind(addr).unwrap(); 14 | let join_handle_inner_0 = thread::spawn(move || { 15 | let _stream = listener.accept().unwrap(); 16 | }); 17 | let join_handle_inner_1 = thread::spawn(move || { 18 | let _stream = TcpStream::connect(addr).unwrap(); 19 | }); 20 | join_handle_inner_0.join().unwrap(); 21 | join_handle_inner_1.join().unwrap(); 22 | } 23 | 24 | -------------------------------------------------------------------------------- /examples/nat.rs: -------------------------------------------------------------------------------- 1 | use { 2 | std::{ 3 | str, 4 | str::FromStr, 5 | time::Duration, 6 | net::SocketAddr, 7 | }, 8 | netsim::{ 9 | Machine, ipv4_network, Ipv4Network, 10 | device::{IpHub, NatBuilder}, 11 | }, 12 | net_literals::addrv4, 13 | tokio::net::UdpSocket, 14 | }; 15 | 16 | #[tokio::main] 17 | async fn main() { 18 | let mut rng = rand::thread_rng(); 19 | 20 | let machine_0 = Machine::new().unwrap(); 21 | let machine_1 = Machine::new().unwrap(); 22 | 23 | let network_0 = ipv4_network!("192.168.0.0/16"); 24 | let ipv4_addr_0 = network_0.random_addr(&mut rng); 25 | let iface_0 = { 26 | machine_0 27 | .add_ip_iface() 28 | .ipv4_addr(ipv4_addr_0) 29 | .ipv4_default_route() 30 | .build() 31 | .unwrap() 32 | }; 33 | let global_ipv4_addr_0 = Ipv4Network::GLOBAL.random_addr(&mut rng); 34 | let (mut nat_0, nat_iface_0) = NatBuilder::new(global_ipv4_addr_0, network_0).port_restricted().build(); 35 | nat_0.insert_iface(iface_0); 36 | 37 | let network_1 = ipv4_network!("10.0.0.0/8"); 38 | let ipv4_addr_1 = network_1.random_addr(&mut rng); 39 | let iface_1 = { 40 | machine_1 41 | .add_ip_iface() 42 | .ipv4_addr(ipv4_addr_1) 43 | .ipv4_default_route() 44 | .build() 45 | .unwrap() 46 | }; 47 | let global_ipv4_addr_1 = Ipv4Network::GLOBAL.random_addr(&mut rng); 48 | let (mut nat_1, nat_iface_1) = NatBuilder::new(global_ipv4_addr_1, network_1).port_restricted().build(); 49 | nat_1.insert_iface(iface_1); 50 | 51 | let machine_rendezvous = Machine::new().unwrap(); 52 | let ipv4_addr_rendezvous = Ipv4Network::GLOBAL.random_addr(&mut rng); 53 | let port_rendezvous = 12345; 54 | let addr_rendezvous = SocketAddr::from((ipv4_addr_rendezvous, port_rendezvous)); 55 | let iface_rendezvous = { 56 | machine_rendezvous 57 | .add_ip_iface() 58 | .ipv4_addr(ipv4_addr_rendezvous) 59 | .ipv4_default_route() 60 | .build() 61 | .unwrap() 62 | }; 63 | 64 | 65 | let mut hub = IpHub::new(); 66 | hub.insert_iface(nat_iface_0); 67 | hub.insert_iface(nat_iface_1); 68 | hub.insert_iface(iface_rendezvous); 69 | 70 | println!("machine 0 has local ip {} and global ip {}", ipv4_addr_0, global_ipv4_addr_0); 71 | println!("machine 1 has local ip {} and global ip {}", ipv4_addr_1, global_ipv4_addr_1); 72 | println!("rendezvous machine has ip {}", ipv4_addr_rendezvous); 73 | 74 | let join_handle_rendezvous = machine_rendezvous.spawn(async move { 75 | let socket = UdpSocket::bind(addr_rendezvous).await.unwrap(); 76 | 77 | let peer_addr_0 = { 78 | let mut recv_bytes = [0u8; 100]; 79 | let (recv_len, peer_addr_0) = socket.recv_from(&mut recv_bytes).await.unwrap(); 80 | assert_eq!(recv_len, 0); 81 | peer_addr_0 82 | }; 83 | println!("rendevous node received packet from {}", peer_addr_0); 84 | 85 | let peer_addr_1 = loop { 86 | let mut recv_bytes = [0u8; 100]; 87 | let (recv_len, peer_addr_1) = socket.recv_from(&mut recv_bytes).await.unwrap(); 88 | assert_eq!(recv_len, 0); 89 | if peer_addr_1 == peer_addr_0 { 90 | println!("rendezvous node ignoring extra packet from {}", peer_addr_0); 91 | continue; 92 | } 93 | break peer_addr_1 94 | }; 95 | println!("rendevous node received packet from {}", peer_addr_1); 96 | 97 | let peer_addr_0_str = peer_addr_0.to_string(); 98 | let peer_addr_1_str = peer_addr_1.to_string(); 99 | socket.send_to(peer_addr_0_str.as_bytes(), peer_addr_1).await.unwrap(); 100 | socket.send_to(peer_addr_1_str.as_bytes(), peer_addr_0).await.unwrap(); 101 | }); 102 | let join_handle_0 = machine_0.spawn(async move { 103 | let socket = UdpSocket::bind(addrv4!("0.0.0.0:0")).await.unwrap(); 104 | let mut recv_bytes = [0u8; 100]; 105 | 106 | let peer_addr_1 = { 107 | let (recv_len, recv_addr) = loop { 108 | socket.send_to(&[], addr_rendezvous).await.unwrap(); 109 | println!("machine 0 sending to {}", addr_rendezvous); 110 | tokio::select! { 111 | recv_result = socket.recv_from(&mut recv_bytes) => break recv_result.unwrap(), 112 | () = tokio::time::sleep(Duration::from_secs(1)) => (), 113 | } 114 | }; 115 | assert_eq!(recv_addr, addr_rendezvous); 116 | let recv_msg = str::from_utf8(&recv_bytes[..recv_len]).unwrap(); 117 | SocketAddr::from_str(recv_msg).unwrap() 118 | }; 119 | let (recv_len, recv_addr) = loop { 120 | socket.send_to("hello from machine 0".as_bytes(), peer_addr_1).await.unwrap(); 121 | tokio::select! { 122 | recv_result = socket.recv_from(&mut recv_bytes) => break recv_result.unwrap(), 123 | () = tokio::time::sleep(Duration::from_secs(1)) => (), 124 | } 125 | }; 126 | assert_eq!(recv_addr, peer_addr_1); 127 | let recv_msg = str::from_utf8(&recv_bytes[..recv_len]).unwrap(); 128 | assert_eq!(recv_msg, "hello from machine 1"); 129 | socket.send_to("hello from machine 0".as_bytes(), peer_addr_1).await.unwrap(); 130 | }); 131 | let join_handle_1 = machine_1.spawn(async move { 132 | let socket = UdpSocket::bind(addrv4!("0.0.0.0:0")).await.unwrap(); 133 | let mut recv_bytes = [0u8; 100]; 134 | 135 | let peer_addr_1 = { 136 | let (recv_len, recv_addr) = loop { 137 | socket.send_to(&[], addr_rendezvous).await.unwrap(); 138 | println!("machine 1 sending to {}", addr_rendezvous); 139 | tokio::select! { 140 | recv_result = socket.recv_from(&mut recv_bytes) => break recv_result.unwrap(), 141 | () = tokio::time::sleep(Duration::from_secs(1)) => (), 142 | } 143 | }; 144 | assert_eq!(recv_addr, addr_rendezvous); 145 | let recv_msg = str::from_utf8(&recv_bytes[..recv_len]).unwrap(); 146 | SocketAddr::from_str(recv_msg).unwrap() 147 | }; 148 | let (recv_len, recv_addr) = loop { 149 | socket.send_to("hello from machine 1".as_bytes(), peer_addr_1).await.unwrap(); 150 | tokio::select! { 151 | recv_result = socket.recv_from(&mut recv_bytes) => break recv_result.unwrap(), 152 | () = tokio::time::sleep(Duration::from_secs(1)) => (), 153 | } 154 | }; 155 | assert_eq!(recv_addr, peer_addr_1); 156 | let recv_msg = str::from_utf8(&recv_bytes[..recv_len]).unwrap(); 157 | assert_eq!(recv_msg, "hello from machine 0"); 158 | socket.send_to("hello from machine 1".as_bytes(), peer_addr_1).await.unwrap(); 159 | }); 160 | 161 | let () = join_handle_0.await.unwrap().unwrap(); 162 | let () = join_handle_1.await.unwrap().unwrap(); 163 | let () = join_handle_rendezvous.await.unwrap().unwrap(); 164 | } 165 | 166 | -------------------------------------------------------------------------------- /examples/ping_pong.rs: -------------------------------------------------------------------------------- 1 | use { 2 | std::{ 3 | time::Duration, 4 | net::SocketAddr, 5 | str, 6 | }, 7 | tokio::net::UdpSocket, 8 | netsim::Machine, 9 | net_literals::ipv4, 10 | }; 11 | 12 | 13 | // This example creates two network-isolated threads and gives each a network interface with which 14 | // they send messages back-and-forth. 15 | #[tokio::main] 16 | async fn main() { 17 | 18 | // First create two machines. 19 | let machine_0 = Machine::new().unwrap(); 20 | let machine_1 = Machine::new().unwrap(); 21 | 22 | 23 | // Then give each machine a network interface. 24 | let ipv4_addr_0 = ipv4!("10.1.2.3"); 25 | let port_0 = 45666; 26 | let addr_0 = SocketAddr::from((ipv4_addr_0, port_0)); 27 | 28 | let ipv4_addr_1 = ipv4!("192.168.5.5"); 29 | let port_1 = 5555; 30 | let addr_1 = SocketAddr::from((ipv4_addr_1, port_1)); 31 | 32 | let iface_0 = { 33 | machine_0 34 | .add_ip_iface() 35 | .ipv4_addr(ipv4_addr_0) 36 | .ipv4_default_route() 37 | .build() 38 | .unwrap() 39 | }; 40 | let iface_1 = { 41 | machine_1 42 | .add_ip_iface() 43 | .ipv4_addr(ipv4_addr_1) 44 | .ipv4_default_route() 45 | .build() 46 | .unwrap() 47 | }; 48 | 49 | 50 | // Connect the network interfaces directly to each other. 51 | netsim::connect(iface_0, iface_1); 52 | 53 | 54 | // Execute a task on machine 0. This task waits to receive a UDP packet then sends a reply. 55 | let join_handle_0 = machine_0.spawn(async move { 56 | let socket = UdpSocket::bind(addr_0).await.unwrap(); 57 | 58 | let mut recv_bytes = [0u8; 100]; 59 | let (recv_len, peer_addr) = socket.recv_from(&mut recv_bytes).await.unwrap(); 60 | assert_eq!(peer_addr, addr_1); 61 | let recv_msg = str::from_utf8(&recv_bytes[..recv_len]).unwrap(); 62 | println!("received msg: '{recv_msg}'"); 63 | 64 | let send_msg = "pong"; 65 | let send_len = socket.send_to(send_msg.as_bytes(), addr_1).await.unwrap(); 66 | assert_eq!(send_len, send_msg.len()); 67 | println!("sent reply: '{send_msg}'"); 68 | }); 69 | // Execute a task on machine 1. This task sends UDP packets until it receives a reply. 70 | let join_handle_1 = machine_1.spawn(async move { 71 | let socket = UdpSocket::bind(addr_1).await.unwrap(); 72 | let mut recv_bytes = [0u8; 100]; 73 | 74 | let (recv_len, peer_addr) = loop { 75 | let send_msg = "ping"; 76 | let send_len = socket.send_to(send_msg.as_bytes(), addr_0).await.unwrap(); 77 | assert_eq!(send_len, send_msg.len()); 78 | println!("sent msg: '{send_msg}'"); 79 | 80 | tokio::select! { 81 | recv_result = socket.recv_from(&mut recv_bytes) => break recv_result.unwrap(), 82 | () = tokio::time::sleep(Duration::from_secs(1)) => (), 83 | } 84 | }; 85 | assert_eq!(peer_addr, addr_0); 86 | let recv_msg = str::from_utf8(&recv_bytes[..recv_len]).unwrap(); 87 | println!("received reply: '{recv_msg}'"); 88 | }); 89 | 90 | 91 | // Wait for both machines to run their tasks to completion. 92 | let () = join_handle_0.await.unwrap().unwrap(); 93 | let () = join_handle_1.await.unwrap().unwrap(); 94 | } 95 | -------------------------------------------------------------------------------- /netsim-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "netsim-macros" 3 | version = "0.3.0" 4 | authors = ["Andrew Cann "] 5 | description = "Macros for netsim." 6 | repository = "https://github.com/canndrew/netsim-ng" 7 | documentation = "https://docs.rs/netsim" 8 | license = "MIT OR BSD-3-Clause" 9 | keywords = ["network", "ip", "testing"] 10 | categories = ["asynchronous", "development-tools::testing", "network-programming", "simulation", "virtualization"] 11 | edition = "2021" 12 | 13 | [lib] 14 | proc-macro = true 15 | 16 | [dependencies] 17 | syn = { version = "2.0.90", features = ["full"] } 18 | quote = { version = "1.0.37" } 19 | proc-macro2 = { version = "1.0.92" } 20 | 21 | -------------------------------------------------------------------------------- /netsim-macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | use { 2 | std::{ 3 | net::{Ipv4Addr, Ipv6Addr}, 4 | str::FromStr, 5 | }, 6 | proc_macro::TokenStream, 7 | syn::spanned::Spanned, 8 | quote::quote_spanned, 9 | }; 10 | 11 | /// Creates a `Ipv4Network` given an address range in CIDR notation. 12 | /// 13 | /// # Example 14 | /// 15 | /// ```rust 16 | /// assert_eq!(Ipv4Network::new(ipv4!("192.168.0.0"), 16), ipv4_network!("192.168.0.0/16")); 17 | /// ``` 18 | #[proc_macro] 19 | pub fn ipv4_network(input: TokenStream) -> TokenStream { 20 | let input = syn::parse_macro_input!(input as syn::LitStr); 21 | let span = input.span(); 22 | let s = input.value(); 23 | let output = match parse_ipv4_network(&s) { 24 | Ok((addr, subnet_mask_bits)) => { 25 | let [b0, b1, b2, b3] = addr.octets(); 26 | quote_spanned!(span=> { 27 | ::netsim::Ipv4Network::new(::std::net::Ipv4Addr::new(#b0, #b1, #b2, #b3), #subnet_mask_bits) 28 | }) 29 | }, 30 | Err(err) => { 31 | quote_spanned!(span=> { 32 | compile_error!(#err) 33 | }) 34 | }, 35 | }; 36 | output.into() 37 | } 38 | 39 | fn parse_ipv4_network(s: &str) -> Result<(Ipv4Addr, u8), String> { 40 | let (addr, subnet_mask_bits) = match s.split_once('/') { 41 | None => return Err(String::from("missing '/' character")), 42 | Some((addr, subnet_mask_bits)) => (addr, subnet_mask_bits), 43 | }; 44 | let addr = match Ipv4Addr::from_str(addr) { 45 | Err(err) => return Err(err.to_string()), 46 | Ok(addr) => addr, 47 | }; 48 | let subnet_mask_bits = match u8::from_str(subnet_mask_bits) { 49 | Err(err) => return Err(err.to_string()), 50 | Ok(subnet_mask_bits) => subnet_mask_bits, 51 | }; 52 | if subnet_mask_bits > 32 { 53 | return Err(String::from("subnet mask bits cannot be greater than 32")); 54 | } 55 | Ok((addr, subnet_mask_bits)) 56 | } 57 | 58 | /// Creates a `Ipv6Network` given an address range in CIDR notation. 59 | /// 60 | /// # Example 61 | /// 62 | /// ```rust 63 | /// assert_eq!(Ipv6Network::new(ipv6!("ff00::"), 8), ipv6_network!("ff00::/8")); 64 | /// ``` 65 | #[proc_macro] 66 | pub fn ipv6_network(input: TokenStream) -> TokenStream { 67 | let input = syn::parse_macro_input!(input as syn::LitStr); 68 | let span = input.span(); 69 | let s = input.value(); 70 | let output = match parse_ipv6_network(&s) { 71 | Ok((addr, subnet_mask_bits)) => { 72 | let [b0, b1, b2, b3, b4, b5, b6, b7] = addr.segments(); 73 | quote_spanned!(span=> { 74 | ::netsim::Ipv6Network::new( 75 | ::std::net::Ipv6Addr::new(#b0, #b1, #b2, #b3, #b4, #b5, #b6, #b7), 76 | #subnet_mask_bits, 77 | ) 78 | }) 79 | }, 80 | Err(err) => { 81 | quote_spanned!(span=> { 82 | compile_error!(#err) 83 | }) 84 | }, 85 | }; 86 | output.into() 87 | } 88 | 89 | fn parse_ipv6_network(s: &str) -> Result<(Ipv6Addr, u8), String> { 90 | let (addr, subnet_mask_bits) = match s.split_once('/') { 91 | None => return Err(String::from("missing '/' character")), 92 | Some((addr, subnet_mask_bits)) => (addr, subnet_mask_bits), 93 | }; 94 | let addr = match Ipv6Addr::from_str(addr) { 95 | Err(err) => return Err(err.to_string()), 96 | Ok(addr) => addr, 97 | }; 98 | let subnet_mask_bits = match u8::from_str(subnet_mask_bits) { 99 | Err(err) => return Err(err.to_string()), 100 | Ok(subnet_mask_bits) => subnet_mask_bits, 101 | }; 102 | if subnet_mask_bits > 128 { 103 | return Err(String::from("subnet mask bits cannot be greater than 128")); 104 | } 105 | Ok((addr, subnet_mask_bits)) 106 | } 107 | 108 | /// Makes a function run in an isolated network environment. 109 | #[proc_macro_attribute] 110 | pub fn isolate(_attr: TokenStream, input: TokenStream) -> TokenStream { 111 | let item_fn = syn::parse_macro_input!(input as syn::ItemFn); 112 | let span = item_fn.span(); 113 | let syn::ItemFn { attrs, vis, sig, block } = item_fn; 114 | let is_async = sig.asyncness.is_some(); 115 | let output = if is_async { 116 | quote_spanned! {span=> 117 | #(#attrs)* 118 | #vis #sig { 119 | let machine = netsim::Machine::new().expect("error creating machine"); 120 | let join_handle = machine.spawn(async move #block); 121 | join_handle.await.unwrap().unwrap() 122 | } 123 | } 124 | } else { 125 | quote_spanned! {span=> 126 | #(#attrs)* 127 | #vis #sig { 128 | let machine = netsim::Machine::new().expect("error creating machine"); 129 | let join_handle = machine.spawn(async move { 130 | ::netsim::tokio::task::spawn_blocking(move || #block).await.unwrap() 131 | }); 132 | join_handle.join_blocking().unwrap().unwrap() 133 | } 134 | } 135 | }; 136 | output.into() 137 | } 138 | 139 | -------------------------------------------------------------------------------- /src/adapter/delay.rs: -------------------------------------------------------------------------------- 1 | use crate::priv_prelude::*; 2 | 3 | /// `Sink`/`Stream` adapter which adds a time delay to items sent/received through the 4 | /// `Sink`/`Stream`. 5 | /// 6 | /// Can be created via [`SinkStreamExt::with_delay`](crate::SinkStreamExt::with_delay). 7 | #[pin_project] 8 | pub struct Delay 9 | where 10 | S: Stream + Sink, 11 | { 12 | min_delay: Duration, 13 | mean_additional_delay: Duration, 14 | stream_finished: bool, 15 | #[pin] 16 | stream: S, 17 | #[pin] 18 | stream_queue: DelayQueue<::Item>, 19 | #[pin] 20 | sink_queue: DelayQueue, 21 | } 22 | 23 | #[pin_project] 24 | struct DelayQueue { 25 | #[pin] 26 | sleep_opt: Option, 27 | pending: BTreeMap>, 28 | } 29 | 30 | impl DelayQueue { 31 | pub fn new() -> DelayQueue { 32 | DelayQueue { 33 | sleep_opt: None, 34 | pending: BTreeMap::new(), 35 | } 36 | } 37 | 38 | pub fn push(self: Pin<&mut Self>, delay: Duration, value: T) { 39 | let mut this = self.project(); 40 | let instant = Instant::now() + delay; 41 | match this.pending.first_entry() { 42 | None => { 43 | this.sleep_opt.set(Some(tokio::time::sleep_until(instant.into()))); 44 | }, 45 | Some(entry) => { 46 | let prev_instant = *entry.key(); 47 | if instant < prev_instant { 48 | let sleep = this.sleep_opt.as_mut().as_pin_mut().unwrap(); 49 | sleep.reset(instant.into()); 50 | } 51 | }, 52 | } 53 | this.pending.entry(instant).or_default().push_back(value); 54 | } 55 | 56 | pub fn pop(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll> { 57 | let mut this = self.project(); 58 | match this.sleep_opt.as_mut().as_pin_mut() { 59 | None => Poll::Ready(None), 60 | Some(mut sleep) => { 61 | match sleep.as_mut().poll(cx) { 62 | Poll::Ready(()) => { 63 | let mut entry = this.pending.first_entry().unwrap(); 64 | let value = entry.get_mut().pop_front().unwrap(); 65 | let next_instant_opt = if entry.get().is_empty() { 66 | let _ = entry.remove(); 67 | this.pending.first_key_value().map(|(&instant, _values)| instant) 68 | } else { 69 | Some(*entry.key()) 70 | }; 71 | match next_instant_opt { 72 | None => this.sleep_opt.set(None), 73 | Some(instant) => sleep.reset(instant.into()), 74 | } 75 | Poll::Ready(Some(value)) 76 | }, 77 | Poll::Pending => Poll::Pending, 78 | } 79 | }, 80 | } 81 | } 82 | } 83 | 84 | impl Delay 85 | where 86 | S: Stream + Sink, 87 | { 88 | /// Creates a new [`Delay`]. See the documentation for 89 | /// [`SinkStreamExt::with_delay`](crate::SinkStreamExt::with_delay). 90 | pub fn new(stream: S, min_delay: Duration, mean_additional_delay: Duration) -> Delay { 91 | Delay { 92 | min_delay, 93 | mean_additional_delay, 94 | stream, 95 | stream_finished: false, 96 | stream_queue: DelayQueue::new(), 97 | sink_queue: DelayQueue::new(), 98 | } 99 | } 100 | } 101 | 102 | impl Stream for Delay 103 | where 104 | S: Stream + Sink, 105 | { 106 | type Item = ::Item; 107 | 108 | fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll> { 109 | let mut this = self.project(); 110 | if !*this.stream_finished { 111 | loop { 112 | match this.stream.as_mut().poll_next(cx) { 113 | Poll::Ready(Some(value)) => { 114 | let delay = *this.min_delay + adapter::expovariate_duration( 115 | *this.mean_additional_delay, 116 | &mut rand::thread_rng(), 117 | ); 118 | this.stream_queue.as_mut().push(delay, value); 119 | }, 120 | Poll::Ready(None) => { 121 | *this.stream_finished = true; 122 | break; 123 | }, 124 | Poll::Pending => break, 125 | } 126 | } 127 | } 128 | let pending_finished = match this.stream_queue.pop(cx) { 129 | Poll::Pending => false, 130 | Poll::Ready(None) => true, 131 | Poll::Ready(Some(value)) => return Poll::Ready(Some(value)), 132 | }; 133 | if *this.stream_finished && pending_finished { 134 | Poll::Ready(None) 135 | } else { 136 | Poll::Pending 137 | } 138 | } 139 | } 140 | 141 | impl Sink for Delay 142 | where 143 | S: Stream, 144 | S: Sink, 145 | { 146 | type Error = >::Error; 147 | 148 | fn poll_ready(self: Pin<&mut Self>, _cx: &mut task::Context) -> Poll> { 149 | Poll::Ready(Ok(())) 150 | } 151 | 152 | fn start_send(self: Pin<&mut Self>, item: T) -> Result<(), Self::Error> { 153 | let this = self.project(); 154 | let delay = *this.min_delay + adapter::expovariate_duration( 155 | *this.mean_additional_delay, 156 | &mut rand::thread_rng(), 157 | ); 158 | this.sink_queue.push(delay, item); 159 | Ok(()) 160 | } 161 | 162 | fn poll_flush(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll> { 163 | let mut this = self.project(); 164 | loop { 165 | match this.stream.as_mut().poll_ready(cx) { 166 | Poll::Pending => return Poll::Pending, 167 | Poll::Ready(Err(err)) => return Poll::Ready(Err(err)), 168 | Poll::Ready(Ok(())) => (), 169 | } 170 | match this.sink_queue.as_mut().pop(cx) { 171 | Poll::Pending => return Poll::Pending, 172 | Poll::Ready(None) => { 173 | return this.stream.poll_flush(cx); 174 | }, 175 | Poll::Ready(Some(item)) => { 176 | match this.stream.as_mut().start_send(item) { 177 | Ok(()) => (), 178 | Err(err) => return Poll::Ready(Err(err)), 179 | } 180 | match this.stream.as_mut().poll_flush(cx) { 181 | Poll::Pending => (), 182 | Poll::Ready(Ok(())) => (), 183 | Poll::Ready(Err(err)) => return Poll::Ready(Err(err)), 184 | } 185 | }, 186 | } 187 | }; 188 | } 189 | 190 | fn poll_close(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll> { 191 | let this = self.project(); 192 | this.stream.poll_close(cx) 193 | } 194 | } 195 | 196 | -------------------------------------------------------------------------------- /src/adapter/loss.rs: -------------------------------------------------------------------------------- 1 | use crate::priv_prelude::*; 2 | 3 | /// `Sink`/`Stream` adapter which randomly drops items. 4 | /// 5 | /// Can be created via [`SinkStreamExt::with_loss`](crate::SinkStreamExt::with_loss). 6 | #[pin_project] 7 | pub struct Loss { 8 | #[pin] 9 | stream: S, 10 | jitter: Jitter, 11 | } 12 | 13 | struct Jitter { 14 | loss_rate: f64, 15 | jitter_period: Duration, 16 | currently_dropping: bool, 17 | prev_switch_instant: Instant, 18 | next_switch_instant: Instant, 19 | } 20 | 21 | impl Jitter { 22 | pub fn new(loss_rate: f64, jitter_period: Duration) -> Jitter { 23 | assert!(0.0 <= loss_rate); 24 | assert!(loss_rate <= 1.0); 25 | let now = Instant::now(); 26 | let mut jitter = Jitter { 27 | loss_rate, 28 | jitter_period, 29 | currently_dropping: false, 30 | prev_switch_instant: now, 31 | next_switch_instant: now, 32 | }; 33 | jitter.reset(now); 34 | jitter 35 | } 36 | 37 | pub fn reset(&mut self, switch_instant: Instant) { 38 | self.prev_switch_instant = switch_instant; 39 | self.currently_dropping = rand::thread_rng().gen::() < self.loss_rate; 40 | self.set_next_switch_instant(); 41 | } 42 | 43 | pub fn advance(&mut self) { 44 | let now = Instant::now(); 45 | if self.next_switch_instant + (self.jitter_period * 10) < now { 46 | self.reset(now); 47 | return; 48 | } 49 | while self.next_switch_instant < now { 50 | self.prev_switch_instant = self.next_switch_instant; 51 | self.currently_dropping = !self.currently_dropping; 52 | self.set_next_switch_instant(); 53 | } 54 | } 55 | 56 | pub fn currently_dropping(&self) -> bool { 57 | self.currently_dropping 58 | } 59 | 60 | fn set_next_switch_instant(&mut self) { 61 | let delay = if self.currently_dropping { 62 | self.jitter_period.mul_f64(self.loss_rate) 63 | } else { 64 | self.jitter_period.mul_f64(1.0 - self.loss_rate) 65 | }; 66 | self.next_switch_instant = { 67 | self.prev_switch_instant + adapter::expovariate_duration(delay, &mut rand::thread_rng()) 68 | }; 69 | } 70 | } 71 | 72 | impl Loss { 73 | /// Creates a new [`Loss`]. See the documentation for 74 | /// [`SinkStreamExt::with_loss`](crate::SinkStreamExt::with_loss). 75 | pub fn new(stream: S, loss_rate: f64, jitter_period: Duration) -> Loss { 76 | Loss { 77 | stream, 78 | jitter: Jitter::new(loss_rate, jitter_period), 79 | } 80 | } 81 | } 82 | 83 | impl Stream for Loss 84 | where 85 | S: Stream, 86 | { 87 | type Item = S::Item; 88 | 89 | fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll> { 90 | let mut this = self.project(); 91 | this.jitter.advance(); 92 | loop { 93 | match this.stream.as_mut().poll_next(cx) { 94 | Poll::Ready(Some(value)) => { 95 | if this.jitter.currently_dropping() { 96 | continue; 97 | } 98 | break Poll::Ready(Some(value)); 99 | }, 100 | Poll::Ready(None) => break Poll::Ready(None), 101 | Poll::Pending => break Poll::Pending, 102 | } 103 | } 104 | } 105 | } 106 | 107 | impl Sink for Loss 108 | where 109 | S: Stream, 110 | S: Sink, 111 | { 112 | type Error = >::Error; 113 | 114 | fn poll_ready(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll> { 115 | let this = self.project(); 116 | this.stream.poll_ready(cx) 117 | } 118 | 119 | fn start_send(self: Pin<&mut Self>, item: T) -> Result<(), Self::Error> { 120 | let this = self.project(); 121 | this.jitter.advance(); 122 | if this.jitter.currently_dropping() { 123 | return Ok(()); 124 | } 125 | this.stream.start_send(item) 126 | } 127 | 128 | fn poll_flush(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll> { 129 | let this = self.project(); 130 | this.stream.poll_flush(cx) 131 | } 132 | 133 | fn poll_close(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll> { 134 | let this = self.project(); 135 | this.stream.poll_close(cx) 136 | } 137 | } 138 | 139 | impl FusedStream for Loss 140 | where 141 | S: FusedStream, 142 | { 143 | fn is_terminated(&self) -> bool { 144 | self.stream.is_terminated() 145 | } 146 | } 147 | 148 | -------------------------------------------------------------------------------- /src/adapter/mod.rs: -------------------------------------------------------------------------------- 1 | //! `Sink`/`Stream` adapters. 2 | //! 3 | //! Intended for use with `Sink`s and `Stream`s transporting packets. 4 | 5 | use crate::priv_prelude::*; 6 | 7 | mod delay; 8 | mod loss; 9 | 10 | pub use self::{ 11 | delay::Delay, 12 | loss::Loss, 13 | }; 14 | 15 | pub(crate) fn expovariate_duration( 16 | mean_duration: Duration, 17 | rng: &mut R, 18 | ) -> Duration 19 | where 20 | R: Rng, 21 | { 22 | let mean_duration = mean_duration.as_secs_f64(); 23 | loop { 24 | let duration = mean_duration * -rng.gen::().ln(); 25 | match Duration::try_from_secs_f64(duration) { 26 | Ok(duration) => break duration, 27 | Err(_) => continue, 28 | } 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/connect.rs: -------------------------------------------------------------------------------- 1 | use crate::priv_prelude::*; 2 | 3 | /// Convenience method for connecting two [`IpSinkStream`](crate::IpSinkStream)s to each other. 4 | /// 5 | /// Packets from either `IpSinkStream` are forwarded to the other. See [`IpHub`](crate::device::IpHub) if 6 | /// you want to connect more than two interfaces. 7 | pub fn connect(plug_0: T0, plug_1: T1) 8 | where 9 | T0: IpSinkStream, 10 | T1: IpSinkStream, 11 | { 12 | let (sink_0, stream_0) = plug_0.split(); 13 | let (sink_1, stream_1) = plug_1.split(); 14 | let _detach = tokio::spawn(stream_0.forward(sink_1).map(|_res| ())); 15 | let _detach = tokio::spawn(stream_1.forward(sink_0).map(|_res| ())); 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/device/channel.rs: -------------------------------------------------------------------------------- 1 | use crate::priv_prelude::*; 2 | pub use futures::channel::mpsc::SendError; 3 | 4 | /// A bi-directional channel where both ends can both send and receive items. 5 | pub struct BiChannel { 6 | sender: mpsc::Sender, 7 | receiver: mpsc::Receiver, 8 | } 9 | 10 | impl BiChannel { 11 | /// Creates a connected pair of `BiChannel`s. 12 | pub fn new(capacity: usize) -> (BiChannel, BiChannel) { 13 | let (sender_0, receiver_0) = mpsc::channel(capacity); 14 | let (sender_1, receiver_1) = mpsc::channel(capacity); 15 | let bi_channel_0 = BiChannel { 16 | sender: sender_0, 17 | receiver: receiver_1, 18 | }; 19 | let bi_channel_1 = BiChannel { 20 | sender: sender_1, 21 | receiver: receiver_0, 22 | }; 23 | (bi_channel_0, bi_channel_1) 24 | } 25 | } 26 | 27 | impl Stream for BiChannel { 28 | type Item = T; 29 | 30 | fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll> { 31 | let this = self.get_mut(); 32 | Pin::new(&mut this.receiver).poll_next(cx) 33 | } 34 | } 35 | 36 | impl Sink for BiChannel { 37 | type Error = SendError; 38 | 39 | fn poll_flush(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll> { 40 | let this = self.get_mut(); 41 | Pin::new(&mut this.sender).poll_flush(cx) 42 | } 43 | 44 | fn poll_ready(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll> { 45 | let this = self.get_mut(); 46 | Pin::new(&mut this.sender).poll_ready(cx) 47 | } 48 | 49 | fn poll_close(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll> { 50 | let this = self.get_mut(); 51 | Pin::new(&mut this.sender).poll_close(cx) 52 | } 53 | 54 | fn start_send(self: Pin<&mut Self>, packet: T) -> Result<(), SendError> { 55 | let this = self.get_mut(); 56 | Pin::new(&mut this.sender).start_send(packet) 57 | } 58 | } 59 | 60 | /// A bi-directional channel for sending/receiving IP packets. 61 | pub struct IpChannel { 62 | packet_channel: BiChannel>, 63 | } 64 | 65 | impl IpChannel { 66 | /// Creates a connected pair of `IpChannel`s. Packets sent on one will be received on the 67 | /// other. 68 | pub fn new(capacity: usize) -> (IpChannel, IpChannel) { 69 | let (packet_channel_0, packet_channel_1) = BiChannel::new(capacity); 70 | let channel_0 = IpChannel { 71 | packet_channel: packet_channel_0, 72 | }; 73 | let channel_1 = IpChannel { 74 | packet_channel: packet_channel_1, 75 | }; 76 | (channel_0, channel_1) 77 | } 78 | } 79 | 80 | impl Stream for IpChannel { 81 | type Item = io::Result>; 82 | 83 | fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll>>> { 84 | let this = self.get_mut(); 85 | match Pin::new(&mut this.packet_channel).poll_next(cx) { 86 | Poll::Ready(Some(packet)) => Poll::Ready(Some(Ok(packet))), 87 | Poll::Ready(None) => Poll::Ready(None), 88 | Poll::Pending => Poll::Pending, 89 | } 90 | } 91 | } 92 | 93 | impl Sink> for IpChannel { 94 | type Error = io::Error; 95 | 96 | fn poll_flush(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll> { 97 | let this = self.get_mut(); 98 | match Pin::new(&mut this.packet_channel).poll_flush(cx) { 99 | Poll::Ready(Ok(())) => Poll::Ready(Ok(())), 100 | Poll::Ready(Err(err)) => Poll::Ready(Err(as_io_error(err))), 101 | Poll::Pending => Poll::Pending, 102 | } 103 | } 104 | 105 | fn poll_ready(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll> { 106 | let this = self.get_mut(); 107 | match Pin::new(&mut this.packet_channel).poll_ready(cx) { 108 | Poll::Ready(Ok(())) => Poll::Ready(Ok(())), 109 | Poll::Ready(Err(err)) => Poll::Ready(Err(as_io_error(err))), 110 | Poll::Pending => Poll::Pending, 111 | } 112 | } 113 | 114 | fn poll_close(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll> { 115 | let this = self.get_mut(); 116 | match Pin::new(&mut this.packet_channel).poll_close(cx) { 117 | Poll::Ready(Ok(())) => Poll::Ready(Ok(())), 118 | Poll::Ready(Err(err)) => Poll::Ready(Err(as_io_error(err))), 119 | Poll::Pending => Poll::Pending, 120 | } 121 | } 122 | 123 | fn start_send(self: Pin<&mut Self>, packet: Box) -> io::Result<()> { 124 | let this = self.get_mut(); 125 | match Pin::new(&mut this.packet_channel).start_send(packet) { 126 | Ok(()) => Ok(()), 127 | Err(err) => Err(as_io_error(err)), 128 | } 129 | } 130 | } 131 | 132 | fn as_io_error(err: SendError) -> io::Error { 133 | let err = if err.is_disconnected() { 134 | io::ErrorKind::NotConnected 135 | } else if err.is_full() { 136 | io::ErrorKind::WouldBlock 137 | } else { 138 | io::ErrorKind::Other 139 | }; 140 | err.into() 141 | } 142 | 143 | impl FusedStream for BiChannel { 144 | fn is_terminated(&self) -> bool { 145 | self.receiver.is_terminated() 146 | } 147 | } 148 | 149 | impl FusedStream for IpChannel { 150 | fn is_terminated(&self) -> bool { 151 | self.packet_channel.is_terminated() 152 | } 153 | } 154 | 155 | -------------------------------------------------------------------------------- /src/device/hub.rs: -------------------------------------------------------------------------------- 1 | use crate::priv_prelude::*; 2 | 3 | /// A simple IP network hub. 4 | /// 5 | /// Insert any number of interfaces into the hub and any packet 6 | /// received on one will be forwarded to all other interfaces. 7 | pub struct IpHub { 8 | iface_sender: mpsc::UnboundedSender>>, 9 | } 10 | 11 | struct IpHubTask { 12 | iface_receiver: mpsc::UnboundedReceiver>>, 13 | ifaces: Vec>>, 14 | } 15 | 16 | impl IpHub { 17 | /// Create a new `IpHub`. Must be called within a `tokio` context. 18 | #[allow(clippy::new_without_default)] 19 | pub fn new() -> IpHub { 20 | let (iface_sender, iface_receiver) = mpsc::unbounded(); 21 | let task = IpHubTask { 22 | iface_receiver, 23 | ifaces: Vec::new(), 24 | }; 25 | tokio::spawn(task); 26 | IpHub { iface_sender } 27 | } 28 | 29 | /// Insert a `Sink`/`Stream` of IP packets into the hub. Any packet created by this `Stream` 30 | /// will be forwarded to all other inserted `Sink`s and this `Sink` will receive any packet 31 | /// produced by any other inserted `Stream`. 32 | pub fn insert_iface(&mut self, iface: S) 33 | where 34 | S: IpSinkStream, 35 | { 36 | let iface = Box::pin(iface); 37 | self.iface_sender.unbounded_send(iface).unwrap(); 38 | } 39 | } 40 | 41 | impl IpHubTask { 42 | fn poll_flush_outgoing(&mut self, cx: &mut task::Context) -> Poll<()> { 43 | let mut index = 0; 44 | let mut any_pending = false; 45 | while let Some(iface) = self.ifaces.get_mut(index) { 46 | match iface.as_mut().poll_flush(cx) { 47 | Poll::Ready(Ok(())) => (), 48 | Poll::Ready(Err(_)) => { 49 | self.ifaces.remove(index); 50 | continue; 51 | }, 52 | Poll::Pending => { 53 | any_pending = true; 54 | }, 55 | } 56 | index += 1; 57 | } 58 | if any_pending { 59 | Poll::Pending 60 | } else { 61 | Poll::Ready(()) 62 | } 63 | } 64 | 65 | fn poll_ready_outgoing(&mut self, cx: &mut task::Context) -> Poll<()> { 66 | match self.poll_flush_outgoing(cx) { 67 | Poll::Ready(()) => return Poll::Ready(()), 68 | Poll::Pending => (), 69 | } 70 | 71 | let mut index = 0; 72 | let mut any_pending = false; 73 | while let Some(iface) = self.ifaces.get_mut(index) { 74 | match iface.as_mut().poll_ready(cx) { 75 | Poll::Ready(Ok(())) => (), 76 | Poll::Ready(Err(_)) => { 77 | self.ifaces.swap_remove(index); 78 | continue; 79 | }, 80 | Poll::Pending => { 81 | any_pending = true; 82 | }, 83 | } 84 | index += 1; 85 | } 86 | if any_pending { 87 | Poll::Pending 88 | } else { 89 | Poll::Ready(()) 90 | } 91 | } 92 | 93 | fn start_send_outgoing(&mut self, mut recv_index: usize, packet: Box) { 94 | let mut send_index = 0; 95 | loop { 96 | if send_index == recv_index { 97 | send_index += 1; 98 | continue; 99 | } 100 | let iface = match self.ifaces.get_mut(send_index) { 101 | Some(iface) => iface, 102 | None => break, 103 | }; 104 | match iface.as_mut().start_send(packet.clone()) { 105 | Ok(()) => (), 106 | Err(_) => { 107 | self.ifaces.swap_remove(send_index); 108 | if recv_index == self.ifaces.len() { 109 | recv_index = send_index; 110 | } 111 | continue; 112 | }, 113 | } 114 | send_index += 1; 115 | } 116 | } 117 | 118 | fn poll_next_incoming(&mut self, cx: &mut task::Context) -> Poll<(usize, Box)> { 119 | let mut index = 0; 120 | while let Some(iface) = self.ifaces.get_mut(index) { 121 | match iface.as_mut().poll_next(cx) { 122 | Poll::Ready(Some(Ok(packet))) => { 123 | return Poll::Ready((index, packet)); 124 | }, 125 | Poll::Ready(Some(Err(_))) | Poll::Ready(None) => { 126 | self.ifaces.swap_remove(index); 127 | continue; 128 | }, 129 | Poll::Pending => (), 130 | } 131 | index += 1; 132 | } 133 | Poll::Pending 134 | } 135 | 136 | fn poll_inner(&mut self, cx: &mut task::Context) -> Poll<()> { 137 | loop { 138 | match Pin::new(&mut self.iface_receiver).poll_next(cx) { 139 | Poll::Ready(Some(iface)) => { 140 | self.ifaces.push(iface); 141 | }, 142 | Poll::Ready(None) => { 143 | return Poll::Ready(()); 144 | }, 145 | Poll::Pending => break, 146 | } 147 | } 148 | 149 | loop { 150 | match self.poll_ready_outgoing(cx) { 151 | Poll::Ready(()) => (), 152 | Poll::Pending => return Poll::Pending, 153 | } 154 | 155 | let (recv_index, packet) = match self.poll_next_incoming(cx) { 156 | Poll::Ready((index, packet)) => (index, packet), 157 | Poll::Pending => return Poll::Pending, 158 | }; 159 | 160 | if log_enabled!(Level::Debug) { 161 | debug!("recieved on iface #{} {:?}", recv_index, packet); 162 | } 163 | 164 | self.start_send_outgoing(recv_index, packet); 165 | } 166 | } 167 | } 168 | 169 | impl Future for IpHubTask { 170 | type Output = (); 171 | 172 | fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll<()> { 173 | let this = self.get_mut(); 174 | this.poll_inner(cx) 175 | } 176 | } 177 | 178 | -------------------------------------------------------------------------------- /src/device/mod.rs: -------------------------------------------------------------------------------- 1 | //! IP devices for creating networks with. 2 | 3 | mod channel; 4 | mod hub; 5 | mod nat; 6 | 7 | pub use self::{ 8 | channel::{BiChannel, IpChannel}, 9 | hub::IpHub, 10 | nat::{Nat, NatBuilder}, 11 | }; 12 | -------------------------------------------------------------------------------- /src/device/nat/mod.rs: -------------------------------------------------------------------------------- 1 | use { 2 | crate::priv_prelude::*, 3 | self::{ 4 | port_map::PortMap, 5 | restrictions::Restrictions, 6 | }, 7 | }; 8 | mod port_map; 9 | mod restrictions; 10 | 11 | /// A simple NAT (network address translation) implementation. 12 | /// 13 | /// For testing network code across NATs. 14 | pub struct Nat { 15 | iface_sender: mpsc::UnboundedSender>>, 16 | } 17 | 18 | /// Builder for creating a [`Nat`](crate::device::Nat). 19 | pub struct NatBuilder { 20 | external_ipv4: Ipv4Addr, 21 | internal_ipv4_network: Ipv4Network, 22 | hair_pinning: bool, 23 | address_restricted: bool, 24 | port_restricted: bool, 25 | reply_with_rst_to_unexpected_tcp_packets: bool, 26 | } 27 | 28 | impl NatBuilder { 29 | /// Starts building a [`Nat`](crate::device::Nat). Use to configure the NAT then call 30 | /// [`build`](crate::device::NatBuilder::build) to create the NAT. 31 | /// 32 | /// * `external_ipv4` is the IPv4 address that the NAT uses on its external side. 33 | /// * `internal_ipv4_network` is the IPv4 network (eg. 192.168.0.0/16) on the internal side of 34 | /// the NAT. The NAT won't forward any packets on its internal side that don't originate from 35 | /// this network. 36 | pub fn new(external_ipv4: Ipv4Addr, internal_ipv4_network: Ipv4Network) -> NatBuilder { 37 | NatBuilder { 38 | external_ipv4, 39 | internal_ipv4_network, 40 | hair_pinning: false, 41 | address_restricted: false, 42 | port_restricted: false, 43 | reply_with_rst_to_unexpected_tcp_packets: false, 44 | } 45 | } 46 | 47 | /// Enables [NAT hair-pinning](https://en.wikipedia.org/wiki/Network_address_translation#NAT_hairpinning). 48 | pub fn hair_pinning(mut self) -> Self { 49 | self.hair_pinning = true; 50 | self 51 | } 52 | 53 | pub fn reply_with_rst_to_unexpected_tcp_packets(mut self) -> Self { 54 | self.reply_with_rst_to_unexpected_tcp_packets = true; 55 | self 56 | } 57 | 58 | /// Makes this NAT [address restricted](https://en.wikipedia.org/wiki/Network_address_translation#Methods_of_translation). 59 | pub fn address_restricted(mut self) -> Self { 60 | self.address_restricted = true; 61 | self 62 | } 63 | 64 | /// Makes this NAT [port restricted](https://en.wikipedia.org/wiki/Network_address_translation#Methods_of_translation). 65 | pub fn port_restricted(mut self) -> Self { 66 | self.port_restricted = true; 67 | self 68 | } 69 | 70 | /// Build the NAT. The returned `IpChannel` is the external interface of the NAT. 71 | pub fn build(self) -> (Nat, IpChannel) { 72 | let NatBuilder { 73 | external_ipv4, 74 | internal_ipv4_network, 75 | hair_pinning, 76 | address_restricted, 77 | port_restricted, 78 | reply_with_rst_to_unexpected_tcp_packets, 79 | } = self; 80 | let (iface_sender, iface_receiver) = mpsc::unbounded(); 81 | let (channel_0, channel_1) = IpChannel::new(1); 82 | let tcpv4_restrictions = match (port_restricted, address_restricted) { 83 | (false, false) => Restrictions::Unrestricted, 84 | (false, true) => Restrictions::RestrictIpAddr { sent_to: HashMap::new() }, 85 | (true, _) => Restrictions::RestrictSocketAddr { sent_to: HashMap::new() }, 86 | }; 87 | let udpv4_restrictions = match (port_restricted, address_restricted) { 88 | (false, false) => Restrictions::Unrestricted, 89 | (false, true) => Restrictions::RestrictIpAddr { sent_to: HashMap::new() }, 90 | (true, _) => Restrictions::RestrictSocketAddr { sent_to: HashMap::new() }, 91 | }; 92 | let task = NatTask { 93 | iface_receiver, 94 | external_iface_opt: Some(channel_0), 95 | internal_ifaces: HashMap::new(), 96 | next_internal_iface_index: 0, 97 | external_ipv4, 98 | internal_ipv4_network, 99 | internal_addr_indexes: HashMap::new(), 100 | tcpv4_port_map: PortMap::new(), 101 | udpv4_port_map: PortMap::new(), 102 | hair_pinning, 103 | tcpv4_restrictions, 104 | udpv4_restrictions, 105 | reply_with_rst_to_unexpected_tcp_packets, 106 | }; 107 | tokio::spawn(task); 108 | let nat = Nat { iface_sender }; 109 | (nat, channel_1) 110 | } 111 | } 112 | 113 | struct NatTask { 114 | iface_receiver: mpsc::UnboundedReceiver>>, 115 | external_iface_opt: Option, 116 | internal_ifaces: HashMap>>, 117 | next_internal_iface_index: usize, 118 | external_ipv4: Ipv4Addr, 119 | internal_ipv4_network: Ipv4Network, 120 | internal_addr_indexes: HashMap, 121 | tcpv4_port_map: PortMap, 122 | udpv4_port_map: PortMap, 123 | hair_pinning: bool, 124 | tcpv4_restrictions: Restrictions, 125 | udpv4_restrictions: Restrictions, 126 | reply_with_rst_to_unexpected_tcp_packets: bool, 127 | } 128 | 129 | impl Nat { 130 | /// Insert an interface into the internal side of this NAT. Packets sent by this interface to 131 | /// addresses outside the NAT's internal network will be address translated and sent out 132 | /// the NAT's external interface. This creates a port-mapping which allows external hosts to 133 | /// send packets back through the NAT to this interface. 134 | pub fn insert_iface(&mut self, iface: S) 135 | where 136 | S: IpSinkStream, 137 | { 138 | let iface = Box::pin(iface); 139 | self.iface_sender.unbounded_send(iface).unwrap(); 140 | } 141 | } 142 | 143 | impl NatTask { 144 | fn poll_flush_outgoing(&mut self, cx: &mut task::Context) -> Poll<()> { 145 | let mut any_pending = false; 146 | 147 | match &mut self.external_iface_opt { 148 | None => (), 149 | Some(external_iface) => { 150 | match Pin::new(external_iface).poll_flush(cx) { 151 | Poll::Ready(Ok(())) => (), 152 | Poll::Ready(Err(_)) => { 153 | self.external_iface_opt = None; 154 | }, 155 | Poll::Pending => { 156 | any_pending = true; 157 | }, 158 | } 159 | }, 160 | } 161 | 162 | let mut defunct_indexes = Vec::new(); 163 | for (index, internal_iface) in &mut self.internal_ifaces { 164 | match Pin::new(internal_iface).poll_flush(cx) { 165 | Poll::Ready(Ok(())) => (), 166 | Poll::Ready(Err(_)) => { 167 | defunct_indexes.push(*index); 168 | }, 169 | Poll::Pending => { 170 | any_pending = true; 171 | }, 172 | } 173 | } 174 | for index in defunct_indexes { 175 | self.internal_ifaces.remove(&index).unwrap(); 176 | } 177 | if any_pending { 178 | Poll::Pending 179 | } else { 180 | Poll::Ready(()) 181 | } 182 | } 183 | 184 | fn poll_ready_outgoing(&mut self, cx: &mut task::Context) -> Poll<()> { 185 | match self.poll_flush_outgoing(cx) { 186 | Poll::Ready(()) => return Poll::Ready(()), 187 | Poll::Pending => (), 188 | } 189 | 190 | let mut any_pending = false; 191 | 192 | match &mut self.external_iface_opt { 193 | None => (), 194 | Some(external_iface) => { 195 | match Pin::new(external_iface).poll_ready(cx) { 196 | Poll::Ready(Ok(())) => (), 197 | Poll::Ready(Err(_)) => { 198 | self.external_iface_opt = None; 199 | }, 200 | Poll::Pending => { 201 | any_pending = true; 202 | }, 203 | } 204 | }, 205 | } 206 | 207 | let mut defunct_indexes = Vec::new(); 208 | for (index, internal_iface) in &mut self.internal_ifaces { 209 | match Pin::new(internal_iface).poll_ready(cx) { 210 | Poll::Ready(Ok(())) => (), 211 | Poll::Ready(Err(_)) => { 212 | defunct_indexes.push(*index); 213 | }, 214 | Poll::Pending => { 215 | any_pending = true; 216 | }, 217 | } 218 | } 219 | for index in defunct_indexes { 220 | self.internal_ifaces.remove(&index).unwrap(); 221 | } 222 | if any_pending { 223 | Poll::Pending 224 | } else { 225 | Poll::Ready(()) 226 | } 227 | } 228 | 229 | fn poll_next_incoming_external(&mut self, cx: &mut task::Context) -> Poll> { 230 | match &mut self.external_iface_opt { 231 | None => Poll::Pending, 232 | Some(external_iface) => { 233 | match Pin::new(external_iface).poll_next(cx) { 234 | Poll::Ready(Some(Ok(packet))) => Poll::Ready(packet), 235 | Poll::Ready(Some(Err(_))) | Poll::Ready(None) => { 236 | self.external_iface_opt = None; 237 | Poll::Pending 238 | }, 239 | Poll::Pending => Poll::Pending, 240 | } 241 | }, 242 | } 243 | } 244 | 245 | fn poll_next_incoming_internal(&mut self, cx: &mut task::Context) -> Poll<(usize, Box)> { 246 | let mut defunct_indexes = Vec::new(); 247 | let mut index_packet_opt = None; 248 | for (index, internal_iface) in &mut self.internal_ifaces { 249 | match Pin::new(internal_iface).poll_next(cx) { 250 | Poll::Ready(Some(Ok(packet))) => { 251 | index_packet_opt = Some((*index, packet)); 252 | break; 253 | }, 254 | Poll::Ready(Some(Err(_))) | Poll::Ready(None) => { 255 | defunct_indexes.push(*index); 256 | }, 257 | Poll::Pending => (), 258 | } 259 | } 260 | for index in defunct_indexes { 261 | self.internal_ifaces.remove(&index).unwrap(); 262 | } 263 | match index_packet_opt { 264 | Some((index, packet)) => Poll::Ready((index, packet)), 265 | None => Poll::Pending, 266 | } 267 | } 268 | 269 | fn dispatch_incoming_external(&mut self, packet: Box) { 270 | if log_enabled!(Level::Debug) { 271 | debug!("{}: received from external iface: {:?}", self.external_ipv4, packet); 272 | } 273 | 274 | match packet.version_box() { 275 | IpPacketVersion::V6(_) => (), 276 | IpPacketVersion::V4(packet) => { 277 | if packet.destination_addr() != self.external_ipv4 { 278 | debug!( 279 | "{}: dropping external packet addressed to different ip {}", 280 | self.external_ipv4, packet.destination_addr(), 281 | ); 282 | return; 283 | } 284 | match packet.protocol_box() { 285 | Ipv4PacketProtocol::Tcp(mut packet) => { 286 | let port = packet.destination_port(); 287 | let mapped_addr_opt = if self.tcpv4_restrictions.incoming_allowed(port, packet.source_addr()) { 288 | self.tcpv4_port_map.incoming_addr(port) 289 | } else { 290 | None 291 | }; 292 | let mapped_addr = match mapped_addr_opt { 293 | Some(mapped_addr) => mapped_addr, 294 | None => { 295 | if self.reply_with_rst_to_unexpected_tcp_packets { 296 | let mut rst_packet = Tcpv4Packet::new(); 297 | rst_packet.set_flags(TcpPacketFlags { 298 | rst: true, 299 | ack: true, 300 | .. TcpPacketFlags::default() 301 | }); 302 | rst_packet.set_source_addr(packet.destination_addr()); 303 | rst_packet.set_destination_addr(packet.source_addr()); 304 | rst_packet.set_ack_number(packet.seq_number().wrapping_add(1)); 305 | match &mut self.external_iface_opt { 306 | None => (), 307 | Some(external_iface) => { 308 | match Pin::new(external_iface).start_send(rst_packet.ip_packet_box()) { 309 | Ok(()) => (), 310 | Err(_) => { 311 | self.external_iface_opt = None; 312 | }, 313 | } 314 | }, 315 | } 316 | } 317 | debug!( 318 | "{}: dropping external packet addressed to unmapped or disallowed port {}", 319 | self.external_ipv4, packet.destination_addr(), 320 | ); 321 | return; 322 | }, 323 | }; 324 | let iface_index = match self.internal_addr_indexes.get(&IpAddr::V4(*mapped_addr.ip())) { 325 | Some(iface_index) => iface_index, 326 | None => return, 327 | }; 328 | let internal_iface = match self.internal_ifaces.get_mut(iface_index) { 329 | Some(internal_iface) => internal_iface, 330 | None => return, 331 | }; 332 | packet.set_destination_addr(mapped_addr); 333 | if log_enabled!(Level::Debug) { 334 | debug!( 335 | "{}: forwarding translated packet on internal iface #{} {:?}", 336 | self.external_ipv4, 337 | iface_index, 338 | packet, 339 | ); 340 | } 341 | match Pin::new(internal_iface).start_send(packet.ip_packet_box()) { 342 | Ok(()) => (), 343 | Err(_) => { 344 | self.internal_ifaces.remove(iface_index); 345 | }, 346 | } 347 | }, 348 | Ipv4PacketProtocol::Udp(mut packet) => { 349 | let port = packet.destination_port(); 350 | let mapped_addr_opt = if self.udpv4_restrictions.incoming_allowed(port, packet.source_addr()) { 351 | self.udpv4_port_map.incoming_addr(port) 352 | } else { 353 | None 354 | }; 355 | let mapped_addr = match mapped_addr_opt { 356 | Some(mapped_addr) => mapped_addr, 357 | None => { 358 | debug!( 359 | "{}: dropping external packet addressed to unmapped or disallowed port {}", 360 | self.external_ipv4, packet.destination_addr(), 361 | ); 362 | return; 363 | }, 364 | }; 365 | let iface_index = match self.internal_addr_indexes.get(&IpAddr::V4(*mapped_addr.ip())) { 366 | Some(iface_index) => iface_index, 367 | None => return, 368 | }; 369 | let internal_iface = match self.internal_ifaces.get_mut(iface_index) { 370 | Some(internal_iface) => internal_iface, 371 | None => return, 372 | }; 373 | packet.set_destination_addr(mapped_addr); 374 | if log_enabled!(Level::Debug) { 375 | debug!( 376 | "{}: forwarding translated packet on internal iface #{} {:?}", 377 | self.external_ipv4, 378 | iface_index, 379 | packet, 380 | ); 381 | } 382 | match Pin::new(internal_iface).start_send(packet.ip_packet_box()) { 383 | Ok(()) => (), 384 | Err(_) => { 385 | self.internal_ifaces.remove(iface_index); 386 | }, 387 | } 388 | }, 389 | Ipv4PacketProtocol::Icmp(_) => (), 390 | Ipv4PacketProtocol::Unknown { .. } => (), 391 | } 392 | }, 393 | } 394 | } 395 | 396 | fn dispatch_incoming_internal(&mut self, iface_index: usize, packet: Box) { 397 | if log_enabled!(Level::Debug) { 398 | debug!( 399 | "{}: received on internal iface #{}: {:?}", 400 | self.external_ipv4, 401 | iface_index, 402 | packet, 403 | ); 404 | } 405 | 406 | match packet.version_box() { 407 | IpPacketVersion::V6(packet) => { 408 | self.internal_addr_indexes.insert(IpAddr::V6(packet.source_addr()), iface_index); 409 | }, 410 | IpPacketVersion::V4(packet) => { 411 | if !self.internal_ipv4_network.contains(packet.source_addr()) { 412 | debug!( 413 | "{}: dropping internal packet from wrong network {}, {}", 414 | self.external_ipv4, packet.source_addr(), self.internal_ipv4_network, 415 | ); 416 | return; 417 | } 418 | self.internal_addr_indexes.insert(IpAddr::V4(packet.source_addr()), iface_index); 419 | let destination_ip = packet.destination_addr(); 420 | if self.internal_ipv4_network.contains(destination_ip) { 421 | let iface_index = match self.internal_addr_indexes.get(&IpAddr::V4(destination_ip)) { 422 | Some(iface_index) => iface_index, 423 | None => { 424 | debug!( 425 | "{}: dropping internal packet addressed to unknown internal device {}", 426 | self.external_ipv4, packet.destination_addr(), 427 | ); 428 | return; 429 | }, 430 | }; 431 | let internal_iface = match self.internal_ifaces.get_mut(iface_index) { 432 | Some(internal_iface) => internal_iface, 433 | None => return, 434 | }; 435 | match Pin::new(internal_iface).start_send(packet.ip_packet_box()) { 436 | Ok(()) => (), 437 | Err(_) => { 438 | self.internal_ifaces.remove(iface_index); 439 | }, 440 | } 441 | } else { 442 | match packet.protocol_box() { 443 | Ipv4PacketProtocol::Tcp(mut packet) => { 444 | let internal_addr = packet.source_addr(); 445 | let port = self.tcpv4_port_map.outgoing_port(internal_addr); 446 | self.tcpv4_restrictions.sending(port, packet.destination_addr()); 447 | packet.set_source_addr(SocketAddrV4::new(self.external_ipv4, port)); 448 | if log_enabled!(Level::Debug) { 449 | debug!( 450 | "{}: translated outgoing packet {:?}", 451 | self.external_ipv4, 452 | packet, 453 | ); 454 | } 455 | if *packet.destination_addr().ip() == self.external_ipv4 { 456 | if self.hair_pinning { 457 | self.dispatch_incoming_external(packet.ip_packet_box()); 458 | } else { 459 | debug!( 460 | "{}: dropped internal packet from {} addressed to own external address {} since hair-pinning is disabled", 461 | self.external_ipv4, packet.source_addr(), packet.destination_addr(), 462 | ); 463 | } 464 | } else { 465 | match &mut self.external_iface_opt { 466 | None => (), 467 | Some(external_iface) => { 468 | match Pin::new(external_iface).start_send(packet.ip_packet_box()) { 469 | Ok(()) => (), 470 | Err(_) => { 471 | self.external_iface_opt = None; 472 | }, 473 | } 474 | }, 475 | } 476 | } 477 | }, 478 | Ipv4PacketProtocol::Udp(mut packet) => { 479 | let internal_addr = packet.source_addr(); 480 | let port = self.udpv4_port_map.outgoing_port(internal_addr); 481 | self.udpv4_restrictions.sending(port, packet.destination_addr()); 482 | packet.set_source_addr(SocketAddrV4::new(self.external_ipv4, port)); 483 | if log_enabled!(Level::Debug) { 484 | debug!( 485 | "{}: translated outgoing packet {:?}", 486 | self.external_ipv4, 487 | packet, 488 | ); 489 | } 490 | if *packet.destination_addr().ip() == self.external_ipv4 { 491 | if self.hair_pinning { 492 | self.dispatch_incoming_external(packet.ip_packet_box()); 493 | } else { 494 | debug!( 495 | "{}: dropped internal packet from {} addressed to own external address {} since hair-pinning is disabled", 496 | self.external_ipv4, packet.source_addr(), packet.destination_addr(), 497 | ); 498 | } 499 | } else { 500 | match &mut self.external_iface_opt { 501 | None => (), 502 | Some(external_iface) => { 503 | match Pin::new(external_iface).start_send(packet.ip_packet_box()) { 504 | Ok(()) => (), 505 | Err(_) => { 506 | self.external_iface_opt = None; 507 | }, 508 | } 509 | }, 510 | } 511 | } 512 | }, 513 | Ipv4PacketProtocol::Icmp(_) => (), 514 | Ipv4PacketProtocol::Unknown { .. } => (), 515 | } 516 | } 517 | }, 518 | } 519 | } 520 | 521 | fn poll_inner(&mut self, cx: &mut task::Context) -> Poll<()> { 522 | loop { 523 | match Pin::new(&mut self.iface_receiver).poll_next(cx) { 524 | Poll::Ready(Some(iface)) => { 525 | self.internal_ifaces.insert(self.next_internal_iface_index, iface); 526 | self.next_internal_iface_index += 1; 527 | }, 528 | Poll::Ready(None) => return Poll::Ready(()), 529 | Poll::Pending => break, 530 | } 531 | } 532 | 533 | loop { 534 | match self.poll_ready_outgoing(cx) { 535 | Poll::Ready(()) => (), 536 | Poll::Pending => return Poll::Pending, 537 | } 538 | 539 | match self.poll_next_incoming_external(cx) { 540 | Poll::Ready(packet) => { 541 | self.dispatch_incoming_external(packet); 542 | continue; 543 | }, 544 | Poll::Pending => (), 545 | } 546 | 547 | match self.poll_next_incoming_internal(cx) { 548 | Poll::Ready((index, packet)) => { 549 | self.dispatch_incoming_internal(index, packet); 550 | continue; 551 | }, 552 | Poll::Pending => (), 553 | } 554 | 555 | break Poll::Pending; 556 | } 557 | } 558 | } 559 | 560 | impl Future for NatTask { 561 | type Output = (); 562 | 563 | fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> Poll<()> { 564 | let this = self.get_mut(); 565 | this.poll_inner(cx) 566 | } 567 | } 568 | 569 | -------------------------------------------------------------------------------- /src/device/nat/port_map.rs: -------------------------------------------------------------------------------- 1 | use crate::priv_prelude::*; 2 | 3 | pub struct PortMap { 4 | outgoing_map: HashMap, 5 | incoming_map: HashMap, 6 | next_port: u16, 7 | } 8 | 9 | impl PortMap { 10 | const INITIAL_PORT: u16 = 1025; 11 | 12 | pub fn new() -> PortMap { 13 | PortMap { 14 | outgoing_map: HashMap::new(), 15 | incoming_map: HashMap::new(), 16 | next_port: PortMap::INITIAL_PORT, 17 | } 18 | } 19 | 20 | pub fn outgoing_port(&mut self, internal_addr: SocketAddrV4) -> u16 { 21 | match self.outgoing_map.entry(internal_addr) { 22 | hash_map::Entry::Occupied(entry) => *entry.get(), 23 | hash_map::Entry::Vacant(entry) => { 24 | let mut attempts = PortMap::INITIAL_PORT; 25 | let port = loop { 26 | let port = self.next_port; 27 | self.next_port = { 28 | self.next_port.checked_add(1).unwrap_or(PortMap::INITIAL_PORT) 29 | }; 30 | match self.incoming_map.entry(port) { 31 | hash_map::Entry::Occupied(mut entry) => { 32 | attempts = match attempts.checked_add(1) { 33 | Some(attempts) => attempts, 34 | None => { 35 | entry.insert(internal_addr); 36 | break port; 37 | }, 38 | }; 39 | }, 40 | hash_map::Entry::Vacant(entry) => { 41 | entry.insert(internal_addr); 42 | break port; 43 | }, 44 | } 45 | }; 46 | *entry.insert(port) 47 | }, 48 | } 49 | } 50 | 51 | pub fn incoming_addr(&self, port: u16) -> Option { 52 | self.incoming_map.get(&port).copied() 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /src/device/nat/restrictions.rs: -------------------------------------------------------------------------------- 1 | use crate::priv_prelude::*; 2 | 3 | pub enum Restrictions { 4 | Unrestricted, 5 | RestrictIpAddr { 6 | sent_to: HashMap>, 7 | }, 8 | RestrictSocketAddr { 9 | sent_to: HashMap>, 10 | }, 11 | } 12 | 13 | impl Restrictions { 14 | pub fn sending(&mut self, external_port: u16, destination_addr: SocketAddrV4) { 15 | match self { 16 | Restrictions::Unrestricted => (), 17 | Restrictions::RestrictIpAddr { sent_to } => { 18 | sent_to.entry(external_port).or_default().insert(*destination_addr.ip()); 19 | }, 20 | Restrictions::RestrictSocketAddr { sent_to } => { 21 | sent_to.entry(external_port).or_default().insert(destination_addr); 22 | }, 23 | } 24 | } 25 | 26 | pub fn incoming_allowed(&self, external_port: u16, source_addr: SocketAddrV4) -> bool { 27 | match self { 28 | Restrictions::Unrestricted => true, 29 | Restrictions::RestrictIpAddr { sent_to } => { 30 | match sent_to.get(&external_port) { 31 | None => false, 32 | Some(ipv4_addrs) => ipv4_addrs.contains(source_addr.ip()), 33 | } 34 | }, 35 | Restrictions::RestrictSocketAddr { sent_to } => { 36 | match sent_to.get(&external_port) { 37 | None => false, 38 | Some(socket_addrs) => socket_addrs.contains(&source_addr), 39 | } 40 | }, 41 | } 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /src/iface/configure.rs: -------------------------------------------------------------------------------- 1 | use crate::priv_prelude::*; 2 | 3 | pub(crate) fn put_up(iface_name: &str) -> io::Result<()> { 4 | unsafe { 5 | let mut req = new_req(iface_name); 6 | let fd = socket(libc::AF_INET, libc::SOCK_DGRAM, 0)?; 7 | if ioctl::siocgifflags(fd.as_raw_fd(), &mut req) < 0 { 8 | return Err(io::Error::last_os_error()); 9 | } 10 | 11 | req.ifr_ifru.ifru_flags |= (libc::IFF_UP as u32 | libc::IFF_RUNNING as u32) as i16; 12 | 13 | if ioctl::siocsifflags(fd.as_raw_fd(), &req) < 0 { 14 | return Err(io::Error::last_os_error()); 15 | } 16 | } 17 | 18 | Ok(()) 19 | } 20 | 21 | pub(crate) fn set_ipv4_addr( 22 | iface_name: &str, 23 | ipv4_addr: Ipv4Addr, 24 | subnet_mask_bits: u8, 25 | ) -> io::Result<()> { 26 | let mask = if subnet_mask_bits == 32 { !0u32 } else { !(!0 >> subnet_mask_bits) }; 27 | let mut req = new_req(iface_name); 28 | let fd = socket(libc::AF_INET, libc::SOCK_DGRAM, 0)?; 29 | 30 | unsafe { 31 | { 32 | let addr = &mut req.ifr_ifru.ifru_addr; 33 | let addr = addr as *mut libc::sockaddr; 34 | let addr = addr as *mut libc::sockaddr_in; 35 | let addr = &mut *addr; 36 | addr.sin_family = libc::AF_INET as libc::sa_family_t; 37 | addr.sin_port = 0; 38 | addr.sin_addr.s_addr = u32::from(ipv4_addr).to_be(); 39 | } 40 | 41 | if ioctl::siocsifaddr(fd.as_raw_fd(), &req) < 0 { 42 | return Err(io::Error::last_os_error()); 43 | } 44 | 45 | { 46 | let addr = &mut req.ifr_ifru.ifru_addr; 47 | let addr = addr as *mut libc::sockaddr; 48 | let addr = addr as *mut libc::sockaddr_in; 49 | let addr = &mut *addr; 50 | addr.sin_family = libc::AF_INET as libc::sa_family_t; 51 | addr.sin_port = 0; 52 | addr.sin_addr.s_addr = mask.to_be(); 53 | } 54 | 55 | if ioctl::siocsifnetmask(fd.as_raw_fd(), &req) < 0 { 56 | return Err(io::Error::last_os_error()); 57 | } 58 | } 59 | 60 | Ok(()) 61 | } 62 | 63 | fn round_up( 64 | x: usize, 65 | multiple: usize, 66 | ) -> usize { 67 | match x % multiple { 68 | 0 => x, 69 | r => x + (multiple - r), 70 | } 71 | } 72 | 73 | pub(crate) fn set_ipv6_addr( 74 | iface_name: &str, 75 | ipv6_addr: Ipv6Addr, 76 | subnet_mask_bits: u8, 77 | ) -> io::Result<()> { 78 | let mut req = new_req(iface_name); 79 | let fd = socket(libc::AF_INET, libc::SOCK_DGRAM, 0)?; 80 | 81 | unsafe { 82 | if ioctl::siocgifindex(fd.as_raw_fd(), &mut req) < 0 { 83 | return Err(io::Error::last_os_error()); 84 | } 85 | } 86 | 87 | let index = unsafe { 88 | req.ifr_ifru.ifru_ifindex as u32 89 | }; 90 | 91 | let netlink = socket(libc::AF_NETLINK, libc::SOCK_RAW, libc::NETLINK_ROUTE)?; 92 | 93 | let header_start = 0; 94 | let header_end = header_start + mem::size_of::(); 95 | let data_start = round_up(header_end, sys::NLMSG_ALIGNTO); 96 | let data_end = data_start + mem::size_of::(); 97 | let attr_header_start = round_up(data_end, sys::NLMSG_ALIGNTO); 98 | let attr_header_end = attr_header_start + mem::size_of::(); 99 | let attr_data_start = round_up(attr_header_end, sys::NLMSG_ALIGNTO); 100 | let attr_data_end = attr_data_start + mem::size_of::<[u8; 16]>(); 101 | let total_size = attr_data_end; 102 | 103 | let mut buffer: Vec = Vec::with_capacity(total_size); 104 | let nlmsghdr = libc::nlmsghdr { 105 | nlmsg_len: total_size as u32, 106 | nlmsg_type: libc::RTM_NEWADDR, 107 | nlmsg_flags: { 108 | libc::NLM_F_REPLACE | 109 | libc::NLM_F_CREATE | 110 | libc::NLM_F_REQUEST | // TODO: do I need this one? 111 | libc::NLM_F_ACK 112 | } as u16, 113 | nlmsg_seq: 0, 114 | nlmsg_pid: 0, 115 | }; 116 | unsafe { 117 | ptr::write( 118 | buffer.as_mut_ptr() as *mut _, 119 | nlmsghdr, 120 | ) 121 | } 122 | 123 | let ifaddrmsg = sys::ifaddrmsg { 124 | ifa_family: libc::AF_INET6 as u8, 125 | ifa_prefixlen: subnet_mask_bits, 126 | ifa_flags: 0, // TODO: use IFA_F_PERMANENT here? 127 | ifa_scope: 0, 128 | ifa_index: index, 129 | }; 130 | unsafe { 131 | ptr::write( 132 | buffer.as_mut_ptr().add(data_start) as *mut _, 133 | ifaddrmsg, 134 | ) 135 | } 136 | 137 | let rtattr = sys::rtattr { 138 | rta_len: (attr_data_end - attr_header_start) as u16, 139 | rta_type: libc::IFA_ADDRESS, 140 | }; 141 | unsafe { 142 | ptr::write( 143 | buffer.as_mut_ptr().add(attr_header_start) as *mut _, 144 | rtattr, 145 | ) 146 | } 147 | 148 | let addr = ipv6_addr.octets(); 149 | unsafe { 150 | ptr::write( 151 | buffer.as_mut_ptr().add(attr_data_start) as *mut _, 152 | addr, 153 | ) 154 | } 155 | 156 | let n = unsafe { 157 | libc::write(netlink.as_raw_fd(), buffer.as_ptr() as *const _, total_size) 158 | }; 159 | if n < 0 { 160 | return Err(io::Error::last_os_error()); 161 | } 162 | assert_eq!(n as usize, total_size); 163 | 164 | 165 | let header_start = 0; 166 | let header_end = header_start + mem::size_of::(); 167 | let error_start = round_up(header_end, sys::NLMSG_ALIGNTO); 168 | let error_end = error_start + mem::size_of::(); 169 | let total_size = error_end; 170 | 171 | let mut buffer: Vec = Vec::with_capacity(total_size); 172 | loop { 173 | let n = unsafe { 174 | libc::read(netlink.as_raw_fd(), buffer.as_mut_ptr() as *mut _, total_size) 175 | }; 176 | if n < 0 { 177 | return Err(io::Error::last_os_error()); 178 | } 179 | assert!(n as usize >= header_end); 180 | let nlmsghdr: *const libc::nlmsghdr = buffer.as_ptr() as *const _; 181 | let nlmsghdr: &libc::nlmsghdr = unsafe { &*nlmsghdr }; 182 | if nlmsghdr.nlmsg_type == libc::NLMSG_NOOP as u16 { 183 | continue; 184 | } 185 | assert_eq!(nlmsghdr.nlmsg_type, libc::NLMSG_ERROR as u16); 186 | assert_eq!(n as usize, total_size); 187 | 188 | let nlmsgerr: *const libc::nlmsgerr = unsafe { 189 | buffer.as_ptr().add(error_start) as *const _ 190 | }; 191 | let nlmsgerr: &libc::nlmsgerr = unsafe { &*nlmsgerr }; 192 | if nlmsgerr.error != 0 { 193 | return Err(io::Error::from_raw_os_error(-nlmsgerr.error)); 194 | } 195 | 196 | break; 197 | } 198 | Ok(()) 199 | } 200 | 201 | pub(crate) fn add_ipv4_route( 202 | iface_name: &str, 203 | destination: Ipv4Network, 204 | gateway_opt: Option, 205 | ) -> io::Result<()> { 206 | let fd = socket(libc::AF_INET, libc::SOCK_DGRAM, 0)?; 207 | 208 | let mut route: libc::rtentry = unsafe { 209 | mem::zeroed() 210 | }; 211 | 212 | unsafe { 213 | let route_destination = &mut route.rt_dst as *mut _ as *mut libc::sockaddr_in; 214 | (*route_destination).sin_family = libc::AF_INET as u16; 215 | (*route_destination).sin_addr = libc::in_addr { s_addr: u32::from(destination.base_addr()).to_be() }; 216 | }; 217 | 218 | let netmask = Ipv4Addr::from(!((!0u32).checked_shr(u32::from(destination.subnet_mask_bits())).unwrap_or(0))); 219 | unsafe { 220 | let route_genmask = &mut route.rt_genmask as *mut _ as *mut libc::sockaddr_in; 221 | (*route_genmask).sin_family = libc::AF_INET as u16; 222 | (*route_genmask).sin_addr = libc::in_addr { s_addr: u32::from(netmask).to_be() }; 223 | }; 224 | 225 | route.rt_flags = libc::RTF_UP; 226 | if let Some(gateway_addr) = gateway_opt { 227 | unsafe { 228 | let route_gateway = &mut route.rt_gateway as *mut _ as *mut libc::sockaddr_in; 229 | (*route_gateway).sin_family = libc::AF_INET as u16; 230 | (*route_gateway).sin_addr = libc::in_addr { s_addr: u32::from(gateway_addr).to_be() }; 231 | }; 232 | 233 | route.rt_flags |= libc::RTF_GATEWAY; 234 | } 235 | 236 | let c_iface_name = CString::new(iface_name).unwrap(); 237 | 238 | // TODO: This doesn't *actually* need to mutable yeah? 239 | route.rt_dev = c_iface_name.as_ptr() as *mut _; 240 | 241 | let res = unsafe { 242 | libc::ioctl(fd.as_raw_fd(), libc::SIOCADDRT, &route) 243 | }; 244 | if res < 0 { 245 | return Err(io::Error::last_os_error()); 246 | } 247 | 248 | Ok(()) 249 | } 250 | 251 | pub(crate) fn add_ipv6_route( 252 | iface_name: &str, 253 | destination: Ipv6Network, 254 | next_hop: Ipv6Addr, 255 | ) -> io::Result<()> { 256 | let fd = socket(libc::PF_INET6, libc::SOCK_DGRAM, libc::IPPROTO_IP)?; 257 | let mut route: libc::rtentry = unsafe { 258 | mem::zeroed() 259 | }; 260 | 261 | fn as_sockaddr_in6(ptr: &mut libc::sockaddr) -> &mut libc::sockaddr_in6 { 262 | unsafe { mem::transmute(ptr) } 263 | } 264 | 265 | let rt_dst = as_sockaddr_in6(&mut route.rt_dst); 266 | rt_dst.sin6_family = libc::AF_INET6 as u16; 267 | rt_dst.sin6_addr = libc::in6_addr { 268 | s6_addr: destination.base_addr().octets(), 269 | }; 270 | 271 | let netmask = Ipv6Addr::from(!((!0u128).checked_shr(u32::from(destination.subnet_mask_bits())).unwrap_or(0))); 272 | let rt_genmask = as_sockaddr_in6(&mut route.rt_genmask); 273 | rt_genmask.sin6_family = libc::AF_INET6 as u16; 274 | rt_genmask.sin6_addr = libc::in6_addr { 275 | s6_addr: netmask.octets(), 276 | }; 277 | 278 | route.rt_flags = libc::RTF_UP; 279 | let rt_gateway = as_sockaddr_in6(&mut route.rt_gateway); 280 | rt_gateway.sin6_family = libc::AF_INET6 as u16; 281 | rt_gateway.sin6_addr = libc::in6_addr { 282 | s6_addr: next_hop.octets(), 283 | }; 284 | 285 | let c_iface_name = CString::new(iface_name).unwrap(); 286 | 287 | // TODO: This doesn't *actually* need to mutable yeah? 288 | route.rt_dev = c_iface_name.as_ptr() as *mut _; 289 | 290 | let res = unsafe { 291 | libc::ioctl(fd.as_raw_fd(), libc::SIOCADDRT, &route) 292 | }; 293 | if res < 0 { 294 | return Err(io::Error::last_os_error()); 295 | } 296 | 297 | Ok(()) 298 | } 299 | 300 | 301 | fn new_req(iface_name: &str) -> libc::ifreq { 302 | if iface_name.len() >= libc::IF_NAMESIZE { 303 | panic!("interface name too long"); 304 | } 305 | unsafe { 306 | let mut req: libc::ifreq = mem::zeroed(); 307 | ptr::copy_nonoverlapping( 308 | iface_name.as_ptr(), 309 | req.ifr_name.as_mut_ptr() as *mut _, 310 | iface_name.as_bytes().len(), 311 | ); 312 | req 313 | } 314 | } 315 | 316 | fn socket(domain: libc::c_int, ty: libc::c_int, protocol: libc::c_int) -> io::Result { 317 | let raw_fd = unsafe { 318 | libc::socket(domain, ty, protocol) 319 | }; 320 | if raw_fd < 0 { 321 | return Err(io::Error::last_os_error()); 322 | } 323 | let fd = unsafe { OwnedFd::from_raw_fd(raw_fd) }; 324 | Ok(fd) 325 | } 326 | 327 | -------------------------------------------------------------------------------- /src/iface/create.rs: -------------------------------------------------------------------------------- 1 | use crate::priv_prelude::*; 2 | 3 | struct BuildConfig { 4 | name_opt: Option, 5 | ipv4_addr_subnet_opt: Option<(Ipv4Addr, u8)>, 6 | ipv4_routes: Vec<(Ipv4Network, Option)>, 7 | ipv6_addr_subnet_opt: Option<(Ipv6Addr, u8)>, 8 | ipv6_routes: Vec<(Ipv6Network, Ipv6Addr)>, 9 | } 10 | 11 | /// Builder for adding an IP interface to a [`Machine`](crate::Machine). 12 | /// 13 | /// Once you're done configuring the interface you must `await` the builder to actually add the 14 | /// interface. 15 | pub struct IpIfaceBuilder<'m> { 16 | machine: &'m Machine, 17 | build_config: BuildConfig, 18 | } 19 | 20 | impl IpIfaceBuilder<'_> { 21 | pub(crate) fn new(machine: &Machine) -> IpIfaceBuilder<'_> { 22 | IpIfaceBuilder { 23 | machine, 24 | build_config: BuildConfig { 25 | name_opt: None, 26 | ipv4_addr_subnet_opt: None, 27 | ipv4_routes: Vec::new(), 28 | ipv6_addr_subnet_opt: None, 29 | ipv6_routes: Vec::new(), 30 | }, 31 | } 32 | } 33 | 34 | /// Sets the interface name. Defaults to "netsim" if not set. 35 | pub fn name(mut self, name: impl Into) -> Self { 36 | self.build_config.name_opt = Some(name.into()); 37 | self 38 | } 39 | 40 | /// Sets the interface's IPv4 address. 41 | pub fn ipv4_addr(mut self, ipv4_addr: impl Into) -> Self { 42 | let ipv4_addr = ipv4_addr.into(); 43 | let network = Ipv4Network::infer_from_addr(ipv4_addr); 44 | self.build_config.ipv4_addr_subnet_opt = Some((ipv4_addr, network.subnet_mask_bits())); 45 | self 46 | } 47 | 48 | /// Sets the interface's IPv6 address. 49 | pub fn ipv6_addr(mut self, ipv6_addr: impl Into) -> Self { 50 | let ipv6_addr = ipv6_addr.into(); 51 | let network = Ipv6Network::infer_from_addr(ipv6_addr); 52 | self.build_config.ipv6_addr_subnet_opt = Some((ipv6_addr, network.subnet_mask_bits())); 53 | self 54 | } 55 | 56 | /// Adds an IPv4 route for this interface to the machine's routing table. 57 | pub fn ipv4_route(mut self, destination: Ipv4Network) -> Self { 58 | self.build_config.ipv4_routes.push((destination, None)); 59 | self 60 | } 61 | 62 | /// Adds an IPv4 route to the machine's routing table which forwards through this interface via 63 | /// the specified gateway. 64 | pub fn ipv4_route_with_gateway>( 65 | mut self, 66 | destination: Ipv4Network, 67 | gateway: A, 68 | ) -> Self { 69 | let gateway = gateway.into(); 70 | self.build_config.ipv4_routes.push((destination, Some(gateway))); 71 | self 72 | } 73 | 74 | /// Adds an IPv4 route for this interface to the machine's routing table and sets it as the 75 | /// default route. 76 | pub fn ipv4_default_route(self) -> Self { 77 | self.ipv4_route(Ipv4Network::new(Ipv4Addr::UNSPECIFIED, 0)) 78 | } 79 | 80 | /// Adds an IPv4 route to the machine's routing table which forwards through this interface via 81 | /// the specified gateway. Sets it as the default route. 82 | pub fn ipv4_default_route_with_gateway(self, gateway: Ipv4Addr) -> Self { 83 | self.ipv4_route_with_gateway(Ipv4Network::new(Ipv4Addr::UNSPECIFIED, 0), gateway) 84 | } 85 | 86 | /// Adds an IPv6 route for this interface to the machine's routing table. 87 | pub fn ipv6_route(mut self, destination: Ipv6Network, next_hop: Ipv6Addr) -> Self { 88 | self.build_config.ipv6_routes.push((destination, next_hop)); 89 | self 90 | } 91 | 92 | /// Adds an IPv6 route for this interface to the machine's routing table and sets it as the 93 | /// default route. 94 | pub fn ipv6_default_route(mut self, next_hop: Ipv6Addr) -> Self { 95 | self.build_config.ipv6_routes.push((Ipv6Network::new(Ipv6Addr::UNSPECIFIED, 0), next_hop)); 96 | self 97 | } 98 | 99 | /// Builds the interface. The returned [`IpIface`](crate::IpIface) can be used to send/receive packets 100 | /// to/from this interface. 101 | /// 102 | /// # Panics 103 | /// 104 | /// If there is no tokio runtime present. 105 | pub fn build(self) -> io::Result { 106 | let fd = self.build_raw()?; 107 | IpIface::new(fd) 108 | } 109 | 110 | /// Builds the interface, returning a raw file descriptor to the interface's TUN device. Unlike 111 | /// [`build`](Self::build) this can be called in non-async contexts. 112 | pub fn build_raw(self) -> io::Result { 113 | let IpIfaceBuilder { machine, build_config } = self; 114 | let task = async move { 115 | create_tun_interface(build_config) 116 | }; 117 | let res = machine.spawn(task).join_blocking(); 118 | match res { 119 | Ok(res_opt) => res_opt.unwrap(), 120 | Err(err) => panic::resume_unwind(err), 121 | } 122 | } 123 | } 124 | 125 | fn create_tun_interface(build_config: BuildConfig) -> io::Result { 126 | let BuildConfig { 127 | name_opt, 128 | ipv4_addr_subnet_opt, 129 | ipv4_routes, 130 | ipv6_addr_subnet_opt, 131 | ipv6_routes, 132 | } = build_config; 133 | let name = name_opt.as_deref().unwrap_or("netsim"); 134 | let name_cstr = match CString::new(name) { 135 | Ok(name_cstr) => name_cstr, 136 | Err(err) => { 137 | return Err(io::Error::new(io::ErrorKind::InvalidInput, err)); 138 | }, 139 | }; 140 | 141 | #[allow(clippy::unnecessary_cast)] 142 | if name_cstr.as_bytes_with_nul().len() > libc::IF_NAMESIZE as usize { 143 | return Err(io::Error::new(io::ErrorKind::InvalidInput, "name too long")); 144 | } 145 | 146 | let fd = { 147 | let raw_fd = unsafe { 148 | libc::open(c"/dev/net/tun".as_ptr() as *const _, libc::O_RDWR) 149 | }; 150 | if raw_fd < 0 { 151 | let err = io::Error::last_os_error(); 152 | return Err(io::Error::new(err.kind(), "opening /dev/net/tun")); 153 | } 154 | unsafe { 155 | OwnedFd::from_raw_fd(raw_fd) 156 | } 157 | }; 158 | let flags = unsafe { 159 | libc::fcntl(fd.as_raw_fd(), libc::F_GETFL, 0) 160 | }; 161 | if flags < 0 { 162 | let err = io::Error::last_os_error(); 163 | return Err(io::Error::new(err.kind(), "calling fcntl(F_GETFL) on /dev/net/tun")); 164 | } 165 | let res = unsafe { 166 | libc::fcntl(fd.as_raw_fd(), libc::F_SETFL, flags | libc::O_NONBLOCK) 167 | }; 168 | if res < 0 { 169 | let err = io::Error::last_os_error(); 170 | return Err(io::Error::new(err.kind(), "calling fcntl(F_SETFL) on /dev/net/tun")); 171 | } 172 | let mut req = unsafe { 173 | let mut req: libc::ifreq = mem::zeroed(); 174 | ptr::copy_nonoverlapping( 175 | name_cstr.as_ptr(), 176 | req.ifr_name.as_mut_ptr(), 177 | name_cstr.as_bytes().len(), 178 | ); 179 | req.ifr_ifru.ifru_flags = libc::IFF_NO_PI as i16; 180 | req.ifr_ifru.ifru_flags |= libc::IFF_TUN as i16; 181 | req 182 | }; 183 | let res = unsafe { 184 | ioctl::tunsetiff(fd.as_raw_fd(), &mut req as *mut _ as *mut _) 185 | }; 186 | if res < 0 { 187 | let err = io::Error::last_os_error(); 188 | return Err(io::Error::new(err.kind(), "calling ioctl(TUNSETIFF) failed")); 189 | }; 190 | let real_name = { 191 | let name = &req.ifr_name[..]; 192 | let name = match name.iter().position(|b| *b == 0) { 193 | Some(p) => &name[..p], 194 | None => name, 195 | }; 196 | let name = unsafe { 197 | slice::from_raw_parts(name.as_ptr() as *const _, name.len()) 198 | }; 199 | let name = match std::str::from_utf8(name) { 200 | Ok(name) => name, 201 | Err(err) => { 202 | return Err(io::Error::new(io::ErrorKind::InvalidData, err)); 203 | }, 204 | }; 205 | name.to_owned() 206 | }; 207 | 208 | 209 | if let Some((ipv4_addr, subnet_mask_bits)) = ipv4_addr_subnet_opt { 210 | iface::configure::set_ipv4_addr(&real_name, ipv4_addr, subnet_mask_bits)?; 211 | } 212 | if let Some((ipv6_addr, subnet_mask_bits)) = ipv6_addr_subnet_opt { 213 | iface::configure::set_ipv6_addr(&real_name, ipv6_addr, subnet_mask_bits)?; 214 | } 215 | 216 | iface::configure::put_up(&real_name)?; 217 | 218 | for (destination, gateway_opt) in ipv4_routes { 219 | iface::configure::add_ipv4_route(&real_name, destination, gateway_opt)?; 220 | } 221 | for (destination, next_hop) in ipv6_routes { 222 | iface::configure::add_ipv6_route(&real_name, destination, next_hop)?; 223 | } 224 | 225 | Ok(fd) 226 | } 227 | 228 | -------------------------------------------------------------------------------- /src/iface/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod create; 2 | pub mod configure; 3 | pub mod stream; 4 | -------------------------------------------------------------------------------- /src/iface/stream.rs: -------------------------------------------------------------------------------- 1 | use crate::priv_prelude::*; 2 | 3 | /// A handle to a network interface of a [`Machine`](crate::Machine). 4 | /// 5 | /// This can be dropped to remove the interface. It also implements `Stream`+`Sink` in order to 6 | /// read/write packets to the interface. That is, packets sent by tasks executing on the 7 | /// corresponding machine will arrive on this `IpIface`'s `Stream` implementation (if they left the 8 | /// machine via that interface) and you can write packets to the `IpIface`'s `Sink` implementation 9 | /// to make packets arrive on that interface. 10 | pub struct IpIface { 11 | fd: AsyncFd, 12 | incoming_bytes: BytesMut, 13 | send_packet_opt: Option>, 14 | } 15 | 16 | impl IpIface { 17 | pub(crate) fn new(fd: OwnedFd) -> io::Result { 18 | let fd = AsyncFd::new(fd)?; 19 | let iface = IpIface { 20 | fd, 21 | incoming_bytes: BytesMut::new(), 22 | send_packet_opt: None, 23 | }; 24 | Ok(iface) 25 | } 26 | } 27 | 28 | impl Sink> for IpIface { 29 | type Error = io::Error; 30 | 31 | fn poll_ready( 32 | self: Pin<&mut Self>, 33 | cx: &mut task::Context<'_>, 34 | ) -> Poll> { 35 | Self::poll_flush(self, cx) 36 | } 37 | 38 | fn start_send(self: Pin<&mut Self>, item: Box) -> io::Result<()> { 39 | let this = self.get_mut(); 40 | let send_packet_opt = this.send_packet_opt.replace(item); 41 | assert!(send_packet_opt.is_none()); 42 | Ok(()) 43 | } 44 | 45 | fn poll_flush( 46 | self: Pin<&mut Self>, 47 | cx: &mut task::Context<'_>, 48 | ) -> Poll> { 49 | let this = self.get_mut(); 50 | let packet = match this.send_packet_opt.take() { 51 | Some(packet) => packet, 52 | None => return Poll::Ready(Ok(())), 53 | }; 54 | if packet.len() > 1500 { 55 | return Poll::Ready(Ok(())); 56 | } 57 | loop { 58 | let mut guard = ready!(this.fd.poll_write_ready(cx))?; 59 | match guard.try_io(|fd| { 60 | let res = unsafe { 61 | libc::write( 62 | fd.as_raw_fd(), 63 | packet.as_bytes().as_ptr() as *const libc::c_void, 64 | packet.len(), 65 | ) 66 | }; 67 | if res < 0 { 68 | let err = io::Error::last_os_error(); 69 | return Err(err); 70 | } 71 | Ok(res as usize) 72 | }) { 73 | Ok(Ok(n)) => { 74 | assert_eq!(n, packet.len()); 75 | return Poll::Ready(Ok(())); 76 | }, 77 | Ok(Err(err)) => return Poll::Ready(Err(err)), 78 | Err(_would_block) => continue, 79 | } 80 | } 81 | } 82 | 83 | fn poll_close( 84 | self: Pin<&mut Self>, 85 | cx: &mut task::Context<'_>, 86 | ) -> Poll> { 87 | Self::poll_flush(self, cx) 88 | } 89 | } 90 | 91 | impl Stream for IpIface { 92 | type Item = io::Result>; 93 | 94 | fn poll_next( 95 | self: Pin<&mut Self>, 96 | cx: &mut task::Context<'_>, 97 | ) -> Poll>>> { 98 | let this = self.get_mut(); 99 | loop { 100 | let mut guard = ready!(this.fd.poll_read_ready(cx))?; 101 | this.incoming_bytes.reserve(libc::ETH_FRAME_LEN as usize); 102 | let buffer = this.incoming_bytes.spare_capacity_mut(); 103 | match guard.try_io(|fd| { 104 | let res = unsafe { 105 | libc::read( 106 | fd.as_raw_fd(), 107 | buffer.as_mut_ptr() as *mut libc::c_void, 108 | buffer.len(), 109 | ) 110 | }; 111 | if res < 0 { 112 | let err = io::Error::last_os_error(); 113 | return Err(err); 114 | } 115 | Ok(res as usize) 116 | }) { 117 | Ok(Ok(n)) => { 118 | if n == 0 { 119 | return Poll::Ready(None); 120 | } else { 121 | assert_eq!(this.incoming_bytes.len(), 0); 122 | unsafe { 123 | this.incoming_bytes.set_len(n); 124 | } 125 | let data = this.incoming_bytes.split(); 126 | let packet = IpPacket::new_box(Box::from(&data[..])); 127 | return Poll::Ready(Some(Ok(packet))); 128 | } 129 | }, 130 | Ok(Err(err)) => return Poll::Ready(Some(Err(err))), 131 | Err(_would_block) => continue, 132 | } 133 | } 134 | } 135 | } 136 | 137 | /// Helper trait for types which are both `Sink`s and `Stream`s of `Box`. 138 | pub trait IpSinkStream: 139 | Stream>> + Sink, Error = io::Error> + Send + 'static 140 | {} 141 | 142 | impl IpSinkStream for T 143 | where 144 | T: Stream>> + Sink, Error = io::Error> + Send + 'static, 145 | {} 146 | 147 | -------------------------------------------------------------------------------- /src/ioctl.rs: -------------------------------------------------------------------------------- 1 | use crate::priv_prelude::*; 2 | 3 | ioctl!(bad read siocgifflags with 0x8913; libc::ifreq); 4 | ioctl!(bad write siocsifflags with 0x8914; libc::ifreq); 5 | ioctl!(bad write siocsifaddr with 0x8916; libc::ifreq); 6 | //ioctl!(bad read siocgifnetmask with 0x891b; sys::ifreq); 7 | ioctl!(bad write siocsifnetmask with 0x891c; libc::ifreq); 8 | ioctl!(write tunsetiff with b'T', 202; c_int); 9 | ioctl!(bad write siocsifhwaddr with 0x8924; libc::ifreq); 10 | ioctl!(bad read siocgifhwaddr with 0x8927; libc::ifreq); 11 | ioctl!(bad read siocgifindex with 0x8933; libc::ifreq); 12 | 13 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | `netsim` is a library for testing networking code. It allows you to run tests in network-isolated 3 | threads, on virtual network interfaces, in parallel, without tests interfering with each other or 4 | with your actual network. You can also connect these isolated environments to each other 5 | to create simulated networks on which you can capture and inject packets. 6 | 7 | ### Example 1: Isolating tests. 8 | 9 | Suppose you have multiple tests that need to bind to the same port for some reason. By using 10 | `#[netsim::isolate]` you can run your test suite without having to use `--test-threads=1` and 11 | without having to stop any daemons running on your dev machine. 12 | 13 | ```no_run 14 | #[test] 15 | #[netsim::isolate] 16 | fn a_test() { 17 | let _listener = std::net::TcpListener::bind("0.0.0.0:80").unwrap(); 18 | } 19 | 20 | #[test] 21 | #[netsim::isolate] 22 | fn another_test_that_runs_in_parallel() { 23 | let _listener = std::net::TcpListener::bind("0.0.0.0:80").unwrap(); 24 | } 25 | ``` 26 | 27 | ### Example 2: Capturing a packet. 28 | 29 | The `#[netsim::isolate]` attribute showcased above is just a convenient way to setup a `Machine` 30 | and spawn a task onto it. To capture a packet you'll need to do these steps yourself. You'll also 31 | need to give the machine a network interface to send the packet on. 32 | 33 | In this example we create a UDP socket and use it to send the message "hello" towards an arbitary 34 | address. The packet then arrives on our `IpIface`, hoping to be routed somewhere, and we can check 35 | that it still contains the correct message. 36 | 37 | ```rust 38 | # use { 39 | # std::net::SocketAddrV4, 40 | # netsim::{ 41 | # Machine, 42 | # packet::{IpPacketVersion, Ipv4PacketProtocol}, 43 | # }, 44 | # tokio::net::UdpSocket, 45 | # futures::prelude::stream::StreamExt, 46 | # }; 47 | # #[tokio::main] 48 | # async fn main() { 49 | let local_addr: SocketAddrV4 = "10.1.2.3:5555".parse().unwrap(); 50 | let remote_addr: SocketAddrV4 = "1.1.1.1:53".parse().unwrap(); 51 | let machine = Machine::new().unwrap(); 52 | let mut iface = { 53 | machine 54 | .add_ip_iface() 55 | .ipv4_addr(*local_addr.ip()) 56 | .ipv4_default_route() 57 | .build() 58 | .unwrap() 59 | }; 60 | machine.spawn(async move { 61 | let socket = UdpSocket::bind(local_addr).await.unwrap(); 62 | socket.send_to(b"hello", remote_addr).await.unwrap(); 63 | }).await.unwrap(); 64 | 65 | let packet = loop { 66 | let packet = iface.next().await.unwrap().unwrap(); 67 | let IpPacketVersion::V4(packet) = packet.version_box() else { continue }; 68 | let Ipv4PacketProtocol::Udp(packet) = packet.protocol_box() else { continue }; 69 | break packet; 70 | }; 71 | assert_eq!(packet.data(), b"hello"); 72 | # } 73 | ``` 74 | 75 | ### More, longer examples. 76 | 77 | Check out the [examples](https://github.com/canndrew/netsim-ng/tree/master/examples) directory in 78 | the repo. 79 | 80 | */ 81 | 82 | #![allow(clippy::let_unit_value)] 83 | 84 | extern crate self as netsim; 85 | 86 | mod priv_prelude; 87 | mod namespace; 88 | mod machine; 89 | mod iface; 90 | mod ioctl; 91 | mod network; 92 | mod connect; 93 | mod stream_ext; 94 | pub mod adapter; 95 | pub mod device; 96 | pub mod packet; 97 | mod sys; 98 | 99 | pub use { 100 | machine::{Machine, JoinHandle}, 101 | iface::{ 102 | create::IpIfaceBuilder, 103 | stream::{IpIface, IpSinkStream}, 104 | }, 105 | connect::connect, 106 | network::{Ipv4Network, Ipv6Network, NetworkParseError, Ipv4NetworkIter, Ipv6NetworkIter}, 107 | netsim_macros::{ipv4_network, ipv6_network, isolate}, 108 | stream_ext::SinkStreamExt, 109 | tokio, 110 | }; 111 | 112 | #[cfg(test)] 113 | mod tests; 114 | 115 | -------------------------------------------------------------------------------- /src/machine.rs: -------------------------------------------------------------------------------- 1 | use crate::priv_prelude::*; 2 | 3 | /// The entry point for this library. 4 | /// 5 | /// A machine has its own tokio runtime running in a separate network-isolated thread. Use 6 | /// [`spawn`](crate::Machine::spawn) to spawn futures on this runtime and wait for their result. 7 | /// Use [`add_ip_iface`](crate::Machine::add_ip_iface) to add virtual network interfaces to the 8 | /// machine and use the returned [`IpIface`](crate::IpIface) to interfere with its packets or build 9 | /// it into a network. Dropping the machine will shutdown its runtime and cancel any futures 10 | /// executing on it, causing any associated [`JoinHandle`](crate::JoinHandle)s to return `None`. 11 | pub struct Machine { 12 | #[allow(dead_code)] 13 | join_handle: namespace::JoinHandle<()>, 14 | task_tx_opt: Option + Send + 'static>>>>, 15 | } 16 | 17 | /// A handle to a future executing on a [`Machine`](crate::Machine). 18 | /// 19 | /// You can await a `JoinHandle` (or call [`join`](crate::JoinHandle::join)) to get the future's 20 | /// result. 21 | pub struct JoinHandle { 22 | ret_rx: oneshot::Receiver>, 23 | } 24 | 25 | impl Machine { 26 | /// Create a new machine. A machine initially has no network interfaces, so attempting to (eg.) 27 | /// make an outgoing TCP connection from a future running on the machine will fail. 28 | pub fn new() -> io::Result { 29 | let (task_tx, mut task_rx) = mpsc::unbounded:: + Send + 'static>>>(); 30 | let (startup_tx, startup_rx) = oneshot::channel(); 31 | let join_handle_res = namespace::spawn(move || { 32 | match iface::configure::put_up("lo") { 33 | Ok(()) => (), 34 | Err(err) => { 35 | let _ = startup_tx.send(Err(err)); 36 | return; 37 | }, 38 | }; 39 | let runtime_res = tokio::runtime::Runtime::new(); 40 | let runtime = match runtime_res { 41 | Ok(runtime) => { 42 | let _ = startup_tx.send(Ok(())); 43 | runtime 44 | }, 45 | Err(err) => { 46 | let _ = startup_tx.send(Err(err)); 47 | return; 48 | }, 49 | }; 50 | runtime.block_on(async move { 51 | while let Some(task) = task_rx.next().await { 52 | let _detach = tokio::spawn(task); 53 | } 54 | }); 55 | runtime.shutdown_background(); 56 | }); 57 | let join_handle = join_handle_res?; 58 | 59 | let () = startup_rx.recv().unwrap()?; 60 | let task_tx_opt = Some(task_tx); 61 | let machine = Machine { join_handle, task_tx_opt }; 62 | Ok(machine) 63 | } 64 | 65 | /// Executes a future on the machine. The future will start executing immediately. You can use 66 | /// the returned [`JoinHandle`](crate::JoinHandle) to await the future's result. 67 | pub fn spawn(&self, future: F) -> JoinHandle 68 | where 69 | F: Future + Send + 'static, 70 | R: Send + 'static, 71 | { 72 | let (ret_tx, ret_rx) = oneshot::channel(); 73 | let task = Box::pin(async move { 74 | let ret = panic::AssertUnwindSafe(future).catch_unwind().await; 75 | let _ = ret_tx.send(ret); 76 | }); 77 | self.task_tx_opt.as_ref().unwrap().unbounded_send(task).unwrap(); 78 | JoinHandle { ret_rx } 79 | } 80 | 81 | /// Adds a network interface to the machine. See the [`IpIfaceBuilder`](crate::IpIfaceBuilder) 82 | /// docs for details. 83 | pub fn add_ip_iface(&self) -> IpIfaceBuilder<'_> { 84 | IpIfaceBuilder::new(self) 85 | } 86 | } 87 | 88 | impl Drop for Machine { 89 | fn drop(&mut self) { 90 | let _task_tx = self.task_tx_opt.take(); 91 | } 92 | } 93 | 94 | impl JoinHandle { 95 | /// Wait for the future executing on the machine to complete and get its result. 96 | /// 97 | /// # Returns 98 | /// 99 | /// * `Ok(Some(value))` if the future completed normally and returned `value`. 100 | /// * `Ok(None)` if the [`Machine`](crate::Machine) was dropped before the future completed. 101 | /// * `Err(panic_error)` if the future panicked. 102 | pub async fn join(self) -> thread::Result> { 103 | match self.ret_rx.await { 104 | Ok(Ok(val)) => Ok(Some(val)), 105 | Ok(Err(err)) => Err(err), 106 | Err(_recv_err) => Ok(None), 107 | } 108 | } 109 | 110 | /// Block the current thread, wait for the future executing on the machine the complete and get 111 | /// its result. 112 | /// 113 | /// # Returns 114 | /// 115 | /// * `Ok(Some(value))` if the future completed normally and returned `value`. 116 | /// * `Ok(None)` if the [`Machine`](crate::Machine) was dropped before the future completed. 117 | /// * `Err(panic_error)` if the future panicked. 118 | pub fn join_blocking(self) -> thread::Result> { 119 | match self.ret_rx.recv() { 120 | Ok(Ok(val)) => Ok(Some(val)), 121 | Ok(Err(err)) => Err(err), 122 | Err(_recv_err) => Ok(None), 123 | } 124 | } 125 | } 126 | 127 | impl IntoFuture for JoinHandle 128 | where 129 | R: Send + 'static, 130 | { 131 | type Output = thread::Result>; 132 | type IntoFuture = Pin>> + Send + 'static>>; 133 | 134 | fn into_future(self) -> Pin>> + Send + 'static>> { 135 | Box::pin(self.join()) 136 | } 137 | } 138 | 139 | -------------------------------------------------------------------------------- /src/namespace.rs: -------------------------------------------------------------------------------- 1 | use crate::priv_prelude::*; 2 | 3 | const STACK_ALIGN: usize = 16; 4 | 5 | pub struct JoinHandle { 6 | stack_ptr: *mut [u8], 7 | child_tid: *mut libc::pid_t, 8 | ret_rx_opt: Option>>, 9 | } 10 | 11 | unsafe impl Send for JoinHandle {} 12 | unsafe impl Sync for JoinHandle {} 13 | 14 | unsafe fn futex( 15 | uaddr: *mut u32, 16 | futex_op: c_int, 17 | val: u32, 18 | timeout: *const libc::timespec, 19 | uaddr2: *mut u32, 20 | val3: u32, 21 | ) -> c_long { 22 | libc::syscall(libc::SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3) 23 | } 24 | 25 | unsafe fn futex_wait(ptr: *mut u32, value: u32) -> c_long { 26 | futex(ptr, libc::FUTEX_WAIT, value, ptr::null(), ptr::null_mut(), 0) 27 | } 28 | 29 | impl JoinHandle { 30 | fn join_inner(&mut self, ret_rx: oneshot::Receiver>) -> thread::Result { 31 | let ret = ret_rx.recv().expect("sender is never dropped without sending"); 32 | loop { 33 | let res = unsafe { 34 | futex_wait(self.child_tid as *mut u32, !0) 35 | }; 36 | if res == -1 { 37 | let raw_err = unsafe { 38 | *libc::__errno_location() 39 | }; 40 | let err = io::Error::last_os_error(); 41 | match raw_err { 42 | libc::EAGAIN => (), 43 | libc::EINTR => continue, 44 | _ => { 45 | eprintln!("futex_wait returned error: {}", err); 46 | std::process::abort() 47 | }, 48 | } 49 | } 50 | break; 51 | } 52 | let _child_tid = unsafe { Box::from_raw(self.child_tid) }; 53 | let _stack_ptr = unsafe { Box::from_raw(self.stack_ptr) }; 54 | ret 55 | } 56 | 57 | #[allow(unused)] 58 | pub fn join(mut self) -> thread::Result { 59 | let ret_rx = self.ret_rx_opt.take().expect("join_inner has not already been called"); 60 | self.join_inner(ret_rx) 61 | } 62 | } 63 | 64 | impl Drop for JoinHandle { 65 | fn drop(&mut self) { 66 | if let Some(ret_rx) = self.ret_rx_opt.take() { 67 | let _ret = self.join_inner(ret_rx); 68 | } 69 | } 70 | } 71 | 72 | pub fn spawn(func: F) -> io::Result> 73 | where 74 | F: FnOnce() -> R, 75 | F: Send + 'static, 76 | R: Send + 'static, 77 | { 78 | let stack_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as usize; 79 | let stack_size = cmp::max(stack_size, 4096); 80 | 81 | let mut stack = vec![0u8; stack_size + STACK_ALIGN]; 82 | let stack_base = stack.as_mut_ptr(); 83 | let stack = stack.into_boxed_slice(); 84 | let stack_ptr = Box::into_raw(stack); 85 | 86 | let flags = 87 | libc::CLONE_CHILD_CLEARTID | 88 | libc::CLONE_FILES | 89 | libc::CLONE_IO | 90 | libc::CLONE_SIGHAND | 91 | libc::CLONE_VM | 92 | libc::CLONE_SYSVSEM | 93 | //libc::CLONE_THREAD; 94 | libc::CLONE_NEWNET | 95 | libc::CLONE_NEWUTS | 96 | libc::CLONE_NEWUSER; 97 | 98 | struct CbData { 99 | func: Box R + Send + 'static>, 100 | ret_rx_tx: oneshot::Sender>>>, 101 | uid: u32, 102 | gid: u32, 103 | } 104 | 105 | extern "C" fn clone_cb(arg: *mut c_void) -> c_int { 106 | let res = panic::catch_unwind(panic::AssertUnwindSafe(move || { 107 | let data: *mut CbData = arg as *mut _; 108 | let data: Box> = unsafe { Box::from_raw(data) }; 109 | let data = *data; 110 | let CbData { func, ret_rx_tx, uid, gid } = data; 111 | 112 | // WARNING: HACKERY 113 | // 114 | // This should ideally be done without spawning another thread. We're already inside a 115 | // thread (spawned by clone), but that thread doesn't respect rust's thread-local 116 | // storage. Making it do so would require passing CLONE_SETTLS to clone(), but it's not 117 | // clear what tls argument to pass in because it's (I think?) dependent on the 118 | // implementation of std::thread::spawn. So instead we use std::thread::spawn to spawn 119 | // a new sub-thread inside our clone thread in order to get thread-local storage 120 | // working again. 121 | // 122 | // Note that until we're inside the sub-thread things are pretty fragile. For instance 123 | // println! can randomly crash in flames if called outside the sub-thread. But the code 124 | // currently here seems to work. 125 | 126 | let setup = move || { 127 | let res = unsafe { 128 | #[allow(clippy::unnecessary_cast)] 129 | libc::prctl(libc::PR_SET_PDEATHSIG as i32, libc::SIGTERM, 0, 0, 0) 130 | }; 131 | if res == -1 { 132 | let err = io::Error::last_os_error(); 133 | return Err(err); 134 | } 135 | 136 | let mut f = File::create("/proc/self/uid_map")?; 137 | let s = format!("0 {} 1\n", uid); 138 | let n = f.write(s.as_bytes())?; 139 | assert_eq!(n, s.len()); 140 | 141 | let mut f = File::create("/proc/self/setgroups")?; 142 | let s = "deny\n"; 143 | let n = f.write(s.as_bytes())?; 144 | assert_eq!(n, s.len()); 145 | 146 | let mut f = File::create("/proc/self/gid_map")?; 147 | let s = format!("0 {} 1\n", gid); 148 | let n = f.write(s.as_bytes())?; 149 | assert_eq!(n, s.len()); 150 | 151 | Ok(()) 152 | }; 153 | let ret_tx = match setup() { 154 | Ok(()) => { 155 | let (ret_tx, ret_rx) = oneshot::channel(); 156 | let _ = ret_rx_tx.send(Ok(ret_rx)); 157 | ret_tx 158 | }, 159 | Err(err) => { 160 | let _ = ret_rx_tx.send(Err(err)); 161 | return; 162 | }, 163 | }; 164 | 165 | let joiner = thread::spawn(move || { 166 | let ret = panic::catch_unwind(panic::AssertUnwindSafe(func)); 167 | let _ = ret_tx.send(ret); 168 | }); 169 | let _ = joiner.join(); 170 | })); 171 | match res { 172 | Ok(()) => 0, 173 | Err(_err) => std::process::exit(1), 174 | } 175 | } 176 | 177 | let uid = unsafe { libc::geteuid() }; 178 | let gid = unsafe { libc::getegid() }; 179 | let (ret_rx_tx, ret_rx_rx) = oneshot::channel(); 180 | let stack_head = ((stack_base as usize + stack_size + STACK_ALIGN) & !(STACK_ALIGN - 1)) as *mut c_void; 181 | let func = Box::new(func); 182 | let arg: Box> = Box::new(CbData { func, ret_rx_tx, uid, gid }); 183 | let arg = Box::into_raw(arg) as *mut c_void; 184 | let child_tid = Box::new(!0); 185 | let child_tid = Box::into_raw(child_tid); 186 | 187 | let pid = unsafe { 188 | libc::clone( 189 | clone_cb::, 190 | stack_head, 191 | flags, 192 | arg, 193 | ptr::null::(), 194 | ptr::null::(), 195 | child_tid, 196 | ) 197 | }; 198 | if pid == -1 { 199 | let err = io::Error::last_os_error(); 200 | if err.kind() == io::ErrorKind::PermissionDenied { 201 | let utsname = { 202 | let mut utsname = MaybeUninit::uninit(); 203 | let res = unsafe { 204 | libc::uname(utsname.as_mut_ptr()) 205 | }; 206 | assert_eq!(res, 0); 207 | unsafe { utsname.assume_init() } 208 | }; 209 | let version = unsafe { 210 | CStr::from_ptr(utsname.release.as_ptr()) 211 | }; 212 | let version = version.to_str().unwrap(); 213 | let passwd = unsafe { 214 | libc::getpwuid(uid) 215 | }; 216 | let user_name = unsafe { 217 | CStr::from_ptr((*passwd).pw_name) 218 | }; 219 | let user_name = user_name.to_str().unwrap(); 220 | let group = unsafe { 221 | libc::getgrgid(gid) 222 | }; 223 | let group_name = unsafe { 224 | CStr::from_ptr((*group).gr_name) 225 | }; 226 | let group_name = group_name.to_str().unwrap(); 227 | 228 | let msg = format!( 229 | "\ 230 | Failed to call clone(CLONE_NEWUSER | CLONE_NEWNET) (permission denied). \ 231 | Your kernel may be too old. \ 232 | Version >= 3.8 is required, your version is {}. \ 233 | Your user/group must also be valid (not nobody). \ 234 | Your user == {}, group == {}. \ 235 | You cannot use netsim in a chroot.\ 236 | ", 237 | version, user_name, group_name, 238 | ); 239 | let err = io::Error::new(io::ErrorKind::PermissionDenied, msg); 240 | return Err(err); 241 | } 242 | return Err(err); 243 | } 244 | let ret_rx_res = ret_rx_rx.recv().expect("spawned thread sends its return channel"); 245 | let ret_rx_opt = Some(ret_rx_res?); 246 | 247 | Ok(JoinHandle { 248 | stack_ptr, child_tid, ret_rx_opt, 249 | }) 250 | } 251 | 252 | -------------------------------------------------------------------------------- /src/network.rs: -------------------------------------------------------------------------------- 1 | use crate::priv_prelude::*; 2 | 3 | /// A list of special-purpose IPv4 address ranges. Any address for which `Ipv4Addr::is_global` is 4 | /// false is guaranteed to be contained in one of these networks. 5 | pub static RESERVED_IPV4_NETWORKS: &[Ipv4Network] = &[ 6 | ipv4_network!("0.0.0.0/8"), 7 | ipv4_network!("0.0.0.0/32"), 8 | ipv4_network!("10.0.0.0/8"), 9 | ipv4_network!("100.64.0.0/10"), 10 | ipv4_network!("127.0.0.0/8"), 11 | ipv4_network!("169.254.0.0/16"), 12 | ipv4_network!("172.16.0.0/12"), 13 | ipv4_network!("192.0.0.0/24"), 14 | ipv4_network!("192.0.0.0/29"), 15 | ipv4_network!("192.0.0.8/32"), 16 | ipv4_network!("192.0.0.9/32"), 17 | ipv4_network!("192.0.0.10/32"), 18 | ipv4_network!("192.0.0.170/32"), 19 | ipv4_network!("192.0.2.0/24"), 20 | ipv4_network!("192.31.196.0/24"), 21 | ipv4_network!("192.52.193.0/24"), 22 | ipv4_network!("192.88.99.0/24"), 23 | ipv4_network!("192.168.0.0/16"), 24 | ipv4_network!("192.175.48.0/24"), 25 | ipv4_network!("198.18.0.0/15"), 26 | ipv4_network!("198.51.100.0/24"), 27 | ipv4_network!("203.0.113.0/24"), 28 | ipv4_network!("224.0.0.0/4"), 29 | ipv4_network!("233.252.0.0/24"), 30 | ipv4_network!("240.0.0.0/4"), 31 | ipv4_network!("255.255.255.255/32"), 32 | ]; 33 | 34 | /// A list of special-purpose IPv6 address ranges. Any address for which `Ipv6Addr::is_global` is 35 | /// false is guaranteed to be contained in one of these networks. 36 | pub static RESERVED_IPV6_NETWORKS: &[Ipv6Network] = &[ 37 | ipv6_network!("::/128"), 38 | ipv6_network!("::1/128"), 39 | ipv6_network!("::ffff:0:0/96"), 40 | ipv6_network!("::ffff:0:0:0/96"), 41 | ipv6_network!("64:ff9b::/96"), 42 | ipv6_network!("64:ff9b:1::/48"), 43 | ipv6_network!("100::/64"), 44 | ipv6_network!("2001:0000::/32"), 45 | ipv6_network!("2001:20::/28"), 46 | ipv6_network!("2001:db8::/32"), 47 | ipv6_network!("2002::/16"), 48 | ipv6_network!("fc00::/7"), 49 | ipv6_network!("fe80::/64"), 50 | ipv6_network!("ff00::/8"), 51 | ]; 52 | 53 | /// An IPv4 address range, for example 192.168.0.0/16. 54 | #[derive(Clone, Copy, PartialEq, Eq)] 55 | pub struct Ipv4Network { 56 | base_addr: Ipv4Addr, 57 | subnet_mask_bits: u8, 58 | } 59 | 60 | /// An IPv6 address range, for example fc00::/7. 61 | #[derive(Clone, Copy, PartialEq, Eq)] 62 | pub struct Ipv6Network { 63 | base_addr: Ipv6Addr, 64 | subnet_mask_bits: u8, 65 | } 66 | 67 | impl Ipv4Network { 68 | /// The global network containing the entire IPv4 address range. 69 | pub const GLOBAL: Ipv4Network = ipv4_network!("0.0.0.0/0"); 70 | 71 | /// Creates a network range containing `base_addr` and all addresses that share the same 72 | /// `subnet_mask_bits` most-significant bits. 73 | /// 74 | /// # Example 75 | /// 76 | /// `Ipv4Network::new(Ipv4Addr::from([192, 168, 0, 0]), 16)` is the network 77 | /// 192.168.0.0/16. 78 | pub const fn new(base_addr: Ipv4Addr, subnet_mask_bits: u8) -> Ipv4Network { 79 | assert!(subnet_mask_bits <= 32); 80 | let mask = if subnet_mask_bits == 32 { !0u32 } else { !(!0 >> subnet_mask_bits) }; 81 | 82 | // NOTE: Can't use simple `From` conversions here because we need to be const. 83 | 84 | let [b0, b1, b2, b3] = base_addr.octets(); 85 | let base_addr_bits = { 86 | ((b0 as u32) << 24) | 87 | ((b1 as u32) << 16) | 88 | ((b2 as u32) << 8) | 89 | (b3 as u32) 90 | }; 91 | let base_addr_bits = base_addr_bits & mask; 92 | let b0 = (base_addr_bits >> 24) as u8; 93 | let b1 = ((base_addr_bits >> 16) & 0xff) as u8; 94 | let b2 = ((base_addr_bits >> 8) & 0xff) as u8; 95 | let b3 = (base_addr_bits & 0xff) as u8; 96 | let base_addr = Ipv4Addr::new(b0, b1, b2, b3); 97 | Ipv4Network { base_addr, subnet_mask_bits } 98 | } 99 | 100 | /// Checks whether this range contains the given IPv4 address. 101 | pub fn contains(self, addr: Ipv4Addr) -> bool { 102 | let mask = if self.subnet_mask_bits == 32 { !0u32 } else { !(!0 >> self.subnet_mask_bits) }; 103 | let addr_bits = u32::from(addr) & mask; 104 | let base_addr_bits = u32::from(self.base_addr); 105 | addr_bits == base_addr_bits 106 | } 107 | 108 | /// Checks whether this range contains the given range. 109 | pub fn contains_network(self, other: Ipv4Network) -> bool { 110 | (self.subnet_mask_bits <= other.subnet_mask_bits) && 111 | (self.base_addr == Ipv4Addr::from(u32::from(other.base_addr) & !(!0 >> self.subnet_mask_bits))) 112 | } 113 | 114 | /// The base address of the network range. Not necessarily the same value passed to 115 | /// `Ipv4Network::new` since the address will be masked to zero all bits after the first 116 | /// `subnet_mask_bits`. 117 | pub fn base_addr(self) -> Ipv4Addr { 118 | self.base_addr 119 | } 120 | 121 | /// The length of the subnet mask. 122 | pub fn subnet_mask_bits(self) -> u8 { 123 | self.subnet_mask_bits 124 | } 125 | 126 | /// Guesses an `Ipv4Network` from an `Ipv4Addr` by checking which, if any, of the [reserved IP 127 | /// address ranges](https://en.wikipedia.org/wiki/Reserved_IP_addresses) it belongs to. If the 128 | /// address does not belong to a reserved range then this method will return the global range 129 | /// 0.0.0.0/0. 130 | pub fn infer_from_addr(addr: Ipv4Addr) -> Ipv4Network { 131 | RESERVED_IPV4_NETWORKS 132 | .iter() 133 | .copied() 134 | .filter(|network| network.contains(addr)) 135 | .max_by_key(|network| network.subnet_mask_bits) 136 | .unwrap_or(Ipv4Network::GLOBAL) 137 | } 138 | 139 | /// Generate a random address in this range and not in any reserved IPv4 range strictly 140 | /// contained within this range. 141 | pub fn random_addr(&self, rng: &mut impl rand::Rng) -> Ipv4Addr { 142 | let mask = !0u32 >> self.subnet_mask_bits; 143 | 'start: loop { 144 | let addr = Ipv4Addr::from((rng.gen::() & mask) | u32::from(self.base_addr)); 145 | for network in RESERVED_IPV4_NETWORKS { 146 | if self.contains_network(*network) && *self != *network && network.contains(addr) { 147 | continue 'start; 148 | } 149 | } 150 | break addr; 151 | } 152 | } 153 | } 154 | 155 | impl Ipv6Network { 156 | /// The global network containing the entire IPv6 address range. 157 | pub const GLOBAL: Ipv6Network = ipv6_network!("::/0"); 158 | 159 | /// Creates a network range containing `base_addr` and all addresses that share the same 160 | /// `subnet_mask_bits` initial bits. 161 | /// 162 | /// # Example 163 | /// 164 | /// `Ipv6Network::new(Ipv6Addr::from([0xfc00u16, 0, 0, 0, 0, 0, 0, 0]), 7)` is the network 165 | /// fc00::/7. 166 | pub const fn new(base_addr: Ipv6Addr, subnet_mask_bits: u8) -> Ipv6Network { 167 | assert!(subnet_mask_bits <= 128); 168 | let mask = if subnet_mask_bits == 128 { !0u128 } else { !(!0 >> subnet_mask_bits) }; 169 | 170 | // NOTE: Can't use simple `From` conversions here because we need to be const. 171 | 172 | let [s0, s1, s2, s3, s4, s5, s6, s7] = base_addr.segments(); 173 | let base_addr_bits = { 174 | ((s0 as u128) << 112) | 175 | ((s1 as u128) << 96) | 176 | ((s2 as u128) << 80) | 177 | ((s3 as u128) << 64) | 178 | ((s4 as u128) << 48) | 179 | ((s5 as u128) << 32) | 180 | ((s6 as u128) << 16) | 181 | (s7 as u128) 182 | }; 183 | let base_addr_bits = base_addr_bits & mask; 184 | let s0 = ((base_addr_bits >> 112) & 0xffff) as u16; 185 | let s1 = ((base_addr_bits >> 96) & 0xffff) as u16; 186 | let s2 = ((base_addr_bits >> 80) & 0xffff) as u16; 187 | let s3 = ((base_addr_bits >> 64) & 0xffff) as u16; 188 | let s4 = ((base_addr_bits >> 48) & 0xffff) as u16; 189 | let s5 = ((base_addr_bits >> 32) & 0xffff) as u16; 190 | let s6 = ((base_addr_bits >> 16) & 0xffff) as u16; 191 | let s7 = (base_addr_bits & 0xffff) as u16; 192 | let base_addr = Ipv6Addr::new(s0, s1, s2, s3, s4, s5, s6, s7); 193 | Ipv6Network { base_addr, subnet_mask_bits } 194 | } 195 | 196 | /// Checks whether this range contains the given IPv6 address. 197 | pub fn contains(self, addr: Ipv6Addr) -> bool { 198 | let mask = if self.subnet_mask_bits == 128 { !0u128 } else { !(!0 >> self.subnet_mask_bits) }; 199 | let addr_bits = u128::from(addr) & mask; 200 | let base_addr_bits = u128::from(self.base_addr); 201 | addr_bits == base_addr_bits 202 | } 203 | 204 | /// Checks whether this range contains the given range. 205 | pub fn contains_network(self, other: Ipv6Network) -> bool { 206 | (self.subnet_mask_bits <= other.subnet_mask_bits) && 207 | (self.base_addr == Ipv6Addr::from(u128::from(other.base_addr) & !(!0 >> self.subnet_mask_bits))) 208 | } 209 | 210 | /// The base address of the network range. Not necessarily the same value passed to 211 | /// `Ipv6Network::new` since the address will be masked to zero all bits after the first 212 | /// `subnet_mask_bits`. 213 | pub fn base_addr(self) -> Ipv6Addr { 214 | self.base_addr 215 | } 216 | 217 | /// The length of the subnet mask. 218 | pub fn subnet_mask_bits(self) -> u8 { 219 | self.subnet_mask_bits 220 | } 221 | 222 | /// Guesses an `Ipv6Network` from an `Ipv6Addr` by checking which, if any, of the [reserved IP 223 | /// address ranges](https://en.wikipedia.org/wiki/Reserved_IP_addresses) it belongs to. If the 224 | /// address does not belong to a reserved range then this method will return the full range 225 | /// ::/0. 226 | pub fn infer_from_addr(addr: Ipv6Addr) -> Ipv6Network { 227 | RESERVED_IPV6_NETWORKS 228 | .iter() 229 | .copied() 230 | .filter(|network| network.contains(addr)) 231 | .max_by_key(|network| network.subnet_mask_bits) 232 | .unwrap_or(Ipv6Network::GLOBAL) 233 | } 234 | 235 | /// Generate a random address in this range and not in any reserved IPv6 range strictly 236 | /// contained within this range. 237 | pub fn random_addr(&self) -> Ipv6Addr { 238 | let mask = !0u128 >> self.subnet_mask_bits; 239 | 'start: loop { 240 | let addr = Ipv6Addr::from((rand::random::() & mask) | u128::from(self.base_addr)); 241 | for network in RESERVED_IPV6_NETWORKS { 242 | if self.contains_network(*network) && *self != *network && network.contains(addr) { 243 | continue 'start; 244 | } 245 | } 246 | break addr; 247 | } 248 | } 249 | } 250 | 251 | impl fmt::Debug for Ipv4Network { 252 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 253 | write!(f, "ipv4_network!({:?})", self.to_string()) 254 | } 255 | } 256 | 257 | impl fmt::Display for Ipv4Network { 258 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 259 | write!(f, "{}/{}", self.base_addr, self.subnet_mask_bits) 260 | } 261 | } 262 | 263 | impl fmt::Debug for Ipv6Network { 264 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 265 | write!(f, "ipv6_network!({:?})", self.to_string()) 266 | } 267 | } 268 | 269 | impl fmt::Display for Ipv6Network { 270 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 271 | write!(f, "{}/{}", self.base_addr, self.subnet_mask_bits) 272 | } 273 | } 274 | 275 | impl str::FromStr for Ipv4Network { 276 | type Err = NetworkParseError; 277 | 278 | fn from_str(s: &str) -> Result { 279 | let res = match s.split_once('/') { 280 | None => Err(None), 281 | Some((addr, subnet_mask_bits)) => { 282 | match Ipv4Addr::from_str(addr) { 283 | Err(err) => Err(Some(err)), 284 | Ok(addr) => { 285 | match u8::from_str(subnet_mask_bits) { 286 | Err(_err) => Err(None), 287 | Ok(subnet_mask_bits) => { 288 | if subnet_mask_bits <= 32 { 289 | Ok(Ipv4Network::new(addr, subnet_mask_bits)) 290 | } else { 291 | Err(None) 292 | } 293 | }, 294 | } 295 | }, 296 | } 297 | }, 298 | }; 299 | match res { 300 | Ok(ipv4_network) => Ok(ipv4_network), 301 | Err(addr_parse_error_opt) => { 302 | let err = NetworkParseError { 303 | s: s.to_owned(), 304 | addr_parse_error_opt, 305 | }; 306 | Err(err) 307 | }, 308 | } 309 | } 310 | } 311 | 312 | impl str::FromStr for Ipv6Network { 313 | type Err = NetworkParseError; 314 | 315 | fn from_str(s: &str) -> Result { 316 | let res = match s.split_once('/') { 317 | None => Err(None), 318 | Some((addr, subnet_mask_bits)) => { 319 | match Ipv6Addr::from_str(addr) { 320 | Err(err) => Err(Some(err)), 321 | Ok(addr) => { 322 | match u8::from_str(subnet_mask_bits) { 323 | Err(_err) => Err(None), 324 | Ok(subnet_mask_bits) => { 325 | if subnet_mask_bits <= 128 { 326 | Ok(Ipv6Network::new(addr, subnet_mask_bits)) 327 | } else { 328 | Err(None) 329 | } 330 | }, 331 | } 332 | }, 333 | } 334 | }, 335 | }; 336 | match res { 337 | Ok(ipv6_network) => Ok(ipv6_network), 338 | Err(addr_parse_error_opt) => { 339 | let err = NetworkParseError { 340 | s: s.to_owned(), 341 | addr_parse_error_opt, 342 | }; 343 | Err(err) 344 | }, 345 | } 346 | } 347 | } 348 | 349 | /// Error that can be returned when parsing an [`Ipv4Network`](crate::Ipv4Network) or 350 | /// [`Ipv6Network`](crate::Ipv6Network). 351 | #[derive(Debug)] 352 | pub struct NetworkParseError { 353 | s: String, 354 | addr_parse_error_opt: Option, 355 | } 356 | 357 | impl fmt::Display for NetworkParseError { 358 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 359 | write!(f, "{:?} is not valid IP network syntax", self.s) 360 | } 361 | } 362 | 363 | impl std::error::Error for NetworkParseError { 364 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 365 | let err = self.addr_parse_error_opt.as_ref()?; 366 | Some(err) 367 | } 368 | } 369 | 370 | /// An iterator over the IPv4 addresses in an [`Ipv4Network`](crate::Ipv4Network). 371 | pub struct Ipv4NetworkIter { 372 | start_addr_opt: Option, 373 | end_addr_opt: Option, 374 | } 375 | 376 | impl Iterator for Ipv4NetworkIter { 377 | type Item = Ipv4Addr; 378 | 379 | fn next(&mut self) -> Option { 380 | let next_addr = self.start_addr_opt?; 381 | if self.end_addr_opt == Some(next_addr) { 382 | return None; 383 | } 384 | self.start_addr_opt = u32::from(next_addr).checked_add(1).map(Ipv4Addr::from); 385 | Some(next_addr) 386 | } 387 | 388 | fn size_hint(&self) -> (usize, Option) { 389 | let start_addr = match self.start_addr_opt { 390 | Some(start_addr) => start_addr, 391 | None => return (0, Some(0)), 392 | }; 393 | let end_addr = match self.end_addr_opt { 394 | Some(end_addr) => end_addr, 395 | None => { 396 | let diff_m1 = u32::MAX - u32::from(start_addr); 397 | return match usize::try_from(diff_m1) { 398 | Ok(diff_m1) => (diff_m1.saturating_add(1), diff_m1.checked_add(1)), 399 | Err(_) => (usize::MAX, None), 400 | }; 401 | }, 402 | }; 403 | let diff = u32::from(end_addr) - u32::from(start_addr); 404 | match usize::try_from(diff) { 405 | Ok(diff) => (diff, Some(diff)), 406 | Err(_) => (usize::MAX, None), 407 | } 408 | } 409 | } 410 | 411 | impl DoubleEndedIterator for Ipv4NetworkIter { 412 | fn next_back(&mut self) -> Option { 413 | if self.start_addr_opt == self.end_addr_opt { 414 | return None; 415 | } 416 | let next_addr = match self.end_addr_opt { 417 | Some(end_addr) => Ipv4Addr::from(u32::from(end_addr) - 1), 418 | None => Ipv4Addr::from(u32::MAX), 419 | }; 420 | self.end_addr_opt = Some(next_addr); 421 | Some(next_addr) 422 | } 423 | } 424 | 425 | /// An iterator over the IPv6 addresses in an [`Ipv6Network`](crate::Ipv6Network). 426 | pub struct Ipv6NetworkIter { 427 | start_addr_opt: Option, 428 | end_addr_opt: Option, 429 | } 430 | 431 | impl Iterator for Ipv6NetworkIter { 432 | type Item = Ipv6Addr; 433 | 434 | fn next(&mut self) -> Option { 435 | let next_addr = self.start_addr_opt?; 436 | if self.end_addr_opt == Some(next_addr) { 437 | return None; 438 | } 439 | self.start_addr_opt = u128::from(next_addr).checked_add(1).map(Ipv6Addr::from); 440 | Some(next_addr) 441 | } 442 | 443 | fn size_hint(&self) -> (usize, Option) { 444 | let start_addr = match self.start_addr_opt { 445 | Some(start_addr) => start_addr, 446 | None => return (0, Some(0)), 447 | }; 448 | let end_addr = match self.end_addr_opt { 449 | Some(end_addr) => end_addr, 450 | None => { 451 | let diff_m1 = u128::MAX - u128::from(start_addr); 452 | return match usize::try_from(diff_m1) { 453 | Ok(diff_m1) => (diff_m1.saturating_add(1), diff_m1.checked_add(1)), 454 | Err(_) => (usize::MAX, None), 455 | }; 456 | }, 457 | }; 458 | let diff = u128::from(end_addr) - u128::from(start_addr); 459 | match usize::try_from(diff) { 460 | Ok(diff) => (diff, Some(diff)), 461 | Err(_) => (usize::MAX, None), 462 | } 463 | } 464 | } 465 | 466 | impl DoubleEndedIterator for Ipv6NetworkIter { 467 | fn next_back(&mut self) -> Option { 468 | if self.start_addr_opt == self.end_addr_opt { 469 | return None; 470 | } 471 | let next_addr = match self.end_addr_opt { 472 | Some(end_addr) => Ipv6Addr::from(u128::from(end_addr) - 1), 473 | None => Ipv6Addr::from(u128::MAX), 474 | }; 475 | self.end_addr_opt = Some(next_addr); 476 | Some(next_addr) 477 | } 478 | } 479 | 480 | impl IntoIterator for Ipv4Network { 481 | type Item = Ipv4Addr; 482 | type IntoIter = Ipv4NetworkIter; 483 | 484 | fn into_iter(self) -> Ipv4NetworkIter { 485 | Ipv4NetworkIter { 486 | start_addr_opt: Some(self.base_addr), 487 | end_addr_opt: { 488 | (u32::from(self.base_addr) | (!0u32 >> self.subnet_mask_bits)) 489 | .checked_add(1) 490 | .map(Ipv4Addr::from) 491 | }, 492 | } 493 | } 494 | } 495 | 496 | impl IntoIterator for Ipv6Network { 497 | type Item = Ipv6Addr; 498 | type IntoIter = Ipv6NetworkIter; 499 | 500 | fn into_iter(self) -> Ipv6NetworkIter { 501 | Ipv6NetworkIter { 502 | start_addr_opt: Some(self.base_addr), 503 | end_addr_opt: { 504 | (u128::from(self.base_addr) | (!0u128 >> self.subnet_mask_bits)) 505 | .checked_add(1) 506 | .map(Ipv6Addr::from) 507 | }, 508 | } 509 | } 510 | } 511 | 512 | impl std::iter::FusedIterator for Ipv4NetworkIter {} 513 | impl std::iter::FusedIterator for Ipv6NetworkIter {} 514 | 515 | -------------------------------------------------------------------------------- /src/packet.rs: -------------------------------------------------------------------------------- 1 | //! Types for representing IP packets. 2 | //! 3 | //! ### Note 4 | //! 5 | //! This module and the types within are still very incomplete. A complete IP implementation is a 6 | //! lot of work so I've just been adding things as I've needed them. PRs here are welcome. However 7 | //! in a future version I'll probably just rip out this whole module and replace it with one of the 8 | //! several third-party crates that do the same thing. 9 | use crate::priv_prelude::*; 10 | use std::{ 11 | rc::Rc, 12 | borrow::Borrow, 13 | mem::transmute, 14 | }; 15 | 16 | struct Ipv4Hasher { 17 | sum: u16, 18 | } 19 | 20 | impl Ipv4Hasher { 21 | pub fn new() -> Ipv4Hasher { 22 | Ipv4Hasher { 23 | sum: 0, 24 | } 25 | } 26 | 27 | pub fn write_u16(&mut self, word: u16) { 28 | let mut sum = self.sum as u32; 29 | sum += word as u32; 30 | self.sum = loop { 31 | match u16::try_from(sum) { 32 | Ok(sum) => break sum, 33 | Err(_) => { 34 | let hi = sum >> 16; 35 | let lo = sum & 0xffff; 36 | sum = hi + lo; 37 | }, 38 | } 39 | }; 40 | } 41 | 42 | pub fn write_u32(&mut self, word: u32) { 43 | let hi = (word >> 16) as u16; 44 | let lo = (word & 0xffff) as u16; 45 | self.write_u16(hi); 46 | self.write_u16(lo); 47 | } 48 | 49 | pub fn finish(self) -> u16 { 50 | !self.sum 51 | } 52 | } 53 | 54 | macro_rules! bit( 55 | ($val:expr, $index:expr) => ( 56 | ($val >> $index) & 1 == 1 57 | ); 58 | ); 59 | 60 | macro_rules! set_bit( 61 | ($target:expr, $bit:expr, $index:expr) => ( 62 | if $bit { 63 | *$target |= 1 << $index; 64 | } else { 65 | *$target &= !(1 << $index); 66 | } 67 | ); 68 | ); 69 | 70 | macro_rules! slice( 71 | ($val:expr, ..$end:literal) => ( 72 | slice!($val, 0..$end) 73 | ); 74 | ($val:expr, $start:literal..$end:literal) => ({ 75 | const START: usize = $start; 76 | const END: usize = $end; 77 | const LEN: usize = END - START; 78 | std::array::from_fn::<_, LEN, _>(|index| { 79 | $val[index + START] 80 | }) 81 | }); 82 | ); 83 | 84 | macro_rules! slice_mut( 85 | ($val:expr, ..$end:literal) => ( 86 | slice!($val, 0..$end) 87 | ); 88 | ($val:expr, $start:literal..$end:literal) => ({ 89 | const START: usize = $start; 90 | const END: usize = $end; 91 | const LEN: usize = END - START; 92 | let arr: &mut [_; LEN] = TryFrom::try_from(&mut $val[START..END]).unwrap(); 93 | arr 94 | }); 95 | ); 96 | 97 | macro_rules! packet_type( 98 | ($name:ident, [$($parent_box:ident/$parent_arc:ident/$parent_ref:ident/$parent_mut:ident: $parent:ident),* $(,)?] $(,)?) => ( 99 | #[repr(transparent)] 100 | pub struct $name { 101 | data: [u8], 102 | } 103 | 104 | impl Clone for Box<$name> { 105 | fn clone(&self) -> Box<$name> { 106 | let boxed: Box<[u8]> = Box::from(&self.data[..]); 107 | unsafe { transmute(boxed) } 108 | } 109 | } 110 | 111 | #[allow(clippy::missing_transmute_annotations)] 112 | impl $name { 113 | #[allow(clippy::len_without_is_empty)] 114 | pub fn len(&self) -> usize { 115 | self.data.len() 116 | } 117 | 118 | pub fn as_bytes(&self) -> &[u8] { 119 | &self.data[..] 120 | } 121 | 122 | $( 123 | pub fn $parent_box(self: Box<$name>) -> Box<$parent> { 124 | unsafe { transmute(self) } 125 | } 126 | 127 | pub fn $parent_arc(self: Arc<$name>) -> Arc<$parent> { 128 | unsafe { transmute(self) } 129 | } 130 | 131 | pub fn $parent_ref(&self) -> &$parent { 132 | unsafe { transmute(self) } 133 | } 134 | 135 | pub fn $parent_mut(&mut self) -> &mut $parent { 136 | unsafe { transmute(self) } 137 | } 138 | )* 139 | } 140 | ); 141 | ); 142 | 143 | mod protocol_numbers { 144 | pub const HOP_BY_HOP_OPTIONS: u8 = 0; 145 | pub const ICMP_V4: u8 = 1; 146 | pub const TCP: u8 = 6; 147 | pub const UDP: u8 = 17; 148 | pub const ROUTING: u8 = 43; 149 | pub const FRAGMENT: u8 = 44; 150 | pub const AUTHENTICATION_HEADER: u8 = 51; 151 | pub const ICMP_V6: u8 = 58; 152 | } 153 | 154 | #[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Default)] 155 | pub struct TcpPacketFlags { 156 | pub cwr: bool, 157 | pub ece: bool, 158 | pub urg: bool, 159 | pub ack: bool, 160 | pub psh: bool, 161 | pub rst: bool, 162 | pub syn: bool, 163 | pub fin: bool, 164 | } 165 | 166 | impl fmt::Debug for TcpPacketFlags { 167 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 168 | let mut written = false; 169 | let mut write = |s| { 170 | if written { 171 | write!(formatter, " | {}", s) 172 | } else { 173 | written = true; 174 | write!(formatter, "{}", s) 175 | } 176 | }; 177 | if self.cwr { write("CWR")? }; 178 | if self.ece { write("ECE")? }; 179 | if self.urg { write("URG")? }; 180 | if self.ack { write("ACK")? }; 181 | if self.psh { write("PSH")? }; 182 | if self.rst { write("RST")? }; 183 | if self.syn { write("SYN")? }; 184 | if self.fin { write("FIN")? }; 185 | Ok(()) 186 | } 187 | } 188 | 189 | pub trait Pointer: Borrow { 190 | type InsteadPointTo: Sized; 191 | } 192 | 193 | impl Pointer for Box { 194 | type InsteadPointTo = Box; 195 | } 196 | 197 | impl Pointer for Arc { 198 | type InsteadPointTo = Arc; 199 | } 200 | 201 | impl Pointer for Rc { 202 | type InsteadPointTo = Rc; 203 | } 204 | 205 | impl<'a, T: ?Sized + 'static> Pointer for &'a T { 206 | type InsteadPointTo = &'a U; 207 | } 208 | 209 | impl<'a, T: ?Sized + 'static> Pointer for &'a mut T { 210 | type InsteadPointTo = &'a mut U; 211 | } 212 | 213 | packet_type!(IpPacket, []); 214 | packet_type!(Ipv4Packet, [ 215 | ip_packet_box/ip_packet_arc/ip_packet_ref/ip_packet_mut: IpPacket, 216 | ]); 217 | packet_type!(Ipv6Packet, [ 218 | ip_packet_box/ip_packet_arc/ip_packet_ref/ip_packet_mut: IpPacket, 219 | ]); 220 | packet_type!(Tcpv4Packet, [ 221 | ip_packet_box/ip_packet_arc/ip_packet_ref/ip_packet_mut: IpPacket, 222 | ipv4_packet_box/ipv4_packet_arc/ipv4_packet_ref/ipv4_packet_mut: Ipv4Packet, 223 | ]); 224 | packet_type!(Udpv4Packet, [ 225 | ip_packet_box/ip_packet_arc/ip_packet_ref/ip_packet_mut: IpPacket, 226 | ipv4_packet_box/ipv4_packet_arc/ipv4_packet_ref/ipv4_packet_mut: Ipv4Packet, 227 | ]); 228 | packet_type!(Icmpv4Packet, [ 229 | ip_packet_box/ip_packet_arc/ip_packet_ref/ip_packet_mut: IpPacket, 230 | ipv4_packet_box/ipv4_packet_arc/ipv4_packet_ref/ipv4_packet_mut: Ipv4Packet, 231 | ]); 232 | packet_type!(Icmpv6Packet, [ 233 | ip_packet_box/ip_packet_arc/ip_packet_ref/ip_packet_mut: IpPacket, 234 | ipv6_packet_box/ipv6_packet_arc/ipv6_packet_ref/ipv6_packet_mut: Ipv6Packet, 235 | ]); 236 | 237 | pub enum IpPacketVersion

238 | where 239 | P: Pointer, 240 | { 241 | V4(P::InsteadPointTo), 242 | V6(P::InsteadPointTo), 243 | } 244 | 245 | impl IpPacket { 246 | pub(crate) fn new_box(data: Box<[u8]>) -> Box { 247 | unsafe { transmute(data) } 248 | } 249 | 250 | #[allow(clippy::missing_transmute_annotations)] 251 | pub fn version_box(self: Box) -> IpPacketVersion> { 252 | match self.data[0] >> 4 { 253 | 4 => IpPacketVersion::V4(unsafe { transmute(self) }), 254 | 6 => IpPacketVersion::V6(unsafe { transmute(self) }), 255 | _ => panic!("unknown packet version"), 256 | } 257 | } 258 | 259 | #[allow(clippy::missing_transmute_annotations)] 260 | pub fn version_arc(self: Arc) -> IpPacketVersion> { 261 | match self.data[0] >> 4 { 262 | 4 => IpPacketVersion::V4(unsafe { transmute(self) }), 263 | 6 => IpPacketVersion::V6(unsafe { transmute(self) }), 264 | _ => panic!("unknown packet version"), 265 | } 266 | } 267 | 268 | #[allow(clippy::missing_transmute_annotations)] 269 | pub fn version_ref(&self) -> IpPacketVersion<&IpPacket> { 270 | match self.data[0] >> 4 { 271 | 4 => IpPacketVersion::V4(unsafe { transmute(self) }), 272 | 6 => IpPacketVersion::V6(unsafe { transmute(self) }), 273 | _ => panic!("unknown packet version"), 274 | } 275 | } 276 | 277 | #[allow(clippy::missing_transmute_annotations)] 278 | pub fn version_mut(&mut self) -> IpPacketVersion<&mut IpPacket> { 279 | match self.data[0] >> 4 { 280 | 4 => IpPacketVersion::V4(unsafe { transmute(self) }), 281 | 6 => IpPacketVersion::V6(unsafe { transmute(self) }), 282 | _ => panic!("unknown packet version"), 283 | } 284 | } 285 | 286 | pub fn source_addr(&self) -> IpAddr { 287 | match self.version_ref() { 288 | IpPacketVersion::V4(packet) => IpAddr::V4(packet.source_addr()), 289 | IpPacketVersion::V6(packet) => IpAddr::V6(packet.source_addr()), 290 | } 291 | } 292 | 293 | pub fn destination_addr(&self) -> IpAddr { 294 | match self.version_ref() { 295 | IpPacketVersion::V4(packet) => IpAddr::V4(packet.destination_addr()), 296 | IpPacketVersion::V6(packet) => IpAddr::V6(packet.destination_addr()), 297 | } 298 | } 299 | } 300 | 301 | pub enum Ipv4PacketProtocol

302 | where 303 | P: Pointer, 304 | { 305 | Tcp(P::InsteadPointTo), 306 | Udp(P::InsteadPointTo), 307 | Icmp(P::InsteadPointTo), 308 | Unknown { 309 | protocol_number: u8, 310 | }, 311 | } 312 | 313 | impl Ipv4Packet { 314 | #[allow(clippy::missing_transmute_annotations)] 315 | pub fn protocol_box(self: Box) -> Ipv4PacketProtocol> { 316 | match self.data[9] { 317 | protocol_numbers::TCP => Ipv4PacketProtocol::Tcp(unsafe { transmute(self) }), 318 | protocol_numbers::UDP => Ipv4PacketProtocol::Udp(unsafe { transmute(self) }), 319 | protocol_numbers::ICMP_V4 => Ipv4PacketProtocol::Icmp(unsafe { transmute(self) }), 320 | protocol_number => Ipv4PacketProtocol::Unknown { protocol_number }, 321 | } 322 | } 323 | 324 | #[allow(clippy::missing_transmute_annotations)] 325 | pub fn protocol_arc(self: Arc) -> Ipv4PacketProtocol> { 326 | match self.data[9] { 327 | protocol_numbers::TCP => Ipv4PacketProtocol::Tcp(unsafe { transmute(self) }), 328 | protocol_numbers::UDP => Ipv4PacketProtocol::Udp(unsafe { transmute(self) }), 329 | protocol_numbers::ICMP_V4 => Ipv4PacketProtocol::Icmp(unsafe { transmute(self) }), 330 | protocol_number => Ipv4PacketProtocol::Unknown { protocol_number }, 331 | } 332 | } 333 | 334 | #[allow(clippy::missing_transmute_annotations)] 335 | pub fn protocol_ref(&self) -> Ipv4PacketProtocol<&Ipv4Packet> { 336 | match self.data[9] { 337 | protocol_numbers::TCP => Ipv4PacketProtocol::Tcp(unsafe { transmute(self) }), 338 | protocol_numbers::UDP => Ipv4PacketProtocol::Udp(unsafe { transmute(self) }), 339 | protocol_numbers::ICMP_V4 => Ipv4PacketProtocol::Icmp(unsafe { transmute(self) }), 340 | protocol_number => Ipv4PacketProtocol::Unknown { protocol_number }, 341 | } 342 | } 343 | 344 | #[allow(clippy::missing_transmute_annotations)] 345 | pub fn protocol_mut(&mut self) -> Ipv4PacketProtocol<&mut Ipv4Packet> { 346 | match self.data[9] { 347 | protocol_numbers::TCP => Ipv4PacketProtocol::Tcp(unsafe { transmute(self) }), 348 | protocol_numbers::UDP => Ipv4PacketProtocol::Udp(unsafe { transmute(self) }), 349 | protocol_numbers::ICMP_V4 => Ipv4PacketProtocol::Icmp(unsafe { transmute(self) }), 350 | protocol_number => Ipv4PacketProtocol::Unknown { protocol_number }, 351 | } 352 | } 353 | 354 | pub fn source_addr(&self) -> Ipv4Addr { 355 | let addr = slice!(&self.data, 12..16); 356 | Ipv4Addr::from(addr) 357 | } 358 | 359 | pub fn destination_addr(&self) -> Ipv4Addr { 360 | let addr = slice!(&self.data, 16..20); 361 | Ipv4Addr::from(addr) 362 | } 363 | 364 | pub fn set_source_addr(&mut self, addr: Ipv4Addr) { 365 | *slice_mut!(self.data, 12..16) = addr.octets(); 366 | self.fix_checksum(); 367 | } 368 | 369 | pub fn set_destination_addr(&mut self, addr: Ipv4Addr) { 370 | *slice_mut!(self.data, 16..20) = addr.octets(); 371 | self.fix_checksum(); 372 | } 373 | 374 | pub fn ipv4_header_len(&self) -> usize { 375 | (self.data[0] & 0x0f) as usize * 4 376 | } 377 | 378 | fn fix_checksum(&mut self) { 379 | let mut hasher = Ipv4Hasher::new(); 380 | let header_len = self.ipv4_header_len(); 381 | let mut i = 0; 382 | while i < header_len { 383 | if i != 10 { 384 | hasher.write_u16(u16::from_be_bytes(slice!(&self.data[i..], 0..2))); 385 | } 386 | i += 2; 387 | } 388 | *slice_mut!(self.data, 10..12) = hasher.finish().to_be_bytes(); 389 | } 390 | } 391 | 392 | pub enum Ipv6PacketProtocol

393 | where 394 | P: Pointer, 395 | { 396 | Icmp(P::InsteadPointTo), 397 | Unknown { 398 | protocol_number: u8, 399 | }, 400 | } 401 | 402 | impl Ipv6Packet { 403 | #[allow(clippy::missing_transmute_annotations)] 404 | pub fn protocol_box(self: Box) -> Ipv6PacketProtocol> { 405 | match self.protocol_number() { 406 | protocol_numbers::ICMP_V6 => Ipv6PacketProtocol::Icmp(unsafe { transmute(self) }), 407 | protocol_number => Ipv6PacketProtocol::Unknown { protocol_number }, 408 | } 409 | } 410 | 411 | #[allow(clippy::missing_transmute_annotations)] 412 | pub fn protocol_arc(self: Arc) -> Ipv6PacketProtocol> { 413 | match self.protocol_number() { 414 | protocol_numbers::ICMP_V6 => Ipv6PacketProtocol::Icmp(unsafe { transmute(self) }), 415 | protocol_number => Ipv6PacketProtocol::Unknown { protocol_number }, 416 | } 417 | } 418 | 419 | #[allow(clippy::missing_transmute_annotations)] 420 | pub fn protocol_ref(&self) -> Ipv6PacketProtocol<&Ipv6Packet> { 421 | match self.protocol_number() { 422 | protocol_numbers::ICMP_V6 => Ipv6PacketProtocol::Icmp(unsafe { transmute(self) }), 423 | protocol_number => Ipv6PacketProtocol::Unknown { protocol_number }, 424 | } 425 | } 426 | 427 | #[allow(clippy::missing_transmute_annotations)] 428 | pub fn protocol_mut(&mut self) -> Ipv6PacketProtocol<&mut Ipv6Packet> { 429 | match self.protocol_number() { 430 | protocol_numbers::ICMP_V6 => Ipv6PacketProtocol::Icmp(unsafe { transmute(self) }), 431 | protocol_number => Ipv6PacketProtocol::Unknown { protocol_number }, 432 | } 433 | } 434 | 435 | fn protocol_number(&self) -> u8 { 436 | let mut header_position = 0; 437 | let mut next_header_position = 40; 438 | let mut offset = 6; 439 | loop { 440 | let protocol_number = self.data[header_position + offset]; 441 | header_position = next_header_position; 442 | match protocol_number { 443 | protocol_numbers::HOP_BY_HOP_OPTIONS => { 444 | next_header_position += 8 * (1 + self.data[header_position + 1] as usize); 445 | offset = 0; 446 | }, 447 | protocol_numbers::ROUTING => { 448 | next_header_position += 8 * (1 + self.data[header_position + 1] as usize); 449 | offset = 0; 450 | }, 451 | protocol_numbers::FRAGMENT => { 452 | next_header_position += 8; 453 | offset = 0; 454 | }, 455 | protocol_numbers::AUTHENTICATION_HEADER => { 456 | next_header_position += 2 + 4 * self.data[header_position + 1] as usize; 457 | offset = 0; 458 | }, 459 | protocol_number => break protocol_number, 460 | } 461 | } 462 | } 463 | 464 | pub fn source_addr(&self) -> Ipv6Addr { 465 | let addr = slice!(&self.data, 8..24); 466 | Ipv6Addr::from(addr) 467 | } 468 | 469 | pub fn destination_addr(&self) -> Ipv6Addr { 470 | let addr = slice!(&self.data, 24..40); 471 | Ipv6Addr::from(addr) 472 | } 473 | } 474 | 475 | impl Tcpv4Packet { 476 | pub fn source_ip_addr(&self) -> Ipv4Addr { 477 | self.ipv4_packet_ref().source_addr() 478 | } 479 | 480 | pub fn destination_ip_addr(&self) -> Ipv4Addr { 481 | self.ipv4_packet_ref().destination_addr() 482 | } 483 | 484 | pub fn source_port(&self) -> u16 { 485 | let header_len = self.ipv4_packet_ref().ipv4_header_len(); 486 | let port = slice!(&self.data[header_len..], 0..2); 487 | u16::from_be_bytes(port) 488 | } 489 | 490 | pub fn destination_port(&self) -> u16 { 491 | let header_len = self.ipv4_packet_ref().ipv4_header_len(); 492 | let port = slice!(&self.data[header_len..], 2..4); 493 | u16::from_be_bytes(port) 494 | } 495 | 496 | pub fn source_addr(&self) -> SocketAddrV4 { 497 | let ip_addr = self.source_ip_addr(); 498 | let port = self.source_port(); 499 | SocketAddrV4::new(ip_addr, port) 500 | } 501 | 502 | pub fn destination_addr(&self) -> SocketAddrV4 { 503 | let ip_addr = self.destination_ip_addr(); 504 | let port = self.destination_port(); 505 | SocketAddrV4::new(ip_addr, port) 506 | } 507 | 508 | pub fn seq_number(&self) -> u32 { 509 | let header_len = self.ipv4_packet_ref().ipv4_header_len(); 510 | let seq_bytes = slice!(&self.data[header_len..], 4..8); 511 | u32::from_be_bytes(seq_bytes) 512 | } 513 | 514 | pub fn ack_number(&self) -> u32 { 515 | let header_len = self.ipv4_packet_ref().ipv4_header_len(); 516 | let ack_bytes = slice!(&self.data[header_len..], 8..12); 517 | u32::from_be_bytes(ack_bytes) 518 | } 519 | 520 | pub fn flags(&self) -> TcpPacketFlags { 521 | let header_len = self.ipv4_packet_ref().ipv4_header_len(); 522 | let flags = self.data[header_len + 13]; 523 | TcpPacketFlags { 524 | cwr: bit!(flags, 7), 525 | ece: bit!(flags, 6), 526 | urg: bit!(flags, 5), 527 | ack: bit!(flags, 4), 528 | psh: bit!(flags, 3), 529 | rst: bit!(flags, 2), 530 | syn: bit!(flags, 1), 531 | fin: bit!(flags, 0), 532 | } 533 | } 534 | 535 | pub fn set_flags(&mut self, flags: TcpPacketFlags) { 536 | let header_len = self.ipv4_packet_ref().ipv4_header_len(); 537 | let TcpPacketFlags { cwr, ece, urg, ack, psh, rst, syn, fin } = flags; 538 | let byte = &mut self.data[header_len + 13]; 539 | set_bit!(byte, cwr, 7); 540 | set_bit!(byte, ece, 6); 541 | set_bit!(byte, urg, 5); 542 | set_bit!(byte, ack, 4); 543 | set_bit!(byte, psh, 3); 544 | set_bit!(byte, rst, 2); 545 | set_bit!(byte, syn, 1); 546 | set_bit!(byte, fin, 0); 547 | self.fix_checksum(); 548 | } 549 | 550 | pub fn set_source_port(&mut self, port: u16) { 551 | let header_len = self.ipv4_packet_ref().ipv4_header_len(); 552 | *slice_mut!(&mut self.data[header_len..], 0..2) = port.to_be_bytes(); 553 | self.fix_checksum(); 554 | } 555 | 556 | pub fn set_destination_port(&mut self, port: u16) { 557 | let header_len = self.ipv4_packet_ref().ipv4_header_len(); 558 | *slice_mut!(&mut self.data[header_len..], 2..4) = port.to_be_bytes(); 559 | self.fix_checksum(); 560 | } 561 | 562 | pub fn set_source_addr(&mut self, addr: SocketAddrV4) { 563 | self.ipv4_packet_mut().set_source_addr(*addr.ip()); 564 | self.set_source_port(addr.port()); 565 | } 566 | 567 | pub fn set_destination_addr(&mut self, addr: SocketAddrV4) { 568 | self.ipv4_packet_mut().set_destination_addr(*addr.ip()); 569 | self.set_destination_port(addr.port()); 570 | } 571 | 572 | pub fn set_seq_number(&mut self, seq_number: u32) { 573 | let header_len = self.ipv4_packet_ref().ipv4_header_len(); 574 | let seq_bytes = seq_number.to_be_bytes(); 575 | *slice_mut!(&mut self.data[header_len..], 4..8) = seq_bytes; 576 | self.fix_checksum(); 577 | } 578 | 579 | pub fn set_ack_number(&mut self, ack_number: u32) { 580 | let header_len = self.ipv4_packet_ref().ipv4_header_len(); 581 | let ack_bytes = ack_number.to_be_bytes(); 582 | *slice_mut!(&mut self.data[header_len..], 8..12) = ack_bytes; 583 | self.fix_checksum(); 584 | } 585 | 586 | fn fix_checksum(&mut self) { 587 | let ipv4_header_len = self.ipv4_packet_ref().ipv4_header_len(); 588 | let mut hasher = Ipv4Hasher::new(); 589 | hasher.write_u32(u32::from(self.ipv4_packet_ref().source_addr())); 590 | hasher.write_u32(u32::from(self.ipv4_packet_ref().destination_addr())); 591 | hasher.write_u16(protocol_numbers::TCP as u16); 592 | hasher.write_u16((self.data.len() - ipv4_header_len) as u16); 593 | let mut i = ipv4_header_len; 594 | while i + 1 < self.data.len() { 595 | if i != ipv4_header_len + 16 { 596 | hasher.write_u16(u16::from_be_bytes(slice!(&self.data[i..], 0..2))); 597 | } 598 | i += 2; 599 | } 600 | if i < self.data.len() { 601 | debug_assert_eq!(i + 1, self.data.len()); 602 | hasher.write_u16((self.data[i] as u16) << 8); 603 | } 604 | *slice_mut!(&mut self.data[ipv4_header_len..], 16..18) = hasher.finish().to_be_bytes(); 605 | } 606 | 607 | pub fn new() -> Box { 608 | let mut data = Vec::with_capacity(40); 609 | data.push((4u8 << 4) | 5u8); 610 | data.push(0); 611 | data.extend(40u16.to_be_bytes()); 612 | 613 | data.extend(0u16.to_be_bytes()); 614 | data.push(0x40); 615 | data.push(0); 616 | 617 | data.push(64); 618 | data.push(protocol_numbers::TCP); 619 | data.extend([0, 0]); 620 | 621 | data.extend([0; 4]); 622 | data.extend([0; 4]); 623 | 624 | data.extend([0; 2]); 625 | data.extend([0; 2]); 626 | data.extend([0; 4]); 627 | data.extend([0; 4]); 628 | 629 | data.push(5 << 4); 630 | data.push(0); 631 | data.extend(0u16.to_be_bytes()); 632 | 633 | data.extend([0; 2]); 634 | data.extend([0; 2]); 635 | 636 | let ret: Box<[u8]> = data.into(); 637 | let mut ret: Box = unsafe { mem::transmute(ret) }; 638 | ret.ipv4_packet_mut().fix_checksum(); 639 | ret 640 | } 641 | } 642 | 643 | impl Udpv4Packet { 644 | pub fn source_ip_addr(&self) -> Ipv4Addr { 645 | self.ipv4_packet_ref().source_addr() 646 | } 647 | 648 | pub fn destination_ip_addr(&self) -> Ipv4Addr { 649 | self.ipv4_packet_ref().destination_addr() 650 | } 651 | 652 | pub fn source_port(&self) -> u16 { 653 | let header_len = self.ipv4_packet_ref().ipv4_header_len(); 654 | let port = slice!(&self.data[header_len..], 0..2); 655 | u16::from_be_bytes(port) 656 | } 657 | 658 | pub fn destination_port(&self) -> u16 { 659 | let header_len = self.ipv4_packet_ref().ipv4_header_len(); 660 | let port = slice!(&self.data[header_len..], 2..4); 661 | u16::from_be_bytes(port) 662 | } 663 | 664 | pub fn source_addr(&self) -> SocketAddrV4 { 665 | let ip_addr = self.source_ip_addr(); 666 | let port = self.source_port(); 667 | SocketAddrV4::new(ip_addr, port) 668 | } 669 | 670 | pub fn destination_addr(&self) -> SocketAddrV4 { 671 | let ip_addr = self.destination_ip_addr(); 672 | let port = self.destination_port(); 673 | SocketAddrV4::new(ip_addr, port) 674 | } 675 | 676 | pub fn set_source_port(&mut self, port: u16) { 677 | let header_len = self.ipv4_packet_ref().ipv4_header_len(); 678 | *slice_mut!(&mut self.data[header_len..], 0..2) = port.to_be_bytes(); 679 | self.fix_checksum(); 680 | } 681 | 682 | pub fn set_destination_port(&mut self, port: u16) { 683 | let header_len = self.ipv4_packet_ref().ipv4_header_len(); 684 | *slice_mut!(&mut self.data[header_len..], 2..4) = port.to_be_bytes(); 685 | self.fix_checksum(); 686 | } 687 | 688 | pub fn set_source_addr(&mut self, addr: SocketAddrV4) { 689 | self.ipv4_packet_mut().set_source_addr(*addr.ip()); 690 | self.set_source_port(addr.port()); 691 | } 692 | 693 | pub fn set_destination_addr(&mut self, addr: SocketAddrV4) { 694 | self.ipv4_packet_mut().set_destination_addr(*addr.ip()); 695 | self.set_destination_port(addr.port()); 696 | } 697 | 698 | pub fn data(&self) -> &[u8] { 699 | let header_len = self.ipv4_packet_ref().ipv4_header_len(); 700 | let udp_header_len = 8; 701 | let full_header_len = header_len + udp_header_len; 702 | &self.data[full_header_len..] 703 | } 704 | 705 | fn fix_checksum(&mut self) { 706 | let ipv4_header_len = self.ipv4_packet_ref().ipv4_header_len(); 707 | let mut hasher = Ipv4Hasher::new(); 708 | hasher.write_u32(u32::from(self.ipv4_packet_ref().source_addr())); 709 | hasher.write_u32(u32::from(self.ipv4_packet_ref().destination_addr())); 710 | hasher.write_u16(protocol_numbers::UDP as u16); 711 | hasher.write_u16((self.data.len() - ipv4_header_len) as u16); 712 | let mut i = ipv4_header_len; 713 | while i + 1 < self.data.len() { 714 | if i != ipv4_header_len + 6 { 715 | hasher.write_u16(u16::from_be_bytes(slice!(&self.data[i..], 0..2))); 716 | } 717 | i += 2; 718 | } 719 | if i < self.data.len() { 720 | debug_assert_eq!(i + 1, self.data.len()); 721 | hasher.write_u16((self.data[i] as u16) << 8); 722 | } 723 | *slice_mut!(&mut self.data[ipv4_header_len..], 6..8) = hasher.finish().to_be_bytes(); 724 | } 725 | } 726 | 727 | impl Icmpv4Packet { 728 | pub fn source_addr(&self) -> Ipv4Addr { 729 | self.ipv4_packet_ref().source_addr() 730 | } 731 | 732 | pub fn destination_addr(&self) -> Ipv4Addr { 733 | self.ipv4_packet_ref().destination_addr() 734 | } 735 | } 736 | 737 | impl Icmpv6Packet { 738 | pub fn source_addr(&self) -> Ipv6Addr { 739 | self.ipv6_packet_ref().source_addr() 740 | } 741 | 742 | pub fn destination_addr(&self) -> Ipv6Addr { 743 | self.ipv6_packet_ref().destination_addr() 744 | } 745 | } 746 | 747 | impl fmt::Debug for IpPacket { 748 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 749 | match self.version_ref() { 750 | IpPacketVersion::V4(packet) => fmt::Debug::fmt(&packet, formatter), 751 | IpPacketVersion::V6(packet) => fmt::Debug::fmt(&packet, formatter), 752 | } 753 | } 754 | } 755 | 756 | impl fmt::Debug for Ipv4Packet { 757 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 758 | match self.protocol_ref() { 759 | Ipv4PacketProtocol::Tcp(tcp) => fmt::Debug::fmt(&tcp, formatter), 760 | Ipv4PacketProtocol::Udp(udp) => fmt::Debug::fmt(&udp, formatter), 761 | Ipv4PacketProtocol::Icmp(icmp) => fmt::Debug::fmt(&icmp, formatter), 762 | Ipv4PacketProtocol::Unknown { protocol_number } => { 763 | formatter 764 | .debug_struct("Ipv4Packet") 765 | .field("source_addr", &self.source_addr()) 766 | .field("destination_addr", &self.destination_addr()) 767 | .field("protocol", &protocol_number) 768 | .finish() 769 | }, 770 | } 771 | } 772 | } 773 | 774 | impl fmt::Debug for Ipv6Packet { 775 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 776 | match self.protocol_ref() { 777 | Ipv6PacketProtocol::Icmp(icmp) => fmt::Debug::fmt(&icmp, formatter), 778 | Ipv6PacketProtocol::Unknown { protocol_number } => { 779 | formatter 780 | .debug_struct("Ipv6Packet") 781 | .field("source_addr", &self.source_addr()) 782 | .field("destination_addr", &self.destination_addr()) 783 | .field("protocol", &protocol_number) 784 | .finish() 785 | }, 786 | } 787 | } 788 | } 789 | 790 | impl fmt::Debug for Tcpv4Packet { 791 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 792 | formatter 793 | .debug_struct("Tcpv4Packet") 794 | .field("source_addr", &self.source_addr()) 795 | .field("destination_addr", &self.destination_addr()) 796 | .field("seq_number", &self.seq_number()) 797 | .field("ack_number", &self.ack_number()) 798 | .field("flags", &self.flags()) 799 | .finish() 800 | } 801 | } 802 | 803 | impl fmt::Debug for Udpv4Packet { 804 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 805 | formatter 806 | .debug_struct("Udpv4Packet") 807 | .field("source_addr", &self.source_addr()) 808 | .field("destination_addr", &self.destination_addr()) 809 | .finish() 810 | } 811 | } 812 | 813 | impl fmt::Debug for Icmpv4Packet { 814 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 815 | formatter 816 | .debug_struct("Icmpv4Packet") 817 | .field("source_addr", &self.source_addr()) 818 | .field("destination_addr", &self.destination_addr()) 819 | .finish() 820 | } 821 | } 822 | 823 | impl fmt::Debug for Icmpv6Packet { 824 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 825 | formatter 826 | .debug_struct("Icmpv6Packet") 827 | .field("source_addr", &self.source_addr()) 828 | .field("destination_addr", &self.destination_addr()) 829 | .finish() 830 | } 831 | } 832 | 833 | -------------------------------------------------------------------------------- /src/priv_prelude.rs: -------------------------------------------------------------------------------- 1 | pub(crate) use { 2 | std::{ 3 | cmp, fmt, io, mem, panic, ptr, slice, task, thread, str, 4 | collections::{VecDeque, hash_map, HashMap, BTreeMap, HashSet}, 5 | ffi::{CStr, CString}, 6 | future::{Future, IntoFuture}, 7 | fs::File, 8 | io::Write, 9 | mem::MaybeUninit, 10 | net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4}, 11 | os::fd::{OwnedFd, FromRawFd, AsRawFd}, 12 | pin::Pin, 13 | sync::Arc, 14 | task::Poll, 15 | time::{Duration, Instant}, 16 | }, 17 | bytes::BytesMut, 18 | libc::{c_int, c_long, c_void, pid_t}, 19 | tokio::io::unix::AsyncFd, 20 | futures::{ 21 | ready, FutureExt, Sink, Stream, StreamExt, 22 | stream::FusedStream, 23 | channel::mpsc, 24 | }, 25 | ioctl_sys::ioctl, 26 | pin_project::pin_project, 27 | rand::Rng, 28 | log::{log_enabled, debug, Level}, 29 | netsim_macros::{ipv4_network, ipv6_network}, 30 | crate::{ 31 | namespace, ioctl, iface, adapter, sys, 32 | device::IpChannel, 33 | machine::Machine, 34 | iface::{ 35 | create::IpIfaceBuilder, 36 | stream::{IpIface, IpSinkStream}, 37 | }, 38 | network::{Ipv4Network, Ipv6Network}, 39 | packet::{ 40 | IpPacket, IpPacketVersion, Ipv4PacketProtocol, Tcpv4Packet, TcpPacketFlags, 41 | }, 42 | }, 43 | }; 44 | 45 | #[cfg(test)] 46 | pub(crate) use { 47 | net_literals::{ipv4, addrv4}, 48 | tokio::{ 49 | io::{AsyncReadExt, AsyncWriteExt}, 50 | net::{TcpStream, TcpListener}, 51 | }, 52 | futures::{join, SinkExt}, 53 | crate::{ 54 | device::{BiChannel, IpHub, NatBuilder}, 55 | SinkStreamExt, 56 | }, 57 | }; 58 | 59 | -------------------------------------------------------------------------------- /src/stream_ext.rs: -------------------------------------------------------------------------------- 1 | use crate::priv_prelude::*; 2 | 3 | /// Extension trait for types which are both `Sink`s and `Stream`s. 4 | pub trait SinkStreamExt: Stream + Sink { 5 | /// Delays items sent/received through this `Sink`/`Stream`. 6 | /// 7 | /// * `min_delay` is the minimum delay which is applied to all items. 8 | /// * `mean_additional_delay` is the average randomized delay applied to all items in addition 9 | /// to `min_delay`. Setting this to zero disables delay randomization and guarantees that 10 | /// items are recieved in the order they're sent. 11 | fn with_delay( 12 | self, 13 | min_delay: Duration, 14 | mean_additional_delay: Duration, 15 | ) -> crate::adapter::Delay 16 | where 17 | Self: Sized, 18 | { 19 | crate::adapter::Delay::new(self, min_delay, mean_additional_delay) 20 | } 21 | 22 | /// Randomly drops items sent through this `Sink`/`Stream`. 23 | /// 24 | /// * `loss_rate` is what proportion of the items to drop. Setting to `1.0` will drop 25 | /// everything, setting to `0.0` will drop nothing. 26 | /// * `jitter_period` controls the average rate of switching between dropping and not dropping 27 | /// items. Setting this to zero disables jitter so that each item has an independent 28 | /// probability of getting dropped. 29 | fn with_loss( 30 | self, 31 | loss_rate: f64, 32 | jitter_period: Duration, 33 | ) -> crate::adapter::Loss 34 | where 35 | Self: Sized, 36 | { 37 | crate::adapter::Loss::new(self, loss_rate, jitter_period) 38 | } 39 | } 40 | 41 | impl SinkStreamExt for S 42 | where 43 | S: Stream + Sink, 44 | { 45 | } 46 | 47 | -------------------------------------------------------------------------------- /src/sys.rs: -------------------------------------------------------------------------------- 1 | pub const NLMSG_ALIGNTO: usize = 4; 2 | 3 | #[repr(C)] 4 | #[derive(Debug, Clone, Copy)] 5 | pub struct ifaddrmsg { 6 | pub ifa_family: u8, 7 | pub ifa_prefixlen: u8, 8 | pub ifa_flags: u8, 9 | pub ifa_scope: u8, 10 | pub ifa_index: u32, 11 | } 12 | 13 | #[repr(C)] 14 | #[derive(Debug, Clone, Copy)] 15 | pub struct rtattr { 16 | pub rta_len: libc::c_ushort, 17 | pub rta_type: libc::c_ushort, 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/tests/delay.rs: -------------------------------------------------------------------------------- 1 | use crate::priv_prelude::*; 2 | 3 | #[tokio::test(flavor = "multi_thread")] 4 | async fn zero_random_delay_in_order() { 5 | const NUM_MSGS: usize = 1000; 6 | const MIN_DELAY: Duration = Duration::from_millis(200); 7 | 8 | let (chan_0, mut chan_1) = BiChannel::new(NUM_MSGS); 9 | let mut chan_0 = Box::pin(chan_0.with_delay(MIN_DELAY, Duration::ZERO)); 10 | let sender_0 = async move { 11 | for val in 0..NUM_MSGS { 12 | chan_0.feed(val).await.unwrap(); 13 | } 14 | chan_0.flush().await.unwrap(); 15 | chan_0 16 | }; 17 | let receiver_1 = async move { 18 | for expected_val in 0..NUM_MSGS { 19 | let val = chan_1.next().await.unwrap(); 20 | assert_eq!(val, expected_val); 21 | } 22 | chan_1 23 | }; 24 | let sender_0 = tokio::spawn(sender_0); 25 | let receiver_1 = tokio::spawn(receiver_1); 26 | let (chan_0_res, chan_1_res) = join!(sender_0, receiver_1); 27 | let mut chan_0 = chan_0_res.unwrap(); 28 | let mut chan_1 = chan_1_res.unwrap(); 29 | 30 | let sender_1 = async move { 31 | for val in 0..NUM_MSGS { 32 | chan_1.feed(val).await.unwrap(); 33 | } 34 | chan_1.flush().await.unwrap(); 35 | chan_1 36 | }; 37 | let receiver_0 = async move { 38 | for expected_val in 0..NUM_MSGS { 39 | let val = chan_0.next().await.unwrap(); 40 | assert_eq!(val, expected_val); 41 | } 42 | chan_0 43 | }; 44 | let sender_1 = tokio::spawn(sender_1); 45 | let receiver_0 = tokio::spawn(receiver_0); 46 | let (chan_0_res, chan_1_res) = join!(receiver_0, sender_1); 47 | let mut chan_0 = chan_0_res.unwrap(); 48 | let chan_1 = chan_1_res.unwrap(); 49 | 50 | drop(chan_1); 51 | assert!(chan_0.next().await.is_none()); 52 | } 53 | 54 | #[tokio::test(flavor = "multi_thread")] 55 | async fn random_delays_are_approx_correct() { 56 | const NUM_MSGS: usize = 1_000; 57 | const MIN_DELAY: Duration = Duration::from_millis(500); 58 | const MEAN_RANDOM_DELAY: Duration = Duration::from_millis(500); 59 | 60 | let (chan_0, mut chan_1) = BiChannel::new(NUM_MSGS); 61 | let mut chan_0 = Box::pin(chan_0.with_delay(MIN_DELAY, MEAN_RANDOM_DELAY)); 62 | 63 | let check_delays = |delays: Vec| { 64 | let min_delay = delays.iter().copied().min().unwrap(); 65 | assert!(MIN_DELAY <= min_delay); 66 | assert!(min_delay <= MIN_DELAY * 2); 67 | 68 | let total_random_delays: f64 = { 69 | delays 70 | .into_iter() 71 | .map(|delay| delay - min_delay) 72 | .map(|random_delay| random_delay.as_secs_f64()) 73 | .sum() 74 | }; 75 | let mean_random_delay = total_random_delays / (NUM_MSGS as f64); 76 | let expected_mean_random_delay = MEAN_RANDOM_DELAY.as_secs_f64(); 77 | assert!(expected_mean_random_delay * 0.8 < mean_random_delay); 78 | assert!(mean_random_delay < expected_mean_random_delay * 1.2); 79 | }; 80 | 81 | let mut delays = Vec::with_capacity(NUM_MSGS); 82 | let sender = async move { 83 | for _ in 0..NUM_MSGS { 84 | let send_instant = Instant::now(); 85 | chan_0.feed(send_instant).await.unwrap(); 86 | } 87 | chan_0.flush().await.unwrap(); 88 | chan_0 89 | }; 90 | let receiver = async move { 91 | for _ in 0..NUM_MSGS { 92 | let send_instant = chan_1.next().await.unwrap(); 93 | let delay = Instant::now() - send_instant; 94 | delays.push(delay); 95 | } 96 | (chan_1, delays) 97 | }; 98 | let receiver = tokio::spawn(receiver); 99 | tokio::time::sleep(MIN_DELAY).await; 100 | let sender = tokio::spawn(sender); 101 | let (chan_0_res, chan_1_delays_res) = join!(sender, receiver); 102 | let mut chan_0 = chan_0_res.unwrap(); 103 | let (mut chan_1, delays) = chan_1_delays_res.unwrap(); 104 | check_delays(delays); 105 | 106 | let mut delays = Vec::with_capacity(NUM_MSGS); 107 | let sender = async move { 108 | for _ in 0..NUM_MSGS { 109 | let send_instant = Instant::now(); 110 | chan_1.feed(send_instant).await.unwrap(); 111 | } 112 | chan_1.flush().await.unwrap(); 113 | chan_1 114 | }; 115 | let receiver = async move { 116 | for _ in 0..NUM_MSGS { 117 | let send_instant = chan_0.next().await.unwrap(); 118 | let delay = Instant::now() - send_instant; 119 | delays.push(delay); 120 | } 121 | (chan_0, delays) 122 | }; 123 | let receiver = tokio::spawn(receiver); 124 | tokio::time::sleep(MIN_DELAY).await; 125 | let sender = tokio::spawn(sender); 126 | let (chan_1_res, chan_0_delays_res) = join!(sender, receiver); 127 | let chan_1 = chan_1_res.unwrap(); 128 | let (chan_0, delays) = chan_0_delays_res.unwrap(); 129 | check_delays(delays); 130 | 131 | drop((chan_0, chan_1)); 132 | } 133 | 134 | -------------------------------------------------------------------------------- /src/tests/loss.rs: -------------------------------------------------------------------------------- 1 | use crate::priv_prelude::*; 2 | 3 | #[tokio::test(flavor = "multi_thread")] 4 | async fn zero_jitter_is_random() { 5 | const NUM_MSGS: usize = 1000; 6 | const LOSS_RATE: f64 = 0.3; 7 | 8 | let (chan_0, mut chan_1) = BiChannel::new(NUM_MSGS); 9 | let mut chan_0 = Box::pin(chan_0.with_loss(LOSS_RATE, Duration::ZERO)); 10 | let sender = async move { 11 | for val in 0..NUM_MSGS { 12 | chan_0.feed(val).await.unwrap(); 13 | } 14 | chan_0.flush().await.unwrap(); 15 | chan_0.close().await.unwrap(); 16 | }; 17 | let receiver = async move { 18 | let mut received = vec![false; NUM_MSGS]; 19 | 20 | while let Some(val) = chan_1.next().await { 21 | received[val] = true; 22 | } 23 | received 24 | }; 25 | let sender = tokio::spawn(sender); 26 | let receiver = tokio::spawn(receiver); 27 | let (sender_res, receiver_res) = join!(sender, receiver); 28 | let () = sender_res.unwrap(); 29 | let received = receiver_res.unwrap(); 30 | 31 | let lost_count = received.iter().filter(|x| !**x).count(); 32 | let loss_rate = (lost_count as f64) / (NUM_MSGS as f64); 33 | assert!(loss_rate < LOSS_RATE * 1.2); 34 | assert!(LOSS_RATE < loss_rate * 1.2); 35 | 36 | let mut after_received_count = 0; 37 | let mut after_received_lost_count = 0; 38 | for xs in received.windows(2) { 39 | if xs[0] { 40 | after_received_count += 1; 41 | if !xs[1] { 42 | after_received_lost_count += 1; 43 | } 44 | } 45 | } 46 | let after_received_loss_rate = (after_received_lost_count as f64) / (after_received_count as f64); 47 | assert!(after_received_loss_rate < LOSS_RATE * 1.2); 48 | assert!(LOSS_RATE < after_received_loss_rate * 1.2); 49 | } 50 | 51 | #[tokio::test(flavor = "multi_thread")] 52 | async fn non_zero_jitter_is_locally_non_random() { 53 | const NUM_MSGS: usize = 1000; 54 | const LOSS_RATE: f64 = 0.3; 55 | const JITTER_PERIOD: Duration = Duration::from_millis(5); 56 | const MSG_PERIOD: Duration = Duration::from_millis(1); 57 | 58 | let (chan_0, mut chan_1) = BiChannel::new(NUM_MSGS); 59 | let mut chan_0 = Box::pin(chan_0.with_loss(LOSS_RATE, JITTER_PERIOD)); 60 | let sender = async move { 61 | for val in 0..NUM_MSGS { 62 | chan_0.feed(val).await.unwrap(); 63 | tokio::time::sleep(MSG_PERIOD).await; 64 | } 65 | chan_0.flush().await.unwrap(); 66 | chan_0.close().await.unwrap(); 67 | }; 68 | let receiver = async move { 69 | let mut received = vec![false; NUM_MSGS]; 70 | 71 | while let Some(val) = chan_1.next().await { 72 | received[val] = true; 73 | } 74 | received 75 | }; 76 | let sender = tokio::spawn(sender); 77 | let receiver = tokio::spawn(receiver); 78 | let (sender_res, receiver_res) = join!(sender, receiver); 79 | let () = sender_res.unwrap(); 80 | let received = receiver_res.unwrap(); 81 | 82 | let lost_count = received.iter().filter(|x| !**x).count(); 83 | let loss_rate = (lost_count as f64) / (NUM_MSGS as f64); 84 | assert!(loss_rate < LOSS_RATE * 1.2); 85 | assert!(LOSS_RATE < loss_rate * 1.2); 86 | 87 | let mut after_received_count = 0; 88 | let mut after_received_lost_count = 0; 89 | for xs in received.windows(2) { 90 | if xs[0] { 91 | after_received_count += 1; 92 | if !xs[1] { 93 | after_received_lost_count += 1; 94 | } 95 | } 96 | } 97 | let after_received_loss_rate = (after_received_lost_count as f64) / (after_received_count as f64); 98 | assert!(after_received_loss_rate < LOSS_RATE); 99 | } 100 | 101 | -------------------------------------------------------------------------------- /src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | mod loss; 2 | mod delay; 3 | mod nat; 4 | 5 | -------------------------------------------------------------------------------- /src/tests/nat.rs: -------------------------------------------------------------------------------- 1 | use crate::priv_prelude::*; 2 | 3 | #[tokio::test] 4 | async fn connect_to_outside_world() { 5 | let internal_addr = addrv4!("172.16.5.5:45666"); 6 | let external_ip = ipv4!("115.70.254.200"); 7 | let addr_1 = addrv4!("115.70.254.190:45000"); 8 | 9 | let machine_0 = Machine::new().unwrap(); 10 | let machine_1 = Machine::new().unwrap(); 11 | 12 | let iface_0 = { 13 | machine_0 14 | .add_ip_iface() 15 | .ipv4_addr(*internal_addr.ip()) 16 | .ipv4_default_route() 17 | .build() 18 | .unwrap() 19 | }; 20 | let iface_1 = { 21 | machine_1 22 | .add_ip_iface() 23 | .ipv4_addr(*addr_1.ip()) 24 | .ipv4_default_route() 25 | .build() 26 | .unwrap() 27 | }; 28 | 29 | let (mut nat, nat_iface) = { 30 | NatBuilder::new( 31 | external_ip, 32 | Ipv4Network::infer_from_addr(*internal_addr.ip()), 33 | ) 34 | .build() 35 | }; 36 | nat.insert_iface(iface_0); 37 | 38 | let mut hub = IpHub::new(); 39 | hub.insert_iface(nat_iface); 40 | hub.insert_iface(iface_1); 41 | 42 | let (ready_tx, ready_rx) = oneshot::channel(); 43 | const MSG_0: &[u8; 4] = b"ping"; 44 | const MSG_1: &[u8; 4] = b"pong"; 45 | 46 | let task_0 = machine_0.spawn(async move { 47 | let () = ready_rx.await.unwrap(); 48 | let mut stream = TcpStream::connect(addr_1).await.unwrap(); 49 | let mut received_msg = [0u8; MSG_0.len()]; 50 | stream.read_exact(&mut received_msg).await.unwrap(); 51 | assert_eq!(received_msg, *MSG_0); 52 | stream.write_all(MSG_1).await.unwrap(); 53 | }); 54 | 55 | let task_1 = machine_1.spawn(async move { 56 | let listener = TcpListener::bind(addr_1).await.unwrap(); 57 | ready_tx.send(()).unwrap(); 58 | let (mut stream, addr) = listener.accept().await.unwrap(); 59 | assert_eq!(addr.ip(), external_ip); 60 | stream.write_all(MSG_0).await.unwrap(); 61 | let mut received_msg = [0u8; MSG_1.len()]; 62 | stream.read_exact(&mut received_msg).await.unwrap(); 63 | assert_eq!(received_msg, *MSG_1); 64 | }); 65 | 66 | let (res_0, res_1) = futures::join!(task_0.join(), task_1.join()); 67 | let () = res_0.unwrap().unwrap(); 68 | let () = res_1.unwrap().unwrap(); 69 | } 70 | 71 | #[tokio::test] 72 | async fn nat_tcp_reset() { 73 | let machine_addr = addrv4!("115.70.254.190:45000"); 74 | let nat_addr = addrv4!("115.70.254.200:45666"); 75 | 76 | let machine = Machine::new().unwrap(); 77 | let machine_iface = { 78 | machine 79 | .add_ip_iface() 80 | .ipv4_addr(*machine_addr.ip()) 81 | .ipv4_default_route() 82 | .build() 83 | .unwrap() 84 | }; 85 | let (_nat, nat_iface) = { 86 | NatBuilder::new( 87 | *nat_addr.ip(), 88 | Ipv4Network::new(ipv4!("192.168.0.0"), 16), 89 | ) 90 | .reply_with_rst_to_unexpected_tcp_packets() 91 | .build() 92 | }; 93 | 94 | crate::connect(machine_iface, nat_iface); 95 | 96 | let res = machine.spawn(async move { 97 | tokio::time::timeout(Duration::from_millis(100), TcpStream::connect(nat_addr)).await 98 | }).await.unwrap().unwrap().unwrap(); 99 | match res { 100 | Ok(_) => unreachable!(), 101 | Err(err) => match err.kind() { 102 | io::ErrorKind::ConnectionRefused => (), 103 | kind => panic!("unexpected error kind: {}", kind), 104 | }, 105 | } 106 | } 107 | 108 | --------------------------------------------------------------------------------