rtnetlink-0.14.1/.cargo_vcs_info.json0000644000000001360000000000100131310ustar { "git": { "sha1": "6d77e01b8decc4a4bcae5a1c490203852e555fea" }, "path_in_vcs": "" }rtnetlink-0.14.1/.github/workflows/clippy-rustfmt.yml000064400000000000000000000012701046102023000210430ustar 00000000000000name: Rustfmt and clippy check on: pull_request: types: [opened, synchronize, reopened] push: branches: - main jobs: rustfmt_clippy: strategy: fail-fast: true name: Rustfmt and clippy check runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install Rust Nightly run: | rustup override set nightly rustup update nightly rustup component add rustfmt clippy - name: rustfmt run: cargo fmt --all -- --check - name: clippy-tokio-socket run: cargo clippy - name: clippy-smol-socket run: cargo clippy --no-default-features --features smol_socket rtnetlink-0.14.1/.github/workflows/license.yml000064400000000000000000000005201046102023000174600ustar 00000000000000name: license on: pull_request: types: [opened, synchronize, reopened] push: branches: - main jobs: check-license: name: Check License runs-on: ubuntu-latest timeout-minutes: 3 steps: - uses: actions/checkout@v3 - name: Check License Header uses: apache/skywalking-eyes@v0.3.0 rtnetlink-0.14.1/.github/workflows/main.yml000064400000000000000000000020201046102023000167570ustar 00000000000000name: CI on: pull_request: types: [opened, synchronize, reopened] push: branches: - main jobs: ci: name: CI (stable) runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install Rust Stable run: | rustup override set stable rustup update stable - name: Test with default feature env: # Needed for the `link::test::create_get_delete_w` test to pass. CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER: "sudo -E" run: cargo test - name: Test with tokio feature env: # Needed for the `link::test::create_get_delete_w` test to pass. CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER: "sudo -E" run: cargo test --features tokio_socket - name: Test with smol_socket feature env: # Needed for the `link::test::create_get_delete_w` test to pass. CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER: "sudo -E" run: cargo test --features smol_socket rtnetlink-0.14.1/.gitignore000064400000000000000000000000451046102023000137100ustar 00000000000000Cargo.lock target vendor/ tags *.swp rtnetlink-0.14.1/.licenserc.yaml000064400000000000000000000003711046102023000146330ustar 00000000000000header: license: content: | SPDX-License-Identifier: MIT paths-ignore: - 'target' - '**/*.toml' - '**/*.lock' - '**/*.yml' - '**/*.md' - 'CHANGELOG' - 'LICENSE-MIT' - '.gitignore' comment: on-failure rtnetlink-0.14.1/.rustfmt.toml000064400000000000000000000001141046102023000143740ustar 00000000000000max_width = 80 wrap_comments = true reorder_imports = true edition = "2021" rtnetlink-0.14.1/CHANGELOG000064400000000000000000000042621046102023000131370ustar 00000000000000# Changelog ## [0.14.1] - 2024-02-01 ### Breaking changes - N/A ### New features - FreeBSD support. (eb04e60) - Support specifying MAC address in `LinkAddRequest`. (d76171c) - Support creating wireguard link in `LinkAddRequest`. (24982ec) - Support setting priority in `RouteAddRequest`. (c840e78) ### Bug fixes - Fixing docs of AddressGetRequest::set_address_filter. (006a348) ## [0.14.0] - 2023-12-05 ### Breaking changes - Many `VxlanAddRequest` functions changed from u8 to bool. (ba4825a) - Deprecated `LinkSetRequest::master()` in the favor of `LinkSetRequest::controller()`. (ba4825a) - Deprecated `LinkSetRequest::nomaster()` in the favor of `LinkSetRequest::nocontroller()`. (ba4825a) - Many `NeighbourAddRequest` functions changed from u8/u16 to enum. (ba4825a) - Many `TrafficFilterNewRequest` functions changed from u8/u16 to enum. (ba4825a) ### New features - Rule: function to set fw_mark when adding rule. (dabef43) ### Bug fixes - N/A ## [0.13.1] - 2023-07-18 ### Breaking changes - Deprecated `BondAddRequest::active_slave()` in the favor of `BondAddRequest::active_port()`. (9b67c97, bf6dbf0) - Deprecated `BondAddRequest::all_slaves_active()` in the favor of `BondAddRequest::all_ports_active()`. (9b67c97, bf6dbf0) ### New features - Support bond port setting. (7afe563) - Support VLAN QOS setting. (78a58db) ### Bug fixes - N/A ## [0.13.0] - 2023-07-10 ### Breaking changes - `TrafficFilterNewRequest::u32()` changed to return `Result`. (b7f8c73) - `TrafficFilterNewRequest::redirect() changed to return `Result`. (b7f8c73) - Deprecated `RouteAddRequest::table` in the favor of `RouteAddRequest::table_id` in order to support table ID bigger than 255. (0a8eddd) ### New features - Support route table ID bigger than 255. (0a8eddd) - Support creating xfrm tunnel. (5252908) ### Bug fixes - Removed assers. (e6bcf3e) ## [0.12.0] - 2023-01-29 ### Breaking changes - Removed these reexports. (2d58a54) * `rtnetlink::packet` * `rtnetlink::proto` * `rtnetlink::sys` ### New features - Allow adding macvtap on a link. (ad1207f) - Support setting priority when adding rules. (b771ffd) ### Bug fixes - Fix ip_monitor example. (b12f061) rtnetlink-0.14.1/Cargo.lock0000644000001013200000000000100111010ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "addr2line" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] name = "anyhow" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] name = "async-attributes" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" dependencies = [ "quote", "syn 1.0.109", ] [[package]] name = "async-channel" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" dependencies = [ "concurrent-queue", "event-listener 2.5.3", "futures-core", ] [[package]] name = "async-channel" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" dependencies = [ "concurrent-queue", "event-listener 4.0.3", "event-listener-strategy", "futures-core", "pin-project-lite", ] [[package]] name = "async-executor" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" dependencies = [ "async-lock 3.3.0", "async-task", "concurrent-queue", "fastrand 2.0.1", "futures-lite 2.2.0", "slab", ] [[package]] name = "async-global-executor" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ "async-channel 2.1.1", "async-executor", "async-io 2.3.1", "async-lock 3.3.0", "blocking", "futures-lite 2.2.0", "once_cell", ] [[package]] name = "async-io" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ "async-lock 2.8.0", "autocfg", "cfg-if", "concurrent-queue", "futures-lite 1.13.0", "log", "parking", "polling 2.8.0", "rustix 0.37.27", "slab", "socket2 0.4.10", "waker-fn", ] [[package]] name = "async-io" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f97ab0c5b00a7cdbe5a371b9a782ee7be1316095885c8a4ea1daf490eb0ef65" dependencies = [ "async-lock 3.3.0", "cfg-if", "concurrent-queue", "futures-io", "futures-lite 2.2.0", "parking", "polling 3.3.2", "rustix 0.38.30", "slab", "tracing", "windows-sys 0.52.0", ] [[package]] name = "async-lock" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" dependencies = [ "event-listener 2.5.3", ] [[package]] name = "async-lock" version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" dependencies = [ "event-listener 4.0.3", "event-listener-strategy", "pin-project-lite", ] [[package]] name = "async-std" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" dependencies = [ "async-attributes", "async-channel 1.9.0", "async-global-executor", "async-io 1.13.0", "async-lock 2.8.0", "crossbeam-utils", "futures-channel", "futures-core", "futures-io", "futures-lite 1.13.0", "gloo-timers", "kv-log-macro", "log", "memchr", "once_cell", "pin-project-lite", "pin-utils", "slab", "wasm-bindgen-futures", ] [[package]] name = "async-task" version = "4.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" [[package]] name = "atomic-waker" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", ] [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "blocking" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ "async-channel 2.1.1", "async-lock 3.3.0", "async-task", "fastrand 2.0.1", "futures-io", "futures-lite 2.2.0", "piper", "tracing", ] [[package]] name = "bumpalo" version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "libc", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "concurrent-queue" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "env_logger" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" dependencies = [ "humantime", "is-terminal", "log", "regex", "termcolor", ] [[package]] name = "errno" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "event-listener" version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" dependencies = [ "concurrent-queue", "parking", "pin-project-lite", ] [[package]] name = "event-listener-strategy" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" dependencies = [ "event-listener 4.0.3", "pin-project-lite", ] [[package]] name = "fastrand" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] [[package]] name = "fastrand" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "futures" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", "futures-executor", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-lite" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ "fastrand 1.9.0", "futures-core", "futures-io", "memchr", "parking", "pin-project-lite", "waker-fn", ] [[package]] name = "futures-lite" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba" dependencies = [ "fastrand 2.0.1", "futures-core", "futures-io", "parking", "pin-project-lite", ] [[package]] name = "futures-macro" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", "syn 2.0.48", ] [[package]] name = "futures-sink" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-macro", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "gimli" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "gloo-timers" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" dependencies = [ "futures-channel", "futures-core", "js-sys", "wasm-bindgen", ] [[package]] name = "hermit-abi" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "instant" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", ] [[package]] name = "io-lifetimes" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi", "libc", "windows-sys 0.48.0", ] [[package]] name = "ipnetwork" version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4088d739b183546b239688ddbc79891831df421773df95e236daf7867866d355" dependencies = [ "serde", ] [[package]] name = "is-terminal" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" dependencies = [ "hermit-abi", "rustix 0.38.30", "windows-sys 0.52.0", ] [[package]] name = "js-sys" version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" dependencies = [ "wasm-bindgen", ] [[package]] name = "kv-log-macro" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" dependencies = [ "log", ] [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "linux-raw-sys" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" dependencies = [ "value-bag", ] [[package]] name = "macaddr" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baee0bbc17ce759db233beb01648088061bf678383130602a298e6998eedb2d8" [[package]] name = "memchr" version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "miniz_oxide" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] [[package]] name = "mio" version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "wasi", "windows-sys 0.48.0", ] [[package]] name = "netlink-packet-core" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4" dependencies = [ "anyhow", "byteorder", "netlink-packet-utils", ] [[package]] name = "netlink-packet-route" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74c171cd77b4ee8c7708da746ce392440cb7bcf618d122ec9ecc607b12938bf4" dependencies = [ "anyhow", "byteorder", "libc", "log", "netlink-packet-core", "netlink-packet-utils", ] [[package]] name = "netlink-packet-utils" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" dependencies = [ "anyhow", "byteorder", "paste", "thiserror", ] [[package]] name = "netlink-proto" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b33524dc0968bfad349684447bfce6db937a9ac3332a1fe60c0c5a5ce63f21" dependencies = [ "bytes", "futures", "log", "netlink-packet-core", "netlink-sys", "thiserror", "tokio", ] [[package]] name = "netlink-sys" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" dependencies = [ "async-io 1.13.0", "bytes", "futures", "libc", "log", "tokio", ] [[package]] name = "nix" version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ "bitflags 2.4.2", "cfg-if", "libc", ] [[package]] name = "num_cpus" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", ] [[package]] name = "object" version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "parking" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "paste" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pin-project-lite" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "piper" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" dependencies = [ "atomic-waker", "fastrand 2.0.1", "futures-io", ] [[package]] name = "polling" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", "bitflags 1.3.2", "cfg-if", "concurrent-queue", "libc", "log", "pin-project-lite", "windows-sys 0.48.0", ] [[package]] name = "polling" version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "545c980a3880efd47b2e262f6a4bb6daad6555cf3367aa9c4e52895f69537a41" dependencies = [ "cfg-if", "concurrent-queue", "pin-project-lite", "rustix 0.38.30", "tracing", "windows-sys 0.52.0", ] [[package]] name = "proc-macro2" version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] name = "regex" version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "rtnetlink" version = "0.14.1" dependencies = [ "async-global-executor", "async-std", "env_logger", "futures", "ipnetwork", "log", "macaddr", "netlink-packet-core", "netlink-packet-route", "netlink-packet-utils", "netlink-proto", "netlink-sys", "nix", "thiserror", "tokio", ] [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" version = "0.37.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" dependencies = [ "bitflags 1.3.2", "errno", "io-lifetimes", "libc", "linux-raw-sys 0.3.8", "windows-sys 0.48.0", ] [[package]] name = "rustix" version = "0.38.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" dependencies = [ "bitflags 2.4.2", "errno", "libc", "linux-raw-sys 0.4.13", "windows-sys 0.52.0", ] [[package]] name = "serde" version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", "syn 2.0.48", ] [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "socket2" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", ] [[package]] name = "socket2" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", "windows-sys 0.48.0", ] [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "termcolor" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", "syn 2.0.48", ] [[package]] name = "tokio" version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ "backtrace", "bytes", "libc", "mio", "num_cpus", "pin-project-lite", "socket2 0.5.5", "tokio-macros", "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", "syn 2.0.48", ] [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", "tracing-core", ] [[package]] name = "tracing-core" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "value-bag" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "126e423afe2dd9ac52142e7e9d5ce4135d7e13776c529d27fd6bc49f19e3280b" [[package]] name = "waker-fn" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn 2.0.48", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" dependencies = [ "cfg-if", "js-sys", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" dependencies = [ "proc-macro2", "quote", "syn 2.0.48", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" [[package]] name = "web-sys" version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets 0.48.5", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.0", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm 0.48.5", "windows_aarch64_msvc 0.48.5", "windows_i686_gnu 0.48.5", "windows_i686_msvc 0.48.5", "windows_x86_64_gnu 0.48.5", "windows_x86_64_gnullvm 0.48.5", "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ "windows_aarch64_gnullvm 0.52.0", "windows_aarch64_msvc 0.52.0", "windows_i686_gnu 0.52.0", "windows_i686_msvc 0.52.0", "windows_x86_64_gnu 0.52.0", "windows_x86_64_gnullvm 0.52.0", "windows_x86_64_msvc 0.52.0", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" rtnetlink-0.14.1/Cargo.toml0000644000000040340000000000100111300ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" rust-version = "1.66.1" name = "rtnetlink" version = "0.14.1" authors = ["Corentin Henry "] description = "manipulate linux networking resources via netlink" homepage = "https://github.com/rust-netlink/rtnetlink" readme = "README.md" keywords = [ "netlink", "ip", "linux", ] license = "MIT" repository = "https://github.com/rust-netlink/rtnetlink" [dependencies.async-global-executor] version = "2.0.2" optional = true [dependencies.futures] version = "0.3.11" [dependencies.log] version = "0.4.8" [dependencies.netlink-packet-core] version = "0.7" [dependencies.netlink-packet-route] version = "0.19" [dependencies.netlink-packet-utils] version = "0.5" [dependencies.netlink-proto] version = "0.11" default-features = false [dependencies.netlink-sys] version = "0.8" [dependencies.nix] version = "0.27.1" features = [ "fs", "mount", "sched", "signal", ] default-features = false [dependencies.thiserror] version = "1" [dependencies.tokio] version = "1.0.1" features = ["rt"] optional = true [dev-dependencies.async-std] version = "1.9.0" features = ["attributes"] [dev-dependencies.env_logger] version = "0.10.0" [dev-dependencies.ipnetwork] version = "0.18.0" [dev-dependencies.macaddr] version = "1.0" [dev-dependencies.tokio] version = "1.0.1" features = [ "macros", "rt", "rt-multi-thread", ] [features] default = ["tokio_socket"] smol_socket = [ "netlink-proto/smol_socket", "async-global-executor", ] test_as_root = [] tokio_socket = [ "netlink-proto/tokio_socket", "tokio", ] rtnetlink-0.14.1/Cargo.toml.orig000064400000000000000000000024441046102023000146140ustar 00000000000000[package] name = "rtnetlink" version = "0.14.1" authors = ["Corentin Henry "] edition = "2018" homepage = "https://github.com/rust-netlink/rtnetlink" keywords = ["netlink", "ip", "linux"] license = "MIT" readme = "README.md" repository = "https://github.com/rust-netlink/rtnetlink" description = "manipulate linux networking resources via netlink" rust-version = "1.66.1" [features] test_as_root = [] default = ["tokio_socket"] tokio_socket = ["netlink-proto/tokio_socket", "tokio"] smol_socket = ["netlink-proto/smol_socket", "async-global-executor"] [dependencies] futures = "0.3.11" log = "0.4.8" thiserror = "1" netlink-sys = { version = "0.8" } netlink-packet-utils = { version = "0.5" } netlink-packet-route = { version = "0.19" } netlink-packet-core = { version = "0.7" } netlink-proto = { default-features = false, version = "0.11" } nix = { version = "0.27.1", default-features = false, features = ["fs", "mount", "sched", "signal"] } tokio = { version = "1.0.1", features = ["rt"], optional = true} async-global-executor = { version = "2.0.2", optional = true } [dev-dependencies] env_logger = "0.10.0" ipnetwork = "0.18.0" tokio = { version = "1.0.1", features = ["macros", "rt", "rt-multi-thread"] } async-std = { version = "1.9.0", features = ["attributes"]} macaddr = "1.0" rtnetlink-0.14.1/LICENSE-MIT000064400000000000000000000027731046102023000133660ustar 00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. Distributions of all or part of the Software intended to be used by the recipients as they would use the unmodified Software, containing modifications that substantially alter, remove, or disable functionality of the Software, outside of the documented configuration mechanisms provided by the Software, shall be modified such that the Original Author's bug reporting email addresses and urls are either replaced with the contact information of the parties responsible for the changes, or removed entirely. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. rtnetlink-0.14.1/README.md000064400000000000000000000005131046102023000131770ustar 00000000000000# Rust crate for rtnetlink protocol This crate provides methods to manipulate networking resources (links, addresses, arp tables, route tables) via the [netlink route protocol][1]. Rust crate document could be found at [docs.rs][2]. [1]: https://www.man7.org/linux/man-pages/man7/rtnetlink.7.html [2]: https://docs.rs/rtnetlink rtnetlink-0.14.1/examples/add_address.rs000064400000000000000000000030231046102023000163400ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; use std::env; use ipnetwork::IpNetwork; use rtnetlink::{new_connection, Error, Handle}; #[tokio::main] async fn main() -> Result<(), ()> { let args: Vec = env::args().collect(); if args.len() != 3 { usage(); return Ok(()); } let link_name = &args[1]; let ip: IpNetwork = args[2].parse().unwrap_or_else(|_| { eprintln!("invalid address"); std::process::exit(1); }); let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); if let Err(e) = add_address(link_name, ip, handle.clone()).await { eprintln!("{e}"); } Ok(()) } async fn add_address( link_name: &str, ip: IpNetwork, handle: Handle, ) -> Result<(), Error> { let mut links = handle .link() .get() .match_name(link_name.to_string()) .execute(); if let Some(link) = links.try_next().await? { handle .address() .add(link.header.index, ip.ip(), ip.prefix()) .execute() .await? } Ok(()) } fn usage() { eprintln!( "usage: cargo run --example add_address -- Note that you need to run this program as root. Instead of running cargo as root, build the example normally: cd rtnetlink ; cargo build --example add_address Then find the binary in the target directory: cd ../target/debug/example ; sudo ./add_address " ); } rtnetlink-0.14.1/examples/add_neighbour.rs000064400000000000000000000030331046102023000166760ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; use rtnetlink::{new_connection, Error, Handle}; use std::{env, net::IpAddr}; #[tokio::main] async fn main() -> Result<(), ()> { let args: Vec = env::args().collect(); if args.len() != 3 { usage(); return Ok(()); } let link_name = &args[1]; let ip: IpAddr = args[2].parse().unwrap_or_else(|_| { eprintln!("invalid IP address"); std::process::exit(1); }); let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); if let Err(e) = add_neighbour(link_name, ip, handle.clone()).await { eprintln!("{e}"); } Ok(()) } async fn add_neighbour( link_name: &str, ip: IpAddr, handle: Handle, ) -> Result<(), Error> { let mut links = handle .link() .get() .match_name(link_name.to_string()) .execute(); if let Some(link) = links.try_next().await? { handle .neighbours() .add(link.header.index, ip) .execute() .await?; println!("Done"); } Ok(()) } fn usage() { eprintln!( "usage: cargo run --example add_neighbour -- Note that you need to run this program as root. Instead of running cargo as root, build the example normally: cd rtnetlink ; cargo build --example add_neighbour Then find the binary in the target directory: cd ../target/debug/example ; sudo ./add_neighbour " ); } rtnetlink-0.14.1/examples/add_netns.rs000064400000000000000000000016121046102023000160440ustar 00000000000000// SPDX-License-Identifier: MIT #[cfg(not(target_os = "freebsd"))] use rtnetlink::NetworkNamespace; use std::env; #[cfg(target_os = "freebsd")] fn main() -> () {} #[cfg(not(target_os = "freebsd"))] #[tokio::main] async fn main() -> Result<(), String> { env_logger::init(); let args: Vec = env::args().collect(); if args.len() != 2 { usage(); return Ok(()); } let ns_name = &args[1]; NetworkNamespace::add(ns_name.to_string()) .await .map_err(|e| format!("{e}")) } fn usage() { eprintln!( "usage: cargo run --example add_netns -- Note that you need to run this program as root. Instead of running cargo as root, build the example normally: cd netlink-ip ; cargo build --example add_netns Then find the binary in the target directory: cd ../target/debug/example ; sudo ./add_netns " ); } rtnetlink-0.14.1/examples/add_netns_async.rs000064400000000000000000000016161046102023000172450ustar 00000000000000// SPDX-License-Identifier: MIT #[cfg(not(target_os = "freebsd"))] use rtnetlink::NetworkNamespace; use std::env; #[cfg(target_os = "freebsd")] fn main() -> () {} #[cfg(not(target_os = "freebsd"))] #[async_std::main] async fn main() -> Result<(), String> { env_logger::init(); let args: Vec = env::args().collect(); if args.len() != 2 { usage(); return Ok(()); } let ns_name = &args[1]; NetworkNamespace::add(ns_name.to_string()) .await .map_err(|e| format!("{e}")) } fn usage() { eprintln!( "usage: cargo run --example add_netns -- Note that you need to run this program as root. Instead of running cargo as root, build the example normally: cd netlink-ip ; cargo build --example add_netns Then find the binary in the target directory: cd ../target/debug/example ; sudo ./add_netns " ); } rtnetlink-0.14.1/examples/add_route.rs000064400000000000000000000031101046102023000160460ustar 00000000000000// SPDX-License-Identifier: MIT use std::env; use ipnetwork::Ipv4Network; use rtnetlink::{new_connection, Error, Handle}; const TEST_TABLE_ID: u32 = 299; #[tokio::main] async fn main() -> Result<(), ()> { let args: Vec = env::args().collect(); if args.len() != 3 { usage(); return Ok(()); } let dest: Ipv4Network = args[1].parse().unwrap_or_else(|_| { eprintln!("invalid destination"); std::process::exit(1); }); let gateway: Ipv4Network = args[2].parse().unwrap_or_else(|_| { eprintln!("invalid gateway"); std::process::exit(1); }); let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); if let Err(e) = add_route(&dest, &gateway, handle.clone()).await { eprintln!("{e}"); } else { println!("Route has been added to table {TEST_TABLE_ID}"); } Ok(()) } async fn add_route( dest: &Ipv4Network, gateway: &Ipv4Network, handle: Handle, ) -> Result<(), Error> { let route = handle.route(); route .add() .v4() .destination_prefix(dest.ip(), dest.prefix()) .gateway(gateway.ip()) .table_id(TEST_TABLE_ID) .execute() .await?; Ok(()) } fn usage() { eprintln!( "\ usage: cargo run --example add_route -- / Note that you need to run this program as root: env CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER='sudo -E' \\ cargo run --example add_route -- / \ " ); } rtnetlink-0.14.1/examples/add_route_pref_src.rs000064400000000000000000000037531046102023000177460ustar 00000000000000// SPDX-License-Identifier: MIT use futures::TryStreamExt; use std::{env, net::Ipv4Addr}; use ipnetwork::Ipv4Network; use rtnetlink::{new_connection, Error, Handle}; #[tokio::main] async fn main() -> Result<(), ()> { let args: Vec = env::args().collect(); if args.len() != 4 { usage(); return Ok(()); } let dest: Ipv4Network = args[1].parse().unwrap_or_else(|_| { eprintln!("invalid destination"); std::process::exit(1); }); let iface: String = args[2].parse().unwrap_or_else(|_| { eprintln!("invalid interface"); std::process::exit(1); }); let source: Ipv4Addr = args[3].parse().unwrap_or_else(|_| { eprintln!("invalid source"); std::process::exit(1); }); let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); if let Err(e) = add_route(&dest, iface, source, handle.clone()).await { eprintln!("{e}"); } Ok(()) } async fn add_route( dest: &Ipv4Network, iface: String, source: Ipv4Addr, handle: Handle, ) -> Result<(), Error> { let iface_idx = handle .link() .get() .match_name(iface) .execute() .try_next() .await? .unwrap() .header .index; let route = handle.route(); route .add() .v4() .destination_prefix(dest.ip(), dest.prefix()) .output_interface(iface_idx) .pref_source(source) .execute() .await?; Ok(()) } fn usage() { eprintln!( "usage: cargo run --example add_route_pref_src -- / Note that you need to run this program as root. Instead of running cargo as root, build the example normally: cd rtnetlink ; cargo build --example add_route_pref_src Then find the binary in the target directory: cd ../target/debug/example ; sudo ./add_route_pref_src / " ); } rtnetlink-0.14.1/examples/add_rule.rs000064400000000000000000000027671046102023000157000ustar 00000000000000// SPDX-License-Identifier: MIT use std::env; use ipnetwork::Ipv4Network; use rtnetlink::{new_connection, Error, Handle}; #[tokio::main] async fn main() -> Result<(), ()> { let args: Vec = env::args().collect(); if args.len() != 3 { usage(); return Ok(()); } let dst: Ipv4Network = args[1].parse().unwrap_or_else(|_| { eprintln!("invalid destination"); std::process::exit(1); }); let table_id: u32 = args[2].parse().unwrap_or_else(|_| { eprintln!("invalid route table ID"); std::process::exit(1); }); let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); if let Err(e) = add_rule(&dst, table_id, handle.clone()).await { eprintln!("{e}"); } else { println!("Route rule has been added for {dst} and lookup {table_id}") } Ok(()) } async fn add_rule( dst: &Ipv4Network, table_id: u32, handle: Handle, ) -> Result<(), Error> { let rule = handle.rule(); rule.add() .v4() .destination_prefix(dst.ip(), dst.prefix()) .table_id(table_id) .execute() .await?; Ok(()) } fn usage() { eprintln!( "\ usage: cargo run --example add_rule -- / Note that you need to run this program as root: env CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER='sudo -E' \\ cargo run --example add_rule -- / \ " ); } rtnetlink-0.14.1/examples/add_tc_qdisc_ingress.rs000064400000000000000000000021371046102023000202430ustar 00000000000000// SPDX-License-Identifier: MIT use std::env; use rtnetlink::new_connection; #[cfg(target_os = "freebsd")] fn main() -> () {} #[cfg(not(target_os = "freebsd"))] #[tokio::main] async fn main() -> Result<(), ()> { env_logger::init(); let args: Vec = env::args().collect(); if args.len() != 2 { usage(); return Ok(()); } let index: u32 = args[1].parse().unwrap_or_else(|_| { eprintln!("invalid index"); std::process::exit(1); }); let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); if let Err(e) = handle.qdisc().add(index as i32).ingress().execute().await { eprintln!("{e}"); } Ok(()) } fn usage() { eprintln!( "usage: cargo run --example add_tc_qdisc_ingress -- Note that you need to run this program as root. Instead of running cargo as root, build the example normally: cd rtnetlink ; cargo build --example add_tc_qdisc_ingress Then find the binary in the target directory: cd ../target/debug/example ; sudo ./add_tc_qdisc_ingress " ); } rtnetlink-0.14.1/examples/create_bond.rs000064400000000000000000000014211046102023000163500ustar 00000000000000// SPDX-License-Identifier: MIT use rtnetlink::new_connection; use std::net::{Ipv4Addr, Ipv6Addr}; #[tokio::main] async fn main() -> Result<(), String> { let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); handle .link() .add() .bond("my-bond".into()) .mode(1) .miimon(100) .updelay(100) .downdelay(100) .min_links(2) .arp_ip_target(vec![ Ipv4Addr::new(6, 6, 7, 7), Ipv4Addr::new(8, 8, 9, 10), ]) .ns_ip6_target(vec![ Ipv6Addr::new(0xfd01, 0, 0, 0, 0, 0, 0, 1), Ipv6Addr::new(0xfd02, 0, 0, 0, 0, 0, 0, 2), ]) .up() .execute() .await .map_err(|e| format!("{e}")) } rtnetlink-0.14.1/examples/create_bridge.rs000064400000000000000000000005541046102023000166700ustar 00000000000000// SPDX-License-Identifier: MIT use rtnetlink::new_connection; #[tokio::main] async fn main() -> Result<(), String> { let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); handle .link() .add() .bridge("my-bridge-1".into()) .execute() .await .map_err(|e| format!("{e}")) } rtnetlink-0.14.1/examples/create_macvlan.rs000064400000000000000000000040161046102023000170520ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; use macaddr::MacAddr; use rtnetlink::{new_connection, Error, Handle}; use std::{env, str::FromStr}; use netlink_packet_route::link::LinkAttribute; #[tokio::main] async fn main() -> Result<(), String> { let args: Vec = env::args().collect(); if args.len() != 2 && args.len() != 3 { usage(); return Ok(()); } let link_name = &args[1]; let mac: Option> = if args.len() == 3 { let mac_address_arg = (&args[2]).to_string(); let mac_address = MacAddr::from_str(mac_address_arg.as_str()) .map_err(|e| format!("{e}"))?; Some(mac_address.as_bytes().into()) } else { None }; let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); create_macvlan(handle, link_name.to_string(), mac) .await .map_err(|e| format!("{e}")) } async fn create_macvlan( handle: Handle, link_name: String, mac_address: Option>, ) -> Result<(), Error> { let mut links = handle.link().get().match_name(link_name.clone()).execute(); if let Some(link) = links.try_next().await? { let mut request = handle.link().add().macvlan( "test_macvlan".into(), link.header.index, 4u32, // bridge mode ); if let Some(mac) = mac_address { request .message_mut() .attributes .push(LinkAttribute::Address(mac)); } request.execute().await? } else { println!("no link {link_name} found"); } Ok(()) } fn usage() { eprintln!( "usage: cargo run --example create_macvlan -- [mac address] Note that you need to run this program as root. Instead of running cargo as root, build the example normally: cargo build --example create_macvlan Then find the binary in the target directory: cd target/debug/examples ; sudo ./create_macvlan [mac address]" ); } rtnetlink-0.14.1/examples/create_macvtap.rs000064400000000000000000000026661046102023000170750ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; use rtnetlink::{new_connection, Error, Handle}; use std::env; #[tokio::main] async fn main() -> Result<(), String> { let args: Vec = env::args().collect(); if args.len() != 2 { usage(); return Ok(()); } let link_name = &args[1]; let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); create_macvtap(handle, link_name.to_string()) .await .map_err(|e| format!("{e}")) } async fn create_macvtap( handle: Handle, veth_name: String, ) -> Result<(), Error> { let mut links = handle.link().get().match_name(veth_name.clone()).execute(); if let Some(link) = links.try_next().await? { // hard code mode: 4u32 i.e bridge mode let request = handle.link().add().macvtap( "test_macvtap".into(), link.header.index, 4u32, ); request.execute().await? } else { println!("no link link {veth_name} found"); } Ok(()) } fn usage() { eprintln!( "usage: cargo run --example create_macvtap -- Note that you need to run this program as root. Instead of running cargo as root, build the example normally: cd rtnetlink; cargo build --example create_macvtap Then find the binary in the target directory: cd ../target/debug/example ; sudo ./create_macvtap " ); } rtnetlink-0.14.1/examples/create_veth.rs000064400000000000000000000005741046102023000164040ustar 00000000000000// SPDX-License-Identifier: MIT use rtnetlink::new_connection; #[tokio::main] async fn main() -> Result<(), String> { let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); handle .link() .add() .veth("veth-rs-1".into(), "veth-rs-2".into()) .execute() .await .map_err(|e| format!("{e}")) } rtnetlink-0.14.1/examples/create_vlan.rs000064400000000000000000000075471046102023000164050ustar 00000000000000// SPDX-License-Identifier: MIT use std::{env, error::Error as StdError, str::FromStr}; use rtnetlink::{new_connection, QosMapping}; fn parse_mapping(parameter: &str) -> Result> { let (from, to) = parameter .split_once(':') .ok_or("Failed to parse mapping..")?; Ok(QosMapping { from: u32::from_str(from)?, to: u32::from_str(to)?, }) } const ARG_BASE: &str = "--base"; const ARG_NAME: &str = "--name"; const ARG_ID: &str = "--id"; const ARG_INGRESS_QOS: &str = "--ingress-qos-mapping"; const ARG_EGRESS_QOS: &str = "--egress-qos-mapping"; enum ParsingMode { None, Base, Name, Id, Ingress, Egress, } #[tokio::main] async fn main() -> Result<(), String> { let mut args: Vec = env::args().collect(); let mut base_interface = None; let mut name = None; let mut id = None; let mut ingress = Vec::new(); let mut egress = Vec::new(); let mut mode = ParsingMode::None; for argument in args.drain(1..) { fn match_argument(argument: String) -> Result { match argument.to_lowercase().as_str() { ARG_BASE => Ok(ParsingMode::Base), ARG_NAME => Ok(ParsingMode::Name), ARG_ID => Ok(ParsingMode::Id), ARG_INGRESS_QOS => Ok(ParsingMode::Ingress), ARG_EGRESS_QOS => Ok(ParsingMode::Egress), other => { usage(); Err(format!("Unexpected argument: {other}")) } } } mode = match mode { ParsingMode::None => match_argument(argument)?, ParsingMode::Base => { base_interface = u32::from_str(&argument).ok(); ParsingMode::None } ParsingMode::Name => { name = Some(argument); ParsingMode::None } ParsingMode::Id => { id = u16::from_str(&argument).ok(); ParsingMode::None } mode @ ParsingMode::Ingress => match parse_mapping(&argument) { Ok(mapping) => { ingress.push(mapping); mode } Err(_) => match_argument(argument)?, }, mode @ ParsingMode::Egress => match parse_mapping(&argument) { Ok(mapping) => { egress.push(mapping); mode } Err(_) => match_argument(argument)?, }, } } let Some(base) = base_interface else { usage(); return Err( "Missing or invalid argument for base interface!".to_owned() ); }; let Some(name) = name else { usage(); return Err( "Missing or invalid argument for new interface name!".to_owned() ); }; let Some(id) = id else { usage(); return Err("Missing or invalid argument for vlan id!".to_owned()); }; let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); handle .link() .add() .vlan_with_qos(name, base, id, ingress, egress) .execute() .await .map_err(|err| format!("Netlink request failed: {err}")) } fn usage() { eprintln!( "usage: cargo run --example create_vlan -- --base --name --id [--ingress-qos-mapping : ..>] [--egress-qos-mapping : ..>] Note that you need to run this program as root. Instead of running cargo as root, build the example normally: cd netlink-ip ; cargo build --example create_vlan Then find the binary in the target directory: cd ../target/debug/example ; sudo ./create_vlan " ); } rtnetlink-0.14.1/examples/create_vxlan.rs000064400000000000000000000026141046102023000165630ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; use rtnetlink::{new_connection, Error, Handle}; use std::env; #[tokio::main] async fn main() -> Result<(), String> { let args: Vec = env::args().collect(); if args.len() != 2 { usage(); return Ok(()); } let link_name = &args[1]; let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); create_vxlan(handle, link_name.to_string()) .await .map_err(|e| format!("{e}")) } async fn create_vxlan(handle: Handle, name: String) -> Result<(), Error> { let mut links = handle.link().get().match_name(name.clone()).execute(); if let Some(link) = links.try_next().await? { handle .link() .add() .vxlan("vxlan0".into(), 10u32) .link(link.header.index) .port(4789) .up() .execute() .await? } else { println!("no link link {name} found"); } Ok(()) } fn usage() { eprintln!( "usage: cargo run --example create_vxlan -- Note that you need to run this program as root. Instead of running cargo as root, build the example normally: cd netlink-ip ; cargo build --example create_vxlan Then find the binary in the target directory: cd ../target/debug/example ; sudo ./create_vxlan " ); } rtnetlink-0.14.1/examples/del_link.rs000064400000000000000000000023451046102023000156720ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; use rtnetlink::{new_connection, Error, Handle}; use std::env; #[tokio::main] async fn main() -> Result<(), ()> { let args: Vec = env::args().collect(); if args.len() != 2 { usage(); return Ok(()); } let link_name = &args[1]; let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); if let Err(e) = del_link(handle, link_name.to_string()).await { eprintln!("{e}"); } Ok(()) } async fn del_link(handle: Handle, name: String) -> Result<(), Error> { let mut links = handle.link().get().match_name(name.clone()).execute(); if let Some(link) = links.try_next().await? { handle.link().del(link.header.index).execute().await } else { eprintln!("link {name} not found"); Ok(()) } } fn usage() { eprintln!( "usage: cargo run --example del_link -- Note that you need to run this program as root. Instead of running cargo as root, build the example normally: cd rtnetlink ; cargo build --example del_link Then find the binary in the target directory: cd ../target/debug/example ; sudo ./del_link " ); } rtnetlink-0.14.1/examples/del_netns.rs000064400000000000000000000015621046102023000160640ustar 00000000000000// SPDX-License-Identifier: MIT #[cfg(not(target_os = "freebsd"))] use rtnetlink::NetworkNamespace; use std::env; #[cfg(target_os = "freebsd")] fn main() -> () {} #[cfg(not(target_os = "freebsd"))] #[tokio::main] async fn main() -> Result<(), String> { let args: Vec = env::args().collect(); if args.len() != 2 { usage(); return Ok(()); } let ns_name = &args[1]; NetworkNamespace::del(ns_name.to_string()) .await .map_err(|e| format!("{e}")) } fn usage() { eprintln!( "usage: cargo run --example del_netns -- Note that you need to run this program as root. Instead of running cargo as root, build the example normally: cd netlink-ip ; cargo build --example del_netns Then find the binary in the target directory: cd ../target/debug/example ; sudo ./del_netns " ); } rtnetlink-0.14.1/examples/del_netns_async.rs000064400000000000000000000015661046102023000172650ustar 00000000000000// SPDX-License-Identifier: MIT #[cfg(not(target_os = "freebsd"))] use rtnetlink::NetworkNamespace; use std::env; #[cfg(target_os = "freebsd")] fn main() -> () {} #[cfg(not(target_os = "freebsd"))] #[async_std::main] async fn main() -> Result<(), String> { let args: Vec = env::args().collect(); if args.len() != 2 { usage(); return Ok(()); } let ns_name = &args[1]; NetworkNamespace::del(ns_name.to_string()) .await .map_err(|e| format!("{e}")) } fn usage() { eprintln!( "usage: cargo run --example del_netns -- Note that you need to run this program as root. Instead of running cargo as root, build the example normally: cd netlink-ip ; cargo build --example del_netns Then find the binary in the target directory: cd ../target/debug/example ; sudo ./del_netns " ); } rtnetlink-0.14.1/examples/flush_addresses.rs000064400000000000000000000031371046102023000172670ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; use rtnetlink::{new_connection, Error, Handle}; use std::env; #[tokio::main] async fn main() -> Result<(), ()> { let args: Vec = env::args().collect(); if args.len() != 2 { usage(); return Ok(()); } let link_name = &args[1]; let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); if let Err(e) = flush_addresses(handle, link_name.to_string()).await { eprintln!("{e}"); } Ok(()) } async fn flush_addresses(handle: Handle, link: String) -> Result<(), Error> { let mut links = handle.link().get().match_name(link.clone()).execute(); if let Some(link) = links.try_next().await? { // We should have received only one message assert!(links.try_next().await?.is_none()); let mut addresses = handle .address() .get() .set_link_index_filter(link.header.index) .execute(); while let Some(addr) = addresses.try_next().await? { handle.address().del(addr).execute().await?; } Ok(()) } else { eprintln!("link {link} not found"); Ok(()) } } fn usage() { eprintln!( "usage: cargo run --example flush_addresses -- Note that you need to run this program as root. Instead of running cargo as root, build the example normally: cd rtnetlink ; cargo build --example flush_addresses Then find the binary in the target directory: cd ../target/debug/example ; sudo ./flush_addresses " ); } rtnetlink-0.14.1/examples/get_address.rs000064400000000000000000000017571046102023000164030ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; use rtnetlink::{new_connection, Error, Handle}; #[tokio::main] async fn main() -> Result<(), ()> { let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); let link = "lo".to_string(); println!("dumping address for link \"{link}\""); if let Err(e) = dump_addresses(handle, link).await { eprintln!("{e}"); } Ok(()) } async fn dump_addresses(handle: Handle, link: String) -> Result<(), Error> { let mut links = handle.link().get().match_name(link.clone()).execute(); if let Some(link) = links.try_next().await? { let mut addresses = handle .address() .get() .set_link_index_filter(link.header.index) .execute(); while let Some(msg) = addresses.try_next().await? { println!("{msg:?}"); } Ok(()) } else { eprintln!("link {link} not found"); Ok(()) } } rtnetlink-0.14.1/examples/get_bond_port_settings.rs000064400000000000000000000031061046102023000206520ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; use rtnetlink::{new_connection, Error, Handle}; use std::env; #[tokio::main] async fn main() -> Result<(), ()> { let args: Vec = env::args().collect(); if args.len() != 2 { usage(); return Ok(()); } let link_name = &args[1]; let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); let linkname = link_name.to_string(); println!("dumping bond port settings for link \"{linkname}\""); if let Err(e) = dump_bond_port_settings(handle, linkname).await { eprintln!("{e}"); } Ok(()) } async fn dump_bond_port_settings( handle: Handle, linkname: String, ) -> Result<(), Error> { let mut links = handle.link().get().match_name(linkname.clone()).execute(); if let Some(_link) = links.try_next().await? { let mut link_messgage = handle.link().get().match_name(linkname).execute(); while let Some(msg) = link_messgage.try_next().await? { println!("{msg:?}"); } Ok(()) } else { eprintln!("link {linkname} not found"); Ok(()) } } fn usage() { eprintln!( "usage: cargo run --example get_bond_port_settings -- Note that you need to run this program as root. Instead of running cargo as root, build the example normally: cd netlink-ip ; cargo build --example get_bond_port_settings Then find the binary in the target directory: cd ../target/debug/example ; sudo ./get_bond_port_settings " ); } rtnetlink-0.14.1/examples/get_links.rs000064400000000000000000000067641046102023000161010ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; use netlink_packet_route::{ link::{LinkAttribute, LinkExtentMask}, AddressFamily, }; use rtnetlink::{new_connection, Error, Handle}; #[cfg(target_os = "freebsd")] fn main() -> () {} #[cfg(not(target_os = "freebsd"))] #[tokio::main] async fn main() -> Result<(), ()> { env_logger::init(); let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); // Fetch a link by its index let index = 1; println!("*** retrieving link with index {index} ***"); if let Err(e) = get_link_by_index(handle.clone(), index).await { eprintln!("{e}"); } // Fetch a link by its name let name = "lo"; println!("*** retrieving link named \"{name}\" ***"); if let Err(e) = get_link_by_name(handle.clone(), name.to_string()).await { eprintln!("{e}"); } // Dump all the links and print their index and name println!("*** dumping links ***"); if let Err(e) = dump_links(handle.clone()).await { eprintln!("{e}"); } // Dump all the bridge vlan information if let Err(e) = dump_bridge_filter_info(handle.clone()).await { eprintln!("{e}"); } Ok(()) } async fn get_link_by_index(handle: Handle, index: u32) -> Result<(), Error> { let mut links = handle.link().get().match_index(index).execute(); let msg = if let Some(msg) = links.try_next().await? { msg } else { eprintln!("no link with index {index} found"); return Ok(()); }; // We should have received only one message assert!(links.try_next().await?.is_none()); for nla in msg.attributes.into_iter() { if let LinkAttribute::IfName(name) = nla { println!("found link with index {index} (name = {name})"); return Ok(()); } } eprintln!( "found link with index {index}, but this link does not have a name" ); Ok(()) } async fn get_link_by_name(handle: Handle, name: String) -> Result<(), Error> { let mut links = handle.link().get().match_name(name.clone()).execute(); if (links.try_next().await?).is_some() { println!("found link {name}"); // We should only have one link with that name assert!(links.try_next().await?.is_none()); } else { println!("no link link {name} found"); } Ok(()) } async fn dump_links(handle: Handle) -> Result<(), Error> { let mut links = handle.link().get().execute(); 'outer: while let Some(msg) = links.try_next().await? { for nla in msg.attributes.into_iter() { if let LinkAttribute::IfName(name) = nla { println!("found link {} ({})", msg.header.index, name); continue 'outer; } } eprintln!("found link {}, but the link has no name", msg.header.index); } Ok(()) } #[cfg(not(target_os = "freebsd"))] async fn dump_bridge_filter_info(handle: Handle) -> Result<(), Error> { let mut links = handle .link() .get() .set_filter_mask(AddressFamily::Bridge, vec![LinkExtentMask::Brvlan]) .execute(); 'outer: while let Some(msg) = links.try_next().await? { for nla in msg.attributes.into_iter() { if let LinkAttribute::AfSpecBridge(data) = nla { println!( "found interface {} with AfSpecBridge data {:?})", msg.header.index, data ); continue 'outer; } } } Ok(()) } rtnetlink-0.14.1/examples/get_links_async.rs000064400000000000000000000070021046102023000172600ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; use netlink_packet_route::{ link::{LinkAttribute, LinkExtentMask}, AddressFamily, }; use rtnetlink::{new_connection, Error, Handle}; #[cfg(target_os = "freebsd")] fn main() -> () {} #[cfg(not(target_os = "freebsd"))] #[async_std::main] async fn main() -> Result<(), ()> { env_logger::init(); let (connection, handle, _) = new_connection().unwrap(); async_std::task::spawn(connection); // Fetch a link by its index let index = 1; println!("*** retrieving link with index {index} ***"); if let Err(e) = get_link_by_index(handle.clone(), index).await { eprintln!("{e}"); } // Fetch a link by its name let name = "lo"; println!("*** retrieving link named \"{name}\" ***"); if let Err(e) = get_link_by_name(handle.clone(), name.to_string()).await { eprintln!("{e}"); } // Dump all the links and print their index and name println!("*** dumping links ***"); if let Err(e) = dump_links(handle.clone()).await { eprintln!("{e}"); } // Dump all the bridge vlan information if let Err(e) = dump_bridge_filter_info(handle.clone()).await { eprintln!("{e}"); } Ok(()) } async fn get_link_by_index(handle: Handle, index: u32) -> Result<(), Error> { let mut links = handle.link().get().match_index(index).execute(); let msg = if let Some(msg) = links.try_next().await? { msg } else { eprintln!("no link with index {index} found"); return Ok(()); }; // We should have received only one message assert!(links.try_next().await?.is_none()); for nla in msg.attributes.into_iter() { if let LinkAttribute::IfName(name) = nla { println!("found link with index {index} (name = {name})"); return Ok(()); } } eprintln!( "found link with index {index}, but this link does not have a name" ); Ok(()) } async fn get_link_by_name(handle: Handle, name: String) -> Result<(), Error> { let mut links = handle.link().get().match_name(name.clone()).execute(); if (links.try_next().await?).is_some() { println!("found link {name}"); // We should only have one link with that name assert!(links.try_next().await?.is_none()); } else { println!("no link link {name} found"); } Ok(()) } async fn dump_links(handle: Handle) -> Result<(), Error> { let mut links = handle.link().get().execute(); 'outer: while let Some(msg) = links.try_next().await? { for nla in msg.attributes.into_iter() { if let LinkAttribute::IfName(name) = nla { println!("found link {} ({})", msg.header.index, name); continue 'outer; } } eprintln!("found link {}, but the link has no name", msg.header.index); } Ok(()) } #[cfg(not(target_os = "freebsd"))] async fn dump_bridge_filter_info(handle: Handle) -> Result<(), Error> { let mut links = handle .link() .get() .set_filter_mask(AddressFamily::Bridge, vec![LinkExtentMask::Brvlan]) .execute(); 'outer: while let Some(msg) = links.try_next().await? { for nla in msg.attributes.into_iter() { if let LinkAttribute::AfSpecBridge(data) = nla { println!( "found interface {} with AfSpecBridge data {:?})", msg.header.index, data ); continue 'outer; } } } Ok(()) } rtnetlink-0.14.1/examples/get_links_thread_builder.rs000064400000000000000000000075401046102023000211270ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; use netlink_packet_route::{ link::{LinkAttribute, LinkExtentMask}, AddressFamily, }; #[cfg(not(target_os = "freebsd"))] use rtnetlink::new_connection; use rtnetlink::{Error, Handle}; #[cfg(not(target_os = "freebsd"))] async fn do_it(rt: &tokio::runtime::Runtime) -> Result<(), ()> { env_logger::init(); let (connection, handle, _) = new_connection().unwrap(); rt.spawn(connection); // Fetch a link by its index let index = 1; println!("*** retrieving link with index {index} ***"); if let Err(e) = get_link_by_index(handle.clone(), index).await { eprintln!("{e}"); } // Fetch a link by its name let name = "lo"; println!("*** retrieving link named \"{name}\" ***"); if let Err(e) = get_link_by_name(handle.clone(), name.to_string()).await { eprintln!("{e}"); } // Dump all the links and print their index and name println!("*** dumping links ***"); if let Err(e) = dump_links(handle.clone()).await { eprintln!("{e}"); } // Dump all the bridge vlan information if let Err(e) = dump_bridge_filter_info(handle.clone()).await { eprintln!("{e}"); } Ok(()) } async fn get_link_by_index(handle: Handle, index: u32) -> Result<(), Error> { let mut links = handle.link().get().match_index(index).execute(); let msg = if let Some(msg) = links.try_next().await? { msg } else { eprintln!("no link with index {index} found"); return Ok(()); }; // We should have received only one message assert!(links.try_next().await?.is_none()); for nla in msg.attributes.into_iter() { if let LinkAttribute::IfName(name) = nla { println!("found link with index {index} (name = {name})"); return Ok(()); } } eprintln!( "found link with index {index}, but this link does not have a name" ); Ok(()) } async fn get_link_by_name(handle: Handle, name: String) -> Result<(), Error> { let mut links = handle.link().get().match_name(name.clone()).execute(); if (links.try_next().await?).is_some() { println!("found link {name}"); // We should only have one link with that name assert!(links.try_next().await?.is_none()); } else { println!("no link link {name} found"); } Ok(()) } async fn dump_links(handle: Handle) -> Result<(), Error> { let mut links = handle.link().get().execute(); 'outer: while let Some(msg) = links.try_next().await? { for nla in msg.attributes.into_iter() { if let LinkAttribute::IfName(name) = nla { println!("found link {} ({})", msg.header.index, name); continue 'outer; } } eprintln!("found link {}, but the link has no name", msg.header.index); } Ok(()) } #[cfg(not(target_os = "freebsd"))] async fn dump_bridge_filter_info(handle: Handle) -> Result<(), Error> { let mut links = handle .link() .get() .set_filter_mask(AddressFamily::Bridge, vec![LinkExtentMask::Brvlan]) .execute(); 'outer: while let Some(msg) = links.try_next().await? { for nla in msg.attributes.into_iter() { if let LinkAttribute::AfSpecBridge(data) = nla { println!( "found interface {} with AfSpecBridge data {:?})", msg.header.index, data ); continue 'outer; } } } Ok(()) } #[cfg(target_os = "freebsd")] fn main() -> () {} #[cfg(not(target_os = "freebsd"))] fn main() -> Result<(), String> { let rt = tokio::runtime::Builder::new_multi_thread() .enable_io() .build() .unwrap(); let future = do_it(&rt); println!("blocking in main"); rt.handle().block_on(future).unwrap(); Ok(()) } rtnetlink-0.14.1/examples/get_neighbours.rs000064400000000000000000000013271046102023000171140ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; use rtnetlink::{new_connection, Error, Handle, IpVersion}; #[tokio::main] async fn main() -> Result<(), ()> { let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); println!("dumping neighbours"); if let Err(e) = dump_neighbours(handle.clone()).await { eprintln!("{e}"); } println!(); Ok(()) } async fn dump_neighbours(handle: Handle) -> Result<(), Error> { let mut neighbours = handle .neighbours() .get() .set_family(IpVersion::V4) .execute(); while let Some(route) = neighbours.try_next().await? { println!("{route:?}"); } Ok(()) } rtnetlink-0.14.1/examples/get_route.rs000064400000000000000000000015561046102023000161110ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; use rtnetlink::{new_connection, Error, Handle, IpVersion}; #[tokio::main] async fn main() -> Result<(), ()> { let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); println!("dumping routes for IPv4"); if let Err(e) = dump_addresses(handle.clone(), IpVersion::V4).await { eprintln!("{e}"); } println!(); println!("dumping routes for IPv6"); if let Err(e) = dump_addresses(handle.clone(), IpVersion::V6).await { eprintln!("{e}"); } println!(); Ok(()) } async fn dump_addresses( handle: Handle, ip_version: IpVersion, ) -> Result<(), Error> { let mut routes = handle.route().get(ip_version).execute(); while let Some(route) = routes.try_next().await? { println!("{route:?}"); } Ok(()) } rtnetlink-0.14.1/examples/get_rule.rs000064400000000000000000000015471046102023000157220ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; use rtnetlink::{new_connection, Error, Handle, IpVersion}; #[tokio::main] async fn main() -> Result<(), ()> { let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); println!("dumping rules for IPv4"); if let Err(e) = dump_addresses(handle.clone(), IpVersion::V4).await { eprintln!("{e}"); } println!(); println!("dumping rules for IPv6"); if let Err(e) = dump_addresses(handle.clone(), IpVersion::V6).await { eprintln!("{e}"); } println!(); Ok(()) } async fn dump_addresses( handle: Handle, ip_version: IpVersion, ) -> Result<(), Error> { let mut rules = handle.rule().get(ip_version).execute(); while let Some(rule) = rules.try_next().await? { println!("{rule:?}"); } Ok(()) } rtnetlink-0.14.1/examples/ip_monitor.rs000064400000000000000000000050261046102023000162670ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::StreamExt; use netlink_sys::{AsyncSocket, SocketAddr}; use rtnetlink::new_connection; const RTNLGRP_LINK: u32 = 1; const RTNLGRP_NEIGH: u32 = 3; const RTNLGRP_IPV4_IFADDR: u32 = 5; const RTNLGRP_IPV4_MROUTE: u32 = 6; const RTNLGRP_IPV4_ROUTE: u32 = 7; const RTNLGRP_IPV4_RULE: u32 = 8; const RTNLGRP_IPV6_IFADDR: u32 = 9; const RTNLGRP_IPV6_MROUTE: u32 = 10; const RTNLGRP_IPV6_ROUTE: u32 = 11; const RTNLGRP_IPV6_RULE: u32 = 19; const RTNLGRP_IPV4_NETCONF: u32 = 24; const RTNLGRP_IPV6_NETCONF: u32 = 25; const RTNLGRP_MPLS_ROUTE: u32 = 27; const RTNLGRP_NSID: u32 = 28; const RTNLGRP_MPLS_NETCONF: u32 = 29; const fn nl_mgrp(group: u32) -> u32 { if group > 31 { panic!("use netlink_sys::Socket::add_membership() for this group"); } if group == 0 { 0 } else { 1 << (group - 1) } } #[tokio::main] async fn main() -> Result<(), String> { // conn - `Connection` that has a netlink socket which is a `Future` that // polls the socket and thus must have an event loop // // handle - `Handle` to the `Connection`. Used to send/recv netlink // messages. // // messages - A channel receiver. let (mut conn, mut _handle, mut messages) = new_connection().map_err(|e| format!("{e}"))?; // These flags specify what kinds of broadcast messages we want to listen // for. let groups = nl_mgrp(RTNLGRP_LINK) | nl_mgrp(RTNLGRP_IPV4_IFADDR) | nl_mgrp(RTNLGRP_IPV6_IFADDR) | nl_mgrp(RTNLGRP_IPV4_ROUTE) | nl_mgrp(RTNLGRP_IPV6_ROUTE) | nl_mgrp(RTNLGRP_MPLS_ROUTE) | nl_mgrp(RTNLGRP_IPV4_MROUTE) | nl_mgrp(RTNLGRP_IPV6_MROUTE) | nl_mgrp(RTNLGRP_NEIGH) | nl_mgrp(RTNLGRP_IPV4_NETCONF) | nl_mgrp(RTNLGRP_IPV6_NETCONF) | nl_mgrp(RTNLGRP_IPV4_RULE) | nl_mgrp(RTNLGRP_IPV6_RULE) | nl_mgrp(RTNLGRP_NSID) | nl_mgrp(RTNLGRP_MPLS_NETCONF); let addr = SocketAddr::new(0, groups); conn.socket_mut() .socket_mut() .bind(&addr) .expect("Failed to bind"); // Spawn `Connection` to start polling netlink socket. tokio::spawn(conn); // Use `Handle` to send request to kernel to start multicasting rtnetlink // events. tokio::spawn(async move { // Create message to enable }); // Start receiving events through `messages` channel. while let Some((message, _)) = messages.next().await { let payload = message.payload; println!("{payload:?}"); } Ok(()) } rtnetlink-0.14.1/examples/listen.rs000064400000000000000000000023121046102023000154010ustar 00000000000000// SPDX-License-Identifier: MIT //! This example opens a netlink socket, registers for IPv4 and IPv6 routing //! changes, listens for said changes and prints the received messages. use futures::stream::StreamExt; use netlink_sys::{AsyncSocket, SocketAddr}; use rtnetlink::{ constants::{RTMGRP_IPV4_ROUTE, RTMGRP_IPV6_ROUTE}, new_connection, }; #[tokio::main] async fn main() -> Result<(), String> { // Open the netlink socket let (mut connection, _, mut messages) = new_connection().map_err(|e| format!("{e}"))?; // These flags specify what kinds of broadcast messages we want to listen // for. let mgroup_flags = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE; // A netlink socket address is created with said flags. let addr = SocketAddr::new(0, mgroup_flags); // Said address is bound so new conenctions and thus new message broadcasts // can be received. connection .socket_mut() .socket_mut() .bind(&addr) .expect("failed to bind"); tokio::spawn(connection); while let Some((message, _)) = messages.next().await { let payload = message.payload; println!("Route change message - {payload:?}"); } Ok(()) } rtnetlink-0.14.1/examples/property_altname.rs000064400000000000000000000070421046102023000174750ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; use netlink_packet_route::link::{LinkAttribute, LinkMessage, Prop}; use rtnetlink::{new_connection, Error, Handle}; use std::env; #[tokio::main] async fn main() -> Result<(), ()> { let args: Vec = env::args().collect(); if args.len() < 3 { usage(); return Ok(()); } let link_name = &args[1]; let action = &args[2]; let alt_ifnames: Vec<&str> = args[3..].iter().map(String::as_str).collect(); let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); match action.as_str() { "add" => { if let Err(e) = add_property_alt_ifnames( link_name, alt_ifnames.clone(), handle.clone(), ) .await { eprintln!("{e}"); } } "del" => { if let Err(e) = del_property_alt_ifnames( link_name, alt_ifnames.clone(), handle.clone(), ) .await { eprintln!("{e}"); } } "show" => { if let Err(e) = show_property_alt_ifnames(link_name, handle.clone()).await { eprintln!("{e}"); } } _ => panic!("Unknown action {:?}", action), } Ok(()) } async fn show_property_alt_ifnames( link_name: &str, handle: Handle, ) -> Result<(), Error> { for nla in get_link(link_name, handle).await?.attributes.into_iter() { if let LinkAttribute::PropList(ref prop_list) = nla { for prop in prop_list { if let Prop::AltIfName(altname) = prop { println!("altname: {altname}"); } } } } Ok(()) } async fn add_property_alt_ifnames( link_name: &str, alt_ifnames: Vec<&str>, handle: Handle, ) -> Result<(), Error> { let link_index = get_link_index(link_name, handle.clone()).await?; handle .link() .property_add(link_index) .alt_ifname(&alt_ifnames) .execute() .await?; Ok(()) } async fn del_property_alt_ifnames( link_name: &str, alt_ifnames: Vec<&str>, handle: Handle, ) -> Result<(), Error> { let link_index = get_link_index(link_name, handle.clone()).await?; handle .link() .property_del(link_index) .alt_ifname(&alt_ifnames) .execute() .await?; Ok(()) } async fn get_link( link_name: &str, handle: Handle, ) -> Result { let mut links = handle .link() .get() .match_name(link_name.to_string()) .execute(); match links.try_next().await? { Some(msg) => Ok(msg), _ => { eprintln!("Interface {link_name} not found"); Err(Error::RequestFailed) } } } async fn get_link_index(link_name: &str, handle: Handle) -> Result { Ok(get_link(link_name, handle.clone()).await?.header.index) } fn usage() { eprintln!( "usage: cargo run --example property_altname -- [add | del | show] ALTNAME [ALTNAME ...] Note that you need to run this program as root for add and del. Instead of running cargo as root, build the example normally: cd rtnetlink ; cargo build --example property_altname Then find the binary in the target directory: cd ../target/debug/example ; sudo ./property_altname " ); } rtnetlink-0.14.1/examples/set_bond_port_settings.rs000064400000000000000000000033071046102023000206710ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; use rtnetlink::{new_connection, Error, Handle}; use std::env; #[tokio::main] async fn main() -> Result<(), String> { let args: Vec = env::args().collect(); if args.len() != 2 { usage(); return Ok(()); } let link_name = &args[1]; let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); set_bond_port_settings(handle, link_name.to_string()) .await .map_err(|e| format!("{e}")) } // The bond port priority is only supported to set when bonding mode is // active-backup(1) or balance-tlb (5) or balance-alb (6) async fn set_bond_port_settings( handle: Handle, name: String, ) -> Result<(), Error> { let mut links = handle.link().get().match_name(name.clone()).execute(); if let Some(link) = links.try_next().await? { // This is equivalent to `ip link set name NAME type bond_slave queue_id // 0 prio 1`. The port priority setting is supported in kernel // since v6.0 handle .link() .set_bond_port(link.header.index) .queue_id(0) .prio(1) .execute() .await? } else { println!("no link link {name} found"); } Ok(()) } fn usage() { eprintln!( "usage: cargo run --example set_bond_port_settings -- Note that you need to run this program as root. Instead of running cargo as root, build the example normally: cd netlink-ip ; cargo build --example set_bond_port_settings Then find the binary in the target directory: cd ../target/debug/example ; sudo ./set_bond_port_settings " ); } rtnetlink-0.14.1/examples/set_link_down.rs000064400000000000000000000024741046102023000167530ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; use rtnetlink::{new_connection, Error, Handle}; use std::env; #[tokio::main] async fn main() -> Result<(), String> { let args: Vec = env::args().collect(); if args.len() != 2 { usage(); return Ok(()); } let link_name = &args[1]; let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); set_link_down(handle, link_name.to_string()) .await .map_err(|e| format!("{e}")) } async fn set_link_down(handle: Handle, name: String) -> Result<(), Error> { let mut links = handle.link().get().match_name(name.clone()).execute(); if let Some(link) = links.try_next().await? { handle .link() .set(link.header.index) .down() .execute() .await? } else { println!("no link link {name} found"); } Ok(()) } fn usage() { eprintln!( "usage: cargo run --example set_link_down -- Note that you need to run this program as root. Instead of running cargo as root, build the example normally: cd netlink-ip ; cargo build --example set_link_down Then find the binary in the target directory: cd ../target/debug/example ; sudo ./set_link_down " ); } rtnetlink-0.14.1/src/addr/add.rs000064400000000000000000000060551046102023000145260ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::StreamExt; use std::net::{IpAddr, Ipv4Addr}; use netlink_packet_core::{ NetlinkMessage, NLM_F_ACK, NLM_F_CREATE, NLM_F_EXCL, NLM_F_REPLACE, NLM_F_REQUEST, }; use netlink_packet_route::{ address::{AddressAttribute, AddressMessage}, AddressFamily, RouteNetlinkMessage, }; use crate::{try_nl, Error, Handle}; /// A request to create a new address. This is equivalent to the `ip address /// add` commands. pub struct AddressAddRequest { handle: Handle, message: AddressMessage, replace: bool, } impl AddressAddRequest { pub(crate) fn new( handle: Handle, index: u32, address: IpAddr, prefix_len: u8, ) -> Self { let mut message = AddressMessage::default(); message.header.prefix_len = prefix_len; message.header.index = index; message.header.family = match address { IpAddr::V4(_) => AddressFamily::Inet, IpAddr::V6(_) => AddressFamily::Inet6, }; if address.is_multicast() { if let IpAddr::V6(a) = address { message.attributes.push(AddressAttribute::Multicast(a)); } } else { message.attributes.push(AddressAttribute::Address(address)); // for IPv4 the IFA_LOCAL address can be set to the same value as // IFA_ADDRESS message.attributes.push(AddressAttribute::Local(address)); // set the IFA_BROADCAST address as well (IPv6 does not support // broadcast) if let IpAddr::V4(a) = address { if prefix_len == 32 { message.attributes.push(AddressAttribute::Broadcast(a)); } else { let ip_addr = u32::from(a); let brd = Ipv4Addr::from( (0xffff_ffff_u32) >> u32::from(prefix_len) | ip_addr, ); message.attributes.push(AddressAttribute::Broadcast(brd)); }; } } AddressAddRequest { handle, message, replace: false, } } /// Replace existing matching address. pub fn replace(self) -> Self { Self { replace: true, ..self } } /// Execute the request. pub async fn execute(self) -> Result<(), Error> { let AddressAddRequest { mut handle, message, replace, } = self; let mut req = NetlinkMessage::from(RouteNetlinkMessage::NewAddress(message)); let replace = if replace { NLM_F_REPLACE } else { NLM_F_EXCL }; req.header.flags = NLM_F_REQUEST | NLM_F_ACK | replace | NLM_F_CREATE; let mut response = handle.request(req)?; while let Some(message) = response.next().await { try_nl!(message); } Ok(()) } /// Return a mutable reference to the request message. pub fn message_mut(&mut self) -> &mut AddressMessage { &mut self.message } } rtnetlink-0.14.1/src/addr/del.rs000064400000000000000000000020501046102023000145310ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::StreamExt; use netlink_packet_core::{NetlinkMessage, NLM_F_ACK, NLM_F_REQUEST}; use netlink_packet_route::{address::AddressMessage, RouteNetlinkMessage}; use crate::{try_nl, Error, Handle}; pub struct AddressDelRequest { handle: Handle, message: AddressMessage, } impl AddressDelRequest { pub(crate) fn new(handle: Handle, message: AddressMessage) -> Self { AddressDelRequest { handle, message } } /// Execute the request pub async fn execute(self) -> Result<(), Error> { let AddressDelRequest { mut handle, message, } = self; let mut req = NetlinkMessage::from(RouteNetlinkMessage::DelAddress(message)); req.header.flags = NLM_F_REQUEST | NLM_F_ACK; let mut response = handle.request(req)?; while let Some(msg) = response.next().await { try_nl!(msg); } Ok(()) } pub fn message_mut(&mut self) -> &mut AddressMessage { &mut self.message } } rtnetlink-0.14.1/src/addr/get.rs000064400000000000000000000073731046102023000145610ustar 00000000000000// SPDX-License-Identifier: MIT use futures::{ future::{self, Either}, stream::{StreamExt, TryStream, TryStreamExt}, FutureExt, }; use std::net::IpAddr; use netlink_packet_core::{NetlinkMessage, NLM_F_DUMP, NLM_F_REQUEST}; use netlink_packet_route::{ address::{AddressAttribute, AddressMessage}, RouteNetlinkMessage, }; use crate::{try_rtnl, Error, Handle}; pub struct AddressGetRequest { handle: Handle, message: AddressMessage, filter_builder: AddressFilterBuilder, } impl AddressGetRequest { pub(crate) fn new(handle: Handle) -> Self { AddressGetRequest { handle, message: AddressMessage::default(), filter_builder: AddressFilterBuilder::new(), } } pub fn message_mut(&mut self) -> &mut AddressMessage { &mut self.message } pub fn execute(self) -> impl TryStream { let AddressGetRequest { mut handle, message, filter_builder, } = self; let mut req = NetlinkMessage::from(RouteNetlinkMessage::GetAddress(message)); req.header.flags = NLM_F_REQUEST | NLM_F_DUMP; let filter = filter_builder.build(); match handle.request(req) { Ok(response) => Either::Left( response .map(move |msg| { Ok(try_rtnl!(msg, RouteNetlinkMessage::NewAddress)) }) .try_filter(move |msg| future::ready(filter(msg))), ), Err(e) => Either::Right( future::err::(e).into_stream(), ), } } /// Return only the addresses of the given interface. pub fn set_link_index_filter(mut self, index: u32) -> Self { self.filter_builder.index = Some(index); self } /// Return only the addresses of the given prefix length. pub fn set_prefix_length_filter(mut self, prefix: u8) -> Self { self.filter_builder.prefix_len = Some(prefix); self } /// Return only AddressMessages filtered by the given address. pub fn set_address_filter(mut self, address: IpAddr) -> Self { self.filter_builder.address = Some(address); self } } // The reason for having filters, is that we cannot retrieve addresses // that match the given message, like we do for links. // // See: // https://lists.infradead.org/pipermail/libnl/2013-June/001014.html // https://patchwork.ozlabs.org/patch/133440/ #[derive(Default)] struct AddressFilterBuilder { index: Option, address: Option, prefix_len: Option, } impl AddressFilterBuilder { fn new() -> Self { Default::default() } fn build(self) -> impl Fn(&AddressMessage) -> bool { use AddressAttribute::*; move |msg: &AddressMessage| { if let Some(index) = self.index { if msg.header.index != index { return false; } } if let Some(prefix_len) = self.prefix_len { if msg.header.prefix_len != prefix_len { return false; } } if let Some(address) = self.address { for nla in msg.attributes.iter() { if let Address(x) | Local(x) = nla { if x == &address { return true; } } else if let Multicast(x) | Anycast(x) = nla { if IpAddr::V6(*x) == address { return true; } } } return false; } true } } } rtnetlink-0.14.1/src/addr/handle.rs000064400000000000000000000016721046102023000152310ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::IpAddr; use super::{AddressAddRequest, AddressDelRequest, AddressGetRequest}; use crate::Handle; use netlink_packet_route::address::AddressMessage; pub struct AddressHandle(Handle); impl AddressHandle { pub fn new(handle: Handle) -> Self { AddressHandle(handle) } /// Retrieve the list of ip addresses (equivalent to `ip addr show`) pub fn get(&self) -> AddressGetRequest { AddressGetRequest::new(self.0.clone()) } /// Add an ip address on an interface (equivalent to `ip addr add`) pub fn add( &self, index: u32, address: IpAddr, prefix_len: u8, ) -> AddressAddRequest { AddressAddRequest::new(self.0.clone(), index, address, prefix_len) } /// Delete the given address pub fn del(&self, address: AddressMessage) -> AddressDelRequest { AddressDelRequest::new(self.0.clone(), address) } } rtnetlink-0.14.1/src/addr/mod.rs000064400000000000000000000002461046102023000145510ustar 00000000000000// SPDX-License-Identifier: MIT mod handle; pub use self::handle::*; mod add; pub use self::add::*; mod del; pub use self::del::*; mod get; pub use self::get::*; rtnetlink-0.14.1/src/connection.rs000064400000000000000000000017131046102023000152170ustar 00000000000000// SPDX-License-Identifier: MIT use std::io; use futures::channel::mpsc::UnboundedReceiver; use netlink_packet_core::NetlinkMessage; use netlink_packet_route::RouteNetlinkMessage; use netlink_proto::Connection; use netlink_sys::{protocols::NETLINK_ROUTE, AsyncSocket, SocketAddr}; use crate::Handle; #[cfg(feature = "tokio_socket")] #[allow(clippy::type_complexity)] pub fn new_connection() -> io::Result<( Connection, Handle, UnboundedReceiver<(NetlinkMessage, SocketAddr)>, )> { new_connection_with_socket() } #[allow(clippy::type_complexity)] pub fn new_connection_with_socket() -> io::Result<( Connection, Handle, UnboundedReceiver<(NetlinkMessage, SocketAddr)>, )> where S: AsyncSocket, { let (conn, handle, messages) = netlink_proto::new_connection_with_socket(NETLINK_ROUTE)?; Ok((conn, Handle::new(handle), messages)) } rtnetlink-0.14.1/src/constants.rs000064400000000000000000000011531046102023000150720ustar 00000000000000// SPDX-License-Identifier: MIT pub const RTMGRP_LINK: u32 = 1; pub const RTMGRP_NOTIFY: u32 = 2; pub const RTMGRP_NEIGH: u32 = 4; pub const RTMGRP_TC: u32 = 8; pub const RTMGRP_IPV4_IFADDR: u32 = 16; pub const RTMGRP_IPV4_MROUTE: u32 = 32; pub const RTMGRP_IPV4_ROUTE: u32 = 64; pub const RTMGRP_IPV4_RULE: u32 = 128; pub const RTMGRP_IPV6_IFADDR: u32 = 256; pub const RTMGRP_IPV6_MROUTE: u32 = 512; pub const RTMGRP_IPV6_ROUTE: u32 = 1024; pub const RTMGRP_IPV6_IFINFO: u32 = 2048; pub const RTMGRP_DECNET_IFADDR: u32 = 4096; pub const RTMGRP_DECNET_ROUTE: u32 = 16_384; pub const RTMGRP_IPV6_PREFIX: u32 = 131_072; rtnetlink-0.14.1/src/errors.rs000064400000000000000000000017751046102023000144040ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_core::{ErrorMessage, NetlinkMessage}; use netlink_packet_route::RouteNetlinkMessage; use thiserror::Error; #[derive(Clone, Eq, PartialEq, Debug, Error)] pub enum Error { #[error("Received an unexpected message {0:?}")] UnexpectedMessage(NetlinkMessage), #[error("Received a netlink error message {0}")] NetlinkError(ErrorMessage), #[error("A netlink request failed")] RequestFailed, #[error("Namespace error {0}")] NamespaceError(String), #[error( "Received a link message (RTM_GETLINK, RTM_NEWLINK, RTM_SETLINK or RTMGETLINK) with an invalid hardware address attribute: {0:?}." )] InvalidHardwareAddress(Vec), #[error("Failed to parse an IP address: {0:?}")] InvalidIp(Vec), #[error("Failed to parse a network address (IP and mask): {0:?}/{1:?}")] InvalidAddress(Vec, Vec), #[error("Attempting to set and Invalid NLA: {0}")] InvalidNla(String), } rtnetlink-0.14.1/src/handle.rs000064400000000000000000000065021046102023000143140ustar 00000000000000// SPDX-License-Identifier: MIT use futures::Stream; use netlink_packet_core::NetlinkMessage; use netlink_packet_route::RouteNetlinkMessage; use netlink_proto::{sys::SocketAddr, ConnectionHandle}; use crate::{ AddressHandle, Error, LinkHandle, NeighbourHandle, RouteHandle, RuleHandle, }; #[cfg(not(target_os = "freebsd"))] use crate::{ QDiscHandle, TrafficChainHandle, TrafficClassHandle, TrafficFilterHandle, }; #[derive(Clone, Debug)] pub struct Handle(ConnectionHandle); impl Handle { pub(crate) fn new(conn: ConnectionHandle) -> Self { Handle(conn) } pub fn request( &mut self, message: NetlinkMessage, ) -> Result>, Error> { self.0 .request(message, SocketAddr::new(0, 0)) .map_err(|_| Error::RequestFailed) } pub fn notify( &mut self, msg: NetlinkMessage, ) -> Result<(), Error> { self.0 .notify(msg, SocketAddr::new(0, 0)) .map_err(|_| Error::RequestFailed)?; Ok(()) } /// Create a new handle, specifically for link requests (equivalent to `ip /// link` commands) pub fn link(&self) -> LinkHandle { LinkHandle::new(self.clone()) } /// Create a new handle, specifically for address requests (equivalent to /// `ip addr` commands) pub fn address(&self) -> AddressHandle { AddressHandle::new(self.clone()) } /// Create a new handle, specifically for routing table requests (equivalent /// to `ip route` commands) pub fn route(&self) -> RouteHandle { RouteHandle::new(self.clone()) } /// Create a new handle, specifically for routing rule requests (equivalent /// to `ip rule` commands) pub fn rule(&self) -> RuleHandle { RuleHandle::new(self.clone()) } /// Create a new handle, specifically for routing neighbours requests /// (equivalent to `ip neighbour` commands) pub fn neighbours(&self) -> NeighbourHandle { NeighbourHandle::new(self.clone()) } /// Create a new handle, specifically for traffic control qdisc requests /// (equivalent to `tc qdisc show` commands) #[cfg(not(target_os = "freebsd"))] pub fn qdisc(&self) -> QDiscHandle { QDiscHandle::new(self.clone()) } /// Create a new handle, specifically for traffic control class requests /// (equivalent to `tc class show dev ` commands) #[cfg(not(target_os = "freebsd"))] pub fn traffic_class(&self, ifindex: i32) -> TrafficClassHandle { TrafficClassHandle::new(self.clone(), ifindex) } /// Create a new handle, specifically for traffic control filter requests /// (equivalent to `tc filter show dev ` commands) #[cfg(not(target_os = "freebsd"))] pub fn traffic_filter(&self, ifindex: i32) -> TrafficFilterHandle { TrafficFilterHandle::new(self.clone(), ifindex) } /// Create a new handle, specifically for traffic control chain requests /// (equivalent to `tc chain show dev ` commands) #[cfg(not(target_os = "freebsd"))] pub fn traffic_chain(&self, ifindex: i32) -> TrafficChainHandle { TrafficChainHandle::new(self.clone(), ifindex) } } rtnetlink-0.14.1/src/lib.rs000064400000000000000000000014211046102023000136220ustar 00000000000000// SPDX-License-Identifier: MIT //! This crate provides methods to manipulate networking resources (links, //! addresses, arp tables, route tables) via the netlink protocol. #![allow(clippy::module_inception)] mod handle; pub use crate::handle::*; #[cfg(not(target_os = "freebsd"))] mod ns; #[cfg(not(target_os = "freebsd"))] pub use crate::ns::*; mod errors; pub use crate::errors::*; mod link; pub use crate::link::*; mod addr; pub use crate::addr::*; mod route; pub use crate::route::*; mod rule; pub use crate::rule::*; mod connection; pub use crate::connection::*; #[cfg(not(target_os = "freebsd"))] mod traffic_control; #[cfg(not(target_os = "freebsd"))] pub use crate::traffic_control::*; mod neighbour; pub use crate::neighbour::*; pub mod constants; mod macros; rtnetlink-0.14.1/src/link/add.rs000064400000000000000000000717431046102023000145570ustar 00000000000000// SPDX-License-Identifier: MIT use std::{ iter::empty, net::{Ipv4Addr, Ipv6Addr}, }; use futures::stream::StreamExt; use netlink_packet_core::{ NetlinkMessage, NLM_F_ACK, NLM_F_CREATE, NLM_F_EXCL, NLM_F_REPLACE, NLM_F_REQUEST, }; use netlink_packet_route::{ link::{ InfoBond, InfoData, InfoKind, InfoMacVlan, InfoMacVtap, InfoVeth, InfoVlan, InfoVxlan, InfoXfrm, LinkAttribute, LinkFlag, LinkInfo, LinkMessage, VlanQosMapping, }, RouteNetlinkMessage, }; use crate::{try_nl, Error, Handle}; pub struct BondAddRequest { request: LinkAddRequest, info_data: Vec, } impl BondAddRequest { /// Execute the request. pub async fn execute(self) -> Result<(), Error> { let s = self .request .link_info(InfoKind::Bond, Some(InfoData::Bond(self.info_data))); s.execute().await } /// Sets the interface up /// This is equivalent to `ip link set up dev NAME`. pub fn up(mut self) -> Self { self.request = self.request.up(); self } /// Adds the `mode` attribute to the bond /// This is equivalent to `ip link add name NAME type bond mode MODE`. pub fn mode(mut self, mode: u8) -> Self { self.info_data.push(InfoBond::Mode(mode)); self } #[deprecated(note = "Please use `active_port` instead")] pub fn active_slave(mut self, active_port: u32) -> Self { self.info_data.push(InfoBond::ActivePort(active_port)); self } /// Adds the `active_port` attribute to the bond, where `active_port` /// is the ifindex of an interface attached to the bond. /// This is equivalent to `ip link add name NAME type bond active_slave /// ACTIVE_PORT_NAME`. pub fn active_port(mut self, active_port: u32) -> Self { self.info_data.push(InfoBond::ActivePort(active_port)); self } /// Adds the `miimon` attribute to the bond /// This is equivalent to `ip link add name NAME type bond miimon MIIMON`. pub fn miimon(mut self, miimon: u32) -> Self { self.info_data.push(InfoBond::MiiMon(miimon)); self } /// Adds the `updelay` attribute to the bond /// This is equivalent to `ip link add name NAME type bond updelay UPDELAY`. pub fn updelay(mut self, updelay: u32) -> Self { self.info_data.push(InfoBond::UpDelay(updelay)); self } /// Adds the `downdelay` attribute to the bond /// This is equivalent to `ip link add name NAME type bond downdelay /// DOWNDELAY`. pub fn downdelay(mut self, downdelay: u32) -> Self { self.info_data.push(InfoBond::DownDelay(downdelay)); self } /// Adds the `use_carrier` attribute to the bond /// This is equivalent to `ip link add name NAME type bond use_carrier /// USE_CARRIER`. pub fn use_carrier(mut self, use_carrier: u8) -> Self { self.info_data.push(InfoBond::UseCarrier(use_carrier)); self } /// Adds the `arp_interval` attribute to the bond /// This is equivalent to `ip link add name NAME type bond arp_interval /// ARP_INTERVAL`. pub fn arp_interval(mut self, arp_interval: u32) -> Self { self.info_data.push(InfoBond::ArpInterval(arp_interval)); self } /// Adds the `arp_validate` attribute to the bond /// This is equivalent to `ip link add name NAME type bond arp_validate /// ARP_VALIDATE`. pub fn arp_validate(mut self, arp_validate: u32) -> Self { self.info_data.push(InfoBond::ArpValidate(arp_validate)); self } /// Adds the `arp_all_targets` attribute to the bond /// This is equivalent to `ip link add name NAME type bond arp_all_targets /// ARP_ALL_TARGETS` pub fn arp_all_targets(mut self, arp_all_targets: u32) -> Self { self.info_data .push(InfoBond::ArpAllTargets(arp_all_targets)); self } /// Adds the `primary` attribute to the bond, where `primary` is the ifindex /// of an interface. /// This is equivalent to `ip link add name NAME type bond primary /// PRIMARY_NAME` pub fn primary(mut self, primary: u32) -> Self { self.info_data.push(InfoBond::Primary(primary)); self } /// Adds the `primary_reselect` attribute to the bond /// This is equivalent to `ip link add name NAME type bond primary_reselect /// PRIMARY_RESELECT`. pub fn primary_reselect(mut self, primary_reselect: u8) -> Self { self.info_data .push(InfoBond::PrimaryReselect(primary_reselect)); self } /// Adds the `fail_over_mac` attribute to the bond /// This is equivalent to `ip link add name NAME type bond fail_over_mac /// FAIL_OVER_MAC`. pub fn fail_over_mac(mut self, fail_over_mac: u8) -> Self { self.info_data.push(InfoBond::FailOverMac(fail_over_mac)); self } /// Adds the `xmit_hash_policy` attribute to the bond /// This is equivalent to `ip link add name NAME type bond xmit_hash_policy /// XMIT_HASH_POLICY`. pub fn xmit_hash_policy(mut self, xmit_hash_policy: u8) -> Self { self.info_data .push(InfoBond::XmitHashPolicy(xmit_hash_policy)); self } /// Adds the `resend_igmp` attribute to the bond /// This is equivalent to `ip link add name NAME type bond resend_igmp /// RESEND_IGMP`. pub fn resend_igmp(mut self, resend_igmp: u32) -> Self { self.info_data.push(InfoBond::ResendIgmp(resend_igmp)); self } /// Adds the `num_peer_notif` attribute to the bond /// This is equivalent to `ip link add name NAME type bond num_peer_notif /// NUM_PEER_NOTIF`. pub fn num_peer_notif(mut self, num_peer_notif: u8) -> Self { self.info_data.push(InfoBond::NumPeerNotif(num_peer_notif)); self } #[deprecated(note = "Please use `all_ports_active` instead")] pub fn all_slaves_active(mut self, all_ports_active: u8) -> Self { self.info_data .push(InfoBond::AllPortsActive(all_ports_active)); self } /// Adds the `all_ports_active` attribute to the bond /// This is equivalent to `ip link add name NAME type bond all_slaves_active /// ALL_PORTS_ACTIVE`. pub fn all_ports_active(mut self, all_ports_active: u8) -> Self { self.info_data .push(InfoBond::AllPortsActive(all_ports_active)); self } /// Adds the `min_links` attribute to the bond /// This is equivalent to `ip link add name NAME type bond min_links /// MIN_LINKS`. pub fn min_links(mut self, min_links: u32) -> Self { self.info_data.push(InfoBond::MinLinks(min_links)); self } /// Adds the `lp_interval` attribute to the bond /// This is equivalent to `ip link add name NAME type bond lp_interval /// LP_INTERVAL`. pub fn lp_interval(mut self, lp_interval: u32) -> Self { self.info_data.push(InfoBond::LpInterval(lp_interval)); self } /// Adds the `packets_per_port` attribute to the bond /// This is equivalent to `ip link add name NAME type bond packets_per_slave /// PACKETS_PER_PORT`. pub fn packets_per_port(mut self, packets_per_port: u32) -> Self { self.info_data .push(InfoBond::PacketsPerPort(packets_per_port)); self } /// Adds the `ad_lacp_rate` attribute to the bond /// This is equivalent to `ip link add name NAME type bond ad_lacp_rate /// AD_LACP_RATE`. pub fn ad_lacp_rate(mut self, ad_lacp_rate: u8) -> Self { self.info_data.push(InfoBond::AdLacpRate(ad_lacp_rate)); self } /// Adds the `ad_select` attribute to the bond /// This is equivalent to `ip link add name NAME type bond ad_select /// AD_SELECT`. pub fn ad_select(mut self, ad_select: u8) -> Self { self.info_data.push(InfoBond::AdSelect(ad_select)); self } /// Adds the `ad_actor_sys_prio` attribute to the bond /// This is equivalent to `ip link add name NAME type bond ad_actor_sys_prio /// AD_ACTOR_SYS_PRIO`. pub fn ad_actor_sys_prio(mut self, ad_actor_sys_prio: u16) -> Self { self.info_data .push(InfoBond::AdActorSysPrio(ad_actor_sys_prio)); self } /// Adds the `ad_user_port_key` attribute to the bond /// This is equivalent to `ip link add name NAME type bond ad_user_port_key /// AD_USER_PORT_KEY`. pub fn ad_user_port_key(mut self, ad_user_port_key: u16) -> Self { self.info_data .push(InfoBond::AdUserPortKey(ad_user_port_key)); self } /// Adds the `ad_actor_system` attribute to the bond /// This is equivalent to `ip link add name NAME type bond ad_actor_system /// AD_ACTOR_SYSTEM`. pub fn ad_actor_system(mut self, ad_actor_system: [u8; 6]) -> Self { self.info_data .push(InfoBond::AdActorSystem(ad_actor_system)); self } /// Adds the `tlb_dynamic_lb` attribute to the bond /// This is equivalent to `ip link add name NAME type bond tlb_dynamic_lb /// TLB_DYNAMIC_LB`. pub fn tlb_dynamic_lb(mut self, tlb_dynamic_lb: u8) -> Self { self.info_data.push(InfoBond::TlbDynamicLb(tlb_dynamic_lb)); self } /// Adds the `peer_notif_delay` attribute to the bond /// This is equivalent to `ip link add name NAME type bond peer_notif_delay /// PEER_NOTIF_DELAY`. pub fn peer_notif_delay(mut self, peer_notif_delay: u32) -> Self { self.info_data .push(InfoBond::PeerNotifDelay(peer_notif_delay)); self } /// Adds the `ad_lacp_active` attribute to the bond /// This is equivalent to `ip link add name NAME type bond ad_lacp_active /// AD_LACP_ACTIVE`. pub fn ad_lacp_active(mut self, ad_lacp_active: u8) -> Self { self.info_data.push(InfoBond::AdLacpActive(ad_lacp_active)); self } /// Adds the `missed_max` attribute to the bond /// This is equivalent to `ip link add name NAME type bond missed_max /// MISSED_MAX`. pub fn missed_max(mut self, missed_max: u8) -> Self { self.info_data.push(InfoBond::MissedMax(missed_max)); self } /// Adds the `arp_ip_target` attribute to the bond /// This is equivalent to `ip link add name NAME type bond arp_ip_target /// LIST`. pub fn arp_ip_target(mut self, arp_ip_target: Vec) -> Self { self.info_data.push(InfoBond::ArpIpTarget(arp_ip_target)); self } /// Adds the `ns_ip6_target` attribute to the bond /// This is equivalent to `ip link add name NAME type bond ns_ip6_target /// LIST`. pub fn ns_ip6_target(mut self, ns_ip6_target: Vec) -> Self { self.info_data.push(InfoBond::NsIp6Target(ns_ip6_target)); self } } /// A request to create a new vxlan link. /// This is equivalent to `ip link add NAME vxlan id ID ...` commands. /// It provides methods to customize the creation of the vxlan interface /// It provides almost all parameters that are listed by `man ip link`. pub struct VxlanAddRequest { request: LinkAddRequest, info_data: Vec, } impl VxlanAddRequest { /// Execute the request. pub async fn execute(self) -> Result<(), Error> { let s = self .request .link_info(InfoKind::Vxlan, Some(InfoData::Vxlan(self.info_data))); s.execute().await } /// Sets the interface up /// This is equivalent to `ip link set up dev NAME`. pub fn up(mut self) -> Self { self.request = self.request.up(); self } /// Adds the `dev` attribute to the VXLAN /// This is equivalent to `ip link add name NAME type vxlan id VNI dev /// LINK`, dev LINK - specifies the physical device to use /// for tunnel endpoint communication. /// But instead of specifing a link name (`LINK`), we specify a link index. pub fn link(mut self, index: u32) -> Self { self.info_data.push(InfoVxlan::Link(index)); self } /// Adds the `dstport` attribute to the VXLAN /// This is equivalent to `ip link add name NAME type vxlan id VNI dstport /// PORT`. dstport PORT - specifies the UDP destination port to /// communicate to the remote VXLAN tunnel endpoint. pub fn port(mut self, port: u16) -> Self { self.info_data.push(InfoVxlan::Port(port)); self } /// Adds the `group` attribute to the VXLAN /// This is equivalent to `ip link add name NAME type vxlan id VNI group /// IPADDR`, group IPADDR - specifies the multicast IP address to join. /// This function takes an IPv4 address /// WARNING: only one between `remote` and `group` can be present. pub fn group(mut self, addr: std::net::Ipv4Addr) -> Self { self.info_data .push(InfoVxlan::Group(addr.octets().to_vec())); self } /// Adds the `group` attribute to the VXLAN /// This is equivalent to `ip link add name NAME type vxlan id VNI group /// IPADDR`, group IPADDR - specifies the multicast IP address to join. /// This function takes an IPv6 address /// WARNING: only one between `remote` and `group` can be present. pub fn group6(mut self, addr: std::net::Ipv6Addr) -> Self { self.info_data .push(InfoVxlan::Group6(addr.octets().to_vec())); self } /// Adds the `remote` attribute to the VXLAN /// This is equivalent to `ip link add name NAME type vxlan id VNI remote /// IPADDR`, remote IPADDR - specifies the unicast destination IP /// address to use in outgoing packets when the /// destination link layer address is not known in the /// VXLAN device forwarding database. /// This function takes an IPv4 address. /// WARNING: only one between `remote` and `group` can be present. pub fn remote(self, addr: std::net::Ipv4Addr) -> Self { self.group(addr) } /// Adds the `remote` attribute to the VXLAN /// This is equivalent to `ip link add name NAME type vxlan id VNI remote /// IPADDR`, remote IPADDR - specifies the unicast destination IP /// address to use in outgoing packets when the /// destination link layer address is not known in the /// VXLAN device forwarding database. /// This function takes an IPv6 address. /// WARNING: only one between `remote` and `group` can be present. pub fn remote6(self, addr: std::net::Ipv6Addr) -> Self { self.group6(addr) } /// Adds the `local` attribute to the VXLAN /// This is equivalent to `ip link add name NAME type vxlan id VNI local /// IPADDR`, local IPADDR - specifies the source IP address to use in /// outgoing packets. This function takes an IPv4 address. pub fn local(mut self, addr: std::net::Ipv4Addr) -> Self { self.info_data .push(InfoVxlan::Local(addr.octets().to_vec())); self } /// Adds the `local` attribute to the VXLAN /// This is equivalent to `ip link add name NAME type vxlan id VNI local /// IPADDR`, local IPADDR - specifies the source IP address to use in /// outgoing packets. This function takes an IPv6 address. pub fn local6(mut self, addr: std::net::Ipv6Addr) -> Self { self.info_data .push(InfoVxlan::Local6(addr.octets().to_vec())); self } /// Adds the `tos` attribute to the VXLAN /// This is equivalent to `ip link add name NAME type vxlan id VNI tos TOS`. /// tos TOS - specifies the TOS value to use in outgoing packets. pub fn tos(mut self, tos: u8) -> Self { self.info_data.push(InfoVxlan::Tos(tos)); self } /// Adds the `ttl` attribute to the VXLAN /// This is equivalent to `ip link add name NAME type vxlan id VNI ttl TTL`. /// ttl TTL - specifies the TTL value to use in outgoing packets. pub fn ttl(mut self, ttl: u8) -> Self { self.info_data.push(InfoVxlan::Ttl(ttl)); self } /// Adds the `flowlabel` attribute to the VXLAN /// This is equivalent to `ip link add name NAME type vxlan id VNI flowlabel /// LABEL`. flowlabel LABEL - specifies the flow label to use in /// outgoing packets. pub fn label(mut self, label: u32) -> Self { self.info_data.push(InfoVxlan::Label(label)); self } /// Adds the `learning` attribute to the VXLAN /// This is equivalent to `ip link add name NAME type vxlan id VNI /// [no]learning`. [no]learning - specifies if unknown source link layer /// addresses and IP addresses are entered into the VXLAN /// device forwarding database. pub fn learning(mut self, learning: bool) -> Self { self.info_data.push(InfoVxlan::Learning(learning)); self } /// Adds the `ageing` attribute to the VXLAN /// This is equivalent to `ip link add name NAME type vxlan id VNI ageing /// SECONDS`. ageing SECONDS - specifies the lifetime in seconds of /// FDB entries learnt by the kernel. pub fn ageing(mut self, seconds: u32) -> Self { self.info_data.push(InfoVxlan::Ageing(seconds)); self } /// Adds the `maxaddress` attribute to the VXLAN /// This is equivalent to `ip link add name NAME type vxlan id VNI /// maxaddress LIMIT`. maxaddress LIMIT - specifies the maximum number /// of FDB entries. pub fn limit(mut self, limit: u32) -> Self { self.info_data.push(InfoVxlan::Limit(limit)); self } /// Adds the `srcport` attribute to the VXLAN /// This is equivalent to `ip link add name NAME type vxlan id VNI srcport /// MIN MAX`. srcport MIN MAX - specifies the range of port numbers /// to use as UDP source ports to communicate to the /// remote VXLAN tunnel endpoint. pub fn port_range(mut self, min: u16, max: u16) -> Self { self.info_data.push(InfoVxlan::PortRange((min, max))); self } /// Adds the `proxy` attribute to the VXLAN /// This is equivalent to `ip link add name NAME type vxlan id VNI /// [no]proxy`. [no]proxy - specifies ARP proxy is turned on. pub fn proxy(mut self, proxy: bool) -> Self { self.info_data.push(InfoVxlan::Proxy(proxy)); self } /// Adds the `rsc` attribute to the VXLAN /// This is equivalent to `ip link add name NAME type vxlan id VNI [no]rsc`. /// [no]rsc - specifies if route short circuit is turned on. pub fn rsc(mut self, rsc: bool) -> Self { self.info_data.push(InfoVxlan::Rsc(rsc)); self } // Adds the `l2miss` attribute to the VXLAN /// This is equivalent to `ip link add name NAME type vxlan id VNI /// [no]l2miss`. [no]l2miss - specifies if netlink LLADDR miss /// notifications are generated. pub fn l2miss(mut self, l2miss: bool) -> Self { self.info_data.push(InfoVxlan::L2Miss(l2miss)); self } // Adds the `l3miss` attribute to the VXLAN /// This is equivalent to `ip link add name NAME type vxlan id VNI /// [no]l3miss`. [no]l3miss - specifies if netlink IP ADDR miss /// notifications are generated. pub fn l3miss(mut self, l3miss: bool) -> Self { self.info_data.push(InfoVxlan::L3Miss(l3miss)); self } pub fn collect_metadata(mut self, collect_metadata: bool) -> Self { self.info_data .push(InfoVxlan::CollectMetadata(collect_metadata)); self } // Adds the `udp_csum` attribute to the VXLAN /// This is equivalent to `ip link add name NAME type vxlan id VNI /// [no]udp_csum`. [no]udpcsum - specifies if UDP checksum is calculated /// for transmitted packets over IPv4. pub fn udp_csum(mut self, udp_csum: bool) -> Self { self.info_data.push(InfoVxlan::UDPCsum(udp_csum)); self } } /// A request to create a new link. This is equivalent to the `ip link add` /// commands. /// /// A few methods for common actions (creating a veth pair, creating a vlan /// interface, etc.) are provided, but custom requests can be made using the /// [`message_mut()`](#method.message_mut) accessor. pub struct LinkAddRequest { handle: Handle, message: LinkMessage, replace: bool, } /// A quality-of-service mapping between the internal priority `from` to the /// external vlan priority `to`. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct QosMapping { pub from: u32, pub to: u32, } impl From for VlanQosMapping { fn from(QosMapping { from, to }: QosMapping) -> Self { Self::Mapping(from, to) } } impl LinkAddRequest { pub(crate) fn new(handle: Handle) -> Self { LinkAddRequest { handle, message: LinkMessage::default(), replace: false, } } /// Execute the request. pub async fn execute(self) -> Result<(), Error> { let LinkAddRequest { mut handle, message, replace, } = self; let mut req = NetlinkMessage::from(RouteNetlinkMessage::NewLink(message)); let replace = if replace { NLM_F_REPLACE } else { NLM_F_EXCL }; req.header.flags = NLM_F_REQUEST | NLM_F_ACK | replace | NLM_F_CREATE; let mut response = handle.request(req)?; while let Some(message) = response.next().await { try_nl!(message); } Ok(()) } /// Return a mutable reference to the request message. /// /// # Example /// /// Let's say we want to create a vlan interface on a link with id 6. By /// default, the [`vlan()`](#method.vlan) method would create a request /// with the `LinkFlag::Up` link set, so that the interface is up after /// creation. If we want to create a interface that is down by default we /// could do: /// /// ```rust,no_run /// use futures::Future; /// use netlink_packet_route::link::LinkFlag; /// use rtnetlink::{Handle, new_connection}; /// /// async fn run(handle: Handle) -> Result<(), String> { /// let vlan_id = 100; /// let link_id = 6; /// let mut request = handle.link().add().vlan("my-vlan-itf".into(), /// link_id, vlan_id); /// request.message_mut().header.flags.push(LinkFlag::Up); /// request.message_mut().header.change_mask.retain( /// |f| *f != LinkFlag::Up); /// // send the request /// request.execute().await.map_err(|e| format!("{}", e)) /// } pub fn message_mut(&mut self) -> &mut LinkMessage { &mut self.message } /// Create a dummy link. /// This is equivalent to `ip link add NAME type dummy`. pub fn dummy(self, name: String) -> Self { self.name(name).link_info(InfoKind::Dummy, None).up() } /// Create a veth pair. /// This is equivalent to `ip link add NAME1 type veth peer name NAME2`. pub fn veth(self, name: String, peer_name: String) -> Self { // NOTE: `name` is the name of the peer in the netlink message (ie the // link created via the InfoVeth::Peer attribute, and // `peer_name` is the name in the main netlink message. // This is a bit weird, but it's all hidden from the user. let mut peer = LinkMessage::default(); // FIXME: we get a -107 (ENOTCONN) (???) when trying to set `name` up. // peer.header.flags.push(LinkFlag::Up); // peer.header.change_mask.push(LinkFlag::Up); peer.attributes.push(LinkAttribute::IfName(name)); let link_info_data = InfoData::Veth(InfoVeth::Peer(peer)); self.name(peer_name) .up() // iproute2 does not set this one up .link_info(InfoKind::Veth, Some(link_info_data)) } /// Create VLAN on a link. /// This is equivalent to `ip link add link LINK name NAME type vlan id /// VLAN_ID`, but instead of specifying a link name (`LINK`), we specify /// a link index. pub fn vlan(self, name: String, index: u32, vlan_id: u16) -> Self { self.vlan_with_qos(name, index, vlan_id, empty(), empty()) } /// Create VLAN on a link with ingress and egress qos mappings. /// This is equivalent to `ip link add link LINK name NAME type vlan id /// VLAN_ID ingress-qos-mapping INGRESS_QOS egress-qos-mapping EGRESS_QOS`, /// but instead of specifying a link name (`LINK`), we specify a link index. pub fn vlan_with_qos< I: IntoIterator, E: IntoIterator, >( self, name: String, index: u32, vlan_id: u16, ingress_qos: I, egress_qos: E, ) -> Self { let mut info = vec![InfoVlan::Id(vlan_id)]; let ingress: Vec<_> = ingress_qos.into_iter().map(VlanQosMapping::from).collect(); if !ingress.is_empty() { info.push(InfoVlan::IngressQos(ingress)); } let egress: Vec<_> = egress_qos.into_iter().map(VlanQosMapping::from).collect(); if !egress.is_empty() { info.push(InfoVlan::EgressQos(egress)); } self.name(name) .link_info(InfoKind::Vlan, Some(InfoData::Vlan(info))) .append_nla(LinkAttribute::Link(index)) .up() } /// Create macvlan on a link. /// This is equivalent to `ip link add name NAME link LINK type macvlan mode /// MACVLAN_MODE`, but instead of specifying a link name (`LINK`), we /// specify a link index. The MACVLAN_MODE is an integer consisting of /// flags from MACVLAN_MODE (netlink-packet-route/src/rtnl/constants.rs) /// being: _PRIVATE, _VEPA, _BRIDGE, _PASSTHRU, _SOURCE, which can be /// *combined*. pub fn macvlan(self, name: String, index: u32, mode: u32) -> Self { self.name(name) .link_info( InfoKind::MacVlan, Some(InfoData::MacVlan(vec![InfoMacVlan::Mode(mode)])), ) .append_nla(LinkAttribute::Link(index)) .up() } /// Create macvtap on a link. /// This is equivalent to `ip link add name NAME link LINK type macvtap mode /// MACVTAP_MODE`, but instead of specifying a link name (`LINK`), we /// specify a link index. The MACVTAP_MODE is an integer consisting of /// flags from MACVTAP_MODE (netlink-packet-route/src/rtnl/constants.rs) /// being: _PRIVATE, _VEPA, _BRIDGE, _PASSTHRU, _SOURCE, which can be /// *combined*. pub fn macvtap(self, name: String, index: u32, mode: u32) -> Self { self.name(name) .link_info( InfoKind::MacVtap, Some(InfoData::MacVtap(vec![InfoMacVtap::Mode(mode)])), ) .append_nla(LinkAttribute::Link(index)) .up() } /// Create a VxLAN /// This is equivalent to `ip link add name NAME type vxlan id VNI`, /// it returns a VxlanAddRequest to further customize the vxlan /// interface creation. pub fn vxlan(self, name: String, vni: u32) -> VxlanAddRequest { let s = self.name(name); VxlanAddRequest { request: s, info_data: vec![InfoVxlan::Id(vni)], } } /// Create xfrm tunnel /// This is equivalent to `ip link add name NAME type xfrm if_id NUMBER`, /// The NUMBER is a XFRM if_id which may be connected to IPsec policy pub fn xfrmtun(self, name: String, ifid: u32) -> Self { self.name(name) .link_info( InfoKind::Xfrm, Some(InfoData::Xfrm(vec![InfoXfrm::IfId(ifid)])), ) .up() } /// Create a new bond. /// This is equivalent to `ip link add link NAME type bond`. pub fn bond(self, name: String) -> BondAddRequest { let s = self.name(name); BondAddRequest { request: s, info_data: vec![], } } /// Create a new bridge. /// This is equivalent to `ip link add link NAME type bridge`. pub fn bridge(self, name: String) -> Self { self.name(name.clone()) .link_info(InfoKind::Bridge, None) .append_nla(LinkAttribute::IfName(name)) } /// Create a wireguard link. /// This is equivalent to `ip link add NAME type wireguard`. pub fn wireguard(self, name: String) -> Self { let mut request = self.name(name).link_info(InfoKind::Wireguard, None); request .message_mut() .header .flags .retain(|f| *f != LinkFlag::Up); request } /// Replace existing matching link. pub fn replace(self) -> Self { Self { replace: true, ..self } } fn up(mut self) -> Self { self.message.header.flags.push(LinkFlag::Up); self.message.header.change_mask.push(LinkFlag::Up); self } fn link_info(self, kind: InfoKind, data: Option) -> Self { let mut link_info_nlas = vec![LinkInfo::Kind(kind)]; if let Some(data) = data { link_info_nlas.push(LinkInfo::Data(data)); } self.append_nla(LinkAttribute::LinkInfo(link_info_nlas)) } pub fn name(self, name: String) -> Self { self.append_nla(LinkAttribute::IfName(name)) } /// Define the hardware address of the link when creating it (equivalent to /// `ip link add NAME address ADDRESS`) pub fn address(self, address: Vec) -> Self { self.append_nla(LinkAttribute::Address(address)) } fn append_nla(mut self, nla: LinkAttribute) -> Self { self.message.attributes.push(nla); self } } rtnetlink-0.14.1/src/link/del.rs000064400000000000000000000022211046102023000145540ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::StreamExt; use netlink_packet_core::{NetlinkMessage, NLM_F_ACK, NLM_F_REQUEST}; use netlink_packet_route::{link::LinkMessage, RouteNetlinkMessage}; use crate::{try_nl, Error, Handle}; pub struct LinkDelRequest { handle: Handle, message: LinkMessage, } impl LinkDelRequest { pub(crate) fn new(handle: Handle, index: u32) -> Self { let mut message = LinkMessage::default(); message.header.index = index; LinkDelRequest { handle, message } } /// Execute the request pub async fn execute(self) -> Result<(), Error> { let LinkDelRequest { mut handle, message, } = self; let mut req = NetlinkMessage::from(RouteNetlinkMessage::DelLink(message)); req.header.flags = NLM_F_REQUEST | NLM_F_ACK; let mut response = handle.request(req)?; while let Some(message) = response.next().await { try_nl!(message) } Ok(()) } /// Return a mutable reference to the request pub fn message_mut(&mut self) -> &mut LinkMessage { &mut self.message } } rtnetlink-0.14.1/src/link/get.rs000064400000000000000000000053751046102023000146040ustar 00000000000000// SPDX-License-Identifier: MIT use futures::{ future::{self, Either}, stream::{StreamExt, TryStream}, FutureExt, }; use netlink_packet_core::{NetlinkMessage, NLM_F_DUMP, NLM_F_REQUEST}; use netlink_packet_route::{ link::{LinkAttribute, LinkExtentMask, LinkMessage}, AddressFamily, RouteNetlinkMessage, }; use crate::{try_rtnl, Error, Handle}; pub struct LinkGetRequest { handle: Handle, message: LinkMessage, // There are two ways to retrieve links: we can either dump them // all and filter the result, or if we already know the index or // the name of the link we're looking for, we can just retrieve // that one. If `dump` is `true`, all the links are fetched. // Otherwise, only the link that match the given index or name // is fetched. dump: bool, } impl LinkGetRequest { pub(crate) fn new(handle: Handle) -> Self { LinkGetRequest { handle, message: LinkMessage::default(), dump: true, } } /// Setting filter mask pub fn set_filter_mask( mut self, family: AddressFamily, filter_mask: Vec, ) -> Self { self.message.header.interface_family = family; self.message .attributes .push(LinkAttribute::ExtMask(filter_mask)); self } /// Execute the request pub fn execute(self) -> impl TryStream { let LinkGetRequest { mut handle, message, dump, } = self; let mut req = NetlinkMessage::from(RouteNetlinkMessage::GetLink(message)); if dump { req.header.flags = NLM_F_REQUEST | NLM_F_DUMP; } else { req.header.flags = NLM_F_REQUEST; } match handle.request(req) { Ok(response) => Either::Left(response.map(move |msg| { Ok(try_rtnl!(msg, RouteNetlinkMessage::NewLink)) })), Err(e) => Either::Right( future::err::(e).into_stream(), ), } } /// Return a mutable reference to the request pub fn message_mut(&mut self) -> &mut LinkMessage { &mut self.message } /// Lookup a link by index pub fn match_index(mut self, index: u32) -> Self { self.dump = false; self.message.header.index = index; self } /// Lookup a link by name /// /// This function requires support from your kernel (>= 2.6.33). If yours is /// older, consider filtering the resulting stream of links. pub fn match_name(mut self, name: String) -> Self { self.dump = false; self.message.attributes.push(LinkAttribute::IfName(name)); self } } rtnetlink-0.14.1/src/link/handle.rs000064400000000000000000000022631046102023000152510ustar 00000000000000// SPDX-License-Identifier: MIT use super::{ BondPortSetRequest, LinkAddRequest, LinkDelPropRequest, LinkDelRequest, LinkGetRequest, LinkNewPropRequest, LinkSetRequest, }; use crate::Handle; pub struct LinkHandle(Handle); impl LinkHandle { pub fn new(handle: Handle) -> Self { LinkHandle(handle) } pub fn set(&self, index: u32) -> LinkSetRequest { LinkSetRequest::new(self.0.clone(), index) } pub fn add(&self) -> LinkAddRequest { LinkAddRequest::new(self.0.clone()) } pub fn property_add(&self, index: u32) -> LinkNewPropRequest { LinkNewPropRequest::new(self.0.clone(), index) } pub fn property_del(&self, index: u32) -> LinkDelPropRequest { LinkDelPropRequest::new(self.0.clone(), index) } pub fn del(&mut self, index: u32) -> LinkDelRequest { LinkDelRequest::new(self.0.clone(), index) } /// Retrieve the list of links (equivalent to `ip link show`) pub fn get(&mut self) -> LinkGetRequest { LinkGetRequest::new(self.0.clone()) } pub fn set_bond_port(&mut self, index: u32) -> BondPortSetRequest { BondPortSetRequest::new(self.0.clone(), index) } } rtnetlink-0.14.1/src/link/mod.rs000064400000000000000000000005661046102023000146010ustar 00000000000000// SPDX-License-Identifier: MIT mod handle; pub use self::handle::*; mod add; pub use self::add::*; mod del; pub use self::del::*; mod get; pub use self::get::*; mod set; pub use self::set::*; mod set_bond_port; pub use self::set_bond_port::*; mod property_add; pub use self::property_add::*; mod property_del; pub use self::property_del::*; #[cfg(test)] mod test; rtnetlink-0.14.1/src/link/property_add.rs000064400000000000000000000035641046102023000165170ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::StreamExt; use netlink_packet_core::{ NetlinkMessage, NetlinkPayload, NLM_F_ACK, NLM_F_APPEND, NLM_F_CREATE, NLM_F_EXCL, NLM_F_REQUEST, }; use netlink_packet_route::{ link::{LinkAttribute, LinkMessage, Prop}, RouteNetlinkMessage, }; use crate::{Error, Handle}; pub struct LinkNewPropRequest { handle: Handle, message: LinkMessage, } impl LinkNewPropRequest { pub(crate) fn new(handle: Handle, index: u32) -> Self { let mut message = LinkMessage::default(); message.header.index = index; LinkNewPropRequest { handle, message } } /// Execute the request pub async fn execute(self) -> Result<(), Error> { let LinkNewPropRequest { mut handle, message, } = self; let mut req = NetlinkMessage::from(RouteNetlinkMessage::NewLinkProp(message)); req.header.flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE | NLM_F_APPEND; let mut response = handle.request(req)?; while let Some(message) = response.next().await { if let NetlinkPayload::Error(err) = message.payload { return Err(Error::NetlinkError(err)); } } Ok(()) } /// Return a mutable reference to the request pub fn message_mut(&mut self) -> &mut LinkMessage { &mut self.message } /// Add alternative name to the link. This is equivalent to `ip link /// property add altname ALT_IFNAME dev LINK`. pub fn alt_ifname(mut self, alt_ifnames: &[&str]) -> Self { let mut props = Vec::new(); for alt_ifname in alt_ifnames { props.push(Prop::AltIfName(alt_ifname.to_string())); } self.message.attributes.push(LinkAttribute::PropList(props)); self } } rtnetlink-0.14.1/src/link/property_del.rs000064400000000000000000000034111046102023000165220ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::StreamExt; use netlink_packet_core::{ NetlinkMessage, NetlinkPayload, NLM_F_ACK, NLM_F_EXCL, NLM_F_REQUEST, }; use netlink_packet_route::{ link::{LinkAttribute, LinkMessage, Prop}, RouteNetlinkMessage, }; use crate::{Error, Handle}; pub struct LinkDelPropRequest { handle: Handle, message: LinkMessage, } impl LinkDelPropRequest { pub(crate) fn new(handle: Handle, index: u32) -> Self { let mut message = LinkMessage::default(); message.header.index = index; LinkDelPropRequest { handle, message } } /// Execute the request pub async fn execute(self) -> Result<(), Error> { let LinkDelPropRequest { mut handle, message, } = self; let mut req = NetlinkMessage::from(RouteNetlinkMessage::DelLinkProp(message)); req.header.flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL; let mut response = handle.request(req)?; while let Some(message) = response.next().await { if let NetlinkPayload::Error(err) = message.payload { return Err(Error::NetlinkError(err)); } } Ok(()) } /// Return a mutable reference to the request pub fn message_mut(&mut self) -> &mut LinkMessage { &mut self.message } /// Remove alternative name to the link. This is equivalent to `ip link /// property del altname ALT_IFNAME dev LINK`. pub fn alt_ifname(mut self, alt_ifnames: &[&str]) -> Self { let mut props = Vec::new(); for alt_ifname in alt_ifnames { props.push(Prop::AltIfName(alt_ifname.to_string())); } self.message.attributes.push(LinkAttribute::PropList(props)); self } } rtnetlink-0.14.1/src/link/set.rs000064400000000000000000000135601046102023000146130ustar 00000000000000// SPDX-License-Identifier: MIT use std::os::unix::io::RawFd; use futures::stream::StreamExt; use netlink_packet_core::{ NetlinkMessage, NLM_F_ACK, NLM_F_CREATE, NLM_F_EXCL, NLM_F_REQUEST, }; use netlink_packet_route::{ link::{LinkAttribute, LinkFlag, LinkMessage}, RouteNetlinkMessage, }; use crate::{try_nl, Error, Handle}; pub struct LinkSetRequest { handle: Handle, message: LinkMessage, } impl LinkSetRequest { pub(crate) fn new(handle: Handle, index: u32) -> Self { let mut message = LinkMessage::default(); message.header.index = index; LinkSetRequest { handle, message } } /// Execute the request pub async fn execute(self) -> Result<(), Error> { let LinkSetRequest { mut handle, message, } = self; let mut req = NetlinkMessage::from(RouteNetlinkMessage::SetLink(message)); req.header.flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE; let mut response = handle.request(req)?; while let Some(message) = response.next().await { try_nl!(message); } Ok(()) } /// Return a mutable reference to the request pub fn message_mut(&mut self) -> &mut LinkMessage { &mut self.message } /// Attach the link to a bridge (its _master_). This is equivalent to `ip /// link set LINK master BRIDGE`. To succeed, both the bridge and the /// link that is being attached must be UP. /// /// To Remove a link from a bridge, set its master to zero. /// This is equvalent to `ip link set LINK nomaster` #[deprecated( since = "0.14.0", note = "Please use `LinkSetRequest::controller()` instead" )] pub fn master(mut self, ctrl_index: u32) -> Self { self.message .attributes .push(LinkAttribute::Controller(ctrl_index)); self } /// Attach the link to a bridge (its _controller_). This is equivalent to /// `ip link set LINK master BRIDGE`. To succeed, both the bridge and the /// link that is being attached must be UP. /// /// To Remove a link from a bridge, set its master to zero. /// This is equvalent to `ip link set LINK nomaster` pub fn controller(mut self, ctrl_index: u32) -> Self { self.message .attributes .push(LinkAttribute::Controller(ctrl_index)); self } /// Detach the link from its _master_. This is equivalent to `ip link set /// LINK nomaster`. To succeed, the link that is being detached must be /// UP. #[deprecated( since = "0.14.0", note = "Please use `LinkSetRequest::nocontroller()` instead" )] pub fn nomaster(mut self) -> Self { self.message .attributes .push(LinkAttribute::Controller(0u32)); self } /// Detach the link from its _controller_. This is equivalent to `ip link /// set LINK nomaster`. To succeed, the link that is being detached must be /// UP. pub fn nocontroller(mut self) -> Self { self.message .attributes .push(LinkAttribute::Controller(0u32)); self } /// Set the link with the given index up (equivalent to `ip link set dev DEV /// up`) pub fn up(mut self) -> Self { self.message.header.flags.push(LinkFlag::Up); self.message.header.change_mask.push(LinkFlag::Up); self } /// Set the link with the given index down (equivalent to `ip link set dev /// DEV down`) pub fn down(mut self) -> Self { self.message.header.flags.retain(|f| *f != LinkFlag::Up); self.message.header.change_mask.push(LinkFlag::Up); self } /// Enable or disable promiscious mode of the link with the given index /// (equivalent to `ip link set dev DEV promisc on/off`) pub fn promiscuous(mut self, enable: bool) -> Self { if enable { self.message.header.flags.push(LinkFlag::Promisc); } else { self.message .header .flags .retain(|f| *f != LinkFlag::Promisc); } self.message.header.change_mask.push(LinkFlag::Promisc); self } /// Enable or disable the ARP protocol of the link with the given index /// (equivalent to `ip link set dev DEV arp on/off`) pub fn arp(mut self, enable: bool) -> Self { if enable { self.message.header.flags.retain(|f| *f != LinkFlag::Noarp); } else { self.message.header.flags.push(LinkFlag::Noarp); } self.message.header.change_mask.push(LinkFlag::Noarp); self } /// Set the name of the link with the given index (equivalent to `ip link /// set DEV name NAME`) pub fn name(mut self, name: String) -> Self { self.message.attributes.push(LinkAttribute::IfName(name)); self } /// Set the mtu of the link with the given index (equivalent to `ip link set /// DEV mtu MTU`) pub fn mtu(mut self, mtu: u32) -> Self { self.message.attributes.push(LinkAttribute::Mtu(mtu)); self } /// Set the hardware address of the link with the given index (equivalent to /// `ip link set DEV address ADDRESS`) pub fn address(mut self, address: Vec) -> Self { self.message .attributes .push(LinkAttribute::Address(address)); self } /// Move this network device into the network namespace of the process with /// the given `pid`. pub fn setns_by_pid(mut self, pid: u32) -> Self { self.message.attributes.push(LinkAttribute::NetNsPid(pid)); self } /// Move this network device into the network namespace corresponding to the /// given file descriptor. pub fn setns_by_fd(mut self, fd: RawFd) -> Self { self.message.attributes.push(LinkAttribute::NetNsFd(fd)); self } } rtnetlink-0.14.1/src/link/set_bond_port.rs000064400000000000000000000041651046102023000166620ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::StreamExt; use netlink_packet_core::{NetlinkMessage, NLM_F_ACK, NLM_F_REQUEST}; use netlink_packet_route::{ link::{ InfoBondPort, InfoPortData, InfoPortKind, LinkAttribute, LinkInfo, LinkMessage, }, RouteNetlinkMessage, }; use crate::{try_nl, Error, Handle}; pub struct BondPortSetRequest { handle: Handle, index: u32, port_nlas: Vec, } impl BondPortSetRequest { pub(crate) fn new(handle: Handle, index: u32) -> Self { BondPortSetRequest { handle, index, port_nlas: Vec::new(), } } /// Execute the request. pub async fn execute(self) -> Result<(), Error> { let BondPortSetRequest { mut handle, index, port_nlas, } = self; let mut message = LinkMessage::default(); message.header.index = index; message.attributes.push(LinkAttribute::LinkInfo(vec![ LinkInfo::PortKind(InfoPortKind::Bond), LinkInfo::PortData(InfoPortData::BondPort(port_nlas)), ])); let mut req = NetlinkMessage::from(RouteNetlinkMessage::NewLink(message)); req.header.flags = NLM_F_REQUEST | NLM_F_ACK; let mut response = handle.request(req)?; while let Some(message) = response.next().await { try_nl!(message); } Ok(()) } /// Return a mutable reference to the Vec pub fn info_port_nlas_mut(&mut self) -> &mut Vec { &mut self.port_nlas } /// Adds the `queue_id` attribute to the bond port /// This is equivalent to /// `ip link set name NAME type bond_slave queue_id QUEUE_ID`. pub fn queue_id(mut self, queue_id: u16) -> Self { self.port_nlas.push(InfoBondPort::QueueId(queue_id)); self } /// Adds the `prio` attribute to the bond port /// This is equivalent to `ip link set name NAME type bond_slave prio PRIO`. pub fn prio(mut self, prio: i32) -> Self { self.port_nlas.push(InfoBondPort::Prio(prio)); self } } rtnetlink-0.14.1/src/link/test.rs000064400000000000000000000076171046102023000150050ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; use netlink_packet_route::link::{ InfoData, InfoKind, InfoMacVlan, LinkAttribute, LinkInfo, LinkMessage, }; use tokio::runtime::Runtime; use crate::{new_connection, Error, LinkHandle}; const IFACE_NAME: &str = "wg142"; // rand? #[test] fn create_get_delete_wg() { let rt = Runtime::new().unwrap(); let handle = rt.block_on(_create_wg()); assert!(handle.is_ok()); let mut handle = handle.unwrap(); let msg = rt.block_on(_get_iface(&mut handle, IFACE_NAME.to_owned())); assert!(msg.is_ok()); let msg = msg.unwrap(); assert!(has_nla( &msg, &LinkAttribute::LinkInfo(vec![LinkInfo::Kind(InfoKind::Wireguard)]) )); rt.block_on(_del_iface(&mut handle, msg.header.index)) .unwrap(); } #[test] fn create_get_delete_macvlan() { const MACVLAN_IFACE_NAME: &str = "mvlan1"; const LOWER_DEVICE_IDX: u32 = 2; const MACVLAN_MODE: u32 = 4; // bridge let mac_address = &vec![2u8, 0, 0, 0, 0, 1]; let rt = Runtime::new().unwrap(); let handle = rt.block_on(_create_macvlan( &MACVLAN_IFACE_NAME.to_owned(), LOWER_DEVICE_IDX, /* assuming there's always a network interface in * the system ... */ MACVLAN_MODE, mac_address.to_vec(), )); assert!(handle.is_ok()); let mut handle = handle.unwrap(); let msg = rt.block_on(_get_iface(&mut handle, MACVLAN_IFACE_NAME.to_owned())); assert!(msg.is_ok()); assert!(has_nla( &msg.as_ref().unwrap(), &LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::MacVlan), LinkInfo::Data(InfoData::MacVlan(vec![ InfoMacVlan::Mode(MACVLAN_MODE), InfoMacVlan::Flags(0), // defaulted by the kernel InfoMacVlan::MacAddrCount(0), // defaulted by the kernel InfoMacVlan::BcQueueLen(1000), // defaulted by the kernel InfoMacVlan::BcQueueLenUsed(1000) // defaulted by the kernel ])) ]) )); assert!(has_nla( &msg.as_ref().unwrap(), &LinkAttribute::IfName(MACVLAN_IFACE_NAME.to_string()) )); assert!(has_nla( &msg.as_ref().unwrap(), &LinkAttribute::Link(LOWER_DEVICE_IDX) )); assert!(has_nla( &msg.as_ref().unwrap(), &LinkAttribute::Address(mac_address.to_vec()) )); rt.block_on(_del_iface(&mut handle, msg.unwrap().header.index)) .unwrap(); } fn has_nla(msg: &LinkMessage, nla: &LinkAttribute) -> bool { msg.attributes.iter().any(|x| x == nla) } async fn _create_wg() -> Result { let (conn, handle, _) = new_connection().unwrap(); tokio::spawn(conn); let link_handle = handle.link(); let mut req = link_handle.add(); let mutator = req.message_mut(); let info = LinkAttribute::LinkInfo(vec![LinkInfo::Kind(InfoKind::Wireguard)]); mutator.attributes.push(info); mutator .attributes .push(LinkAttribute::IfName(IFACE_NAME.to_owned())); req.execute().await?; Ok(link_handle) } async fn _get_iface( handle: &mut LinkHandle, iface_name: String, ) -> Result { let mut links = handle.get().match_name(iface_name).execute(); let msg = links.try_next().await?; msg.ok_or(Error::RequestFailed) } async fn _del_iface(handle: &mut LinkHandle, index: u32) -> Result<(), Error> { handle.del(index).execute().await } async fn _create_macvlan( name: &String, lower_device_index: u32, mode: u32, mac: Vec, ) -> Result { let (conn, handle, _) = new_connection().unwrap(); tokio::spawn(conn); let link_handle = handle.link(); let req = link_handle .add() .macvlan(name.to_string(), lower_device_index, mode) .name(name.to_owned()) .address(mac); req.execute().await?; Ok(link_handle) } rtnetlink-0.14.1/src/macros.rs000064400000000000000000000016541046102023000143500ustar 00000000000000// SPDX-License-Identifier: MIT #[macro_export] macro_rules! try_rtnl { ($msg: expr, $message_type:path) => {{ use netlink_packet_core::{NetlinkMessage, NetlinkPayload}; use netlink_packet_route::RouteNetlinkMessage; use $crate::Error; let (header, payload) = $msg.into_parts(); match payload { NetlinkPayload::InnerMessage($message_type(msg)) => msg, NetlinkPayload::Error(err) => return Err(Error::NetlinkError(err)), _ => { return Err(Error::UnexpectedMessage(NetlinkMessage::new( header, payload, ))) } } }}; } #[macro_export] macro_rules! try_nl { ($msg: expr) => {{ use netlink_packet_core::NetlinkPayload; use $crate::Error; if let NetlinkPayload::Error(err) = $msg.payload { return Err(Error::NetlinkError(err)); } }}; } rtnetlink-0.14.1/src/neighbour/add.rs000064400000000000000000000121501046102023000155670ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::StreamExt; use netlink_packet_core::{ NetlinkMessage, NetlinkPayload, NLM_F_ACK, NLM_F_CREATE, NLM_F_EXCL, NLM_F_REPLACE, NLM_F_REQUEST, }; use netlink_packet_route::{ neighbour::{ NeighbourAddress, NeighbourAttribute, NeighbourFlag, NeighbourMessage, NeighbourState, }, route::RouteType, AddressFamily, RouteNetlinkMessage, }; use crate::{Error, Handle}; use std::net::IpAddr; pub struct NeighbourAddRequest { handle: Handle, message: NeighbourMessage, replace: bool, } impl NeighbourAddRequest { pub(crate) fn new(handle: Handle, index: u32, destination: IpAddr) -> Self { let mut message = NeighbourMessage::default(); message.header.family = match destination { IpAddr::V4(_) => AddressFamily::Inet, IpAddr::V6(_) => AddressFamily::Inet6, }; message.header.ifindex = index; message.header.state = NeighbourState::Permanent; message.header.kind = RouteType::Unspec; message.attributes.push(NeighbourAttribute::Destination( match destination { IpAddr::V4(v4) => NeighbourAddress::Inet(v4), IpAddr::V6(v6) => NeighbourAddress::Inet6(v6), }, )); NeighbourAddRequest { handle, message, replace: false, } } #[cfg(not(target_os = "freebsd"))] pub(crate) fn new_bridge(handle: Handle, index: u32, lla: &[u8]) -> Self { let mut message = NeighbourMessage::default(); message.header.family = AddressFamily::Bridge; message.header.ifindex = index; message.header.state = NeighbourState::Permanent; message.header.kind = RouteType::Unspec; message .attributes .push(NeighbourAttribute::LinkLocalAddress(lla.to_vec())); NeighbourAddRequest { handle, message, replace: false, } } /// Set a bitmask of states for the neighbor cache entry. /// It should be a combination of `NUD_*` constants. pub fn state(mut self, state: NeighbourState) -> Self { self.message.header.state = state; self } /// Set flags for the neighbor cache entry. /// It should be a combination of `NTF_*` constants. pub fn flags(mut self, flags: Vec) -> Self { self.message.header.flags = flags; self } /// Set attributes applicable to the the neighbor cache entry. /// It should be one of `NDA_*` constants. pub fn kind(mut self, kind: RouteType) -> Self { self.message.header.kind = kind; self } /// Set a neighbor cache link layer address (see `NDA_LLADDR` for details). pub fn link_local_address(mut self, addr: &[u8]) -> Self { let lla = self.message .attributes .iter_mut() .find_map(|nla| match nla { NeighbourAttribute::LinkLocalAddress(lla) => Some(lla), _ => None, }); if let Some(lla) = lla { *lla = addr.to_vec(); } else { self.message .attributes .push(NeighbourAttribute::LinkLocalAddress(addr.to_vec())); } self } /// Set the destination address for the neighbour (see `NDA_DST` for /// details). pub fn destination(mut self, addr: IpAddr) -> Self { let dst = self.message .attributes .iter_mut() .find_map(|nla| match nla { NeighbourAttribute::Destination(dst) => Some(dst), _ => None, }); let addr = match addr { IpAddr::V4(v4) => NeighbourAddress::Inet(v4), IpAddr::V6(v6) => NeighbourAddress::Inet6(v6), }; if let Some(dst) = dst { *dst = addr; } else { self.message .attributes .push(NeighbourAttribute::Destination(addr)); } self } /// Replace existing matching neighbor. pub fn replace(self) -> Self { Self { replace: true, ..self } } /// Execute the request. pub async fn execute(self) -> Result<(), Error> { let NeighbourAddRequest { mut handle, message, replace, } = self; let mut req = NetlinkMessage::from(RouteNetlinkMessage::NewNeighbour(message)); let replace = if replace { NLM_F_REPLACE } else { NLM_F_EXCL }; req.header.flags = NLM_F_REQUEST | NLM_F_ACK | replace | NLM_F_CREATE; let mut response = handle.request(req)?; while let Some(message) = response.next().await { if let NetlinkPayload::Error(err) = message.payload { return Err(Error::NetlinkError(err)); } } Ok(()) } /// Return a mutable reference to the request message. pub fn message_mut(&mut self) -> &mut NeighbourMessage { &mut self.message } } rtnetlink-0.14.1/src/neighbour/del.rs000064400000000000000000000022571046102023000156120ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::StreamExt; use netlink_packet_core::{ NetlinkMessage, NetlinkPayload, NLM_F_ACK, NLM_F_REQUEST, }; use netlink_packet_route::{neighbour::NeighbourMessage, RouteNetlinkMessage}; use crate::{Error, Handle}; pub struct NeighbourDelRequest { handle: Handle, message: NeighbourMessage, } impl NeighbourDelRequest { pub(crate) fn new(handle: Handle, message: NeighbourMessage) -> Self { NeighbourDelRequest { handle, message } } /// Execute the request pub async fn execute(self) -> Result<(), Error> { let NeighbourDelRequest { mut handle, message, } = self; let mut req = NetlinkMessage::from(RouteNetlinkMessage::DelNeighbour(message)); req.header.flags = NLM_F_REQUEST | NLM_F_ACK; let mut response = handle.request(req)?; while let Some(msg) = response.next().await { if let NetlinkPayload::Error(e) = msg.payload { return Err(Error::NetlinkError(e)); } } Ok(()) } pub fn message_mut(&mut self) -> &mut NeighbourMessage { &mut self.message } } rtnetlink-0.14.1/src/neighbour/get.rs000064400000000000000000000043261046102023000156240ustar 00000000000000// SPDX-License-Identifier: MIT use futures::{ future::{self, Either}, stream::{StreamExt, TryStream}, FutureExt, }; use netlink_packet_core::{ NetlinkMessage, NetlinkPayload, NLM_F_DUMP, NLM_F_REQUEST, }; use netlink_packet_route::{ neighbour::{NeighbourFlag, NeighbourMessage}, RouteNetlinkMessage, }; use crate::{Error, Handle, IpVersion}; pub struct NeighbourGetRequest { handle: Handle, message: NeighbourMessage, } impl NeighbourGetRequest { pub(crate) fn new(handle: Handle) -> Self { let message = NeighbourMessage::default(); NeighbourGetRequest { handle, message } } /// List neighbor proxies in the system (equivalent to: `ip neighbor show /// proxy`). pub fn proxies(mut self) -> Self { self.message.header.flags.push(NeighbourFlag::Proxy); self } pub fn set_family(mut self, ip_version: IpVersion) -> Self { self.message.header.family = ip_version.family(); self } /// Execute the request pub fn execute( self, ) -> impl TryStream { let NeighbourGetRequest { mut handle, message, } = self; let mut req = NetlinkMessage::from(RouteNetlinkMessage::GetNeighbour(message)); req.header.flags = NLM_F_REQUEST | NLM_F_DUMP; match handle.request(req) { Ok(response) => Either::Left(response.map(move |msg| { let (header, payload) = msg.into_parts(); match payload { NetlinkPayload::InnerMessage( RouteNetlinkMessage::NewNeighbour(msg), ) => Ok(msg), NetlinkPayload::Error(err) => Err(Error::NetlinkError(err)), _ => Err(Error::UnexpectedMessage(NetlinkMessage::new( header, payload, ))), } })), Err(e) => Either::Right( future::err::(e).into_stream(), ), } } /// Return a mutable reference to the request pub fn message_mut(&mut self) -> &mut NeighbourMessage { &mut self.message } } rtnetlink-0.14.1/src/neighbour/handle.rs000064400000000000000000000022611046102023000162740ustar 00000000000000// SPDX-License-Identifier: MIT use crate::{ Handle, NeighbourAddRequest, NeighbourDelRequest, NeighbourGetRequest, }; use netlink_packet_route::neighbour::NeighbourMessage; use std::net::IpAddr; pub struct NeighbourHandle(Handle); impl NeighbourHandle { pub fn new(handle: Handle) -> Self { NeighbourHandle(handle) } /// List neighbour entries (equivalent to `ip neighbour show`) pub fn get(&self) -> NeighbourGetRequest { NeighbourGetRequest::new(self.0.clone()) } /// Add a new neighbour entry (equivalent to `ip neighbour add`) pub fn add(&self, index: u32, destination: IpAddr) -> NeighbourAddRequest { NeighbourAddRequest::new(self.0.clone(), index, destination) } #[cfg(not(target_os = "freebsd"))] /// Add a new fdb entry (equivalent to `bridge fdb add`) pub fn add_bridge(&self, index: u32, lla: &[u8]) -> NeighbourAddRequest { NeighbourAddRequest::new_bridge(self.0.clone(), index, lla) } /// Delete a neighbour entry (equivalent to `ip neighbour delete`) pub fn del(&self, message: NeighbourMessage) -> NeighbourDelRequest { NeighbourDelRequest::new(self.0.clone(), message) } } rtnetlink-0.14.1/src/neighbour/mod.rs000064400000000000000000000002461046102023000156210ustar 00000000000000// SPDX-License-Identifier: MIT mod handle; pub use self::handle::*; mod get; pub use self::get::*; mod add; pub use self::add::*; mod del; pub use self::del::*; rtnetlink-0.14.1/src/ns.rs000064400000000000000000000270121046102023000135000ustar 00000000000000// SPDX-License-Identifier: MIT use crate::Error; use nix::{ fcntl::OFlag, sched::CloneFlags, sys::{ stat::Mode, wait::{waitpid, WaitStatus}, }, unistd::{fork, ForkResult}, }; use std::{option::Option, os::fd::BorrowedFd, path::Path, process::exit}; // if "only" smol or smol+tokio were enabled, we use smol because // it doesn't require an active tokio runtime - just to be sure. #[cfg(feature = "smol_socket")] async fn try_spawn_blocking(fut: F) -> R where F: FnOnce() -> R + Send + 'static, R: Send + 'static, { async_global_executor::spawn_blocking(fut).await } // only tokio enabled, so use tokio #[cfg(all(not(feature = "smol_socket"), feature = "tokio_socket"))] async fn try_spawn_blocking(fut: F) -> R where F: FnOnce() -> R + Send + 'static, R: Send + 'static, { match tokio::task::spawn_blocking(fut).await { Ok(v) => v, Err(err) => { std::panic::resume_unwind(err.into_panic()); } } } // neither smol nor tokio - just run blocking op directly. // hopefully not too blocking... #[cfg(all(not(feature = "smol_socket"), not(feature = "tokio_socket")))] async fn try_spawn_blocking(fut: F) -> R where F: FnOnce() -> R + Send + 'static, R: Send + 'static, { fut() } pub const NETNS_PATH: &str = "/run/netns/"; pub const SELF_NS_PATH: &str = "/proc/self/ns/net"; pub const NONE_FS: &str = "none"; pub struct NetworkNamespace(); impl NetworkNamespace { /// Add a new network namespace. /// This is equivalent to `ip netns add NS_NAME`. pub async fn add(ns_name: String) -> Result<(), Error> { // Forking process to avoid moving caller into new namespace NetworkNamespace::prep_for_fork()?; log::trace!("Forking..."); match unsafe { fork() } { Ok(ForkResult::Parent { child, .. }) => { NetworkNamespace::parent_process(child) } Ok(ForkResult::Child) => { NetworkNamespace::child_process(ns_name); } Err(e) => { let err_msg = format!("Fork failed: {e}"); Err(Error::NamespaceError(err_msg)) } } } /// Remove a network namespace /// This is equivalent to `ip netns del NS_NAME`. pub async fn del(ns_name: String) -> Result<(), Error> { try_spawn_blocking(move || { let mut netns_path = String::new(); netns_path.push_str(NETNS_PATH); netns_path.push_str(&ns_name); let ns_path = Path::new(&netns_path); if nix::mount::umount2(ns_path, nix::mount::MntFlags::MNT_DETACH) .is_err() { let err_msg = String::from( "Namespace unmount failed (are you running as root?)", ); return Err(Error::NamespaceError(err_msg)); } if nix::unistd::unlink(ns_path).is_err() { let err_msg = String::from( "Namespace file remove failed (are you running as root?)", ); return Err(Error::NamespaceError(err_msg)); } Ok(()) }) .await } pub fn prep_for_fork() -> Result<(), Error> { // Placeholder function, nothing to do here. Ok(()) } /// This is the parent process form the fork, it waits for the /// child to exit properly pub fn parent_process(child: nix::unistd::Pid) -> Result<(), Error> { log::trace!("parent_process child PID: {}", child); log::trace!("Waiting for child to finish..."); match waitpid(child, None) { Ok(wait_status) => match wait_status { WaitStatus::Exited(_, res) => { log::trace!("Child exited with: {}", res); if res == 0 { return Ok(()); } log::error!("Error child result: {}", res); let err_msg = format!("Error child result: {res}"); Err(Error::NamespaceError(err_msg)) } WaitStatus::Signaled(_, signal, has_dump) => { log::error!("Error child killed by signal: {}", signal); let err_msg = format!( "Error child process was killed by signal: {signal} with core dump {has_dump}" ); Err(Error::NamespaceError(err_msg)) } _ => { log::error!("Unknown child process status"); let err_msg = String::from("Unknown child process status"); Err(Error::NamespaceError(err_msg)) } }, Err(e) => { log::error!("wait error: {}", e); let err_msg = format!("wait error: {e}"); Err(Error::NamespaceError(err_msg)) } } } fn child_process(ns_name: String) -> ! { let res = std::panic::catch_unwind(|| -> Result<(), Error> { let netns_path = NetworkNamespace::child_process_create_ns(ns_name)?; NetworkNamespace::unshare_processing(netns_path)?; Ok(()) }); match res { Err(_panic) => { // panic should have already been printed by the handler log::error!("child process crashed"); std::process::abort() } Ok(Err(fail)) => { log::error!("child process failed: {}", fail); exit(1) } Ok(Ok(())) => exit(0), } } /// This is the child process, it will actually create the namespace /// resources. It creates the folder and namespace file. /// Returns the namespace file path pub fn child_process_create_ns(ns_name: String) -> Result { log::trace!("child_process will create the namespace"); let mut netns_path = String::new(); let dir_path = Path::new(NETNS_PATH); let mut mkdir_mode = Mode::empty(); let mut open_flags = OFlag::empty(); let mut mount_flags = nix::mount::MsFlags::empty(); let none_fs = Path::new(&NONE_FS); let none_p4: Option<&Path> = None; // flags in mkdir mkdir_mode.insert(Mode::S_IRWXU); mkdir_mode.insert(Mode::S_IRGRP); mkdir_mode.insert(Mode::S_IXGRP); mkdir_mode.insert(Mode::S_IROTH); mkdir_mode.insert(Mode::S_IXOTH); open_flags.insert(OFlag::O_RDONLY); open_flags.insert(OFlag::O_CREAT); open_flags.insert(OFlag::O_EXCL); netns_path.push_str(NETNS_PATH); netns_path.push_str(&ns_name); // creating namespaces folder if not exists #[allow(clippy::collapsible_if)] if nix::sys::stat::stat(dir_path).is_err() { if let Err(e) = nix::unistd::mkdir(dir_path, mkdir_mode) { log::error!("mkdir error: {}", e); let err_msg = format!("mkdir error: {e}"); return Err(Error::NamespaceError(err_msg)); } } // Try to mount /run/netns, with MS_REC | MS_SHARED // If it fails, creates the mount with MS_BIND | MS_REC // This is the same strategy used by `ip netns add NS` mount_flags.insert(nix::mount::MsFlags::MS_REC); mount_flags.insert(nix::mount::MsFlags::MS_SHARED); if nix::mount::mount( Some(Path::new("")), dir_path, Some(none_fs), mount_flags, none_p4, ) .is_err() { mount_flags = nix::mount::MsFlags::empty(); mount_flags.insert(nix::mount::MsFlags::MS_BIND); mount_flags.insert(nix::mount::MsFlags::MS_REC); if let Err(e) = nix::mount::mount( Some(Path::new(dir_path)), dir_path, Some(none_fs), mount_flags, none_p4, ) { log::error!("mount error: {}", e); let err_msg = format!("mount error: {e}"); return Err(Error::NamespaceError(err_msg)); } } mount_flags = nix::mount::MsFlags::empty(); mount_flags.insert(nix::mount::MsFlags::MS_REC); mount_flags.insert(nix::mount::MsFlags::MS_SHARED); if let Err(e) = nix::mount::mount( Some(Path::new("")), dir_path, Some(none_fs), mount_flags, none_p4, ) { log::error!("mount error: {}", e); let err_msg = format!("mount error: {e}"); return Err(Error::NamespaceError(err_msg)); } let ns_path = Path::new(&netns_path); // creating the netns file let fd = match nix::fcntl::open(ns_path, open_flags, Mode::empty()) { Ok(raw_fd) => raw_fd, Err(e) => { log::error!("open error: {}", e); let err_msg = format!("open error: {e}"); return Err(Error::NamespaceError(err_msg)); } }; if let Err(e) = nix::unistd::close(fd) { log::error!("close error: {}", e); let err_msg = format!("close error: {e}"); let _ = nix::unistd::unlink(ns_path); return Err(Error::NamespaceError(err_msg)); } Ok(netns_path) } /// This function unshare the calling process and move into /// the given network namespace #[allow(unused)] pub fn unshare_processing(netns_path: String) -> Result<(), Error> { let mut setns_flags = CloneFlags::empty(); let mut open_flags = OFlag::empty(); let ns_path = Path::new(&netns_path); let none_fs = Path::new(&NONE_FS); let none_p4: Option<&Path> = None; // unshare to the new network namespace if let Err(e) = nix::sched::unshare(CloneFlags::CLONE_NEWNET) { log::error!("unshare error: {}", e); let err_msg = format!("unshare error: {e}"); let _ = nix::unistd::unlink(ns_path); return Err(Error::NamespaceError(err_msg)); } open_flags = OFlag::empty(); open_flags.insert(OFlag::O_RDONLY); open_flags.insert(OFlag::O_CLOEXEC); let fd = match nix::fcntl::open( Path::new(&SELF_NS_PATH), open_flags, Mode::empty(), ) { Ok(raw_fd) => raw_fd, Err(e) => { log::error!("open error: {}", e); let err_msg = format!("open error: {e}"); return Err(Error::NamespaceError(err_msg)); } }; let self_path = Path::new(&SELF_NS_PATH); // bind to the netns if let Err(e) = nix::mount::mount( Some(self_path), ns_path, Some(none_fs), nix::mount::MsFlags::MS_BIND, none_p4, ) { log::error!("mount error: {}", e); let err_msg = format!("mount error: {e}"); let _ = nix::unistd::unlink(ns_path); return Err(Error::NamespaceError(err_msg)); } setns_flags.insert(CloneFlags::CLONE_NEWNET); if let Err(e) = nix::sched::setns( unsafe { BorrowedFd::borrow_raw(fd) }, setns_flags, ) { log::error!("setns error: {}", e); let err_msg = format!("setns error: {e}"); let _ = nix::unistd::unlink(ns_path); return Err(Error::NamespaceError(err_msg)); } Ok(()) } } rtnetlink-0.14.1/src/route/add.rs000064400000000000000000000155301046102023000147500ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::StreamExt; use std::{ marker::PhantomData, net::{Ipv4Addr, Ipv6Addr}, }; use netlink_packet_core::{ NetlinkMessage, NLM_F_ACK, NLM_F_CREATE, NLM_F_EXCL, NLM_F_REPLACE, NLM_F_REQUEST, }; use netlink_packet_route::{ route::{ RouteAddress, RouteAttribute, RouteHeader, RouteMessage, RouteProtocol, RouteScope, RouteType, }, AddressFamily, RouteNetlinkMessage, }; use crate::{try_nl, Error, Handle}; /// A request to create a new route. This is equivalent to the `ip route add` /// commands. pub struct RouteAddRequest { handle: Handle, message: RouteMessage, replace: bool, _phantom: PhantomData, } impl RouteAddRequest { pub(crate) fn new(handle: Handle) -> Self { let mut message = RouteMessage::default(); message.header.table = RouteHeader::RT_TABLE_MAIN; message.header.protocol = RouteProtocol::Static; message.header.scope = RouteScope::Universe; message.header.kind = RouteType::Unicast; RouteAddRequest { handle, message, replace: false, _phantom: Default::default(), } } /// Sets the input interface index. pub fn input_interface(mut self, index: u32) -> Self { self.message.attributes.push(RouteAttribute::Iif(index)); self } /// Sets the output interface index. pub fn output_interface(mut self, index: u32) -> Self { self.message.attributes.push(RouteAttribute::Oif(index)); self } /// Sets the route priority (metric) pub fn priority(mut self, priority: u32) -> Self { self.message .attributes .push(RouteAttribute::Priority(priority)); self } /// Sets the route table. /// /// Default is main route table. #[deprecated(note = "Please use `table_id` instead")] pub fn table(mut self, table: u8) -> Self { self.message.header.table = table; self } /// Sets the route table ID. /// /// Default is main route table. pub fn table_id(mut self, table: u32) -> Self { if table > 255 { self.message.attributes.push(RouteAttribute::Table(table)); } else { self.message.header.table = table as u8; } self } /// Sets the route protocol. /// /// Default is static route protocol. pub fn protocol(mut self, protocol: RouteProtocol) -> Self { self.message.header.protocol = protocol; self } /// Sets the route scope. /// /// Default is universe route scope. pub fn scope(mut self, scope: RouteScope) -> Self { self.message.header.scope = scope; self } /// Sets the route kind. /// /// Default is unicast route kind. pub fn kind(mut self, kind: RouteType) -> Self { self.message.header.kind = kind; self } /// Build an IP v4 route request pub fn v4(mut self) -> RouteAddRequest { self.message.header.address_family = AddressFamily::Inet; RouteAddRequest { handle: self.handle, message: self.message, replace: false, _phantom: Default::default(), } } /// Build an IP v6 route request pub fn v6(mut self) -> RouteAddRequest { self.message.header.address_family = AddressFamily::Inet6; RouteAddRequest { handle: self.handle, message: self.message, replace: false, _phantom: Default::default(), } } /// Replace existing matching route. pub fn replace(self) -> Self { Self { replace: true, ..self } } /// Execute the request. pub async fn execute(self) -> Result<(), Error> { let RouteAddRequest { mut handle, message, replace, .. } = self; let mut req = NetlinkMessage::from(RouteNetlinkMessage::NewRoute(message)); let replace = if replace { NLM_F_REPLACE } else { NLM_F_EXCL }; req.header.flags = NLM_F_REQUEST | NLM_F_ACK | replace | NLM_F_CREATE; let mut response = handle.request(req)?; while let Some(message) = response.next().await { try_nl!(message); } Ok(()) } /// Return a mutable reference to the request message. pub fn message_mut(&mut self) -> &mut RouteMessage { &mut self.message } } impl RouteAddRequest { /// Sets the source address prefix. pub fn source_prefix(mut self, addr: Ipv4Addr, prefix_length: u8) -> Self { self.message.header.source_prefix_length = prefix_length; self.message .attributes .push(RouteAttribute::Source(RouteAddress::Inet(addr))); self } /// Sets the preferred source address. pub fn pref_source(mut self, addr: Ipv4Addr) -> Self { self.message .attributes .push(RouteAttribute::PrefSource(RouteAddress::Inet(addr))); self } /// Sets the destination address prefix. pub fn destination_prefix( mut self, addr: Ipv4Addr, prefix_length: u8, ) -> Self { self.message.header.destination_prefix_length = prefix_length; self.message .attributes .push(RouteAttribute::Destination(RouteAddress::Inet(addr))); self } /// Sets the gateway (via) address. pub fn gateway(mut self, addr: Ipv4Addr) -> Self { self.message .attributes .push(RouteAttribute::Gateway(RouteAddress::Inet(addr))); self } } impl RouteAddRequest { /// Sets the source address prefix. pub fn source_prefix(mut self, addr: Ipv6Addr, prefix_length: u8) -> Self { self.message.header.source_prefix_length = prefix_length; self.message .attributes .push(RouteAttribute::Source(RouteAddress::Inet6(addr))); self } /// Sets the preferred source address. pub fn pref_source(mut self, addr: Ipv6Addr) -> Self { self.message .attributes .push(RouteAttribute::PrefSource(RouteAddress::Inet6(addr))); self } /// Sets the destination address prefix. pub fn destination_prefix( mut self, addr: Ipv6Addr, prefix_length: u8, ) -> Self { self.message.header.destination_prefix_length = prefix_length; self.message .attributes .push(RouteAttribute::Destination(RouteAddress::Inet6(addr))); self } /// Sets the gateway (via) address. pub fn gateway(mut self, addr: Ipv6Addr) -> Self { self.message .attributes .push(RouteAttribute::Gateway(RouteAddress::Inet6(addr))); self } } rtnetlink-0.14.1/src/route/del.rs000064400000000000000000000022071046102023000147610ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::StreamExt; use netlink_packet_core::{ NetlinkMessage, NetlinkPayload, NLM_F_ACK, NLM_F_REQUEST, }; use netlink_packet_route::{route::RouteMessage, RouteNetlinkMessage}; use crate::{Error, Handle}; pub struct RouteDelRequest { handle: Handle, message: RouteMessage, } impl RouteDelRequest { pub(crate) fn new(handle: Handle, message: RouteMessage) -> Self { RouteDelRequest { handle, message } } /// Execute the request pub async fn execute(self) -> Result<(), Error> { let RouteDelRequest { mut handle, message, } = self; let mut req = NetlinkMessage::from(RouteNetlinkMessage::DelRoute(message)); req.header.flags = NLM_F_REQUEST | NLM_F_ACK; let mut response = handle.request(req)?; while let Some(msg) = response.next().await { if let NetlinkPayload::Error(e) = msg.payload { return Err(Error::NetlinkError(e)); } } Ok(()) } pub fn message_mut(&mut self) -> &mut RouteMessage { &mut self.message } } rtnetlink-0.14.1/src/route/get.rs000064400000000000000000000050431046102023000147750ustar 00000000000000// SPDX-License-Identifier: MIT use futures::{ future::{self, Either}, stream::{StreamExt, TryStream}, FutureExt, }; use netlink_packet_core::{NetlinkMessage, NLM_F_DUMP, NLM_F_REQUEST}; use netlink_packet_route::{ route::{RouteHeader, RouteMessage, RouteProtocol, RouteScope, RouteType}, AddressFamily, RouteNetlinkMessage, }; use crate::{try_rtnl, Error, Handle}; pub struct RouteGetRequest { handle: Handle, message: RouteMessage, } /// Internet Protocol (IP) version. #[derive(Debug, Clone, Eq, PartialEq, PartialOrd)] pub enum IpVersion { /// IPv4 V4, /// IPv6 V6, } impl IpVersion { pub(crate) fn family(self) -> AddressFamily { match self { IpVersion::V4 => AddressFamily::Inet, IpVersion::V6 => AddressFamily::Inet6, } } } impl RouteGetRequest { pub(crate) fn new(handle: Handle, ip_version: IpVersion) -> Self { let mut message = RouteMessage::default(); message.header.address_family = ip_version.family(); // As per rtnetlink(7) documentation, setting the following // fields to 0 gets us all the routes from all the tables // // > For RTM_GETROUTE, setting rtm_dst_len and rtm_src_len to 0 // > means you get all entries for the specified routing table. // > For the other fields, except rtm_table and rtm_protocol, 0 // > is the wildcard. message.header.destination_prefix_length = 0; message.header.source_prefix_length = 0; message.header.scope = RouteScope::Universe; message.header.kind = RouteType::Unspec; // I don't know if these two fields matter message.header.table = RouteHeader::RT_TABLE_UNSPEC; message.header.protocol = RouteProtocol::Unspec; RouteGetRequest { handle, message } } pub fn message_mut(&mut self) -> &mut RouteMessage { &mut self.message } pub fn execute(self) -> impl TryStream { let RouteGetRequest { mut handle, message, } = self; let mut req = NetlinkMessage::from(RouteNetlinkMessage::GetRoute(message)); req.header.flags = NLM_F_REQUEST | NLM_F_DUMP; match handle.request(req) { Ok(response) => Either::Left(response.map(move |msg| { Ok(try_rtnl!(msg, RouteNetlinkMessage::NewRoute)) })), Err(e) => Either::Right( future::err::(e).into_stream(), ), } } } rtnetlink-0.14.1/src/route/handle.rs000064400000000000000000000015611046102023000154520ustar 00000000000000// SPDX-License-Identifier: MIT use crate::{ Handle, IpVersion, RouteAddRequest, RouteDelRequest, RouteGetRequest, }; use netlink_packet_route::route::RouteMessage; pub struct RouteHandle(Handle); impl RouteHandle { pub fn new(handle: Handle) -> Self { RouteHandle(handle) } /// Retrieve the list of routing table entries (equivalent to `ip route /// show`) pub fn get(&self, ip_version: IpVersion) -> RouteGetRequest { RouteGetRequest::new(self.0.clone(), ip_version) } /// Add an routing table entry (equivalent to `ip route add`) pub fn add(&self) -> RouteAddRequest { RouteAddRequest::new(self.0.clone()) } /// Delete the given routing table entry (equivalent to `ip route del`) pub fn del(&self, route: RouteMessage) -> RouteDelRequest { RouteDelRequest::new(self.0.clone(), route) } } rtnetlink-0.14.1/src/route/mod.rs000064400000000000000000000002461046102023000147750ustar 00000000000000// SPDX-License-Identifier: MIT mod handle; pub use self::handle::*; mod add; pub use self::add::*; mod del; pub use self::del::*; mod get; pub use self::get::*; rtnetlink-0.14.1/src/rule/add.rs000064400000000000000000000125571046102023000145670ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::StreamExt; use std::{ marker::PhantomData, net::{Ipv4Addr, Ipv6Addr}, }; use netlink_packet_core::{ NetlinkMessage, NLM_F_ACK, NLM_F_CREATE, NLM_F_EXCL, NLM_F_REPLACE, NLM_F_REQUEST, }; use netlink_packet_route::{ route::RouteHeader, rule::{RuleAction, RuleAttribute, RuleMessage}, AddressFamily, RouteNetlinkMessage, }; use crate::{try_nl, Error, Handle}; /// A request to create a new rule. This is equivalent to the `ip rule add` /// command. pub struct RuleAddRequest { handle: Handle, message: RuleMessage, replace: bool, _phantom: PhantomData, } impl RuleAddRequest { pub(crate) fn new(handle: Handle) -> Self { let mut message = RuleMessage::default(); message.header.table = RouteHeader::RT_TABLE_MAIN; message.header.action = RuleAction::Unspec; RuleAddRequest { handle, message, replace: false, _phantom: Default::default(), } } /// Sets the input interface name. pub fn input_interface(mut self, ifname: String) -> Self { self.message.attributes.push(RuleAttribute::Iifname(ifname)); self } /// Sets the output interface name. pub fn output_interface(mut self, ifname: String) -> Self { self.message.attributes.push(RuleAttribute::Oifname(ifname)); self } /// Sets the rule table. /// /// Default is main rule table. #[deprecated(note = "Please use `table_id` instead")] pub fn table(mut self, table: u8) -> Self { self.message.header.table = table; self } /// Sets the rule table ID. /// /// Default is main rule table. pub fn table_id(mut self, table: u32) -> Self { if table > 255 { self.message.attributes.push(RuleAttribute::Table(table)); } else { self.message.header.table = table as u8; } self } /// Set the tos. pub fn tos(mut self, tos: u8) -> Self { self.message.header.tos = tos; self } /// Set action. pub fn action(mut self, action: RuleAction) -> Self { self.message.header.action = action; self } /// Set the priority. pub fn priority(mut self, priority: u32) -> Self { self.message .attributes .push(RuleAttribute::Priority(priority)); self } /// Set the fwmark pub fn fw_mark(mut self, fw_mark: u32) -> Self { self.message.attributes.push(RuleAttribute::FwMark(fw_mark)); self } /// Build an IP v4 rule pub fn v4(mut self) -> RuleAddRequest { self.message.header.family = AddressFamily::Inet; RuleAddRequest { handle: self.handle, message: self.message, replace: false, _phantom: Default::default(), } } /// Build an IP v6 rule pub fn v6(mut self) -> RuleAddRequest { self.message.header.family = AddressFamily::Inet6; RuleAddRequest { handle: self.handle, message: self.message, replace: false, _phantom: Default::default(), } } /// Replace existing matching rule. pub fn replace(self) -> Self { Self { replace: true, ..self } } /// Execute the request. pub async fn execute(self) -> Result<(), Error> { let RuleAddRequest { mut handle, message, replace, .. } = self; let mut req = NetlinkMessage::from(RouteNetlinkMessage::NewRule(message)); let replace = if replace { NLM_F_REPLACE } else { NLM_F_EXCL }; req.header.flags = NLM_F_REQUEST | NLM_F_ACK | replace | NLM_F_CREATE; let mut response = handle.request(req)?; while let Some(message) = response.next().await { try_nl!(message); } Ok(()) } pub fn message_mut(&mut self) -> &mut RuleMessage { &mut self.message } } impl RuleAddRequest { /// Sets the source address prefix. pub fn source_prefix(mut self, addr: Ipv4Addr, prefix_length: u8) -> Self { self.message.header.src_len = prefix_length; self.message .attributes .push(RuleAttribute::Source(addr.into())); self } /// Sets the destination address prefix. pub fn destination_prefix( mut self, addr: Ipv4Addr, prefix_length: u8, ) -> Self { self.message.header.dst_len = prefix_length; self.message .attributes .push(RuleAttribute::Destination(addr.into())); self } } impl RuleAddRequest { /// Sets the source address prefix. pub fn source_prefix(mut self, addr: Ipv6Addr, prefix_length: u8) -> Self { self.message.header.src_len = prefix_length; self.message .attributes .push(RuleAttribute::Source(addr.into())); self } /// Sets the destination address prefix. pub fn destination_prefix( mut self, addr: Ipv6Addr, prefix_length: u8, ) -> Self { self.message.header.dst_len = prefix_length; self.message .attributes .push(RuleAttribute::Destination(addr.into())); self } } rtnetlink-0.14.1/src/rule/del.rs000064400000000000000000000020121046102023000145640ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::StreamExt; use netlink_packet_core::{NetlinkMessage, NLM_F_ACK, NLM_F_REQUEST}; use netlink_packet_route::{rule::RuleMessage, RouteNetlinkMessage}; use crate::{try_nl, Error, Handle}; pub struct RuleDelRequest { handle: Handle, message: RuleMessage, } impl RuleDelRequest { pub(crate) fn new(handle: Handle, message: RuleMessage) -> Self { RuleDelRequest { handle, message } } /// Execute the request pub async fn execute(self) -> Result<(), Error> { let RuleDelRequest { mut handle, message, } = self; let mut req = NetlinkMessage::from(RouteNetlinkMessage::DelRule(message)); req.header.flags = NLM_F_REQUEST | NLM_F_ACK; let mut response = handle.request(req)?; while let Some(msg) = response.next().await { try_nl!(msg); } Ok(()) } pub fn message_mut(&mut self) -> &mut RuleMessage { &mut self.message } } rtnetlink-0.14.1/src/rule/get.rs000064400000000000000000000031541046102023000146070ustar 00000000000000// SPDX-License-Identifier: MIT use futures::{ future::{self, Either}, stream::{StreamExt, TryStream}, FutureExt, }; use netlink_packet_core::{NetlinkMessage, NLM_F_DUMP, NLM_F_REQUEST}; use netlink_packet_route::{ route::RouteHeader, rule::{RuleAction, RuleMessage}, RouteNetlinkMessage, }; use crate::{try_rtnl, Error, Handle, IpVersion}; pub struct RuleGetRequest { handle: Handle, message: RuleMessage, } impl RuleGetRequest { pub(crate) fn new(handle: Handle, ip_version: IpVersion) -> Self { let mut message = RuleMessage::default(); message.header.family = ip_version.family(); message.header.dst_len = 0; message.header.src_len = 0; message.header.tos = 0; message.header.action = RuleAction::Unspec; message.header.table = RouteHeader::RT_TABLE_UNSPEC; RuleGetRequest { handle, message } } pub fn message_mut(&mut self) -> &mut RuleMessage { &mut self.message } pub fn execute(self) -> impl TryStream { let RuleGetRequest { mut handle, message, } = self; let mut req = NetlinkMessage::from(RouteNetlinkMessage::GetRule(message)); req.header.flags = NLM_F_REQUEST | NLM_F_DUMP; match handle.request(req) { Ok(response) => Either::Left(response.map(move |msg| { Ok(try_rtnl!(msg, RouteNetlinkMessage::NewRule)) })), Err(e) => Either::Right( future::err::(e).into_stream(), ), } } } rtnetlink-0.14.1/src/rule/handle.rs000064400000000000000000000015131046102023000152600ustar 00000000000000// SPDX-License-Identifier: MIT use crate::{ Handle, IpVersion, RuleAddRequest, RuleDelRequest, RuleGetRequest, }; use netlink_packet_route::rule::RuleMessage; pub struct RuleHandle(Handle); impl RuleHandle { pub fn new(handle: Handle) -> Self { RuleHandle(handle) } /// Retrieve the list of route rule entries (equivalent to `ip rule show`) pub fn get(&self, ip_version: IpVersion) -> RuleGetRequest { RuleGetRequest::new(self.0.clone(), ip_version) } /// Add a route rule entry (equivalent to `ip rule add`) pub fn add(&self) -> RuleAddRequest { RuleAddRequest::new(self.0.clone()) } /// Delete the given route rule entry (equivalent to `ip rule del`) pub fn del(&self, rule: RuleMessage) -> RuleDelRequest { RuleDelRequest::new(self.0.clone(), rule) } } rtnetlink-0.14.1/src/rule/mod.rs000064400000000000000000000002461046102023000146060ustar 00000000000000// SPDX-License-Identifier: MIT mod handle; pub use self::handle::*; mod add; pub use self::add::*; mod del; pub use self::del::*; mod get; pub use self::get::*; rtnetlink-0.14.1/src/traffic_control/add_filter.rs000064400000000000000000000263571046102023000203460ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::StreamExt; use netlink_packet_core::{NetlinkMessage, NLM_F_ACK, NLM_F_REQUEST}; use netlink_packet_route::{ tc::{ TcAction, TcActionAttribute, TcActionGeneric, TcActionMirror, TcActionMirrorOption, TcActionOption, TcActionType, TcAttribute, TcFilterU32, TcFilterU32Option, TcHandle, TcHeader, TcMessage, TcMirror, TcMirrorActionType, TcOption, TcU32Key, TcU32Selector, TcU32SelectorFlag, }, RouteNetlinkMessage, }; use crate::{try_nl, Error, Handle}; pub struct TrafficFilterNewRequest { handle: Handle, message: TcMessage, flags: u16, } impl TrafficFilterNewRequest { pub(crate) fn new(handle: Handle, ifindex: i32, flags: u16) -> Self { Self { handle, message: TcMessage::with_index(ifindex), flags: NLM_F_REQUEST | flags, } } /// Execute the request pub async fn execute(self) -> Result<(), Error> { let Self { mut handle, message, flags, } = self; let mut req = NetlinkMessage::from( RouteNetlinkMessage::NewTrafficFilter(message), ); req.header.flags = NLM_F_ACK | flags; let mut response = handle.request(req)?; while let Some(message) = response.next().await { try_nl!(message); } Ok(()) } /// Set interface index. /// Equivalent to `dev STRING`, dev and block are mutually exlusive. pub fn index(mut self, index: i32) -> Self { self.message.header.index = index; self } /// Set block index. /// Equivalent to `block BLOCK_INDEX`. pub fn block(mut self, block_index: u32) -> Self { self.message.header.index = TcHeader::TCM_IFINDEX_MAGIC_BLOCK as i32; self.message.header.parent = block_index.into(); self } /// Set parent. /// Equivalent to `[ root | ingress | egress | parent CLASSID ]` /// command args. They are mutually exclusive. pub fn parent(mut self, parent: u32) -> Self { self.message.header.parent = parent.into(); self } /// Set parent to root. pub fn root(mut self) -> Self { self.message.header.parent = TcHandle::ROOT; self } /// Set parent to ingress. pub fn ingress(mut self) -> Self { self.message.header.parent = TcHandle { major: 0xffff, minor: TcHandle::MIN_INGRESS, }; self } /// Set parent to egress. pub fn egress(mut self) -> Self { self.message.header.parent = TcHandle { major: 0xffff, minor: TcHandle::MIN_EGRESS, }; self } /// Set priority. /// Equivalent to `priority PRIO` or `pref PRIO`. pub fn priority(mut self, priority: u16) -> Self { self.message.header.info = u32::from(TcHandle { major: priority, minor: priority, }); self } /// Set protocol. /// Equivalent to `protocol PROT`. /// Default: ETH_P_ALL 0x0003, see llproto_names at iproute2/lib/ll_proto.c. pub fn protocol(mut self, protocol: u16) -> Self { self.message.header.info = u32::from(TcHandle { major: (self.message.header.info >> 16) as u16, minor: protocol, }); self } /// The 32bit filter allows to match arbitrary bitfields in the packet. /// Equivalent to `tc filter ... u32`. pub fn u32(mut self, options: &[TcFilterU32Option]) -> Result { if self .message .attributes .iter() .any(|nla| matches!(nla, TcAttribute::Kind(_))) { return Err(Error::InvalidNla( "message kind has already been set.".to_string(), )); } self.message .attributes .push(TcAttribute::Kind(TcFilterU32::KIND.to_string())); let mut nla_opts = Vec::new(); for opt in options { nla_opts.push(TcOption::U32(opt.clone())); } self.message.attributes.push(TcAttribute::Options(nla_opts)); Ok(self) } /// Use u32 to implement traffic redirect. /// Equivalent to /// `tc filter add [dev source] [parent ffff:] [protocol all] \ /// u32 match u8 0 0 action mirred egress redirect dev dest` /// You need to set the `parent` and `protocol` before call redirect. pub fn redirect(self, dst_index: u32) -> Result { let mut sel_na = TcU32Selector::default(); sel_na.flags = vec![TcU32SelectorFlag::Terminal]; sel_na.nkeys = 1; sel_na.keys = vec![TcU32Key::default()]; let mut tc_mirror_nla = TcMirror::default(); tc_mirror_nla.generic = TcActionGeneric::default(); tc_mirror_nla.generic.action = TcActionType::Stolen; tc_mirror_nla.eaction = TcMirrorActionType::EgressRedir; tc_mirror_nla.ifindex = dst_index; let mut action_nla = TcAction::default(); action_nla.attributes = vec![ TcActionAttribute::Kind(TcActionMirror::KIND.to_string()), TcActionAttribute::Options(vec![TcActionOption::Mirror( TcActionMirrorOption::Parms(tc_mirror_nla), )]), ]; let u32_nla = vec![ TcFilterU32Option::Selector(sel_na), TcFilterU32Option::Action(vec![action_nla]), ]; self.u32(&u32_nla) } } #[cfg(test)] mod test { use std::{fs::File, os::fd::AsFd, path::Path}; use futures::stream::TryStreamExt; use netlink_packet_route::{ link::LinkMessage, tc::{ TcAttribute, TcFilterU32, TcFilterU32Option, TcOption, TcU32Key, TcU32SelectorFlag, }, }; use nix::sched::{setns, CloneFlags}; use tokio::runtime::Runtime; use crate::{ new_connection, Handle, NetworkNamespace, NETNS_PATH, SELF_NS_PATH, }; const TEST_NS: &str = "netlink_test_filter_ns"; const TEST_VETH_1: &str = "test_veth_1"; const TEST_VETH_2: &str = "test_veth_2"; struct Netns { path: String, _cur: File, last: File, } impl Netns { async fn new(path: &str) -> Self { // record current ns let last = File::open(Path::new(SELF_NS_PATH)).unwrap(); // create new ns NetworkNamespace::add(path.to_string()).await.unwrap(); // entry new ns let ns_path = Path::new(NETNS_PATH); let file = File::open(ns_path.join(path)).unwrap(); setns(file.as_fd(), CloneFlags::CLONE_NEWNET).unwrap(); Self { path: path.to_string(), _cur: file, last, } } } impl Drop for Netns { fn drop(&mut self) { println!("exit ns: {}", self.path); setns(self.last.as_fd(), CloneFlags::CLONE_NEWNET).unwrap(); let ns_path = Path::new(NETNS_PATH).join(&self.path); nix::mount::umount2(&ns_path, nix::mount::MntFlags::MNT_DETACH) .unwrap(); nix::unistd::unlink(&ns_path).unwrap(); // _cur File will be closed auto // Since there is no async drop, NetworkNamespace::del cannot be // called here. Dummy interface will be deleted // automatically after netns is deleted. } } async fn setup_env() -> (Handle, LinkMessage, LinkMessage, Netns) { let netns = Netns::new(TEST_NS).await; // Notice: The Handle can only be created after the setns, so that the // Handle is the connection within the new ns. let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); handle .link() .add() .veth(TEST_VETH_1.to_string(), TEST_VETH_2.to_string()) .execute() .await .unwrap(); let mut links = handle .link() .get() .match_name(TEST_VETH_1.to_string()) .execute(); let link1 = links.try_next().await.unwrap(); links = handle .link() .get() .match_name(TEST_VETH_2.to_string()) .execute(); let link2 = links.try_next().await.unwrap(); (handle, link1.unwrap(), link2.unwrap(), netns) } async fn test_async_new_filter() { let (handle, test1, test2, _netns) = setup_env().await; handle .qdisc() .add(test1.header.index as i32) .ingress() .execute() .await .unwrap(); handle .qdisc() .add(test2.header.index as i32) .ingress() .execute() .await .unwrap(); handle .traffic_filter(test1.header.index as i32) .add() .parent(0xffff0000) .protocol(0x0003) .redirect(test2.header.index) .unwrap() .execute() .await .unwrap(); // Verify that attempting to set 2 redirects causes and error assert!(handle .traffic_filter(test1.header.index as i32) .add() .parent(0xffff0000) .protocol(0x0003) .redirect(test2.header.index) .unwrap() .redirect(test1.header.index) .is_err()); let mut filters_iter = handle .traffic_filter(test1.header.index as i32) .get() .root() .execute(); let mut found = false; while let Some(nl_msg) = filters_iter.try_next().await.unwrap() { //filters.push(nl_msg.clone()); if nl_msg.header.handle == 0x80000800.into() { let mut iter = nl_msg.attributes.iter(); assert_eq!( iter.next().unwrap(), &TcAttribute::Kind(TcFilterU32::KIND.to_string()), ); assert!(matches!(iter.next().unwrap(), &TcAttribute::Chain(_))); // TCA_OPTIONS let nla = iter.next().unwrap(); let filter = if let TcAttribute::Options(f) = nla { f } else { panic!("expect options nla"); }; let mut fi = filter.iter(); let fa = fi.next().unwrap(); let ua = if let TcOption::U32(u) = fa { u } else { panic!("expect u32 nla"); }; // TCA_U32_SEL let sel = if let TcFilterU32Option::Selector(s) = ua { s } else { panic!("expect sel nla"); }; assert_eq!(sel.flags, vec![TcU32SelectorFlag::Terminal]); assert_eq!(sel.nkeys, 1); assert_eq!(sel.keys.len(), 1); assert_eq!(sel.keys[0], TcU32Key::default()); found = true; break; } } if !found { panic!("not found :{} filter.", test1.header.index); } } #[test] fn test_new_filter() { Runtime::new().unwrap().block_on(test_async_new_filter()); } } rtnetlink-0.14.1/src/traffic_control/add_qdisc.rs000064400000000000000000000134401046102023000201510ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::StreamExt; use netlink_packet_core::{NetlinkMessage, NLM_F_ACK, NLM_F_REQUEST}; use netlink_packet_route::{ tc::{TcAttribute, TcHandle, TcMessage}, RouteNetlinkMessage, }; use crate::{try_nl, Error, Handle}; pub struct QDiscNewRequest { handle: Handle, message: TcMessage, flags: u16, } impl QDiscNewRequest { pub(crate) fn new(handle: Handle, message: TcMessage, flags: u16) -> Self { Self { handle, message, flags: NLM_F_REQUEST | flags, } } /// Execute the request pub async fn execute(self) -> Result<(), Error> { let Self { mut handle, message, flags, } = self; let mut req = NetlinkMessage::from( RouteNetlinkMessage::NewQueueDiscipline(message), ); req.header.flags = NLM_F_ACK | flags; let mut response = handle.request(req)?; while let Some(message) = response.next().await { try_nl!(message); } Ok(()) } /// Set handle, pub fn handle(mut self, major: u16, minor: u16) -> Self { self.message.header.handle = TcHandle { major, minor }; self } /// Set parent to root. pub fn root(mut self) -> Self { self.message.header.parent = TcHandle::ROOT; self } /// Set parent pub fn parent(mut self, parent: u32) -> Self { self.message.header.parent = parent.into(); self } /// New a ingress qdisc pub fn ingress(mut self) -> Self { self.message.header.parent = TcHandle::INGRESS; self.message.header.handle = TcHandle::from(0xffff0000); self.message .attributes .push(TcAttribute::Kind("ingress".to_string())); self } } #[cfg(test)] mod test { use std::{fs::File, os::fd::AsFd, path::Path}; use futures::stream::TryStreamExt; use nix::sched::{setns, CloneFlags}; use tokio::runtime::Runtime; use super::*; use crate::{new_connection, NetworkNamespace, NETNS_PATH, SELF_NS_PATH}; use netlink_packet_route::{ link::LinkMessage, tc::TcAttribute, AddressFamily, }; const TEST_NS: &str = "netlink_test_qdisc_ns"; const TEST_DUMMY: &str = "test_dummy"; struct Netns { path: String, _cur: File, last: File, } impl Netns { async fn new(path: &str) -> Self { // record current ns let last = File::open(Path::new(SELF_NS_PATH)).unwrap(); // create new ns NetworkNamespace::add(path.to_string()).await.unwrap(); // entry new ns let ns_path = Path::new(NETNS_PATH); let file = File::open(ns_path.join(path)).unwrap(); setns(file.as_fd(), CloneFlags::CLONE_NEWNET).unwrap(); Self { path: path.to_string(), _cur: file, last, } } } impl Drop for Netns { fn drop(&mut self) { println!("exit ns: {}", self.path); setns(self.last.as_fd(), CloneFlags::CLONE_NEWNET).unwrap(); let ns_path = Path::new(NETNS_PATH).join(&self.path); nix::mount::umount2(&ns_path, nix::mount::MntFlags::MNT_DETACH) .unwrap(); nix::unistd::unlink(&ns_path).unwrap(); // _cur File will be closed auto // Since there is no async drop, NetworkNamespace::del cannot be // called here. Dummy interface will be deleted // automatically after netns is deleted. } } async fn setup_env() -> (Handle, LinkMessage, Netns) { let netns = Netns::new(TEST_NS).await; // Notice: The Handle can only be created after the setns, so that the // Handle is the connection within the new ns. let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); handle .link() .add() .dummy(TEST_DUMMY.to_string()) .execute() .await .unwrap(); let mut links = handle .link() .get() .match_name(TEST_DUMMY.to_string()) .execute(); let link = links.try_next().await.unwrap(); (handle, link.unwrap(), netns) } async fn test_async_new_qdisc() { let (handle, test_link, _netns) = setup_env().await; handle .qdisc() .add(test_link.header.index as i32) .ingress() .execute() .await .unwrap(); let mut qdiscs_iter = handle .qdisc() .get() .index(test_link.header.index as i32) .ingress() .execute(); let mut found = false; while let Some(nl_msg) = qdiscs_iter.try_next().await.unwrap() { if nl_msg.header.index == test_link.header.index as i32 && nl_msg.header.handle == 0xffff0000.into() { assert_eq!(nl_msg.header.family, AddressFamily::Unspec); assert_eq!(nl_msg.header.handle, 0xffff0000.into()); assert_eq!(nl_msg.header.parent, TcHandle::INGRESS); assert_eq!(nl_msg.header.info, 1); // refcount assert_eq!( nl_msg.attributes[0], TcAttribute::Kind("ingress".to_string()) ); assert_eq!(nl_msg.attributes[2], TcAttribute::HwOffload(0)); found = true; break; } } if !found { panic!("not found dev:{} qdisc.", test_link.header.index); } } #[test] fn test_new_qdisc() { Runtime::new().unwrap().block_on(test_async_new_qdisc()); } } rtnetlink-0.14.1/src/traffic_control/del_qdisc.rs000064400000000000000000000021131046102023000201600ustar 00000000000000// SPDX-License-Identifier: MIT use futures::StreamExt; use netlink_packet_core::{NetlinkMessage, NLM_F_ACK, NLM_F_REQUEST}; use netlink_packet_route::{tc::TcMessage, RouteNetlinkMessage}; use crate::{try_nl, Error, Handle}; pub struct QDiscDelRequest { handle: Handle, message: TcMessage, } impl QDiscDelRequest { pub(crate) fn new(handle: Handle, message: TcMessage) -> Self { QDiscDelRequest { handle, message } } // Execute the request pub async fn execute(self) -> Result<(), Error> { let QDiscDelRequest { mut handle, message, } = self; let mut req = NetlinkMessage::from( RouteNetlinkMessage::DelQueueDiscipline(message), ); req.header.flags = NLM_F_REQUEST | NLM_F_ACK; let mut response = handle.request(req)?; while let Some(message) = response.next().await { try_nl!(message) } Ok(()) } /// Return a mutable reference to the request pub fn message_mut(&mut self) -> &mut TcMessage { &mut self.message } } rtnetlink-0.14.1/src/traffic_control/get.rs000064400000000000000000000111771046102023000170220ustar 00000000000000// SPDX-License-Identifier: MIT use futures::{ future::{self, Either}, stream::{StreamExt, TryStream}, FutureExt, }; use netlink_packet_core::{NetlinkMessage, NLM_F_DUMP, NLM_F_REQUEST}; use netlink_packet_route::{ tc::{TcHandle, TcMessage}, RouteNetlinkMessage, }; use crate::{try_rtnl, Error, Handle}; pub struct QDiscGetRequest { handle: Handle, message: TcMessage, } impl QDiscGetRequest { pub(crate) fn new(handle: Handle) -> Self { QDiscGetRequest { handle, message: TcMessage::default(), } } /// Execute the request pub fn execute(self) -> impl TryStream { let QDiscGetRequest { mut handle, message, } = self; let mut req = NetlinkMessage::from( RouteNetlinkMessage::GetQueueDiscipline(message), ); req.header.flags = NLM_F_REQUEST | NLM_F_DUMP; match handle.request(req) { Ok(response) => Either::Left(response.map(move |msg| { Ok(try_rtnl!(msg, RouteNetlinkMessage::NewQueueDiscipline)) })), Err(e) => { Either::Right(future::err::(e).into_stream()) } } } pub fn index(mut self, index: i32) -> Self { self.message.header.index = index; self } /// Get ingress qdisc pub fn ingress(mut self) -> Self { self.message.header.parent = TcHandle::INGRESS; self } } pub struct TrafficClassGetRequest { handle: Handle, message: TcMessage, } impl TrafficClassGetRequest { pub(crate) fn new(handle: Handle, ifindex: i32) -> Self { let mut message = TcMessage::default(); message.header.index = ifindex; TrafficClassGetRequest { handle, message } } /// Execute the request pub fn execute(self) -> impl TryStream { let TrafficClassGetRequest { mut handle, message, } = self; let mut req = NetlinkMessage::from(RouteNetlinkMessage::GetTrafficClass(message)); req.header.flags = NLM_F_REQUEST | NLM_F_DUMP; match handle.request(req) { Ok(response) => Either::Left(response.map(move |msg| { Ok(try_rtnl!(msg, RouteNetlinkMessage::NewTrafficClass)) })), Err(e) => { Either::Right(future::err::(e).into_stream()) } } } } pub struct TrafficFilterGetRequest { handle: Handle, message: TcMessage, } impl TrafficFilterGetRequest { pub(crate) fn new(handle: Handle, ifindex: i32) -> Self { let mut message = TcMessage::default(); message.header.index = ifindex; TrafficFilterGetRequest { handle, message } } /// Execute the request pub fn execute(self) -> impl TryStream { let TrafficFilterGetRequest { mut handle, message, } = self; let mut req = NetlinkMessage::from( RouteNetlinkMessage::GetTrafficFilter(message), ); req.header.flags = NLM_F_REQUEST | NLM_F_DUMP; match handle.request(req) { Ok(response) => Either::Left(response.map(move |msg| { Ok(try_rtnl!(msg, RouteNetlinkMessage::NewTrafficFilter)) })), Err(e) => { Either::Right(future::err::(e).into_stream()) } } } /// Set parent to root. pub fn root(mut self) -> Self { self.message.header.parent = TcHandle::ROOT; self } } pub struct TrafficChainGetRequest { handle: Handle, message: TcMessage, } impl TrafficChainGetRequest { pub(crate) fn new(handle: Handle, ifindex: i32) -> Self { let mut message = TcMessage::default(); message.header.index = ifindex; TrafficChainGetRequest { handle, message } } /// Execute the request pub fn execute(self) -> impl TryStream { let TrafficChainGetRequest { mut handle, message, } = self; let mut req = NetlinkMessage::from(RouteNetlinkMessage::GetTrafficChain(message)); req.header.flags = NLM_F_REQUEST | NLM_F_DUMP; match handle.request(req) { Ok(response) => Either::Left(response.map(move |msg| { Ok(try_rtnl!(msg, RouteNetlinkMessage::NewTrafficChain)) })), Err(e) => { Either::Right(future::err::(e).into_stream()) } } } } rtnetlink-0.14.1/src/traffic_control/handle.rs000064400000000000000000000106141046102023000174710ustar 00000000000000// SPDX-License-Identifier: MIT use super::{ QDiscDelRequest, QDiscGetRequest, QDiscNewRequest, TrafficChainGetRequest, TrafficClassGetRequest, TrafficFilterGetRequest, TrafficFilterNewRequest, }; use crate::Handle; use netlink_packet_core::{NLM_F_CREATE, NLM_F_EXCL, NLM_F_REPLACE}; use netlink_packet_route::tc::TcMessage; pub struct QDiscHandle(Handle); impl QDiscHandle { pub fn new(handle: Handle) -> Self { QDiscHandle(handle) } /// Retrieve the list of qdisc (equivalent to `tc qdisc show`) pub fn get(&mut self) -> QDiscGetRequest { QDiscGetRequest::new(self.0.clone()) } /// Create a new qdisc, don't replace if the object already exists. /// ( equivalent to `tc qdisc add dev STRING`) pub fn add(&mut self, index: i32) -> QDiscNewRequest { let msg = TcMessage::with_index(index); QDiscNewRequest::new(self.0.clone(), msg, NLM_F_EXCL | NLM_F_CREATE) } /// Change the qdisc, the handle cannot be changed and neither can the /// parent. In other words, change cannot move a node. /// ( equivalent to `tc qdisc change dev STRING`) pub fn change(&mut self, index: i32) -> QDiscNewRequest { let msg = TcMessage::with_index(index); QDiscNewRequest::new(self.0.clone(), msg, 0) } /// Replace existing matching qdisc, create qdisc if it doesn't already /// exist. ( equivalent to `tc qdisc replace dev STRING`) pub fn replace(&mut self, index: i32) -> QDiscNewRequest { let msg = TcMessage::with_index(index); QDiscNewRequest::new(self.0.clone(), msg, NLM_F_CREATE | NLM_F_REPLACE) } /// Performs a replace where the node must exist already. /// ( equivalent to `tc qdisc link dev STRING`) pub fn link(&mut self, index: i32) -> QDiscNewRequest { let msg = TcMessage::with_index(index); QDiscNewRequest::new(self.0.clone(), msg, NLM_F_REPLACE) } /// Delete the qdisc ( equivalent to `tc qdisc del dev STRING`) pub fn del(&mut self, index: i32) -> QDiscDelRequest { let msg = TcMessage::with_index(index); QDiscDelRequest::new(self.0.clone(), msg) } } pub struct TrafficClassHandle { handle: Handle, ifindex: i32, } impl TrafficClassHandle { pub fn new(handle: Handle, ifindex: i32) -> Self { TrafficClassHandle { handle, ifindex } } /// Retrieve the list of traffic class (equivalent to /// `tc class show dev `) pub fn get(&mut self) -> TrafficClassGetRequest { TrafficClassGetRequest::new(self.handle.clone(), self.ifindex) } } pub struct TrafficFilterHandle { handle: Handle, ifindex: i32, } impl TrafficFilterHandle { pub fn new(handle: Handle, ifindex: i32) -> Self { TrafficFilterHandle { handle, ifindex } } /// Retrieve the list of filter (equivalent to /// `tc filter show dev `) pub fn get(&mut self) -> TrafficFilterGetRequest { TrafficFilterGetRequest::new(self.handle.clone(), self.ifindex) } /// Add a filter to a node, don't replace if the object already exists. /// ( equivalent to `tc filter add dev STRING`) pub fn add(&mut self) -> TrafficFilterNewRequest { TrafficFilterNewRequest::new( self.handle.clone(), self.ifindex, NLM_F_EXCL | NLM_F_CREATE, ) } /// Change the filter, the handle cannot be changed and neither can the /// parent. In other words, change cannot move a node. /// ( equivalent to `tc filter change dev STRING`) pub fn change(&mut self) -> TrafficFilterNewRequest { TrafficFilterNewRequest::new(self.handle.clone(), self.ifindex, 0) } /// Replace existing matching filter, create filter if it doesn't already /// exist. ( equivalent to `tc filter replace dev STRING`) pub fn replace(&mut self) -> TrafficFilterNewRequest { TrafficFilterNewRequest::new( self.handle.clone(), self.ifindex, NLM_F_CREATE, ) } } pub struct TrafficChainHandle { handle: Handle, ifindex: i32, } impl TrafficChainHandle { pub fn new(handle: Handle, ifindex: i32) -> Self { TrafficChainHandle { handle, ifindex } } /// Retrieve the list of chain (equivalent to /// `tc chain show dev `) pub fn get(&mut self) -> TrafficChainGetRequest { TrafficChainGetRequest::new(self.handle.clone(), self.ifindex) } } rtnetlink-0.14.1/src/traffic_control/mod.rs000064400000000000000000000004041046102023000170110ustar 00000000000000// SPDX-License-Identifier: MIT mod handle; pub use self::handle::*; mod get; pub use self::get::*; mod add_qdisc; pub use self::add_qdisc::*; mod del_qdisc; pub use self::del_qdisc::*; mod add_filter; pub use self::add_filter::*; #[cfg(test)] mod test; rtnetlink-0.14.1/src/traffic_control/test.rs000064400000000000000000000220171046102023000172150ustar 00000000000000// SPDX-License-Identifier: MIT use std::process::Command; use futures::stream::TryStreamExt; use netlink_packet_core::ErrorMessage; use netlink_packet_route::{ tc::{TcAttribute, TcMessage}, AddressFamily, }; use tokio::runtime::Runtime; use crate::{new_connection, Error::NetlinkError}; static TEST_DUMMY_NIC: &str = "netlink-test"; async fn _get_qdiscs() -> Vec { let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); let mut qdiscs_iter = handle.qdisc().get().execute(); let mut qdiscs = Vec::new(); while let Some(nl_msg) = qdiscs_iter.try_next().await.unwrap() { qdiscs.push(nl_msg.clone()); } qdiscs } #[test] fn test_get_qdiscs() { let qdiscs = Runtime::new().unwrap().block_on(_get_qdiscs()); let qdisc_of_loopback_nic = &qdiscs[0]; assert_eq!(qdisc_of_loopback_nic.header.family, AddressFamily::Unspec); assert_eq!(qdisc_of_loopback_nic.header.index, 1); assert_eq!(qdisc_of_loopback_nic.header.handle, 0.into()); assert_eq!(qdisc_of_loopback_nic.header.parent, u32::MAX.into()); assert_eq!(qdisc_of_loopback_nic.header.info, 2); // refcount assert_eq!( qdisc_of_loopback_nic.attributes[0], TcAttribute::Kind("noqueue".to_string()) ); assert_eq!( qdisc_of_loopback_nic.attributes[1], TcAttribute::HwOffload(0) ); } async fn _get_tclasses(ifindex: i32) -> Vec { let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); let mut tclasses_iter = handle.traffic_class(ifindex).get().execute(); let mut tclasses = Vec::new(); while let Some(nl_msg) = tclasses_iter.try_next().await.unwrap() { tclasses.push(nl_msg.clone()); } tclasses } // Return 0 for not found fn _get_test_dummy_interface_index() -> i32 { let output = Command::new("ip") .args(["-o", "link", "show", TEST_DUMMY_NIC]) .output() .expect("failed to run ip command"); if !output.status.success() { 0 } else { let line = std::str::from_utf8(&output.stdout).unwrap(); line.split(": ").next().unwrap().parse::().unwrap() } } fn _add_test_dummy_interface() -> i32 { if _get_test_dummy_interface_index() == 0 { let output = Command::new("ip") .args(["link", "add", TEST_DUMMY_NIC, "type", "dummy"]) .output() .expect("failed to run ip command"); if !output.status.success() { eprintln!( "Failed to create dummy interface {TEST_DUMMY_NIC} : {output:?}" ); } assert!(output.status.success()); } _get_test_dummy_interface_index() } fn _remove_test_dummy_interface() { let output = Command::new("ip") .args(["link", "del", TEST_DUMMY_NIC]) .output() .expect("failed to run ip command"); if !output.status.success() { eprintln!( "Failed to remove dummy interface {TEST_DUMMY_NIC} : {output:?}" ); } assert!(output.status.success()); } fn _add_test_tclass_to_dummy() { let output = Command::new("tc") .args([ "qdisc", "add", "dev", TEST_DUMMY_NIC, "root", "handle", "1:", "htb", "default", "6", ]) .output() .expect("failed to run tc command"); if !output.status.success() { eprintln!( "Failed to add qdisc to dummy interface {TEST_DUMMY_NIC} : {output:?}" ); } assert!(output.status.success()); let output = Command::new("tc") .args([ "class", "add", "dev", TEST_DUMMY_NIC, "parent", "1:", "classid", "1:1", "htb", "rate", "10mbit", "ceil", "10mbit", ]) .output() .expect("failed to run tc command"); if !output.status.success() { eprintln!( "Failed to add traffic class to dummy interface {TEST_DUMMY_NIC}: {output:?}" ); } assert!(output.status.success()); } fn _add_test_filter_to_dummy() { let output = Command::new("tc") .args([ "filter", "add", "dev", TEST_DUMMY_NIC, "parent", "1:", "basic", "match", "meta(priority eq 6)", "classid", "1:1", ]) .output() .expect("failed to run tc command"); if !output.status.success() { eprintln!("Failed to add trafice filter to lo: {output:?}"); } assert!(output.status.success()); } fn _remove_test_tclass_from_dummy() { Command::new("tc") .args([ "class", "del", "dev", TEST_DUMMY_NIC, "parent", "1:", "classid", "1:1", ]) .status() .unwrap_or_else(|_| { panic!( "failed to remove tclass from dummy interface {}", TEST_DUMMY_NIC ) }); Command::new("tc") .args(["qdisc", "del", "dev", TEST_DUMMY_NIC, "root"]) .status() .unwrap_or_else(|_| { panic!( "failed to remove qdisc from dummy interface {}", TEST_DUMMY_NIC ) }); } fn _remove_test_filter_from_dummy() { Command::new("tc") .args(["filter", "del", "dev", TEST_DUMMY_NIC]) .status() .unwrap_or_else(|_| { panic!( "failed to remove filter from dummy interface {}", TEST_DUMMY_NIC ) }); } async fn _get_filters(ifindex: i32) -> Vec { let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); let mut filters_iter = handle.traffic_filter(ifindex).get().execute(); let mut filters = Vec::new(); while let Some(nl_msg) = filters_iter.try_next().await.unwrap() { filters.push(nl_msg.clone()); } filters } async fn _get_chains(ifindex: i32) -> Vec { let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); let mut chains_iter = handle.traffic_chain(ifindex).get().execute(); let mut chains = Vec::new(); // The traffic control chain is only supported by kernel 4.19+, // hence we might get error: 95 Operation not supported loop { match chains_iter.try_next().await { Ok(Some(nl_msg)) => { chains.push(nl_msg.clone()); } Ok(None) => { break; } Err(NetlinkError(ErrorMessage { code, header: _, .. })) => { assert_eq!(code, std::num::NonZeroI32::new(-95)); eprintln!( "The chain in traffic control is not supported, \ please upgrade your kernel" ); } _ => {} } } chains } // The `cargo test` by default run all tests in parallel, in stead // of create random named veth/dummy for test, just place class, filter, and // chain query test in one test case is much simpler. #[test] #[cfg_attr(not(feature = "test_as_root"), ignore)] fn test_get_traffic_classes_filters_and_chains() { let ifindex = _add_test_dummy_interface(); _add_test_tclass_to_dummy(); _add_test_filter_to_dummy(); let tclasses = Runtime::new().unwrap().block_on(_get_tclasses(ifindex)); let filters = Runtime::new().unwrap().block_on(_get_filters(ifindex)); let chains = Runtime::new().unwrap().block_on(_get_chains(ifindex)); _remove_test_filter_from_dummy(); _remove_test_tclass_from_dummy(); _remove_test_dummy_interface(); assert_eq!(tclasses.len(), 1); let tclass = &tclasses[0]; assert_eq!(tclass.header.family, AddressFamily::Unspec); assert_eq!(tclass.header.index, ifindex); assert_eq!(tclass.header.parent, u32::MAX.into()); assert_eq!(tclass.attributes[0], TcAttribute::Kind("htb".to_string())); assert_eq!(filters.len(), 2); assert_eq!(filters[0].header.family, AddressFamily::Unspec); assert_eq!(filters[0].header.index, ifindex); assert_eq!(filters[0].header.parent, (u16::MAX as u32 + 1).into()); assert_eq!( filters[0].attributes[0], TcAttribute::Kind("basic".to_string()) ); assert_eq!(filters[1].header.family, AddressFamily::Unspec); assert_eq!(filters[1].header.index, ifindex); assert_eq!(filters[1].header.parent, (u16::MAX as u32 + 1).into()); assert_eq!( filters[1].attributes[0], TcAttribute::Kind("basic".to_string()) ); assert!(chains.len() <= 1); if chains.len() == 1 { assert_eq!(chains[0].header.family, AddressFamily::Unspec); assert_eq!(chains[0].header.index, ifindex); assert_eq!(chains[0].header.parent, (u16::MAX as u32 + 1).into()); assert_eq!(chains[0].attributes[0], TcAttribute::Chain(0),); } }