├── .gitignore ├── .gitmodules ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── build.rs ├── demo.gif └── src ├── app.rs ├── bpf ├── p2pflow.bpf.c ├── p2pflow.h └── vmlinux.h ├── display.rs ├── event.rs ├── main.rs └── net.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /src/bpf/.output 2 | /target 3 | .vscode -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libbpf"] 2 | path = libbpf 3 | url = https://github.com/libbpf/libbpf.git 4 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "adler" 7 | version = "1.0.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 10 | 11 | [[package]] 12 | name = "aho-corasick" 13 | version = "0.7.18" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 16 | dependencies = [ 17 | "memchr", 18 | ] 19 | 20 | [[package]] 21 | name = "ansi_term" 22 | version = "0.11.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 25 | dependencies = [ 26 | "winapi", 27 | ] 28 | 29 | [[package]] 30 | name = "anyhow" 31 | version = "1.0.45" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "ee10e43ae4a853c0a3591d4e2ada1719e553be18199d9da9d4a83f5927c2f5c7" 34 | 35 | [[package]] 36 | name = "arraydeque" 37 | version = "0.4.5" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "f0ffd3d69bd89910509a5d31d1f1353f38ccffdd116dd0099bbd6627f7bd8ad8" 40 | 41 | [[package]] 42 | name = "async-channel" 43 | version = "1.6.1" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" 46 | dependencies = [ 47 | "concurrent-queue", 48 | "event-listener", 49 | "futures-core", 50 | ] 51 | 52 | [[package]] 53 | name = "async-executor" 54 | version = "1.4.1" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" 57 | dependencies = [ 58 | "async-task", 59 | "concurrent-queue", 60 | "fastrand", 61 | "futures-lite", 62 | "once_cell", 63 | "slab", 64 | ] 65 | 66 | [[package]] 67 | name = "async-global-executor" 68 | version = "2.0.2" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "9586ec52317f36de58453159d48351bc244bc24ced3effc1fce22f3d48664af6" 71 | dependencies = [ 72 | "async-channel", 73 | "async-executor", 74 | "async-io", 75 | "async-mutex", 76 | "blocking", 77 | "futures-lite", 78 | "num_cpus", 79 | "once_cell", 80 | ] 81 | 82 | [[package]] 83 | name = "async-io" 84 | version = "1.6.0" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b" 87 | dependencies = [ 88 | "concurrent-queue", 89 | "futures-lite", 90 | "libc", 91 | "log", 92 | "once_cell", 93 | "parking", 94 | "polling", 95 | "slab", 96 | "socket2 0.4.2", 97 | "waker-fn", 98 | "winapi", 99 | ] 100 | 101 | [[package]] 102 | name = "async-lock" 103 | version = "2.4.0" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "e6a8ea61bf9947a1007c5cada31e647dbc77b103c679858150003ba697ea798b" 106 | dependencies = [ 107 | "event-listener", 108 | ] 109 | 110 | [[package]] 111 | name = "async-mutex" 112 | version = "1.4.0" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" 115 | dependencies = [ 116 | "event-listener", 117 | ] 118 | 119 | [[package]] 120 | name = "async-std" 121 | version = "1.10.0" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "f8056f1455169ab86dd47b47391e4ab0cbd25410a70e9fe675544f49bafaf952" 124 | dependencies = [ 125 | "async-channel", 126 | "async-global-executor", 127 | "async-io", 128 | "async-lock", 129 | "crossbeam-utils", 130 | "futures-channel", 131 | "futures-core", 132 | "futures-io", 133 | "futures-lite", 134 | "gloo-timers", 135 | "kv-log-macro", 136 | "log", 137 | "memchr", 138 | "num_cpus", 139 | "once_cell", 140 | "pin-project-lite", 141 | "pin-utils", 142 | "slab", 143 | "wasm-bindgen-futures", 144 | ] 145 | 146 | [[package]] 147 | name = "async-std-resolver" 148 | version = "0.20.3" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "ed4e2c3da14d8ad45acb1e3191db7a918e9505b6f155b218e70a7c9a1a48c638" 151 | dependencies = [ 152 | "async-std", 153 | "async-trait", 154 | "futures-io", 155 | "futures-util", 156 | "pin-utils", 157 | "trust-dns-resolver", 158 | ] 159 | 160 | [[package]] 161 | name = "async-task" 162 | version = "4.0.3" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" 165 | 166 | [[package]] 167 | name = "async-trait" 168 | version = "0.1.52" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" 171 | dependencies = [ 172 | "proc-macro2", 173 | "quote", 174 | "syn", 175 | ] 176 | 177 | [[package]] 178 | name = "atomic-waker" 179 | version = "1.0.0" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" 182 | 183 | [[package]] 184 | name = "atty" 185 | version = "0.2.14" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 188 | dependencies = [ 189 | "hermit-abi", 190 | "libc", 191 | "winapi", 192 | ] 193 | 194 | [[package]] 195 | name = "autocfg" 196 | version = "1.0.1" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 199 | 200 | [[package]] 201 | name = "bitflags" 202 | version = "1.3.2" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 205 | 206 | [[package]] 207 | name = "blocking" 208 | version = "1.1.0" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "046e47d4b2d391b1f6f8b407b1deb8dee56c1852ccd868becf2710f601b5f427" 211 | dependencies = [ 212 | "async-channel", 213 | "async-task", 214 | "atomic-waker", 215 | "fastrand", 216 | "futures-lite", 217 | "once_cell", 218 | ] 219 | 220 | [[package]] 221 | name = "bumpalo" 222 | version = "3.8.0" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" 225 | 226 | [[package]] 227 | name = "cache-padded" 228 | version = "1.1.1" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" 231 | 232 | [[package]] 233 | name = "cargo-platform" 234 | version = "0.1.2" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" 237 | dependencies = [ 238 | "serde", 239 | ] 240 | 241 | [[package]] 242 | name = "cargo_metadata" 243 | version = "0.12.3" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "7714a157da7991e23d90686b9524b9e12e0407a108647f52e9328f4b3d51ac7f" 246 | dependencies = [ 247 | "cargo-platform", 248 | "semver 0.11.0", 249 | "semver-parser", 250 | "serde", 251 | "serde_json", 252 | ] 253 | 254 | [[package]] 255 | name = "cassowary" 256 | version = "0.3.0" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" 259 | 260 | [[package]] 261 | name = "cc" 262 | version = "1.0.72" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" 265 | 266 | [[package]] 267 | name = "cfg-if" 268 | version = "1.0.0" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 271 | 272 | [[package]] 273 | name = "clap" 274 | version = "2.33.3" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" 277 | dependencies = [ 278 | "ansi_term", 279 | "atty", 280 | "bitflags", 281 | "strsim", 282 | "textwrap", 283 | "unicode-width", 284 | "vec_map", 285 | ] 286 | 287 | [[package]] 288 | name = "concurrent-queue" 289 | version = "1.2.2" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" 292 | dependencies = [ 293 | "cache-padded", 294 | ] 295 | 296 | [[package]] 297 | name = "crc32fast" 298 | version = "1.2.1" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" 301 | dependencies = [ 302 | "cfg-if", 303 | ] 304 | 305 | [[package]] 306 | name = "crossbeam-utils" 307 | version = "0.8.5" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" 310 | dependencies = [ 311 | "cfg-if", 312 | "lazy_static", 313 | ] 314 | 315 | [[package]] 316 | name = "ctor" 317 | version = "0.1.21" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa" 320 | dependencies = [ 321 | "quote", 322 | "syn", 323 | ] 324 | 325 | [[package]] 326 | name = "ctrlc" 327 | version = "3.2.1" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "a19c6cedffdc8c03a3346d723eb20bd85a13362bb96dc2ac000842c6381ec7bf" 330 | dependencies = [ 331 | "nix 0.23.0", 332 | "winapi", 333 | ] 334 | 335 | [[package]] 336 | name = "data-encoding" 337 | version = "2.3.2" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" 340 | 341 | [[package]] 342 | name = "derivative" 343 | version = "2.2.0" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" 346 | dependencies = [ 347 | "proc-macro2", 348 | "quote", 349 | "syn", 350 | ] 351 | 352 | [[package]] 353 | name = "enum-as-inner" 354 | version = "0.3.3" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "7c5f0096a91d210159eceb2ff5e1c4da18388a170e1e3ce948aac9c8fdbbf595" 357 | dependencies = [ 358 | "heck", 359 | "proc-macro2", 360 | "quote", 361 | "syn", 362 | ] 363 | 364 | [[package]] 365 | name = "event-listener" 366 | version = "2.5.1" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" 369 | 370 | [[package]] 371 | name = "fastrand" 372 | version = "1.5.0" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "b394ed3d285a429378d3b384b9eb1285267e7df4b166df24b7a6939a04dc392e" 375 | dependencies = [ 376 | "instant", 377 | ] 378 | 379 | [[package]] 380 | name = "flate2" 381 | version = "1.0.22" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" 384 | dependencies = [ 385 | "cfg-if", 386 | "crc32fast", 387 | "libc", 388 | "miniz_oxide", 389 | ] 390 | 391 | [[package]] 392 | name = "form_urlencoded" 393 | version = "1.0.1" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" 396 | dependencies = [ 397 | "matches", 398 | "percent-encoding", 399 | ] 400 | 401 | [[package]] 402 | name = "futures-channel" 403 | version = "0.3.18" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "7fc8cd39e3dbf865f7340dce6a2d401d24fd37c6fe6c4f0ee0de8bfca2252d27" 406 | dependencies = [ 407 | "futures-core", 408 | ] 409 | 410 | [[package]] 411 | name = "futures-core" 412 | version = "0.3.18" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "629316e42fe7c2a0b9a65b47d159ceaa5453ab14e8f0a3c5eedbb8cd55b4a445" 415 | 416 | [[package]] 417 | name = "futures-io" 418 | version = "0.3.18" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "e481354db6b5c353246ccf6a728b0c5511d752c08da7260546fc0933869daa11" 421 | 422 | [[package]] 423 | name = "futures-lite" 424 | version = "1.12.0" 425 | source = "registry+https://github.com/rust-lang/crates.io-index" 426 | checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" 427 | dependencies = [ 428 | "fastrand", 429 | "futures-core", 430 | "futures-io", 431 | "memchr", 432 | "parking", 433 | "pin-project-lite", 434 | "waker-fn", 435 | ] 436 | 437 | [[package]] 438 | name = "futures-task" 439 | version = "0.3.18" 440 | source = "registry+https://github.com/rust-lang/crates.io-index" 441 | checksum = "dabf1872aaab32c886832f2276d2f5399887e2bd613698a02359e4ea83f8de12" 442 | 443 | [[package]] 444 | name = "futures-util" 445 | version = "0.3.18" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | checksum = "41d22213122356472061ac0f1ab2cee28d2bac8491410fd68c2af53d1cedb83e" 448 | dependencies = [ 449 | "futures-core", 450 | "futures-task", 451 | "pin-project-lite", 452 | "pin-utils", 453 | "slab", 454 | ] 455 | 456 | [[package]] 457 | name = "getrandom" 458 | version = "0.2.3" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" 461 | dependencies = [ 462 | "cfg-if", 463 | "libc", 464 | "wasi", 465 | ] 466 | 467 | [[package]] 468 | name = "gloo-timers" 469 | version = "0.2.2" 470 | source = "registry+https://github.com/rust-lang/crates.io-index" 471 | checksum = "6f16c88aa13d2656ef20d1c042086b8767bbe2bdb62526894275a1b062161b2e" 472 | dependencies = [ 473 | "futures-channel", 474 | "futures-core", 475 | "js-sys", 476 | "wasm-bindgen", 477 | "web-sys", 478 | ] 479 | 480 | [[package]] 481 | name = "heck" 482 | version = "0.3.3" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 485 | dependencies = [ 486 | "unicode-segmentation", 487 | ] 488 | 489 | [[package]] 490 | name = "hermit-abi" 491 | version = "0.1.19" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 494 | dependencies = [ 495 | "libc", 496 | ] 497 | 498 | [[package]] 499 | name = "hostname" 500 | version = "0.3.1" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" 503 | dependencies = [ 504 | "libc", 505 | "match_cfg", 506 | "winapi", 507 | ] 508 | 509 | [[package]] 510 | name = "idna" 511 | version = "0.2.3" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" 514 | dependencies = [ 515 | "matches", 516 | "unicode-bidi", 517 | "unicode-normalization", 518 | ] 519 | 520 | [[package]] 521 | name = "instant" 522 | version = "0.1.12" 523 | source = "registry+https://github.com/rust-lang/crates.io-index" 524 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 525 | dependencies = [ 526 | "cfg-if", 527 | ] 528 | 529 | [[package]] 530 | name = "ipconfig" 531 | version = "0.2.2" 532 | source = "registry+https://github.com/rust-lang/crates.io-index" 533 | checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7" 534 | dependencies = [ 535 | "socket2 0.3.19", 536 | "widestring", 537 | "winapi", 538 | "winreg", 539 | ] 540 | 541 | [[package]] 542 | name = "ipnet" 543 | version = "2.3.1" 544 | source = "registry+https://github.com/rust-lang/crates.io-index" 545 | checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" 546 | 547 | [[package]] 548 | name = "itoa" 549 | version = "0.4.8" 550 | source = "registry+https://github.com/rust-lang/crates.io-index" 551 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" 552 | 553 | [[package]] 554 | name = "js-sys" 555 | version = "0.3.55" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" 558 | dependencies = [ 559 | "wasm-bindgen", 560 | ] 561 | 562 | [[package]] 563 | name = "kv-log-macro" 564 | version = "1.0.7" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" 567 | dependencies = [ 568 | "log", 569 | ] 570 | 571 | [[package]] 572 | name = "lazy_static" 573 | version = "1.4.0" 574 | source = "registry+https://github.com/rust-lang/crates.io-index" 575 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 576 | 577 | [[package]] 578 | name = "libbpf-cargo" 579 | version = "0.9.3" 580 | source = "registry+https://github.com/rust-lang/crates.io-index" 581 | checksum = "23536bf02ff6387e7bb68055f6ad52d75c20d77b2ab6535977fc1e771041286c" 582 | dependencies = [ 583 | "anyhow", 584 | "cargo_metadata", 585 | "libbpf-sys", 586 | "memmap2", 587 | "num_enum", 588 | "regex", 589 | "scroll", 590 | "scroll_derive", 591 | "semver 1.0.4", 592 | "serde", 593 | "serde_json", 594 | "structopt", 595 | "tempfile", 596 | "thiserror", 597 | ] 598 | 599 | [[package]] 600 | name = "libbpf-rs" 601 | version = "0.14.0" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "87d1f46d8d976bba68a9b772138e2307cfd7432bf66f3e1c83b3944e83ab9002" 604 | dependencies = [ 605 | "bitflags", 606 | "libbpf-sys", 607 | "nix 0.22.0", 608 | "num_enum", 609 | "strum_macros", 610 | "thiserror", 611 | "vsprintf", 612 | ] 613 | 614 | [[package]] 615 | name = "libbpf-sys" 616 | version = "0.5.0-2" 617 | source = "registry+https://github.com/rust-lang/crates.io-index" 618 | checksum = "97982339699ecfda9742ae39311fac5a6cce5ac7330bcf714cb702a49ecc8e2a" 619 | dependencies = [ 620 | "cc", 621 | "pkg-config", 622 | ] 623 | 624 | [[package]] 625 | name = "libc" 626 | version = "0.2.107" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" 629 | 630 | [[package]] 631 | name = "linked-hash-map" 632 | version = "0.5.4" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" 635 | 636 | [[package]] 637 | name = "lock_api" 638 | version = "0.4.5" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" 641 | dependencies = [ 642 | "scopeguard", 643 | ] 644 | 645 | [[package]] 646 | name = "log" 647 | version = "0.4.14" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 650 | dependencies = [ 651 | "cfg-if", 652 | "value-bag", 653 | ] 654 | 655 | [[package]] 656 | name = "lru-cache" 657 | version = "0.1.2" 658 | source = "registry+https://github.com/rust-lang/crates.io-index" 659 | checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" 660 | dependencies = [ 661 | "linked-hash-map", 662 | ] 663 | 664 | [[package]] 665 | name = "match_cfg" 666 | version = "0.1.0" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" 669 | 670 | [[package]] 671 | name = "matches" 672 | version = "0.1.9" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" 675 | 676 | [[package]] 677 | name = "memchr" 678 | version = "2.4.1" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 681 | 682 | [[package]] 683 | name = "memmap2" 684 | version = "0.3.1" 685 | source = "registry+https://github.com/rust-lang/crates.io-index" 686 | checksum = "00b6c2ebff6180198788f5db08d7ce3bc1d0b617176678831a7510825973e357" 687 | dependencies = [ 688 | "libc", 689 | ] 690 | 691 | [[package]] 692 | name = "memoffset" 693 | version = "0.6.4" 694 | source = "registry+https://github.com/rust-lang/crates.io-index" 695 | checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" 696 | dependencies = [ 697 | "autocfg", 698 | ] 699 | 700 | [[package]] 701 | name = "miniz_oxide" 702 | version = "0.4.4" 703 | source = "registry+https://github.com/rust-lang/crates.io-index" 704 | checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" 705 | dependencies = [ 706 | "adler", 707 | "autocfg", 708 | ] 709 | 710 | [[package]] 711 | name = "nix" 712 | version = "0.22.0" 713 | source = "registry+https://github.com/rust-lang/crates.io-index" 714 | checksum = "cf1e25ee6b412c2a1e3fcb6a4499a5c1bfe7f43e014bdce9a6b6666e5aa2d187" 715 | dependencies = [ 716 | "bitflags", 717 | "cc", 718 | "cfg-if", 719 | "libc", 720 | "memoffset", 721 | ] 722 | 723 | [[package]] 724 | name = "nix" 725 | version = "0.23.0" 726 | source = "registry+https://github.com/rust-lang/crates.io-index" 727 | checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188" 728 | dependencies = [ 729 | "bitflags", 730 | "cc", 731 | "cfg-if", 732 | "libc", 733 | "memoffset", 734 | ] 735 | 736 | [[package]] 737 | name = "num_cpus" 738 | version = "1.13.0" 739 | source = "registry+https://github.com/rust-lang/crates.io-index" 740 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 741 | dependencies = [ 742 | "hermit-abi", 743 | "libc", 744 | ] 745 | 746 | [[package]] 747 | name = "num_enum" 748 | version = "0.5.4" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "3f9bd055fb730c4f8f4f57d45d35cd6b3f0980535b056dc7ff119cee6a66ed6f" 751 | dependencies = [ 752 | "derivative", 753 | "num_enum_derive", 754 | ] 755 | 756 | [[package]] 757 | name = "num_enum_derive" 758 | version = "0.5.4" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | checksum = "486ea01961c4a818096de679a8b740b26d9033146ac5291b1c98557658f8cdd9" 761 | dependencies = [ 762 | "proc-macro-crate", 763 | "proc-macro2", 764 | "quote", 765 | "syn", 766 | ] 767 | 768 | [[package]] 769 | name = "numtoa" 770 | version = "0.1.0" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" 773 | 774 | [[package]] 775 | name = "object" 776 | version = "0.25.3" 777 | source = "registry+https://github.com/rust-lang/crates.io-index" 778 | checksum = "a38f2be3697a57b4060074ff41b44c16870d916ad7877c17696e063257482bc7" 779 | dependencies = [ 780 | "flate2", 781 | "memchr", 782 | ] 783 | 784 | [[package]] 785 | name = "once_cell" 786 | version = "1.8.0" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" 789 | 790 | [[package]] 791 | name = "p2pflow" 792 | version = "0.0.6" 793 | dependencies = [ 794 | "anyhow", 795 | "arraydeque", 796 | "async-std", 797 | "async-std-resolver", 798 | "ctrlc", 799 | "libbpf-cargo", 800 | "libbpf-rs", 801 | "libc", 802 | "object", 803 | "plain", 804 | "structopt", 805 | "termion", 806 | "tui", 807 | ] 808 | 809 | [[package]] 810 | name = "parking" 811 | version = "2.0.0" 812 | source = "registry+https://github.com/rust-lang/crates.io-index" 813 | checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" 814 | 815 | [[package]] 816 | name = "parking_lot" 817 | version = "0.11.2" 818 | source = "registry+https://github.com/rust-lang/crates.io-index" 819 | checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" 820 | dependencies = [ 821 | "instant", 822 | "lock_api", 823 | "parking_lot_core", 824 | ] 825 | 826 | [[package]] 827 | name = "parking_lot_core" 828 | version = "0.8.5" 829 | source = "registry+https://github.com/rust-lang/crates.io-index" 830 | checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" 831 | dependencies = [ 832 | "cfg-if", 833 | "instant", 834 | "libc", 835 | "redox_syscall", 836 | "smallvec", 837 | "winapi", 838 | ] 839 | 840 | [[package]] 841 | name = "percent-encoding" 842 | version = "2.1.0" 843 | source = "registry+https://github.com/rust-lang/crates.io-index" 844 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 845 | 846 | [[package]] 847 | name = "pest" 848 | version = "2.1.3" 849 | source = "registry+https://github.com/rust-lang/crates.io-index" 850 | checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" 851 | dependencies = [ 852 | "ucd-trie", 853 | ] 854 | 855 | [[package]] 856 | name = "pin-project-lite" 857 | version = "0.2.7" 858 | source = "registry+https://github.com/rust-lang/crates.io-index" 859 | checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" 860 | 861 | [[package]] 862 | name = "pin-utils" 863 | version = "0.1.0" 864 | source = "registry+https://github.com/rust-lang/crates.io-index" 865 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 866 | 867 | [[package]] 868 | name = "pkg-config" 869 | version = "0.3.22" 870 | source = "registry+https://github.com/rust-lang/crates.io-index" 871 | checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f" 872 | 873 | [[package]] 874 | name = "plain" 875 | version = "0.2.3" 876 | source = "registry+https://github.com/rust-lang/crates.io-index" 877 | checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" 878 | 879 | [[package]] 880 | name = "polling" 881 | version = "2.2.0" 882 | source = "registry+https://github.com/rust-lang/crates.io-index" 883 | checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" 884 | dependencies = [ 885 | "cfg-if", 886 | "libc", 887 | "log", 888 | "wepoll-ffi", 889 | "winapi", 890 | ] 891 | 892 | [[package]] 893 | name = "ppv-lite86" 894 | version = "0.2.15" 895 | source = "registry+https://github.com/rust-lang/crates.io-index" 896 | checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" 897 | 898 | [[package]] 899 | name = "proc-macro-crate" 900 | version = "1.1.0" 901 | source = "registry+https://github.com/rust-lang/crates.io-index" 902 | checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83" 903 | dependencies = [ 904 | "thiserror", 905 | "toml", 906 | ] 907 | 908 | [[package]] 909 | name = "proc-macro-error" 910 | version = "1.0.4" 911 | source = "registry+https://github.com/rust-lang/crates.io-index" 912 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 913 | dependencies = [ 914 | "proc-macro-error-attr", 915 | "proc-macro2", 916 | "quote", 917 | "syn", 918 | "version_check", 919 | ] 920 | 921 | [[package]] 922 | name = "proc-macro-error-attr" 923 | version = "1.0.4" 924 | source = "registry+https://github.com/rust-lang/crates.io-index" 925 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 926 | dependencies = [ 927 | "proc-macro2", 928 | "quote", 929 | "version_check", 930 | ] 931 | 932 | [[package]] 933 | name = "proc-macro2" 934 | version = "1.0.32" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" 937 | dependencies = [ 938 | "unicode-xid", 939 | ] 940 | 941 | [[package]] 942 | name = "quick-error" 943 | version = "1.2.3" 944 | source = "registry+https://github.com/rust-lang/crates.io-index" 945 | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 946 | 947 | [[package]] 948 | name = "quote" 949 | version = "1.0.10" 950 | source = "registry+https://github.com/rust-lang/crates.io-index" 951 | checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" 952 | dependencies = [ 953 | "proc-macro2", 954 | ] 955 | 956 | [[package]] 957 | name = "rand" 958 | version = "0.8.4" 959 | source = "registry+https://github.com/rust-lang/crates.io-index" 960 | checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" 961 | dependencies = [ 962 | "libc", 963 | "rand_chacha", 964 | "rand_core", 965 | "rand_hc", 966 | ] 967 | 968 | [[package]] 969 | name = "rand_chacha" 970 | version = "0.3.1" 971 | source = "registry+https://github.com/rust-lang/crates.io-index" 972 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 973 | dependencies = [ 974 | "ppv-lite86", 975 | "rand_core", 976 | ] 977 | 978 | [[package]] 979 | name = "rand_core" 980 | version = "0.6.3" 981 | source = "registry+https://github.com/rust-lang/crates.io-index" 982 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 983 | dependencies = [ 984 | "getrandom", 985 | ] 986 | 987 | [[package]] 988 | name = "rand_hc" 989 | version = "0.3.1" 990 | source = "registry+https://github.com/rust-lang/crates.io-index" 991 | checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" 992 | dependencies = [ 993 | "rand_core", 994 | ] 995 | 996 | [[package]] 997 | name = "redox_syscall" 998 | version = "0.2.10" 999 | source = "registry+https://github.com/rust-lang/crates.io-index" 1000 | checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" 1001 | dependencies = [ 1002 | "bitflags", 1003 | ] 1004 | 1005 | [[package]] 1006 | name = "redox_termios" 1007 | version = "0.1.2" 1008 | source = "registry+https://github.com/rust-lang/crates.io-index" 1009 | checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f" 1010 | dependencies = [ 1011 | "redox_syscall", 1012 | ] 1013 | 1014 | [[package]] 1015 | name = "regex" 1016 | version = "1.5.4" 1017 | source = "registry+https://github.com/rust-lang/crates.io-index" 1018 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 1019 | dependencies = [ 1020 | "aho-corasick", 1021 | "memchr", 1022 | "regex-syntax", 1023 | ] 1024 | 1025 | [[package]] 1026 | name = "regex-syntax" 1027 | version = "0.6.25" 1028 | source = "registry+https://github.com/rust-lang/crates.io-index" 1029 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 1030 | 1031 | [[package]] 1032 | name = "remove_dir_all" 1033 | version = "0.5.3" 1034 | source = "registry+https://github.com/rust-lang/crates.io-index" 1035 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 1036 | dependencies = [ 1037 | "winapi", 1038 | ] 1039 | 1040 | [[package]] 1041 | name = "resolv-conf" 1042 | version = "0.7.0" 1043 | source = "registry+https://github.com/rust-lang/crates.io-index" 1044 | checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" 1045 | dependencies = [ 1046 | "hostname", 1047 | "quick-error", 1048 | ] 1049 | 1050 | [[package]] 1051 | name = "ryu" 1052 | version = "1.0.5" 1053 | source = "registry+https://github.com/rust-lang/crates.io-index" 1054 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 1055 | 1056 | [[package]] 1057 | name = "scopeguard" 1058 | version = "1.1.0" 1059 | source = "registry+https://github.com/rust-lang/crates.io-index" 1060 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1061 | 1062 | [[package]] 1063 | name = "scroll" 1064 | version = "0.10.2" 1065 | source = "registry+https://github.com/rust-lang/crates.io-index" 1066 | checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec" 1067 | 1068 | [[package]] 1069 | name = "scroll_derive" 1070 | version = "0.10.5" 1071 | source = "registry+https://github.com/rust-lang/crates.io-index" 1072 | checksum = "aaaae8f38bb311444cfb7f1979af0bc9240d95795f75f9ceddf6a59b79ceffa0" 1073 | dependencies = [ 1074 | "proc-macro2", 1075 | "quote", 1076 | "syn", 1077 | ] 1078 | 1079 | [[package]] 1080 | name = "semver" 1081 | version = "0.11.0" 1082 | source = "registry+https://github.com/rust-lang/crates.io-index" 1083 | checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" 1084 | dependencies = [ 1085 | "semver-parser", 1086 | "serde", 1087 | ] 1088 | 1089 | [[package]] 1090 | name = "semver" 1091 | version = "1.0.4" 1092 | source = "registry+https://github.com/rust-lang/crates.io-index" 1093 | checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" 1094 | 1095 | [[package]] 1096 | name = "semver-parser" 1097 | version = "0.10.2" 1098 | source = "registry+https://github.com/rust-lang/crates.io-index" 1099 | checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" 1100 | dependencies = [ 1101 | "pest", 1102 | ] 1103 | 1104 | [[package]] 1105 | name = "serde" 1106 | version = "1.0.130" 1107 | source = "registry+https://github.com/rust-lang/crates.io-index" 1108 | checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" 1109 | dependencies = [ 1110 | "serde_derive", 1111 | ] 1112 | 1113 | [[package]] 1114 | name = "serde_derive" 1115 | version = "1.0.130" 1116 | source = "registry+https://github.com/rust-lang/crates.io-index" 1117 | checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" 1118 | dependencies = [ 1119 | "proc-macro2", 1120 | "quote", 1121 | "syn", 1122 | ] 1123 | 1124 | [[package]] 1125 | name = "serde_json" 1126 | version = "1.0.69" 1127 | source = "registry+https://github.com/rust-lang/crates.io-index" 1128 | checksum = "e466864e431129c7e0d3476b92f20458e5879919a0596c6472738d9fa2d342f8" 1129 | dependencies = [ 1130 | "itoa", 1131 | "ryu", 1132 | "serde", 1133 | ] 1134 | 1135 | [[package]] 1136 | name = "slab" 1137 | version = "0.4.5" 1138 | source = "registry+https://github.com/rust-lang/crates.io-index" 1139 | checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" 1140 | 1141 | [[package]] 1142 | name = "smallvec" 1143 | version = "1.7.0" 1144 | source = "registry+https://github.com/rust-lang/crates.io-index" 1145 | checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" 1146 | 1147 | [[package]] 1148 | name = "socket2" 1149 | version = "0.3.19" 1150 | source = "registry+https://github.com/rust-lang/crates.io-index" 1151 | checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" 1152 | dependencies = [ 1153 | "cfg-if", 1154 | "libc", 1155 | "winapi", 1156 | ] 1157 | 1158 | [[package]] 1159 | name = "socket2" 1160 | version = "0.4.2" 1161 | source = "registry+https://github.com/rust-lang/crates.io-index" 1162 | checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" 1163 | dependencies = [ 1164 | "libc", 1165 | "winapi", 1166 | ] 1167 | 1168 | [[package]] 1169 | name = "strsim" 1170 | version = "0.8.0" 1171 | source = "registry+https://github.com/rust-lang/crates.io-index" 1172 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 1173 | 1174 | [[package]] 1175 | name = "structopt" 1176 | version = "0.3.25" 1177 | source = "registry+https://github.com/rust-lang/crates.io-index" 1178 | checksum = "40b9788f4202aa75c240ecc9c15c65185e6a39ccdeb0fd5d008b98825464c87c" 1179 | dependencies = [ 1180 | "clap", 1181 | "lazy_static", 1182 | "structopt-derive", 1183 | ] 1184 | 1185 | [[package]] 1186 | name = "structopt-derive" 1187 | version = "0.4.18" 1188 | source = "registry+https://github.com/rust-lang/crates.io-index" 1189 | checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" 1190 | dependencies = [ 1191 | "heck", 1192 | "proc-macro-error", 1193 | "proc-macro2", 1194 | "quote", 1195 | "syn", 1196 | ] 1197 | 1198 | [[package]] 1199 | name = "strum_macros" 1200 | version = "0.21.1" 1201 | source = "registry+https://github.com/rust-lang/crates.io-index" 1202 | checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" 1203 | dependencies = [ 1204 | "heck", 1205 | "proc-macro2", 1206 | "quote", 1207 | "syn", 1208 | ] 1209 | 1210 | [[package]] 1211 | name = "syn" 1212 | version = "1.0.81" 1213 | source = "registry+https://github.com/rust-lang/crates.io-index" 1214 | checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" 1215 | dependencies = [ 1216 | "proc-macro2", 1217 | "quote", 1218 | "unicode-xid", 1219 | ] 1220 | 1221 | [[package]] 1222 | name = "tempfile" 1223 | version = "3.2.0" 1224 | source = "registry+https://github.com/rust-lang/crates.io-index" 1225 | checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" 1226 | dependencies = [ 1227 | "cfg-if", 1228 | "libc", 1229 | "rand", 1230 | "redox_syscall", 1231 | "remove_dir_all", 1232 | "winapi", 1233 | ] 1234 | 1235 | [[package]] 1236 | name = "termion" 1237 | version = "1.5.6" 1238 | source = "registry+https://github.com/rust-lang/crates.io-index" 1239 | checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e" 1240 | dependencies = [ 1241 | "libc", 1242 | "numtoa", 1243 | "redox_syscall", 1244 | "redox_termios", 1245 | ] 1246 | 1247 | [[package]] 1248 | name = "textwrap" 1249 | version = "0.11.0" 1250 | source = "registry+https://github.com/rust-lang/crates.io-index" 1251 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 1252 | dependencies = [ 1253 | "unicode-width", 1254 | ] 1255 | 1256 | [[package]] 1257 | name = "thiserror" 1258 | version = "1.0.30" 1259 | source = "registry+https://github.com/rust-lang/crates.io-index" 1260 | checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" 1261 | dependencies = [ 1262 | "thiserror-impl", 1263 | ] 1264 | 1265 | [[package]] 1266 | name = "thiserror-impl" 1267 | version = "1.0.30" 1268 | source = "registry+https://github.com/rust-lang/crates.io-index" 1269 | checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" 1270 | dependencies = [ 1271 | "proc-macro2", 1272 | "quote", 1273 | "syn", 1274 | ] 1275 | 1276 | [[package]] 1277 | name = "tinyvec" 1278 | version = "1.5.1" 1279 | source = "registry+https://github.com/rust-lang/crates.io-index" 1280 | checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" 1281 | dependencies = [ 1282 | "tinyvec_macros", 1283 | ] 1284 | 1285 | [[package]] 1286 | name = "tinyvec_macros" 1287 | version = "0.1.0" 1288 | source = "registry+https://github.com/rust-lang/crates.io-index" 1289 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 1290 | 1291 | [[package]] 1292 | name = "toml" 1293 | version = "0.5.8" 1294 | source = "registry+https://github.com/rust-lang/crates.io-index" 1295 | checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" 1296 | dependencies = [ 1297 | "serde", 1298 | ] 1299 | 1300 | [[package]] 1301 | name = "trust-dns-proto" 1302 | version = "0.20.3" 1303 | source = "registry+https://github.com/rust-lang/crates.io-index" 1304 | checksum = "ad0d7f5db438199a6e2609debe3f69f808d074e0a2888ee0bccb45fe234d03f4" 1305 | dependencies = [ 1306 | "async-trait", 1307 | "cfg-if", 1308 | "data-encoding", 1309 | "enum-as-inner", 1310 | "futures-channel", 1311 | "futures-io", 1312 | "futures-util", 1313 | "idna", 1314 | "ipnet", 1315 | "lazy_static", 1316 | "log", 1317 | "rand", 1318 | "smallvec", 1319 | "thiserror", 1320 | "tinyvec", 1321 | "url", 1322 | ] 1323 | 1324 | [[package]] 1325 | name = "trust-dns-resolver" 1326 | version = "0.20.3" 1327 | source = "registry+https://github.com/rust-lang/crates.io-index" 1328 | checksum = "f6ad17b608a64bd0735e67bde16b0636f8aa8591f831a25d18443ed00a699770" 1329 | dependencies = [ 1330 | "cfg-if", 1331 | "futures-util", 1332 | "ipconfig", 1333 | "lazy_static", 1334 | "log", 1335 | "lru-cache", 1336 | "parking_lot", 1337 | "resolv-conf", 1338 | "smallvec", 1339 | "thiserror", 1340 | "trust-dns-proto", 1341 | ] 1342 | 1343 | [[package]] 1344 | name = "tui" 1345 | version = "0.16.0" 1346 | source = "registry+https://github.com/rust-lang/crates.io-index" 1347 | checksum = "39c8ce4e27049eed97cfa363a5048b09d995e209994634a0efc26a14ab6c0c23" 1348 | dependencies = [ 1349 | "bitflags", 1350 | "cassowary", 1351 | "termion", 1352 | "unicode-segmentation", 1353 | "unicode-width", 1354 | ] 1355 | 1356 | [[package]] 1357 | name = "ucd-trie" 1358 | version = "0.1.3" 1359 | source = "registry+https://github.com/rust-lang/crates.io-index" 1360 | checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" 1361 | 1362 | [[package]] 1363 | name = "unicode-bidi" 1364 | version = "0.3.7" 1365 | source = "registry+https://github.com/rust-lang/crates.io-index" 1366 | checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" 1367 | 1368 | [[package]] 1369 | name = "unicode-normalization" 1370 | version = "0.1.19" 1371 | source = "registry+https://github.com/rust-lang/crates.io-index" 1372 | checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" 1373 | dependencies = [ 1374 | "tinyvec", 1375 | ] 1376 | 1377 | [[package]] 1378 | name = "unicode-segmentation" 1379 | version = "1.8.0" 1380 | source = "registry+https://github.com/rust-lang/crates.io-index" 1381 | checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" 1382 | 1383 | [[package]] 1384 | name = "unicode-width" 1385 | version = "0.1.9" 1386 | source = "registry+https://github.com/rust-lang/crates.io-index" 1387 | checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" 1388 | 1389 | [[package]] 1390 | name = "unicode-xid" 1391 | version = "0.2.2" 1392 | source = "registry+https://github.com/rust-lang/crates.io-index" 1393 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 1394 | 1395 | [[package]] 1396 | name = "url" 1397 | version = "2.2.2" 1398 | source = "registry+https://github.com/rust-lang/crates.io-index" 1399 | checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" 1400 | dependencies = [ 1401 | "form_urlencoded", 1402 | "idna", 1403 | "matches", 1404 | "percent-encoding", 1405 | ] 1406 | 1407 | [[package]] 1408 | name = "value-bag" 1409 | version = "1.0.0-alpha.8" 1410 | source = "registry+https://github.com/rust-lang/crates.io-index" 1411 | checksum = "79923f7731dc61ebfba3633098bf3ac533bbd35ccd8c57e7088d9a5eebe0263f" 1412 | dependencies = [ 1413 | "ctor", 1414 | "version_check", 1415 | ] 1416 | 1417 | [[package]] 1418 | name = "vec_map" 1419 | version = "0.8.2" 1420 | source = "registry+https://github.com/rust-lang/crates.io-index" 1421 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 1422 | 1423 | [[package]] 1424 | name = "version_check" 1425 | version = "0.9.3" 1426 | source = "registry+https://github.com/rust-lang/crates.io-index" 1427 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 1428 | 1429 | [[package]] 1430 | name = "vsprintf" 1431 | version = "2.0.0" 1432 | source = "registry+https://github.com/rust-lang/crates.io-index" 1433 | checksum = "aec2f81b75ca063294776b4f7e8da71d1d5ae81c2b1b149c8d89969230265d63" 1434 | dependencies = [ 1435 | "cc", 1436 | "libc", 1437 | ] 1438 | 1439 | [[package]] 1440 | name = "waker-fn" 1441 | version = "1.1.0" 1442 | source = "registry+https://github.com/rust-lang/crates.io-index" 1443 | checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" 1444 | 1445 | [[package]] 1446 | name = "wasi" 1447 | version = "0.10.2+wasi-snapshot-preview1" 1448 | source = "registry+https://github.com/rust-lang/crates.io-index" 1449 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 1450 | 1451 | [[package]] 1452 | name = "wasm-bindgen" 1453 | version = "0.2.78" 1454 | source = "registry+https://github.com/rust-lang/crates.io-index" 1455 | checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" 1456 | dependencies = [ 1457 | "cfg-if", 1458 | "wasm-bindgen-macro", 1459 | ] 1460 | 1461 | [[package]] 1462 | name = "wasm-bindgen-backend" 1463 | version = "0.2.78" 1464 | source = "registry+https://github.com/rust-lang/crates.io-index" 1465 | checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" 1466 | dependencies = [ 1467 | "bumpalo", 1468 | "lazy_static", 1469 | "log", 1470 | "proc-macro2", 1471 | "quote", 1472 | "syn", 1473 | "wasm-bindgen-shared", 1474 | ] 1475 | 1476 | [[package]] 1477 | name = "wasm-bindgen-futures" 1478 | version = "0.4.28" 1479 | source = "registry+https://github.com/rust-lang/crates.io-index" 1480 | checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" 1481 | dependencies = [ 1482 | "cfg-if", 1483 | "js-sys", 1484 | "wasm-bindgen", 1485 | "web-sys", 1486 | ] 1487 | 1488 | [[package]] 1489 | name = "wasm-bindgen-macro" 1490 | version = "0.2.78" 1491 | source = "registry+https://github.com/rust-lang/crates.io-index" 1492 | checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" 1493 | dependencies = [ 1494 | "quote", 1495 | "wasm-bindgen-macro-support", 1496 | ] 1497 | 1498 | [[package]] 1499 | name = "wasm-bindgen-macro-support" 1500 | version = "0.2.78" 1501 | source = "registry+https://github.com/rust-lang/crates.io-index" 1502 | checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" 1503 | dependencies = [ 1504 | "proc-macro2", 1505 | "quote", 1506 | "syn", 1507 | "wasm-bindgen-backend", 1508 | "wasm-bindgen-shared", 1509 | ] 1510 | 1511 | [[package]] 1512 | name = "wasm-bindgen-shared" 1513 | version = "0.2.78" 1514 | source = "registry+https://github.com/rust-lang/crates.io-index" 1515 | checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" 1516 | 1517 | [[package]] 1518 | name = "web-sys" 1519 | version = "0.3.55" 1520 | source = "registry+https://github.com/rust-lang/crates.io-index" 1521 | checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" 1522 | dependencies = [ 1523 | "js-sys", 1524 | "wasm-bindgen", 1525 | ] 1526 | 1527 | [[package]] 1528 | name = "wepoll-ffi" 1529 | version = "0.1.2" 1530 | source = "registry+https://github.com/rust-lang/crates.io-index" 1531 | checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" 1532 | dependencies = [ 1533 | "cc", 1534 | ] 1535 | 1536 | [[package]] 1537 | name = "widestring" 1538 | version = "0.4.3" 1539 | source = "registry+https://github.com/rust-lang/crates.io-index" 1540 | checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c" 1541 | 1542 | [[package]] 1543 | name = "winapi" 1544 | version = "0.3.9" 1545 | source = "registry+https://github.com/rust-lang/crates.io-index" 1546 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1547 | dependencies = [ 1548 | "winapi-i686-pc-windows-gnu", 1549 | "winapi-x86_64-pc-windows-gnu", 1550 | ] 1551 | 1552 | [[package]] 1553 | name = "winapi-i686-pc-windows-gnu" 1554 | version = "0.4.0" 1555 | source = "registry+https://github.com/rust-lang/crates.io-index" 1556 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1557 | 1558 | [[package]] 1559 | name = "winapi-x86_64-pc-windows-gnu" 1560 | version = "0.4.0" 1561 | source = "registry+https://github.com/rust-lang/crates.io-index" 1562 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1563 | 1564 | [[package]] 1565 | name = "winreg" 1566 | version = "0.6.2" 1567 | source = "registry+https://github.com/rust-lang/crates.io-index" 1568 | checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" 1569 | dependencies = [ 1570 | "winapi", 1571 | ] 1572 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "p2pflow" 3 | version = "0.0.6" 4 | authors = ["Jonas Bostoen "] 5 | edition = "2018" 6 | license = "GPL-2.0 OR BSD-3-Clause" 7 | 8 | [dependencies] 9 | anyhow = "1.0" 10 | libbpf-rs = "0.14.0" 11 | async-std = "1.10.0" 12 | async-std-resolver = "*" 13 | libc = "0.2" 14 | structopt = "0.3" 15 | ctrlc = "3.1" 16 | object = "0.25" 17 | plain = "0.2" 18 | tui = "0.16" 19 | termion = "1.5" 20 | arraydeque = "0.4.5" 21 | 22 | [build-dependencies] 23 | libbpf-cargo = "0.9.3" 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 netbound 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | APP = p2pflow 2 | 3 | .PHONY: install 4 | install: 5 | cargo install --path . 6 | sudo setcap cap_sys_admin,cap_sys_resource+eip $(HOME)/.cargo/bin/$(APP) 7 | 8 | .PHONY: vmlinux 9 | vmlinux: 10 | bpftool btf dump file /sys/kernel/btf/vmlinux format c > src/bpf/vmlinux.h -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # p2pflow 2 | 3 | > An eBPF application to monitor Ethereum p2p network traffic. 4 | 5 | ![Demo GIF](./demo.gif) 6 | 7 | ## Requirements 8 | ```bash 9 | sudo apt-get install pkg-config clang llvm libelf-dev libpcap-dev gcc-multilib build-essential linux-tools-$(uname -r) 10 | ``` 11 | 12 | * Rust 13 | 14 | Install [here](https://www.rust-lang.org/tools/install). Uses the `cargo-bpf` package to build and load the BPF 15 | program into the kernel. 16 | * Up-to-date Linux kernel 17 | 18 | The project is built on technology like `CO-RE` and `BTF`, which is only 19 | available in more recent kernels (5.0-ish). Ubuntu 21.04 has configured and packaged all the required dependencies. 20 | * `vmlinux.h` 21 | 22 | `vmlinux.h` contains all the kernel definitions on your current machine, which we need in the BPF programs. 23 | You can generate it with `bpftool` (part of `linux-tools`): 24 | ```bash 25 | bpftool btf dump file /sys/kernel/btf/vmlinux format c > src/bpf/vmlinux.h 26 | ``` 27 | Or run `make vmlinux`. 28 | 29 | You can verify whether your kernel was built with BTF (BPF Type Format) enabled: 30 | 31 | ```bash 32 | cat /boot/config-$(uname -r) | grep CONFIG_DEBUG_INFO_BTF 33 | ``` 34 | ## Install & Build 35 | `libbpf` is included as a submodule so that we don't have to rely on the system `libbpf`, which 36 | can be out of date. 37 | ``` 38 | git clone --recurse-submodules -j8 https://github.com/netbound/p2pflow 39 | cd p2pflow 40 | cargo build --release 41 | ``` 42 | 43 | ## Run 44 | Running requires root privileges for loading the BPF program into the kernel and attaching it to the proper hooks. 45 | ```bash 46 | sudo ./target/release/p2pflow --process geth 47 | ``` 48 | Or 49 | ```bash 50 | make install 51 | ``` 52 | This will install the binary in `$HOME/.cargo/bin`, and adds the `cap_sys_admin` capability to let it run without sudo. -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::fs::create_dir_all; 2 | use std::path::Path; 3 | 4 | use libbpf_cargo::SkeletonBuilder; 5 | 6 | const SRC: &str = "./src/bpf/p2pflow.bpf.c"; 7 | 8 | fn main() { 9 | // It's unfortunate we cannot use `OUT_DIR` to store the generated skeleton. 10 | // Reasons are because the generated skeleton contains compiler attributes 11 | // that cannot be `include!()`ed via macro. And we cannot use the `#[path = "..."]` 12 | // trick either because you cannot yet `concat!(env!("OUT_DIR"), "/skel.rs")` inside 13 | // the path attribute either (see https://github.com/rust-lang/rust/pull/83366). 14 | // 15 | // However, there is hope! When the above feature stabilizes we can clean this 16 | // all up. 17 | create_dir_all("./src/bpf/.output").unwrap(); 18 | let skel = Path::new("./src/bpf/.output/p2pflow.skel.rs"); 19 | SkeletonBuilder::new(SRC) 20 | .generate(&skel) 21 | .expect("bpf compilation failed"); 22 | println!("cargo:rerun-if-changed={}", SRC); 23 | } 24 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbound/p2pflow/2e591d44348bb0db920385a8671516d4a6b54566/demo.gif -------------------------------------------------------------------------------- /src/app.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::HashMap, 3 | net::{IpAddr, Ipv4Addr, Ipv6Addr}, 4 | sync::{Arc, Mutex}, 5 | }; 6 | 7 | use async_std::task::block_on; 8 | use libbpf_rs::{Map, MapFlags}; 9 | use tui::widgets::TableState; 10 | 11 | use crate::{ 12 | net::{RateMonitor, Resolver}, 13 | PeerV4, PeerV6, ValueType, 14 | }; 15 | 16 | #[derive(Clone)] 17 | pub struct App<'a> { 18 | pub process_name: String, 19 | pub state: TableState, 20 | pub sort_key: SortKey, 21 | pub items: Arc>, 22 | pub prev_bytes: HashMap, 23 | pub v4_peers: Option<&'a Map>, 24 | pub v6_peers: Option<&'a Map>, 25 | pub resolver: Resolver, 26 | pub rate_monitor: RateMonitor, 27 | } 28 | 29 | #[derive(Clone, Debug)] 30 | pub struct Item { 31 | pub ip: IpAddr, 32 | pub is_v4: bool, 33 | pub port: u16, 34 | // Total KiB received 35 | pub tot_rx: u64, 36 | // Total KiB sent 37 | pub tot_tx: u64, 38 | // Receive rate in bps 39 | pub rx_rate: u64, 40 | // Send rate in bps 41 | pub tx_rate: u64, 42 | } 43 | 44 | #[derive(Clone)] 45 | pub struct Items { 46 | pub vec: Vec, 47 | } 48 | 49 | #[derive(Clone, Copy)] 50 | pub enum SortKey { 51 | TotalRx, 52 | TotalTx, 53 | RxRate, 54 | TxRate, 55 | None, 56 | } 57 | 58 | impl Items { 59 | fn new() -> Items { 60 | Items { vec: Vec::new() } 61 | } 62 | 63 | pub fn sort(&mut self, key: SortKey) { 64 | match key { 65 | SortKey::TotalRx => self.vec.sort_by(|a, b| b.tot_rx.cmp(&a.tot_rx)), 66 | SortKey::TotalTx => self.vec.sort_by(|a, b| b.tot_tx.cmp(&a.tot_tx)), 67 | _ => {} 68 | } 69 | } 70 | } 71 | 72 | impl<'a> App<'a> { 73 | pub fn new(process_name: String) -> App<'a> { 74 | App { 75 | process_name, 76 | state: TableState::default(), 77 | sort_key: SortKey::None, 78 | items: Arc::new(Mutex::new(Items::new())), 79 | prev_bytes: HashMap::new(), 80 | v4_peers: None, 81 | v6_peers: None, 82 | resolver: block_on(Resolver::new()), 83 | rate_monitor: RateMonitor::new(), 84 | } 85 | } 86 | 87 | /// Starts the DNS resolver and rate monitor 88 | pub fn start(&mut self) { 89 | self.resolver.start(); 90 | self.rate_monitor.start(Arc::clone(&self.items)); 91 | } 92 | 93 | /// Clears all the items in current state, and reloads them 94 | /// from the BPF maps. 95 | pub fn refresh(&mut self) { 96 | self.items.lock().unwrap().vec.clear(); 97 | if let Some(v4_peers) = self.v4_peers { 98 | self.set_v4_peers(v4_peers); 99 | } 100 | if let Some(v6_peers) = self.v6_peers { 101 | self.set_v6_peers(v6_peers); 102 | } 103 | } 104 | 105 | /// Sets the v4_peers BPF map and parses entries to load into the items array. 106 | pub fn set_v4_peers(&mut self, v4_peers: &'a Map) { 107 | self.v4_peers = Some(v4_peers); 108 | 109 | v4_peers 110 | .keys() 111 | .map(|k| { 112 | let mut key = PeerV4::default(); 113 | let mut value = ValueType::default(); 114 | 115 | plain::copy_from_bytes(&mut key, &k).expect("Couldn't decode key"); 116 | let val = v4_peers.lookup(&k, MapFlags::ANY).unwrap().unwrap(); 117 | plain::copy_from_bytes(&mut value, &val).expect("Couldn't decode value"); 118 | 119 | let b_rx = value.bytes_in; 120 | let b_tx = value.bytes_out; 121 | let ip = IpAddr::V4(Ipv4Addr::from(key.daddr.to_be())); 122 | 123 | let (tx_rate, rx_rate) = self.rate_monitor.get_rates(&format!("{}:{}", ip, key.dport)); 124 | 125 | // TODO: this resets rates to 0 every time we refresh, 126 | // so we can't display proper rates 127 | self.items.lock().unwrap().vec.push(Item { 128 | ip: ip, 129 | is_v4: true, 130 | port: key.dport, 131 | tot_rx: b_rx, 132 | tot_tx: b_tx, 133 | rx_rate, 134 | tx_rate, 135 | }); 136 | }) 137 | .collect() 138 | } 139 | 140 | /// Sets the v6_peers BPF map and parses entries to load into the items array. 141 | pub fn set_v6_peers(&mut self, v6_peers: &'a Map) { 142 | self.v6_peers = Some(v6_peers); 143 | 144 | v6_peers 145 | .keys() 146 | .map(|k| { 147 | let mut key = PeerV6::default(); 148 | let mut value = ValueType::default(); 149 | 150 | plain::copy_from_bytes(&mut key, &k).expect("Couldn't decode key"); 151 | let val = v6_peers.lookup(&k, MapFlags::ANY).unwrap().unwrap(); 152 | plain::copy_from_bytes(&mut value, &val).expect("Couldn't decode value"); 153 | 154 | let b_rx = value.bytes_in; 155 | let b_tx = value.bytes_out; 156 | 157 | let ipv6 = Ipv6Addr::from(key.daddr.to_be()); 158 | let ip = ipv6.to_ipv4().unwrap(); 159 | 160 | let (tx_rate, rx_rate) = self.rate_monitor.get_rates(&format!("{}:{}", ip, key.dport)); 161 | 162 | self.items.lock().unwrap().vec.push(Item { 163 | ip: ip.into(), 164 | is_v4: false, 165 | port: key.dport, 166 | tot_rx: b_rx, 167 | tot_tx: b_tx, 168 | rx_rate, 169 | tx_rate, 170 | }); 171 | }) 172 | .collect() 173 | } 174 | 175 | /// Returns the length of all the items. 176 | fn table_len(&self) -> usize { 177 | self.items.lock().unwrap().vec.len() 178 | } 179 | 180 | /// Selects the next item in the table. 181 | pub fn next(&mut self) { 182 | let i = match self.state.selected() { 183 | Some(i) => { 184 | if i >= self.table_len() - 1 { 185 | 0 186 | } else { 187 | i + 1 188 | } 189 | } 190 | None => 0, 191 | }; 192 | self.state.select(Some(i)); 193 | } 194 | 195 | /// Selects the previous item in the table. 196 | pub fn previous(&mut self) { 197 | let i = match self.state.selected() { 198 | Some(i) => { 199 | if i == 0 { 200 | self.table_len() - 1 201 | } else { 202 | i - 1 203 | } 204 | } 205 | None => 0, 206 | }; 207 | self.state.select(Some(i)); 208 | } 209 | 210 | pub fn first(&mut self) { 211 | self.state.select(Some(0)); 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /src/bpf/p2pflow.bpf.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | #include "p2pflow.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define HOSTNAME_LEN 84 9 | #define ETH_HLEN 14 10 | 11 | #define ETH_P_IP 0x0800 12 | #define MAX_PEERS 4096 13 | 14 | #define AF_INET 2 /* IP protocol family. */ 15 | #define AF_INET6 10 /* IP version 6. */ 16 | 17 | // rodata section, changed in userspace before loading BPF program 18 | const volatile char process_name[16] = "geth"; 19 | 20 | // dummy instances to generate skeleton types 21 | struct peer_v4_t _ipv4 = {}; 22 | struct peer_v6_t _ipv6 = {}; 23 | struct value_t _val = {}; 24 | 25 | struct bpf_map_def SEC("maps") trackers_v4 = { 26 | .type = BPF_MAP_TYPE_HASH, 27 | .max_entries = MAX_PEERS, 28 | .key_size = sizeof(struct peer_v4_t), 29 | .value_size = sizeof(struct value_t) // bytes sent/recvd 30 | }; 31 | 32 | struct bpf_map_def SEC("maps") trackers_v6 = { 33 | .type = BPF_MAP_TYPE_HASH, 34 | .max_entries = MAX_PEERS, 35 | .key_size = sizeof(struct peer_v6_t), 36 | .value_size = sizeof(struct value_t) // bytes sent/recvd 37 | }; 38 | 39 | // Maps all sockets to their corresponding peers 40 | struct bpf_map_def SEC("maps") sockets = { 41 | .type = BPF_MAP_TYPE_HASH, 42 | .max_entries = MAX_PEERS, 43 | .key_size = sizeof(kuid_t), 44 | .value_size = sizeof(struct peer_t)}; 45 | 46 | static __always_inline u32 get_pid() 47 | { 48 | u64 tgid = bpf_get_current_pid_tgid(); 49 | pid_t pid = tgid >> 32; 50 | return (u32)pid; 51 | } 52 | 53 | static __always_inline char *get_pname() 54 | { 55 | char name[16]; 56 | bpf_get_current_comm(name, 16); 57 | return name; 58 | } 59 | 60 | // Checks if the process name is the one we want. 61 | static __always_inline bool is_eth_pname(char *str) 62 | { 63 | char comparand[sizeof(process_name)]; 64 | bpf_probe_read(&comparand, sizeof(comparand), str); 65 | for (int i = 0; i < 4; ++i) 66 | if (process_name[i] != comparand[i]) 67 | return false; 68 | return true; 69 | } 70 | 71 | // Returns 1 if new entry was created, 0 for update 72 | // 73 | // dir (direction): out = true, in = false 74 | static __always_inline int handle_p2p_msg(bool dir, struct peer_t *ev, u64 new_bytes) 75 | { 76 | struct value_t *val; 77 | if (ev->type == AF_INET) 78 | { 79 | val = bpf_map_lookup_elem(&trackers_v4, &ev->ipv4); 80 | 81 | // No map entry yet, set it here 82 | if (!val) 83 | { 84 | struct value_t new_val = {}; 85 | if (dir == true) 86 | new_val.bytes_out = new_bytes; 87 | else 88 | new_val.bytes_in = new_bytes; 89 | 90 | bpf_map_update_elem(&trackers_v4, &ev->ipv4, &new_val, BPF_NOEXIST); 91 | return 1; 92 | } 93 | } 94 | else if (ev->type == AF_INET6) 95 | { 96 | val = bpf_map_lookup_elem(&trackers_v6, &ev->ipv6); 97 | 98 | // No map entry yet, set it here 99 | if (!val) 100 | { 101 | 102 | struct value_t new_val = {}; 103 | if (dir == true) 104 | new_val.bytes_out = new_bytes; 105 | else 106 | new_val.bytes_in = new_bytes; 107 | 108 | bpf_map_update_elem(&trackers_v6, &ev->ipv6, &new_val, BPF_NOEXIST); 109 | return 1; 110 | } 111 | } 112 | 113 | // Add to total bytes 114 | if (dir == true) 115 | __sync_fetch_and_add(&val->bytes_out, new_bytes); 116 | else 117 | __sync_fetch_and_add(&val->bytes_in, new_bytes); 118 | 119 | return 0; 120 | } 121 | 122 | // Kprobe for tracing tcp_sendmsg segments. 123 | SEC("kprobe/tcp_sendmsg") 124 | int BPF_KPROBE(trace_tcp_sendmsg, struct sock *sk, struct msghdr *msg, size_t size) 125 | { 126 | // Check if SKB is from geth 127 | if (!is_eth_pname(get_pname())) 128 | return 0; 129 | 130 | __be16 dport = BPF_CORE_READ(sk, __sk_common.skc_dport); 131 | u16 family = BPF_CORE_READ(sk, __sk_common.skc_family); 132 | kuid_t sock_uid = BPF_CORE_READ(sk, sk_uid); 133 | 134 | if (family == AF_INET) 135 | { 136 | struct peer_v4_t peer_v4 = {.dport = bpf_ntohs(dport)}; 137 | BPF_CORE_READ_INTO(&peer_v4.daddr, sk, __sk_common.skc_daddr); 138 | struct peer_t ev = {.type = AF_INET, .ipv4 = peer_v4}; 139 | if (handle_p2p_msg(true, &ev, (u64)size) == 1) 140 | bpf_map_update_elem(&sockets, &sock_uid, &ev, BPF_NOEXIST); 141 | } 142 | else if (family == AF_INET6) 143 | { 144 | struct peer_v6_t peer_v6 = {.dport = bpf_ntohs(dport)}; 145 | BPF_CORE_READ_INTO(&peer_v6.daddr, sk, __sk_common.skc_v6_daddr.in6_u.u6_addr32); 146 | struct peer_t ev = {.type = AF_INET6, .ipv6 = peer_v6}; 147 | if (handle_p2p_msg(true, &ev, (u64)size) == 1) 148 | bpf_map_update_elem(&sockets, &sock_uid, &ev, BPF_NOEXIST); 149 | } 150 | 151 | return 0; 152 | } 153 | 154 | // https://elixir.bootlin.com/linux/latest/source/net/ipv4/tcp.c#L1545 155 | SEC("kprobe/tcp_cleanup_rbuf") 156 | int BPF_KPROBE(trace_tcp_cleanup_rbuf, struct sock *sk, int copied) 157 | { 158 | // Check if SKB is from geth 159 | if (!is_eth_pname(get_pname())) 160 | return 0; 161 | 162 | if (copied <= 0) 163 | return 0; 164 | 165 | __be16 dport = BPF_CORE_READ(sk, __sk_common.skc_dport); 166 | u16 family = BPF_CORE_READ(sk, __sk_common.skc_family); 167 | kuid_t sock_uid = BPF_CORE_READ(sk, sk_uid); 168 | 169 | if (family == AF_INET) 170 | { 171 | struct peer_v4_t peer_v4 = {.dport = bpf_ntohs(dport)}; 172 | BPF_CORE_READ_INTO(&peer_v4.daddr, sk, __sk_common.skc_daddr); 173 | struct peer_t ev = {.type = AF_INET, .ipv4 = peer_v4}; 174 | if (handle_p2p_msg(false, &ev, (u64)copied) == 1) 175 | bpf_map_update_elem(&sockets, &sock_uid, &ev, BPF_NOEXIST); 176 | } 177 | else if (family == AF_INET6) 178 | { 179 | struct peer_v6_t peer_v6 = {.dport = bpf_ntohs(dport)}; 180 | BPF_CORE_READ_INTO(&peer_v6.daddr, sk, __sk_common.skc_v6_daddr.in6_u.u6_addr32); 181 | struct peer_t ev = {.type = AF_INET6, .ipv6 = peer_v6}; 182 | if (handle_p2p_msg(false, &ev, (u64)copied) == 1) 183 | bpf_map_update_elem(&sockets, &sock_uid, &ev, BPF_NOEXIST); 184 | } 185 | 186 | return 0; 187 | } 188 | 189 | // We remove the socket from our socket map if the connection gets closed. 190 | // https://elixir.bootlin.com/linux/latest/source/include/net/tcp_states.h#L12 191 | SEC("tp_btf/inet_sock_set_state") 192 | int BPF_PROG(trace_inet_sock_set_state, struct sock *sk, int oldstate, int newstate) 193 | { 194 | if (newstate == BPF_TCP_CLOSE || BPF_TCP_FIN_WAIT1 || BPF_TCP_FIN_WAIT2 || BPF_TCP_CLOSE_WAIT) 195 | { 196 | kuid_t sock_uid = BPF_CORE_READ(sk, sk_uid); 197 | struct peer_t *val = bpf_map_lookup_elem(&sockets, &sock_uid); 198 | 199 | if (!val) 200 | return 0; 201 | 202 | // Delete socket from map 203 | bpf_map_delete_elem(&sockets, &sock_uid); 204 | 205 | if (val->type == AF_INET) 206 | bpf_map_delete_elem(&trackers_v4, &val->ipv4); 207 | else if (val->type == AF_INET6) 208 | bpf_map_delete_elem(&trackers_v6, &val->ipv6); 209 | 210 | return 0; 211 | } 212 | 213 | return 0; 214 | } 215 | 216 | char LICENSE[] SEC("license") = "GPL"; 217 | -------------------------------------------------------------------------------- /src/bpf/p2pflow.h: -------------------------------------------------------------------------------- 1 | struct peer_v4_t 2 | { 3 | u32 saddr; 4 | u32 daddr; 5 | u16 lport; 6 | u16 dport; 7 | }; 8 | 9 | struct peer_v6_t 10 | { 11 | unsigned __int128 saddr; 12 | unsigned __int128 daddr; 13 | u16 lport; 14 | u16 dport; 15 | u64 __pad__; 16 | }; 17 | 18 | struct peer_t 19 | { 20 | u16 type; // v4 or v6 21 | union 22 | { 23 | struct peer_v4_t ipv4; 24 | struct peer_v6_t ipv6; 25 | }; 26 | }; 27 | 28 | struct value_t { 29 | u64 bytes_in; 30 | u64 bytes_out; 31 | }; -------------------------------------------------------------------------------- /src/display.rs: -------------------------------------------------------------------------------- 1 | use termion::{input::MouseTerminal, raw::RawTerminal, screen::AlternateScreen}; 2 | use tui::{ 3 | backend::TermionBackend, 4 | layout::{Alignment, Constraint, Layout}, 5 | style::{Color, Modifier, Style}, 6 | text::Span, 7 | widgets::{Block, Borders, Cell, Paragraph, Row, Table}, 8 | Terminal, 9 | }; 10 | 11 | use crate::{app::Item, net::Resolver, App}; 12 | 13 | fn gen_rows<'a>(items: &Vec, resolver: &Resolver) -> Vec> { 14 | let mut rows = Vec::new(); 15 | for i in items { 16 | let v = if i.is_v4 { "IPv4" } else { "IPv6" }; 17 | let entry = vec![ 18 | format!("{}:{}", i.ip.to_string(), i.port.to_string()), 19 | v.to_owned(), 20 | resolver.resolve_ip(i.ip), 21 | format!( 22 | "{}ps / {}ps", 23 | gen_bytes_str(i.tx_rate), 24 | gen_bytes_str(i.rx_rate) 25 | ), 26 | format!("{} / {}", gen_bytes_str(i.tot_tx), gen_bytes_str(i.tot_rx)), 27 | ]; 28 | let cells = entry.iter().map(|c| Cell::from(c.clone())); 29 | rows.push(Row::new(cells).height(1)); 30 | } 31 | 32 | rows 33 | } 34 | 35 | pub fn draw_terminal( 36 | terminal: &mut Terminal< 37 | TermionBackend>>>, 38 | >, 39 | app: &mut App, 40 | ) -> anyhow::Result<()> { 41 | terminal.draw(|f| { 42 | let rects = Layout::default() 43 | .constraints([Constraint::Length(1), Constraint::Min(0)].as_ref()) 44 | .margin(1) 45 | .split(f.size()); 46 | 47 | let selected_style = Style::default().add_modifier(Modifier::REVERSED); 48 | let normal_style = Style::default().add_modifier(Modifier::BOLD); 49 | let header_cells = ["Peer", "Type", "Name", "tx / rx rate", "Total tx / rx"] 50 | .iter() 51 | .map(|h| Cell::from(*h).style(Style::default().fg(Color::Yellow))); 52 | let header = Row::new(header_cells) 53 | .style(normal_style) 54 | .height(1) 55 | .bottom_margin(1); 56 | 57 | let rows = gen_rows(&app.items.lock().unwrap().vec, &app.resolver); 58 | let peer_count = rows.len(); 59 | 60 | let header_text = Span::styled( 61 | format!("Process: {} [{} peers]", app.process_name, peer_count), 62 | Style::default() 63 | .fg(Color::Cyan) 64 | .add_modifier(Modifier::BOLD), 65 | ); 66 | 67 | let header_paragraph = Paragraph::new(header_text).alignment(Alignment::Left); 68 | 69 | let t = Table::new(rows.into_iter()) 70 | .header(header) 71 | .block( 72 | Block::default() 73 | .borders(Borders::ALL) 74 | .title("Peer utilization"), 75 | ) 76 | .highlight_style(selected_style) 77 | .widths(&[ 78 | Constraint::Percentage(20), 79 | Constraint::Percentage(5), 80 | Constraint::Percentage(35), 81 | Constraint::Percentage(20), 82 | Constraint::Percentage(25), 83 | ]); 84 | 85 | f.render_widget(header_paragraph, rects[0]); 86 | f.render_stateful_widget(t, rects[1], &mut app.state); 87 | })?; 88 | 89 | Ok(()) 90 | } 91 | 92 | pub fn gen_bytes_str(bytes: u64) -> String { 93 | let mut bytes_str = format!("{} B", bytes); 94 | 95 | if bytes >= 1024 { 96 | bytes_str = format!("{:.2} KiB", bytes as f32 / 1024f32); 97 | } 98 | 99 | if bytes >= 1024 * 1024 { 100 | bytes_str = format!("{:.2} MiB", bytes as f32 / 1024f32 / 1024f32); 101 | } 102 | 103 | if bytes >= 1024 * 1024 * 1024 { 104 | bytes_str = format!("{:.2} GiB", bytes as f32 / 1024f32 / 1024f32 / 1024f32); 105 | } 106 | 107 | bytes_str 108 | } 109 | -------------------------------------------------------------------------------- /src/event.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::sync::mpsc; 3 | use std::thread; 4 | use std::time::Duration; 5 | 6 | use termion::event::Key; 7 | use termion::input::TermRead; 8 | 9 | pub enum Event { 10 | Input(I), 11 | Tick, 12 | } 13 | 14 | /// A small event handler that wrap termion input and tick events. Each event 15 | /// type is handled in its own thread and returned to a common `Receiver` 16 | pub struct Events { 17 | rx: mpsc::Receiver>, 18 | } 19 | 20 | #[derive(Debug, Clone, Copy)] 21 | pub struct Config { 22 | pub tick_rate: Duration, 23 | } 24 | 25 | impl Default for Config { 26 | fn default() -> Config { 27 | Config { 28 | tick_rate: Duration::from_millis(500), 29 | } 30 | } 31 | } 32 | 33 | impl Events { 34 | pub fn new() -> Events { 35 | Events::with_config(Config::default()) 36 | } 37 | 38 | pub fn with_config(config: Config) -> Events { 39 | let (tx, rx) = mpsc::channel(); 40 | let _input_handle = { 41 | let tx = tx.clone(); 42 | thread::spawn(move || { 43 | let stdin = io::stdin(); 44 | for evt in stdin.keys() { 45 | if let Ok(key) = evt { 46 | if let Err(err) = tx.send(Event::Input(key)) { 47 | eprintln!("{}", err); 48 | return; 49 | } 50 | } 51 | } 52 | }) 53 | }; 54 | 55 | let _tick_handle = { 56 | thread::spawn(move || loop { 57 | if let Err(err) = tx.send(Event::Tick) { 58 | eprintln!("{}", err); 59 | break; 60 | } 61 | thread::sleep(config.tick_rate); 62 | }) 63 | }; 64 | 65 | Events { rx } 66 | } 67 | 68 | pub fn next(&self) -> Result, mpsc::RecvError> { 69 | self.rx.recv() 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{anyhow, bail, Result}; 2 | use object::Object; 3 | use object::ObjectSymbol; 4 | use plain::Plain; 5 | use std::ffi::CString; 6 | use std::{fs, io, path::Path}; 7 | use structopt::StructOpt; 8 | use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen}; 9 | use tui::{backend::TermionBackend, Terminal}; 10 | 11 | mod app; 12 | mod display; 13 | mod event; 14 | mod net; 15 | #[path = "bpf/.output/p2pflow.skel.rs"] 16 | mod p2pflow; 17 | 18 | use app::*; 19 | use display::*; 20 | use event::*; 21 | use p2pflow::*; 22 | 23 | type PeerV4 = p2pflow_bss_types::peer_v4_t; 24 | type PeerV6 = p2pflow_bss_types::peer_v6_t; 25 | type ValueType = p2pflow_bss_types::value_t; 26 | 27 | unsafe impl Plain for PeerV4 {} 28 | unsafe impl Plain for PeerV6 {} 29 | unsafe impl Plain for ValueType {} 30 | 31 | #[derive(Debug, StructOpt)] 32 | struct Command { 33 | /// verbose output 34 | #[structopt(long, short)] 35 | verbose: bool, 36 | /// glibc path 37 | #[structopt(long, short, default_value = "/lib/x86_64-linux-gnu/libc.so.6")] 38 | glibc: String, 39 | /// process name 40 | #[structopt(long, short, default_value = "geth")] 41 | process: String, 42 | /// only IPv4 peer connections 43 | #[structopt(long, short = "4")] 44 | ipv4: bool, 45 | /// only IPv6 peer connections 46 | #[structopt(long, short = "6")] 47 | ipv6: bool, 48 | } 49 | 50 | fn bump_memlock_rlimit() -> Result<()> { 51 | let rlimit = libc::rlimit { 52 | rlim_cur: 128 << 20, 53 | rlim_max: 128 << 20, 54 | }; 55 | 56 | if unsafe { libc::setrlimit(libc::RLIMIT_MEMLOCK, &rlimit) } != 0 { 57 | bail!("Failed to increase rlimit"); 58 | } 59 | 60 | Ok(()) 61 | } 62 | 63 | fn get_symbol_address(so_path: &str, fn_name: &str) -> Result { 64 | let path = Path::new(so_path); 65 | let buffer = fs::read(path)?; 66 | let file = object::File::parse(buffer.as_slice())?; 67 | 68 | let mut symbols = file.dynamic_symbols(); 69 | let symbol = symbols 70 | .find(|symbol| { 71 | if let Ok(name) = symbol.name() { 72 | return name == fn_name; 73 | } 74 | false 75 | }) 76 | .ok_or(anyhow!("symbol not found"))?; 77 | 78 | Ok(symbol.address() as usize) 79 | } 80 | 81 | fn build_skel(opts: &Command) -> Result> { 82 | let mut skel_builder = P2pflowSkelBuilder::default(); 83 | if opts.verbose { 84 | skel_builder.obj_builder.debug(true); 85 | } 86 | 87 | bump_memlock_rlimit()?; 88 | let mut open_skel = skel_builder.open()?; 89 | 90 | let str = CString::new(opts.process.clone()).unwrap(); 91 | let mut buf: [i8; 16] = [0; 16]; 92 | 93 | let buf_ptr = buf.as_mut_ptr(); 94 | 95 | unsafe { 96 | buf_ptr.copy_from(str.as_ptr(), 16); 97 | } 98 | 99 | open_skel.rodata().process_name = buf; 100 | 101 | Ok(open_skel) 102 | } 103 | 104 | fn main() -> Result<()> { 105 | let opts = Command::from_args(); 106 | 107 | // BPF preparation 108 | let open_skel = build_skel(&opts)?; 109 | 110 | let mut skel = open_skel.load()?; 111 | let _address = get_symbol_address(&opts.glibc, "getaddrinfo")?; 112 | 113 | skel.attach()?; 114 | 115 | let maps = skel.maps(); 116 | let trackers_v4 = maps.trackers_v4(); 117 | let trackers_v6 = maps.trackers_v6(); 118 | 119 | let stdout = io::stdout().into_raw_mode()?; 120 | let stdout = MouseTerminal::from(stdout); 121 | let stdout = AlternateScreen::from(stdout); 122 | let backend = TermionBackend::new(stdout); 123 | let mut terminal = Terminal::new(backend)?; 124 | 125 | let events = Events::new(); 126 | let mut app = App::new(opts.process); 127 | 128 | if !opts.ipv6 { 129 | app.set_v4_peers(trackers_v4); 130 | } 131 | if !opts.ipv4 { 132 | app.set_v6_peers(trackers_v6); 133 | } 134 | 135 | app.start(); 136 | 137 | loop { 138 | app.refresh(); 139 | app.items.lock().unwrap().sort(app.sort_key); 140 | 141 | draw_terminal(&mut terminal, &mut app)?; 142 | 143 | match events.next()? { 144 | Event::Input(key) => match key { 145 | Key::Char('q') | Key::Esc => { 146 | break; 147 | } 148 | Key::Char('r') => { 149 | app.first(); 150 | if let SortKey::TotalRx = app.sort_key { 151 | app.sort_key = SortKey::None 152 | } else { 153 | app.sort_key = SortKey::TotalRx; 154 | } 155 | } 156 | Key::Char('t') => { 157 | app.first(); 158 | if let SortKey::TotalTx = app.sort_key { 159 | app.sort_key = SortKey::None 160 | } else { 161 | app.sort_key = SortKey::TotalTx; 162 | } 163 | } 164 | Key::Down | Key::Char('j') => { 165 | app.next(); 166 | } 167 | Key::Up | Key::Char('k') => { 168 | app.previous(); 169 | } 170 | _ => {} 171 | }, 172 | Event::Tick => {} 173 | } 174 | } 175 | 176 | Ok(()) 177 | } 178 | -------------------------------------------------------------------------------- /src/net.rs: -------------------------------------------------------------------------------- 1 | use arraydeque::ArrayDeque; 2 | use async_std::task::spawn; 3 | use async_std_resolver::{proto::rr::Name, resolver_from_system_conf, AsyncStdResolver}; 4 | use std::{ 5 | collections::HashMap, 6 | net::IpAddr, 7 | sync::{mpsc, Arc, Mutex}, 8 | time::Duration, thread, 9 | }; 10 | 11 | use crate::app::{Items}; 12 | 13 | #[derive(Debug, Clone)] 14 | pub struct Resolver { 15 | mappings: Arc>>, 16 | resolver: AsyncStdResolver, 17 | tx: Option>, 18 | } 19 | 20 | impl<'a> Resolver { 21 | pub async fn new() -> Resolver { 22 | Resolver { 23 | mappings: Default::default(), 24 | resolver: resolver_from_system_conf().await.unwrap(), 25 | tx: None, 26 | } 27 | } 28 | 29 | pub fn start(&mut self) { 30 | let (tx, rx) = mpsc::channel(); 31 | self.tx = Some(tx); 32 | let resolver = self.resolver.clone(); 33 | let mappings = self.mappings.clone(); 34 | 35 | spawn(async move { 36 | loop { 37 | let rcv = rx.recv(); 38 | if let Ok(ip) = rcv { 39 | let resolver = resolver.clone(); 40 | let mappings = mappings.clone(); 41 | spawn(async move { 42 | if let Ok(rev) = resolver.reverse_lookup(ip).await { 43 | let name = rev.into_iter().next().unwrap_or(Name::new()); 44 | mappings.lock().unwrap().insert(ip, name.to_string()); 45 | } 46 | }); 47 | } 48 | } 49 | }); 50 | } 51 | 52 | pub fn resolve_ip(&self, ip: IpAddr) -> String { 53 | if let Some(name) = self.mappings.lock().unwrap().get(&ip) { 54 | name.to_owned() 55 | } else { 56 | let tx = self.tx.clone(); 57 | spawn(async move { 58 | if let Some(tx) = tx { 59 | tx.send(ip).unwrap_or_default(); 60 | } 61 | }); 62 | 63 | "".to_string() 64 | } 65 | } 66 | } 67 | 68 | #[derive(Clone)] 69 | pub struct RateMonitor { 70 | rates: Arc>>, 71 | } 72 | 73 | impl RateMonitor { 74 | pub fn new() -> RateMonitor { 75 | RateMonitor { 76 | rates: Default::default(), 77 | } 78 | } 79 | 80 | /// Starts monitoring the items and calculates the bytes per second rate 81 | pub fn start(&mut self, items: Arc>) { 82 | let rates = self.rates.clone(); 83 | thread::spawn(move || { 84 | // Ring buffer to save previous stats 85 | let mut stats: HashMap> = 86 | HashMap::new(); 87 | loop { 88 | for item in &mut items.lock().unwrap().vec { 89 | let key = format!("{}:{}", item.ip, item.port); 90 | if let Some(arr) = stats.get_mut(&key) { 91 | arr.push_back((item.tot_tx, item.tot_rx)); 92 | let (mut tx_rate, mut rx_rate) = arr.back().unwrap_or(&(0, 0)); 93 | let last = arr.front().unwrap_or(&(0, 0)); 94 | // calculation: 95 | // last item - first item = difference in bytes 96 | // divide this by the number of items in the buffer (one added each second) 97 | // and we have bytes per second. 98 | tx_rate = (tx_rate - last.0) / arr.len() as u64; 99 | rx_rate = (rx_rate - last.1) / arr.len() as u64; 100 | rates.lock().unwrap().insert(key, (tx_rate, rx_rate)); 101 | } else { 102 | stats.insert(key, ArrayDeque::new()); 103 | } 104 | } 105 | std::thread::sleep(Duration::from_secs(1)); 106 | } 107 | }); 108 | } 109 | 110 | /// Returns rates in a tuple: (tx_rate, rx_rate) 111 | pub fn get_rates(&self, key: &str) -> (u64, u64) { 112 | *self.rates.lock().unwrap().get(key).unwrap_or(&(0, 0)) 113 | } 114 | } 115 | 116 | #[cfg(test)] 117 | mod tests { 118 | use std::{net::Ipv4Addr, thread, time::Duration}; 119 | 120 | use async_std::task::block_on; 121 | 122 | use super::*; 123 | #[test] 124 | fn resolve() { 125 | let mut resolver = block_on(Resolver::new()); 126 | println!("Resolver connected"); 127 | resolver.start(); 128 | 129 | let test_ip = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)); 130 | 131 | resolver.resolve_ip(test_ip); 132 | thread::sleep(Duration::from_secs(1)); 133 | 134 | let name = resolver.resolve_ip(test_ip); 135 | assert_eq!(name, "one.one.one.one."); 136 | 137 | let test_ip = IpAddr::V4(Ipv4Addr::new(3, 82, 63, 37)); 138 | resolver.resolve_ip(test_ip); 139 | thread::sleep(Duration::from_secs(1)); 140 | 141 | let name = resolver.resolve_ip(test_ip); 142 | assert_eq!(name, "ec2-3-82-63-37.compute-1.amazonaws.com."); 143 | } 144 | } 145 | --------------------------------------------------------------------------------