netlink-packet-route-0.17.0/.cargo_vcs_info.json0000644000000001360000000000100151660ustar { "git": { "sha1": "70b863788401816a98d2ec8da536b98437f6bc1d" }, "path_in_vcs": "" }netlink-packet-route-0.17.0/.github/workflows/clippy-rustfmt.yml000064400000000000000000000011211046102023000230730ustar 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 run: cargo clippy -- -D warnings netlink-packet-route-0.17.0/.github/workflows/license.yml000064400000000000000000000005201046102023000215150ustar 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 netlink-packet-route-0.17.0/.github/workflows/main.yml000064400000000000000000000013721046102023000210250ustar 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 rustup component add llvm-tools-preview - name: Install cargo-llvm-cov uses: taiki-e/install-action@cargo-llvm-cov - name: Test and Generate code coverage run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: files: lcov.info fail_ci_if_error: true netlink-packet-route-0.17.0/.gitignore000064400000000000000000000000461046102023000157460ustar 00000000000000Cargo.lock target vendor/ tags *.swp netlink-packet-route-0.17.0/.licenserc.yaml000064400000000000000000000003711046102023000166700ustar 00000000000000header: license: content: | SPDX-License-Identifier: MIT paths-ignore: - 'target' - '**/*.toml' - '**/*.lock' - '**/*.yml' - '**/*.md' - 'CHANGELOG' - 'LICENSE-MIT' - '.gitignore' comment: on-failure netlink-packet-route-0.17.0/.rustfmt.toml000064400000000000000000000001141046102023000164310ustar 00000000000000max_width = 80 wrap_comments = true reorder_imports = true edition = "2021" netlink-packet-route-0.17.0/CHANGELOG000064400000000000000000000047341046102023000152000ustar 00000000000000# Changelog ## [0.17.0] - 2023-07-10 ### Breaking changes - `InfoVlan::EgressQos(Vec)` changed to `InfoVlan::EgressQos(Vec)`. (2d33edb) - `InfoVlan::IngressQos(Vec)` changed to `InfoVlan::IngressQos(Vec)`. (2d33edb) ### New features - Added rich representation for VLAN QOS mapping. (2d33edb) - Added MacVlan IFLA_MACVLAN_BC_ options. (640be35) ### Bug fixes - N/A ## [0.16.1] - 2023-07-10 ### Breaking changes - N/A ### New features - N/A ### Bug fixes - Use latest rust-netlink crates. (2eda618) ## [0.16.0] - 2023-06-25 ### Breaking changes - Replaced all `slave` to `port`. (bfa1ec3) * `InfoBond::ActiveSlave` -> `InfoBond::ActivePort` * `InfoBond::AllSlavesActive` -> `InfoBond::AllPortsActive` * `InfoBond::PacketsPerSlave` -> `InfoBond::PacketsPerPort` * `SlaveState` -> `BondPortState` * `link_infos::Info::SlaveKind` -> `link_info::Info::PortKind` * `link_infos::Info::SlaveData` -> `link_info::Info::PortData` * `link_infos::InfoSlaveData` -> `link_info::InfoPortData` ### New features - Add support of bond port settings. (83c9689) - Add support of TC matchall filter. (d71b961) - Add egress builder for TcNat type. (62d2411) - Add the nat action to tc. (c80c678) - Add XDP nlas structure support. (75ce74c) ### Bug fixes - Set `NLM_F_NESTED` for `TCA_ACT_OPTIONS` type. (a93b651) ## [0.15.0] - 2023-01-28 ### Breaking changes - Removed these reexports. (8784586) * `netlink_packet_route::ErrorMessage` * `netlink_packet_route::NetlinkBuffer` * `netlink_packet_route::NetlinkHeader` * `netlink_packet_route::NetlinkMessage` * `netlink_packet_route::NetlinkPayload` * `netlink_packet_route::traits` * `netlink_packet_route::DecodeError` - Remove internal fuzz sub-crate. (f2ffa9d) ### New features - N/A ### Bug fixes - N/A ## [0.14.1] - 2023-01-28 ### Breaking changes - N/A ### New features - N/A ### Bug fixes - Bridge VLAN: Fixed incorrect constants. (6994712) ## [0.14.0] - 2023-01-28 ### Breaking changes - All public struct and enum are marked as `non_exhaustive`. Please check https://doc.rust-lang.org/reference/attributes/type_system.html for more detail. (0b98180) ### New features - LinkAddRequest: Allow adding XFRM tunnel with if_id mark. (5507d97) - Bridge: Add support of `IFLA_BR_MCAST_QUERIER_STATE`. (3d91fdf) - LinkAddRequest: Allow adding macvtap on a link. (dabd5dc) ### Bug fixes - Fix panics when using iif or oif with ip rule. (dab602e) netlink-packet-route-0.17.0/Cargo.lock0000644000000474030000000000100131510ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] [[package]] name = "ansi_term" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ "winapi", ] [[package]] name = "anyhow" version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi 0.1.19", "libc", "winapi", ] [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "bitflags", "textwrap", "unicode-width", ] [[package]] name = "criterion" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" dependencies = [ "atty", "cast", "clap", "criterion-plot", "csv", "itertools", "lazy_static", "num-traits", "oorandom", "plotters", "rayon", "regex", "serde", "serde_cbor", "serde_derive", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" dependencies = [ "cast", "itertools", ] [[package]] name = "crossbeam-channel" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-deque" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", "memoffset", "scopeguard", ] [[package]] name = "crossbeam-utils" version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] [[package]] name = "csv" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086" dependencies = [ "csv-core", "itoa", "ryu", "serde", ] [[package]] name = "csv-core" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" dependencies = [ "memchr", ] [[package]] name = "ctor" version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote 1.0.29", "syn 1.0.109", ] [[package]] name = "derive-into-owned" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "576fce04d31d592013a5887ba8d9c3830adff329e5096d7e1eb5e8e61262ca62" dependencies = [ "quote 0.3.15", "syn 0.11.11", ] [[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "either" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "half" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hermit-abi" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "hermit-abi" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" [[package]] name = "js-sys" version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "log" version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] [[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.17.0" dependencies = [ "anyhow", "bitflags", "byteorder", "criterion", "lazy_static", "libc", "netlink-packet-core", "netlink-packet-utils", "netlink-sys", "pcap-file", "pretty_assertions", ] [[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-sys" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" dependencies = [ "bytes", "libc", "log", ] [[package]] name = "num-traits" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi 0.3.2", "libc", ] [[package]] name = "once_cell" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "oorandom" version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "output_vt100" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" dependencies = [ "winapi", ] [[package]] name = "paste" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4b27ab7be369122c218afc2079489cdcb4b517c0a3fc386ff11e1fedfcc2b35" [[package]] name = "pcap-file" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ad13fed1a83120159aea81b265074f21d753d157dd16b10cc3790ecba40a341" dependencies = [ "byteorder", "derive-into-owned", "thiserror", ] [[package]] name = "plotters" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" dependencies = [ "num-traits", "plotters-backend", "plotters-svg", "wasm-bindgen", "web-sys", ] [[package]] name = "plotters-backend" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" [[package]] name = "plotters-svg" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" dependencies = [ "plotters-backend", ] [[package]] name = "pretty_assertions" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cab0e7c02cf376875e9335e0ba1da535775beb5450d21e1dffca068818ed98b" dependencies = [ "ansi_term", "ctor", "diff", "output_vt100", ] [[package]] name = "proc-macro2" version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" [[package]] name = "quote" version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" dependencies = [ "proc-macro2", ] [[package]] name = "rayon" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", "num_cpus", ] [[package]] name = "regex" version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83d3daa6976cffb758ec878f108ba0e062a45b2d6ca3a2cca965338855476caf" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846" [[package]] name = "ryu" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" version = "1.0.171" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" [[package]] name = "serde_cbor" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" dependencies = [ "half", "serde", ] [[package]] name = "serde_derive" version = "1.0.171" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" dependencies = [ "proc-macro2", "quote 1.0.29", "syn 2.0.25", ] [[package]] name = "serde_json" version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "syn" version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" dependencies = [ "quote 0.3.15", "synom", "unicode-xid", ] [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote 1.0.29", "unicode-ident", ] [[package]] name = "syn" version = "2.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" dependencies = [ "proc-macro2", "quote 1.0.29", "unicode-ident", ] [[package]] name = "synom" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" dependencies = [ "unicode-xid", ] [[package]] name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ "unicode-width", ] [[package]] name = "thiserror" version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" dependencies = [ "proc-macro2", "quote 1.0.29", "syn 2.0.25", ] [[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", ] [[package]] name = "unicode-ident" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" [[package]] name = "unicode-width" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "unicode-xid" version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" [[package]] name = "walkdir" version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "wasm-bindgen" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote 1.0.29", "syn 2.0.25", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote 1.0.29", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote 1.0.29", "syn 2.0.25", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" 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.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 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" netlink-packet-route-0.17.0/Cargo.toml0000644000000030120000000000100131600ustar # 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" name = "netlink-packet-route" version = "0.17.0" authors = ["Corentin Henry "] description = "netlink packet types" homepage = "https://github.com/rust-netlink/netlink-packet-route" readme = "README.md" keywords = [ "netlink", "linux", ] license = "MIT" repository = "https://github.com/rust-netlink/netlink-packet-route" [[example]] name = "dump_packet_links" [[bench]] name = "link_message" harness = false [[bench]] name = "rtnetlink_dump" harness = false [dependencies.anyhow] version = "1.0.31" [dependencies.bitflags] version = "1.2.1" [dependencies.byteorder] version = "1.3.2" [dependencies.libc] version = "0.2.66" [dependencies.netlink-packet-core] version = "0.7.0" [dependencies.netlink-packet-utils] version = "0.5.2" [dev-dependencies.criterion] version = "0.3.0" [dev-dependencies.lazy_static] version = "1.4.0" [dev-dependencies.netlink-sys] version = "0.8.5" [dev-dependencies.pcap-file] version = "1.1.1" [dev-dependencies.pretty_assertions] version = "0.7.2" [features] rich_nlas = [] netlink-packet-route-0.17.0/Cargo.toml.orig0000644000000015370000000000100141310ustar [package] authors = ["Corentin Henry "] name = "netlink-packet-route" version = "0.17.0" edition = "2018" homepage = "https://github.com/rust-netlink/netlink-packet-route" keywords = ["netlink", "linux"] license = "MIT" readme = "README.md" repository = "https://github.com/rust-netlink/netlink-packet-route" description = "netlink packet types" [features] rich_nlas = [] [dependencies] anyhow = "1.0.31" byteorder = "1.3.2" libc = "0.2.66" netlink-packet-core = { version = "0.7.0" } netlink-packet-utils = { version = "0.5.2" } bitflags = "1.2.1" [[example]] name = "dump_packet_links" [dev-dependencies] criterion = "0.3.0" pcap-file = "1.1.1" lazy_static = "1.4.0" netlink-sys = { version = "0.8.5" } pretty_assertions = "0.7.2" [[bench]] name = "link_message" harness = false [[bench]] name = "rtnetlink_dump" harness = false netlink-packet-route-0.17.0/Cargo.toml.orig000064400000000000000000000015371046102023000166530ustar 00000000000000[package] authors = ["Corentin Henry "] name = "netlink-packet-route" version = "0.17.0" edition = "2018" homepage = "https://github.com/rust-netlink/netlink-packet-route" keywords = ["netlink", "linux"] license = "MIT" readme = "README.md" repository = "https://github.com/rust-netlink/netlink-packet-route" description = "netlink packet types" [features] rich_nlas = [] [dependencies] anyhow = "1.0.31" byteorder = "1.3.2" libc = "0.2.66" netlink-packet-core = { version = "0.7.0" } netlink-packet-utils = { version = "0.5.2" } bitflags = "1.2.1" [[example]] name = "dump_packet_links" [dev-dependencies] criterion = "0.3.0" pcap-file = "1.1.1" lazy_static = "1.4.0" netlink-sys = { version = "0.8.5" } pretty_assertions = "0.7.2" [[bench]] name = "link_message" harness = false [[bench]] name = "rtnetlink_dump" harness = false netlink-packet-route-0.17.0/LICENSE-MIT000064400000000000000000000027731046102023000154230ustar 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. netlink-packet-route-0.17.0/README.md000064400000000000000000000004421046102023000152350ustar 00000000000000# netlink-packet-route Rust crate providing netlink message parsing and generating support for 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/netlink-packet-route/ netlink-packet-route-0.17.0/data/README.md000064400000000000000000000011151046102023000161440ustar 00000000000000The rtnetlink dump was generated with: ``` sudo ip link add name qemu-br1 type bridge sudo ip link set qemu-br1 up sudo ip address add 192.168.10.1/24 dev qemu-br1 docker run -d -it busybox /bin/sh sudo ip netns add blue sudo ip link add veth0 type veth peer name veth1 sudo ip netns list sudo ip link set veth1 netns blue sudo ip -6 link add vxlan100 type vxlan id 100 dstport 4789 local 2001:db8:1::1 group ff05::100 dev veth0 ttl 5 sudo brctl addbr br100 sudo brctl addif br100 vxlan100 sudo ip link show sudo ip address show sudo ip neigh show sudo ip route show tc qdisc show ``` netlink-packet-route-0.17.0/data/rtnetlink.pcap000064400000000000000000014545101046102023000175600ustar 00000000000000ò8\$$88\8\\ \ 8$8\XOIlo 23() ! noqueue#'/0$  FoFo'V'VdFoFo'V'V +'l ^@: QX`'6,%(g(g4(8\XOC wlp58s0 23 () ! noqueue#'/0$ п -|Q?d-|Q? +'l i(OX@: QX`'6,% 8 Ud d8  4b8\  88\XO docker0 2D3() ! noqueue# '/0$ BhQ d + bridge    F/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%hh4 8\XO8 nlmon0 23() ! noqueue#'/0$!d! + nlmon'l eY@: QX`'6,%48\$$88\XO8\$$$88\8\1  8L8\XOloX8\XOWW wlp58s0p([X8\XO docker08\>8H8\XO H8\XO @4QYi((H8\XO @BhQjj8\J$$88\XO8\008 8\DD84]O 8\G PP8@8\ qemu-br1 bridge8\!8  qemu-br1 2D3() ! noop#'/0$ b d + bridge    /u    ')-*+, c e c !0 " #4 $%&'l ۔\@: QX`'6,%48\!8  qemu-br1 2D3() ! noop#'/0$ b d + bridge    /u    ')-*+, c e c !0 " #4 $%&'l ۔\@: QX`'6,%48\!448$8\]O@8\8\$008  8\$8  qemu-br1 2D3() ! noop#'/0$ b d + bridge    /u    ')-*+, c e c !0 " #4 $%&'l ۔\@: QX`'6,%48\$448$ 8\t;$$88\8\;\ \ 8$8\_OIlo 23() ! noqueue#'/0$  FoFo'V'VdFoFo'V'V +'l ^@: QX`'6,%(g(g4(8\_OC wlp58s0 23 () ! noqueue#'/0$ п .U?Ud.U?U +'l i(OX@: QX`'6,% 8 Ud d8  4b8\; 88\_O docker0 2D3() ! noqueue# '/0$ BhQ d + bridge    E/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%hh4 8\_O8 nlmon0 23() ! noqueue#'/0$!d! + nlmon'l eY@: QX`'6,%48\<88\_O  qemu-br1 2D3() ! noop#'/0$ b d + bridge    /u    ')-*+, c e c !0 " #4 $%&'l ۔\@: QX`'6,%48\<$$88\_O8\5<$$88\8\P<  8L8\_OloX8\_OWW wlp58s0p([X8\_O docker08\k<8H8\_O H8\_O @4QYi((H8\_O @BhQjj8\<$$88\_O8\a008 8\yDD84bO 8\͏008 8\ 8\8 C qemu-br1 2D3() ! noqueue#'/0$ b d + bridge     /u    ')-*+, c e c !0 " #4 $%&'l ۔\@: QX`'6,%48\8 C qemu-br1 2D3() ! noqueue#'/0$ b d + bridge     /u    ')-*+, c e c !0 " #4 $%&'l ۔\@: QX`'6,%48\08t  $ 8\;8t  $ 8\F8t  $ 8\R8t  $ 8\]8t  $ 8\h8t @ $ 8\t8t @ $ 8\8t @ $ 8\8t @ $ 8\8t @ $ 8\448$8\bO 8\8\$$88\8\ț\ \ 8$8\cOIlo 23() ! noqueue#'/0$  FoFo'V'VdFoFo'V'V +'l ^@: QX`'6,%(g(g4(8\cOC wlp58s0 23 () ! noqueue#'/0$ п .U?Ud.U?U +'l i(OX@: QX`'6,% 8 Ud d8  4b8\ 88\cO docker0 2D3() ! noqueue# '/0$ BhQ d + bridge    E/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%hh4 8\cO8 nlmon0 23() ! noqueue#'/0$t"dt" + nlmon'l eY@: QX`'6,%48\188\cO C qemu-br1 2D3() ! noqueue#'/0$ b d + bridge     /u    ')-*+, c e c !0 " #4 $%&'l ޔ\@: QX`'6,%48\<$$88\cO8\H$$88\8\S  8L8\cOloX8\cOWW wlp58s0p([X8\cO docker08\^008H8\cO H8\cO @4QYi((H8\cO @BhQjjH8\cO @ bޔޔ8\j$$88\cO8\888(8\   8\dd8T8\dO    qemu-br18\dd8T8\dO    qemu-br18\LL8<    8\LL8<    8\LL8<    8\LL8<    8\ LL8<    8\!LL8<   8\!$$88\8\,LL8<   8\8LL8<   8\CLL8<   8\NLL8<   8\gLL8<    8\sLL8<    8\~LL8<    8\LL8<    8\LL8<    8\LL8<    8\LL8<    8\LL8<    8\LL8<    8\LL8<    8\448$8\dO(8\8\\ \ 8$8\@Ilo 23() ! noqueue#'/0$  FoFo'V'VdFoFo'V'V +'l ^@: QX`'6,%(g(g4(8\@C wlp58s0 23 () ! noqueue#'/0$ п .U?Ud.U?U +'l i(OX@: QX`'6,% 8 Ud d8  4b8\ 88\@ docker0 2D3() ! noqueue# '/0$ BhQ d + bridge    E/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%hh4 8\@8 nlmon0 23() ! noqueue#'/0$'+"d'+" + nlmon'l eY@: QX`'6,%48\@88\@ C qemu-br1 2D3() ! noqueue#'/0$ b ZdZ + bridge    /u    ')-*+, c e c !0 " #4 $%&'l ޔ\@: QX`'6,%LL48\L$$88\@8\W$$88\8\b``8L8\@loX8\@WW wlp58s0p([X8\@ docker0T8\@    qemu-br18\n008H8\@ H8\@ @4QYi((H8\@ @BhQjjH8\@ @ bޔޔ8\y$$88\@8\Ӈ$$88\8\އ``8L8\ϭloX8\ϭWW wlp58s0p([X8\ϭ docker0T8\ϭ    qemu-br18\008H8\ϭ H8\ϭ @4QYi((H8\ϭ @BhQjjH8\ϭ @ bޔޔ8\$$88\ϭ8\$$88\8\``8L8\6loX8\6WW wlp58s0p([X8\6 docker0T8\6    qemu-br18\008H8\6 H8\6 @4QYi((H8\6 @BhQjjH8\6 @ bޔޔ8\$$88\68\@@80=vetheb648958\TT8D=0=vetheb648958\D@@80>vethc0e60d68\QTT8D>0>vethc0e60d68\u8t?vetheb64895 <veth0,vethc0e60d6 8\TT8Dvethc0e60d6 2D3() ! noop#'/0$ &#bR d + veth'l $T@: QX`'6,%48\!TT8Dvethc0e60d6 2D3() ! noop#'/0$ &#bR d + veth'l $T@: QX`'6,%48\>008 8\S!TT8Dvetheb64895 2D3() ! noop#'/0$ 6r̀F d + veth'l >D@: QX`'6,%48\k!TT8Dvetheb64895 2D3() ! noop#'/0$ 6r̀F d + veth'l >D@: QX`'6,%48\!448$?t?8\!@@80@vetheb648958\!TT8D@vetheb64895 2D3() ! noop#'/0$ 6r̀F d + veth'l >D@: QX`'6,%48\!448$@0@8\"@@80Avetheb648958\4"TT8DAvetheb64895 2D3() ! noop#'/0$ 6r̀F d + veth'l >D@: QX`'6,%48\B"448$A0A8\r"@@80Bvethc0e60d68\"TT8DBvethc0e60d6 2D3() ! noop#'/0$ &#bR d + veth'l $T@: QX`'6,%48\"448$B0B8\"888(C8\"448$C(C8\"888(D8\"448$D(D8\#@@80Evetheb648958\.#TT8DEvetheb64895 2D3() ! noop#'/0$ 6r̀F d + veth'l >D@: QX`'6,%48\;#448$E0E8\b#<<8,F docker08\#8F docker0 2D3() ! noqueue# '/0$ BhQ d + bridge    pE/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%hh48\#448$F,F8\#888(G 8\o$pp8`vetheb64895 2D3()  ! noop#'/0$ 6r̀F d +$ veth bridge    BhQ BhQ !   'l >D@: QX`'6,%48\$pp8`vetheb64895 2D3()  ! noop#'/0$ 6r̀F d +$ veth bridge    BhQ BhQ !   'l >D@: QX`'6,%48\$pp8`vetheb64895 2D3()  ! noop#'/0$ 6r̀F d +$ veth bridge    BhQ BhQ !   'l >D@: QX`'6,%48\$pp8`vetheb64895 2D3()  ! noop#'/0$ 6r̀F d +$ veth bridge    BhQ BhQ !   'l >D@: QX`'6,%48\$8 docker0 2D3() ! noqueue# '/0,$ BhQ d + bridge    pE/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%hh48\ %8 docker0 2D3() ! noqueue# '/0,$ BhQ d + bridge    pE/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%hh48\+%8pvetheb64895  6r̀F     BhQ BhQ !    8\6%8pvetheb64895  6r̀F     BhQ BhQ !    8\E%448$G(G8\%008 H8\%pp8`vetheb64895 2D3()  ! noqueue#'/0$ 6r̀F d +$ veth bridge    BhQ BhQ !   'l >D@: QX`'6,%48\%pp8`vetheb64895 2D3()  ! noqueue#'/0$ 6r̀F d +$ veth bridge    BhQ BhQ !   'l >D@: QX`'6,%48\ &8pvetheb64895  6r̀F     BhQ BhQ !    8\&8pvetheb64895  6r̀F     BhQ BhQ !    8\(&448$H H8\m(TT8Dvethc0e60d6 2D3() ! noop#'/0$ &#bR d + veth'l $T@: QX`'6,%48\(448$ 8\v)008 8\)8 docker0 2D3() ! noqueue# '/0$ BhQ d + bridge    pE/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%hh48\)448$ 8\+008 8\+TT8Dvethc0e60d6 2D3() ! noop#'/0$ &#bR d + veth'l $T@: QX`'6,%48\+448$ 8\\5008 8\s5pp8`vetheb64895 2D3()  ! noqueue#'/0$ 6r̀F d +$ veth bridge    BhQ BhQ !   'l >D@: QX`'6,%48\5448$ 8\@@80Ivethc0e60d68\TT8DIvethc0e60d6 2D3() ! noop#'/0$ &#bR d + veth'l $T@: QX`'6,%48\448$I0I8\&888(J8\epp8`vethc0e60d6 2D3() ! noop#'/0$ &#bR d + veth-18\epp8`vethc0e60d6 2D3() ! noop#'/0$ &#bR d + veth-18\g448$J(J8\)g$$88\ag``8LloXWW wlp58s0p([X docker0T    qemu-br18\xg$$88\g$$8 8\008H H @4QYi((H @BhQjjH @ bޔޔ8\$$88\4$$88\V8<X<DXW<   < << < < < < < W< WW< W<    <    <    8\8t $ 8\8t $ 8\8t $ 8\8t $ 8\ 8t $ 8\!8t @$ 8\,8t @$ 8\78t @$ 8\C8t @$ 8\N8t @$ 8\{8pCvetheb64895  6r̀F     BhQ BhQ !    8\8pCvetheb64895  6r̀F     BhQ BhQ !    8\8pCvetheb64895  6r̀F     BhQ BhQ !    8\8pCvetheb64895  6r̀F     BhQ BhQ !    8\xx8hCvetheb64895 2D3()  ! noqueue#'/0$ 6r̀F d +$ veth bridge    BhQ BhQ !   %'l >D@: QX`'6,%48\xx8hCvetheb64895 2D3()  ! noqueue#'/0$ 6r̀F d +$ veth bridge    BhQ BhQ !   %'l >D@: QX`'6,%48\8C docker0 2D3() ! noqueue# '/0$ BhQ d + bridge    WE/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%hh48\8C docker0 2D3() ! noqueue# '/0$ BhQ d + bridge    WE/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%hh48\A$$88\1$$8 8\R8t $ t @$ t @ $ t @$ t @X$ t $ t BhQ$ t 4QY$ t $ t $ t  $ t $ 8\l$$88\448$&8\8$ noqueue 0,$ noqueue 0,$ noqueue 0,$ noqueue 0,$  noqueue 0,$ noqueue 0,8\$$88\448$.8\$$88\?008 8\Xxx8hCvetheb64895 2D3()  ! noqueue#'/0$ 6r̀F d +$ veth bridge    BhQ BhQ !   %'l >D@: QX`'6,%48\c448$ 8\008 8\8C docker0 2D3() ! noqueue# '/0$ BhQ d + bridge    WE/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%hh48\448$ 8\ $$88\8\! \ \ 8$8\OIlo 23() ! noqueue#'/0$  FoFo'V'VdFoFo'V'V +'l ^@: QX`'6,%(g(g4(8\OC wlp58s0 23 () ! noqueue#'/0$ п ._?d._? +'l i(OX@: QX`'6,% 8 Ud d8  4b8\@ 88\OC docker0 2D3() ! noqueue# '/0$ BhQ rdr + bridge    BE/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%4 8\O8 nlmon0 23() ! noqueue#'/0$t%#dt%# + nlmon'l eY@: QX`'6,%48\] D D 88\O C qemu-br1 2D3() ! noqueue#'/0$ b 4d4 + bridge    t/u    ')-*+, c e c !0 " #4 $%&'l ޔ\@: QX`'6,%4h8\OCvetheb64895 2D3()  ! noqueue#'/0$ 6r̀F  d  +$ veth bridge    BhQ BhQ !   %'l >D@: QX`'6,%48\l $$88\O8\y $$88\8\ ``8L8\OloX8\OWW wlp58s0p([X8\O docker0T8\O    qemu-br18\ xx8H8\O H8\O @4QYi((H8\O @BhQjjH8\O @ bޔޔH8\O @4rF8\ $$88\O8\@ ,,8Z8\@ ,,8XO8\O $$88\8\O \ \ 8$8\OIlo 23() ! noqueue#'/0$  FoFo'V'VdFoFo'V'V +'l ^@: QX`'6,%(g(g4(8\OC wlp58s0 23 () ! noqueue#'/0$ п ._?d._? +'l i(OX@: QX`'6,% 8 Ud d8  4b8\O 88\OC docker0 2D3() ! noqueue# '/0$ BhQ rdr + bridge    AE/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%4 8\O8 nlmon0 23() ! noqueue#'/0$L#dL# + nlmon'l eY@: QX`'6,%48\O D D 88\O C qemu-br1 2D3() ! noqueue#'/0$ b 4d4 + bridge    t/u    ')-*+, c e c !0 " #4 $%&'l ޔ\@: QX`'6,%4h8\OCvetheb64895 2D3()  ! noqueue#'/0$ 6r̀F  d  +$ veth bridge    BhQ BhQ !   %'l >D@: QX`'6,%48\O $$88\O8\P $$88\8\P ``8L8\OloX8\OWW wlp58s0p([X8\O docker0T8\O    qemu-br18\ P xx8H8\O H8\O @4QYi((H8\O @BhQjjH8\O @ bޔޔH8\O @4rF8\+P $$88\O8\{ 008 8\"{ DD84O 8\{{ ll8\8\ veth00veth$  veth18\| PP8@ veth1 2D3() ! noop#'/0$ * d + veth'l vy@: QX`'6,%48\(| PP8@ veth1 2D3() ! noop#'/0$ * d + veth'l vy@: QX`'6,%48\| PP8@ veth0 2D3() ! noop#'/0$ 6 d + veth'l yL@: QX`'6,%48\| PP8@ veth0 2D3() ! noop#'/0$ 6 d + veth'l yL@: QX`'6,%48\| 448$8\O\8\8\ } $$88\} ``8LloXWW wlp58s0p([X docker0T    qemu-br18\,} $$88\[} $$8 8\i} xx8H H @4QYi((H @BhQjjH @ bޔޔH @4rF8\} $$88\} $$88\} 8<X<DXW<   < << < < < < < W< WW< W<    <    <    8\} $$88\} $$8 8\} 8t $ t @$ t @ $ t @$ t @X$ t $ t BhQ$ t 4QY$ t $ t $ t  $ t $ 8\~ $$88\-~ 448$&8\8~ 8$ noqueue 0,$ noqueue 0,$ noqueue 0,$ noqueue 0,$  noqueue 0,$ noqueue 0,8\O~ $$88\e~ 448$.8\q~ $$88\~ 008 8\~ PP8@ veth1 2D3() ! noop#'/0$ * d + veth'l vy@: QX`'6,%48\~ 448$ 8\ 008 8\ PP8@ veth1 2D3() ! noop#'/0$ * d + veth'l vy@: QX`'6,%48\ 448$ 8\3 008 8\J PP8@ veth0 2D3() ! noop#'/0$ 6 d + veth'l yL@: QX`'6,%48\U 448$ 8\l $$88\8\ \ \ 8$8\OIlo 23() ! noqueue#'/0$  FoFo'V'VdFoFo'V'V +'l ^@: QX`'6,%(g(g4(8\OC wlp58s0 23 () ! noqueue#'/0$ п ._?d._? +'l i(OX@: QX`'6,% 8 Ud d8  4b8\ 88\OC docker0 2D3() ! noqueue# '/0$ BhQ rdr + bridge    ?E/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%4 8\O8 nlmon0 23() ! noqueue#'/0$#d# + nlmon'l eY@: QX`'6,%48\ D D 88\O C qemu-br1 2D3() ! noqueue#'/0$ b 4d4 + bridge    t/u    ')-*+, c e c !0 " #4 $%&'l ޔ\@: QX`'6,%4h8\OCvetheb64895 2D3()  ! noqueue#'/0$ 6r̀F  d  +$ veth bridge    BhQ BhQ !   %'l >D@: QX`'6,%48\ 8@8\O veth1 2D3() ! noop#'/0$ * d + veth'l vy@: QX`'6,%4@8\O veth0 2D3() ! noop#'/0$ 6 d + veth'l yL@: QX`'6,%48\ $$88\O8\ $$88\8\( ``8L8\OloX8\OWW wlp58s0p([X8\O docker0T8\O    qemu-br18\3 xx8H8\O H8\O @4QYi((H8\O @BhQjjH8\O @ bޔޔH8\O @4rF8\@ $$88\O8\ ,,8Z8\ ,,8XO8\ ,,8Z8\8\ ,,8X8\T8\ ,,8Z8\8\ ,,8X8\T8\ $$88\8\ \ \ 8$8\OIlo 23() ! noqueue#'/0$  FoFo'V'VdFoFo'V'V +'l ^@: QX`'6,%(g(g4(8\OC wlp58s0 23 () ! noqueue#'/0$ п ._?d._? +'l i(OX@: QX`'6,% 8 Ud d8  4b8\ 88\OC docker0 2D3() ! noqueue# '/0$ BhQ rdr + bridge    =E/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%4 8\O8 nlmon0 23() ! noqueue#'/0$8#d8# + nlmon'l eY@: QX`'6,%48\0 D D 88\O C qemu-br1 2D3() ! noqueue#'/0$ b 4d4 + bridge    t/u    ')-*+, c e c !0 " #4 $%&'l ޔ\@: QX`'6,%4h8\OCvetheb64895 2D3()  ! noqueue#'/0$ 6r̀F  d  +$ veth bridge    BhQ BhQ !   %'l >D@: QX`'6,%48\I 8@8\O veth1 2D3() ! noop#'/0$ * d + veth'l vy@: QX`'6,%4@8\O veth0 2D3() ! noop#'/0$ 6 d + veth'l yL@: QX`'6,%48\T $$88\O8\_ $$88\8\l ``8L8\OloX8\OWW wlp58s0p([X8\O docker0T8\O    qemu-br18\z xx8H8\O H8\O @4QYi((H8\O @BhQjjH8\O @ bޔޔH8\O @4rF8\ $$88\O8\ 008 8\ DD84O 8\ 888(8\8\SL ll8\ veth1 2D3() ! noop#'/0$ * d + veth-18\_L ll8\ veth1 2D3() ! noop#'/0$ * d + veth-18\5M $$88\M 448$8\O(8\8\M ``8LloXWW wlp58s0p([X docker0T    qemu-br18\N $$88\*N $$8 8\5N xx8H H @4QYi((H @BhQjjH @ bޔޔH @4rF8\WN $$88\N $$88\N 8<X<DXW<   < << < < < < < W< WW< W<    <    <    8\N $$88\N $$8 8\O 8t $ t @$ t @ $ t @$ t @X$ t $ t BhQ$ t 4QY$ t $ t $ t  $ t $ 8\*O $$88\NO 448$&8\ZO 8$ noqueue 0,$ noqueue 0,$ noqueue 0,$ noqueue 0,$  noqueue 0,$ noqueue 0,8\pO $$88\O 448$.8\O $$88\O 008 8\O XX8H veth0 2D3() ! noop#'/0$ 6 d + veth%'l yL@: QX`'6,%48\O 448$ 8\ Z $$88\8\#Z \ \ 8$8\OIlo 23() ! noqueue#'/0$  FoFo'V'VdFoFo'V'V +'l ^@: QX`'6,%(g(g4(8\OC wlp58s0 23 () ! noqueue#'/0$ п ._?d._? +'l i(OX@: QX`'6,% 8 Ud d8  4b8\GZ 88\OC docker0 2D3() ! noqueue# '/0$ BhQ rdr + bridge    :E/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%4 8\O8 nlmon0 23() ! noqueue#'/0$ '$d '$ + nlmon'l eY@: QX`'6,%48\]Z D D 88\O C qemu-br1 2D3() ! noqueue#'/0$ b 4d4 + bridge    t/u    ')-*+, c e c !0 " #4 $%&'l ޔ\@: QX`'6,%4h8\OCvetheb64895 2D3()  ! noqueue#'/0$ 6r̀F  d  +$ veth bridge    BhQ BhQ !   %'l >D@: QX`'6,%48\jZ XX8H8\O veth0 2D3() ! noop#'/0$ 6 d + veth%'l yL@: QX`'6,%48\xZ $$88\O8\Z $$88\8\Z ``8L8\OloX8\OWW wlp58s0p([X8\O docker0T8\O    qemu-br18\Z xx8H8\O H8\O @4QYi((H8\O @BhQjjH8\O @ bޔޔH8\O @4rF8\Z $$88\O8\~ 008 8\~ DD84O 8\< 88\ vxlan100d vxlanTd  8\  8 vxlan100 2D3() ! noop#'/0$ f==\ d + vxland     ,  'l 'fk@: QX`'6,%48\  8 vxlan100 2D3() ! noop#'/0$ f==\ d + vxland     ,  'l 'fk@: QX`'6,%48\ 448$8\O8\8\} $$88\ ``8LloXWW wlp58s0p([X docker0T    qemu-br18\ $$88\Į $$8 8\Ϯ xx8H H @4QYi((H @BhQjjH @ bޔޔH @4rF8\ $$88\ $$88\ 8<X<DXW<   < << < < < < < W< WW< W<    <    <    8\- $$88\F $$8 8\\ 8t $ t @$ t @ $ t @$ t @X$ t $ t BhQ$ t 4QY$ t $ t $ t  $ t $ 8\s $$88\ 448$&8\ 8$ noqueue 0,$ noqueue 0,$ noqueue 0,$ noqueue 0,$  noqueue 0,$ noqueue 0,8\ $$88\կ 448$.8\ $$88\ 008 8\  8 vxlan100 2D3() ! noop#'/0$ f==\ d + vxland     ,  'l 'fk@: QX`'6,%48\ 448$ 8\W $$88\8\p \ \ 8$8\OIlo 23() ! noqueue#'/0$  FoFo'V'VdFoFo'V'V +'l ^@: QX`'6,%(g(g4(8\OC wlp58s0 23 () ! noqueue#'/0$ п ._?d._? +'l i(OX@: QX`'6,% 8 Ud d8  4b8\ 88\OC docker0 2D3() ! noqueue# '/0$ BhQ rdr + bridge    8E/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%4 8\O8 nlmon0 23() ! noqueue#'/0$/w$d/w$ + nlmon'l eY@: QX`'6,%48\ D D 88\O C qemu-br1 2D3() ! noqueue#'/0$ b 4d4 + bridge    t/u    ')-*+, c e c !0 " #4 $%&'l ޔ\@: QX`'6,%4h8\OCvetheb64895 2D3()  ! noqueue#'/0$ 6r̀F  d  +$ veth bridge    BhQ BhQ !   %'l >D@: QX`'6,%48\̸ h h 8H8\O veth0 2D3() ! noop#'/0$ 6 d + veth%'l yL@: QX`'6,%48\O vxlan100 2D3() ! noop#'/0$ f==\ d + vxland     ,  'l 'fk@: QX`'6,%48\ڸ $$88\O8\ $$88\8\ ``8L8\OloX8\OWW wlp58s0p([X8\O docker0T8\O    qemu-br18\ xx8H8\O H8\O @4QYi((H8\O @BhQjjH8\O @ bޔޔH8\O @4rF8\ $$88\O8\^ 8 br100 2D3() ! noop#'/0$ 6H d + bridge    /u    ')-*+, c e c !0 " #4 $%&'l )Hq@: QX`'6,%48\i 8 br100 2D3() ! noop#'/0$ 6H d + bridge    /u    ')-*+, c e c !0 " #4 $%&'l )Hq@: QX`'6,%48\ 008 8\ 8 br100 2D3() ! noop#'/0$ 6H d + bridge    /u    ')-*+, c e c !0 " #4 $%&'l )Hq@: QX`'6,%48\ 448$ 8\t $$88\8\ \ \ 8$8\OIlo 23() ! noqueue#'/0$  FoFo'V'VdFoFo'V'V +'l ^@: QX`'6,%(g(g4(8\OC wlp58s0 23 () ! noqueue#'/0$ п ._?d._? +'l i(OX@: QX`'6,% 8 Ud d8  4b8\ 88\OC docker0 2D3() ! noqueue# '/0$ BhQ rdr + bridge    6E/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%4 8\O8 nlmon0 23() ! noqueue#'/0$>$d>$ + nlmon'l eY@: QX`'6,%48\ D D 88\O C qemu-br1 2D3() ! noqueue#'/0$ b 4d4 + bridge    t/u    ')-*+, c e c !0 " #4 $%&'l ޔ\@: QX`'6,%4h8\OCvetheb64895 2D3()  ! noqueue#'/0$ 6r̀F  d  +$ veth bridge    BhQ BhQ !   %'l >D@: QX`'6,%48\ h h 8H8\O veth0 2D3() ! noop#'/0$ 6 d + veth%'l yL@: QX`'6,%48\O vxlan100 2D3() ! noop#'/0$ f==\ d + vxland     ,  'l 'fk@: QX`'6,%48\ 88\O br100 2D3() ! noop#'/0$ 6H d + bridge    /u    ')-*+, c e c !0 " #4 $%&'l )Hq@: QX`'6,%48\ $$88\O8\ $$88\8\ ``8L8\OloX8\OWW wlp58s0p([X8\O docker0T8\O    qemu-br18\% xx8H8\O H8\O @4QYi((H8\O @BhQjjH8\O @ bޔޔH8\O @4rF8\2 $$88\O8\# <<8, vxlan100 2D3()  ! noop#'/0$ f==\ d + vxland     ,  bridge d    !   'l 'fk@: QX`'6,%48\/ <<8, vxlan100 2D3()  ! noop#'/0$ f==\ d + vxland     ,  bridge d    !   'l 'fk@: QX`'6,%48\F <<8, vxlan100 2D3()  ! noop#'/0$ f==\ d + vxland     ,  bridge d    !   'l 'fk@: QX`'6,%48\Q <<8, vxlan100 2D3()  ! noop#'/0$ f==\ d + vxland     ,  bridge d    !   'l 'fk@: QX`'6,%48\\ 8 br100 2D3() ! noop#'/0,$ 6H d + bridge    /u    ')-*+, c e c !0 " #4 $%&'l )Hq@: QX`'6,%48\g 8 br100 2D3() ! noop#'/0,$ 6H d + bridge    /u    ')-*+, c e c !0 " #4 $%&'l )Hq@: QX`'6,%48\s xx8h vxlan100  f==\  d   f==\ f==\ !    8\~ xx8h vxlan100  f==\  d   f==\ f==\ !    8\ 8 br100 2D3() ! noop#'/0$ f==\ d + bridge    /u f==\ f==\   ')-*+, c e c !0 " #4 $%&'l )Hq@: QX`'6,%48\ 8 br100 2D3() ! noop#'/0$ f==\ d + bridge    /u f==\ f==\   ')-*+, c e c !0 " #4 $%&'l )Hq@: QX`'6,%48\ 8 br100 2D3() ! noop#'/0$ f==\ d + bridge    /u f==\ f==\   ')-*+, c e c !0 " #4 $%&'l )Hq@: QX`'6,%48\ 8 br100 2D3() ! noop#'/0$ f==\ d + bridge    /u f==\ f==\   ')-*+, c e c !0 " #4 $%&'l )Hq@: QX`'6,%48\* 008 8\6 8 br100 2D3() ! noop#'/0$ f==\ d + bridge    /u f==\ f==\   ')-*+, c e c !0 " #4 $%&'l )Hq@: QX`'6,%48\A 448$ 8\( $$88\8\( \ \ 8$8\OIlo 23() ! noqueue#'/0$  FoFo'V'VdFoFo'V'V +'l ^@: QX`'6,%(g(g4(8\OC wlp58s0 23 () ! noqueue#'/0$ п ._?d._? +'l i(OX@: QX`'6,% 8 Ud d8  4b8\6( 88\OC docker0 2D3() ! noqueue# '/0$ BhQ rdr + bridge    5E/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%4 8\O8 nlmon0 23() ! noqueue#'/0$XE%dXE% + nlmon'l eY@: QX`'6,%48\L( D D 88\O C qemu-br1 2D3() ! noqueue#'/0$ b 4d4 + bridge    t/u    ')-*+, c e c !0 " #4 $%&'l ޔ\@: QX`'6,%4h8\OCvetheb64895 2D3()  ! noqueue#'/0$ 6r̀F  d  +$ veth bridge    BhQ BhQ !   %'l >D@: QX`'6,%48\f( 8H8\O veth0 2D3() ! noop#'/0$ 6 d + veth%'l yL@: QX`'6,%4,8\O vxlan100 2D3()  ! noop#'/0$ f==\ d + vxland     ,  bridge d   f==\ f==\ !   'l 'fk@: QX`'6,%48\s( 88\O br100 2D3() ! noop#'/0$ f==\ d + bridge    /u f==\ f==\   ')-*+, c e c !0 " #4 $%&'l )Hq@: QX`'6,%48\~( $$88\O8\( $$88\8\( ``8L8\OloX8\OWW wlp58s0p([X8\O docker0T8\O    qemu-br18\( xx8H8\O H8\O @4QYi((H8\O @BhQjjH8\O @ bޔޔH8\O @4rF8\( $$88\O8\sI 008 8\~I DD84O 8\I 888(8\8\I 668$8\OIlo 23() ! noqueue#'/0$  FoFo'V'VdFoFo'V'V +'l ^@: QX`'6,%(g(g408\OC wlp58s0 23 () ! noqueue#'/0$ п ._?d._? +'l i(OX@: QX`'6,% 8 Ud d8  4b8\OC docker0 2D3() ! noqueue# '/0$ BhQ rdr + bridge    4E/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%4 8\O8 nlmon0 23() ! noqueue#'/0$du%ddu% + nlmon'l eY@: QX`'6,%48\O C qemu-br1 2D3() ! noqueue#'/0$ b 4d4 + bridge    t/u    ')-*+, c e c !0 " #4 $%&'l ޔ\@: QX`'6,%4h8\OCvetheb64895 2D3()  ! noqueue#'/0$ 6r̀F  d  +$ veth bridge    BhQ BhQ !   %'l >D@: QX`'6,%4H8\O veth0 2D3() ! noop#'/0$ 6 d + veth%'l yL@: QX`'6,%4,8\O vxlan100 2D3()  ! noop#'/0$ f==\ d + vxland     ,  bridge d   f==\ f==\ !   'l 'fk@: QX`'6,%48\O br100 2D3() ! noop#'/0$ f==\ d + bridge    /u f==\ f==\   ')-*+, c e c !0 " #4 $%&'l )Hq@: QX`'6,%48\I $$88\O8\hJ ,,8Z8\tJ ,,8XO8\J ,,8Z8\8\J ,,8X8\68\AS $$88\8\YS \ \ 8$8\OIlo 23() ! noqueue#'/0$  FoFo'V'VdFoFo'V'V +'l ^@: QX`'6,%(g(g4(8\OC wlp58s0 23 () ! noqueue#'/0$ п ._?d._? +'l i(OX@: QX`'6,% 8 Ud d8  4b8\tS 88\OC docker0 2D3() ! noqueue# '/0$ BhQ rdr + bridge    4E/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%4 8\O8 nlmon0 23() ! noqueue#'/0$lD%dlD% + nlmon'l eY@: QX`'6,%48\S D D 88\O C qemu-br1 2D3() ! noqueue#'/0$ b 4d4 + bridge    t/u    ')-*+, c e c !0 " #4 $%&'l ޔ\@: QX`'6,%4h8\OCvetheb64895 2D3()  ! noqueue#'/0$ 6r̀F  d  +$ veth bridge    BhQ BhQ !   %'l >D@: QX`'6,%48\S 8H8\O veth0 2D3() ! noop#'/0$ 6 d + veth%'l yL@: QX`'6,%4,8\O vxlan100 2D3()  ! noop#'/0$ f==\ d + vxland     ,  bridge d   f==\ f==\ !   'l 'fk@: QX`'6,%48\S 88\O br100 2D3() ! noop#'/0$ f==\ d + bridge    /u f==\ f==\   ')-*+, c e c !0 " #4 $%&'l )Hq@: QX`'6,%48\S $$88\O8\S $$88\8\S ``8L8\OloX8\OWW wlp58s0p([X8\O docker0T8\O    qemu-br18\S xx8H8\O H8\O @4QYi((H8\O @BhQjjH8\O @ bޔޔH8\O @4rF8\S $$88\O8\u 888(8\8\ v d d 8$8\OIlo 23() ! noqueue#'/0$  FoFo'V'VdFoFo'V'V +'l ^@: QX`'6,%(g(g408\OC wlp58s0 23 () ! noqueue#'/0$ п ._?d._? +'l i(OX@: QX`'6,% 8 Ud d8  4b8\6v 88\OC docker0 2D3() ! noqueue# '/0$ BhQ rdr + bridge    3E/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%4 8\O8 nlmon0 23() ! noqueue#'/0$w %dw % + nlmon'l eY@: QX`'6,%48\tv D D 88\O C qemu-br1 2D3() ! noqueue#'/0$ b 4d4 + bridge    t/u    ')-*+, c e c !0 " #4 $%&'l ޔ\@: QX`'6,%4h8\OCvetheb64895 2D3()  ! noqueue#'/0$ 6r̀F  d  +$ veth bridge    BhQ BhQ !   %'l >D@: QX`'6,%48\v 8H8\O veth0 2D3() ! noop#'/0$ 6 d + veth%'l yL@: QX`'6,%4,8\O vxlan100 2D3()  ! noop#'/0$ f==\ d + vxland     ,  bridge d   f==\ f==\ !   'l 'fk@: QX`'6,%48\v 88\O br100 2D3() ! noop#'/0$ f==\ d + bridge    /u f==\ f==\   ')-*+, c e c !0 " #4 $%&'l )Hq@: QX`'6,%48\v $$88\O8\v ((88\8\v ``8L8\OloX8\OWW wlp58s0p([X8\O docker0T8\O    qemu-br18\v xx8H8\O H8\O @4QYi((H8\O @BhQjjH8\O @ bޔޔH8\O @4rF8\v $$88\O8\rw ,,8Z8\}w ,,8XO8\w ,,8Z8\8\w ,,8X8\G8\ $$88\8\ \ \ 8$8\OIlo 23() ! noqueue#'/0$  FoFo'V'VdFoFo'V'V +'l ^@: QX`'6,%(g(g4(8\OC wlp58s0 23 () ! noqueue#'/0$ п ._?d._? +'l i(OX@: QX`'6,% 8 Ud d8  4b8\ 88\OC docker0 2D3() ! noqueue# '/0$ BhQ rdr + bridge    3E/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%4 8\O8 nlmon0 23() ! noqueue#'/0$,+&d,+& + nlmon'l eY@: QX`'6,%48\ D D 88\O C qemu-br1 2D3() ! noqueue#'/0$ b 4d4 + bridge    t/u    ')-*+, c e c !0 " #4 $%&'l ޔ\@: QX`'6,%4h8\OCvetheb64895 2D3()  ! noqueue#'/0$ 6r̀F  d  +$ veth bridge    BhQ BhQ !   %'l >D@: QX`'6,%48\ 8H8\O veth0 2D3() ! noop#'/0$ 6 d + veth%'l yL@: QX`'6,%4,8\O vxlan100 2D3()  ! noop#'/0$ f==\ d + vxland     ,  bridge d   f==\ f==\ !   'l 'fk@: QX`'6,%48\7 88\O br100 2D3() ! noop#'/0$ f==\ d + bridge    /u f==\ f==\   ')-*+, c e c !0 " #4 $%&'l )Hq@: QX`'6,%48\C $$88\O8\N $$88\8\e ``8L8\OloX8\OWW wlp58s0p([X8\O docker0T8\O    qemu-br18\p xx8H8\O H8\O @4QYi((H8\O @BhQjjH8\O @ bޔޔH8\O @4rF8\{ $$88\O8\ 888(8\8\ d d 8$8\OIlo 23() ! noqueue#'/0$  FoFo'V'VdFoFo'V'V +'l ^@: QX`'6,%(g(g408\OC wlp58s0 23 () ! noqueue#'/0$ п ._?d._? +'l i(OX@: QX`'6,% 8 Ud d8  4b8\& 88\OC docker0 2D3() ! noqueue# '/0$ BhQ rdr + bridge    2E/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%4 8\O8 nlmon0 23() ! noqueue#'/0$d&dd& + nlmon'l eY@: QX`'6,%48\_ D D 88\O C qemu-br1 2D3() ! noqueue#'/0$ b 4d4 + bridge    t/u    ')-*+, c e c !0 " #4 $%&'l ޔ\@: QX`'6,%4h8\OCvetheb64895 2D3()  ! noqueue#'/0$ 6r̀F  d  +$ veth bridge    BhQ BhQ !   %'l >D@: QX`'6,%48\} 8H8\O veth0 2D3() ! noop#'/0$ 6 d + veth%'l yL@: QX`'6,%4,8\O vxlan100 2D3()  ! noop#'/0$ f==\ d + vxland     ,  bridge d   f==\ f==\ !   'l 'fk@: QX`'6,%48\ 88\O br100 2D3() ! noop#'/0$ f==\ d + bridge    /u f==\ f==\   ')-*+, c e c !0 " #4 $%&'l )Hq@: QX`'6,%48\ $$88\O8\ ,,88\8\֥ 8L8\O >X8\O @b 33b998\ $$88\O8\í $$88\8\ۭ \ \ 8$8\OIlo 23() ! noqueue#'/0$  FoFo'V'VdFoFo'V'V +'l ^@: QX`'6,%(g(g4(8\OC wlp58s0 23 () ! noqueue#'/0$ п ._?d._? +'l i(OX@: QX`'6,% 8 Ud d8  4b8\ 88\OC docker0 2D3() ! noqueue# '/0$ BhQ rdr + bridge    1E/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%4 8\O8 nlmon0 23() ! noqueue#'/0$ȟ&dȟ& + nlmon'l eY@: QX`'6,%48\ D D 88\O C qemu-br1 2D3() ! noqueue#'/0$ b 4d4 + bridge    t/u    ')-*+, c e c !0 " #4 $%&'l ޔ\@: QX`'6,%4h8\OCvetheb64895 2D3()  ! noqueue#'/0$ 6r̀F  d  +$ veth bridge    BhQ BhQ !   %'l >D@: QX`'6,%48\& 8H8\O veth0 2D3() ! noop#'/0$ 6 d + veth%'l yL@: QX`'6,%4,8\O vxlan100 2D3()  ! noop#'/0$ f==\ d + vxland     ,  bridge d   f==\ f==\ !   'l 'fk@: QX`'6,%48\? 88\O br100 2D3() ! noop#'/0$ f==\ d + bridge    /u f==\ f==\   ')-*+, c e c !0 " #4 $%&'l )Hq@: QX`'6,%48\J $$88\O8\W $$88\8\d ``8L8\OloX8\OWW wlp58s0p([X8\O docker0T8\O    qemu-br18\r xx8H8\O H8\O @4QYi((H8\O @BhQjjH8\O @ bޔޔH8\O @4rF8\} $$88\O8\W ,,88\8\r 8<8\OX<8\OD8\OXW<8\O   <8\O <8\O<8\O <8\O <8\O <8\O <8\O <8\O W<8\O WW<8\O W<8\O    <8\O    <8\O    8\~ $$88\O8\ 888(8\8\ d d 8$8\OIlo 23() ! noqueue#'/0$  FoFo'V'VdFoFo'V'V +'l ^@: QX`'6,%(g(g408\OC wlp58s0 23 () ! noqueue#'/0$ п ._?d._? +'l i(OX@: QX`'6,% 8 Ud d8  4b8\ 88\OC docker0 2D3() ! noqueue# '/0$ BhQ rdr + bridge    0E/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%4 8\O8 nlmon0 23() ! noqueue#'/0$&d& + nlmon'l eY@: QX`'6,%48\ D D 88\O C qemu-br1 2D3() ! noqueue#'/0$ b 4d4 + bridge    t/u    ')-*+, c e c !0 " #4 $%&'l ޔ\@: QX`'6,%4h8\OCvetheb64895 2D3()  ! noqueue#'/0$ 6r̀F  d  +$ veth bridge    BhQ BhQ !   %'l >D@: QX`'6,%48\ 8H8\O veth0 2D3() ! noop#'/0$ 6 d + veth%'l yL@: QX`'6,%4,8\O vxlan100 2D3()  ! noop#'/0$ f==\ d + vxland     ,  bridge d   f==\ f==\ !   'l 'fk@: QX`'6,%48\ 88\O br100 2D3() ! noop#'/0$ f==\ d + bridge    /u f==\ f==\   ')-*+, c e c !0 " #4 $%&'l )Hq@: QX`'6,%48\ $$88\O8\6 448$&8\8\D 8$8\O noqueue 0,$8\O noqueue 0,$8\O noqueue 0,$8\O noqueue 0,$8\O  noqueue 0,$8\O noqueue 0,8\O $$88\O8\;$$88\8\G``8L8\?loX8\?WW wlp58s0p([X8\? docker0T8\?    qemu-br18\ixx8H8\? H8\? @4QYi((H8\? @BhQjjH8\? @ bޔޔH8\? @4rF8\t$$88\?8\XX8H @ bޔޔ8\XX8H @ bޔޔ8\78t b $ 8\D8t b $ 8\P8t b $ 8\[8t b $ 8\g8t b $ 8\$$88\8\&\ \ 8$8\fIlo 23() ! noqueue#'/0$  FoFo'V'VdFoFo'V'V +'l ^@: QX`'6,%(g(g4(8\fC wlp58s0 23 () ! noqueue#'/0$ п .0g?d.0g? +'l i(OX@: QX`'6,% Ud d  4b8\` 88\fC docker0 2D3() ! noqueue# '/0$ BhQ rdr + bridge    E/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%4 8\f8 nlmon0 23() ! noqueue#'/0$'d' + nlmon'l eY@: QX`'6,%48\D D 88\f C qemu-br1 2D3() ! noqueue#'/0$ b d + bridge    t/u    ')-*+, c e c !0 " #4 $%&'l ޔ\@: QX`'6,%dd4h8\fCvetheb64895 2D3()  ! noqueue#'/0$ 6r̀F  d  +$ veth bridge    BhQ BhQ !   %'l >D@: QX`'6,%48\ 8H8\f veth0 2D3() ! noop#'/0$ 6 d + veth%'l yL@: QX`'6,%4,8\f vxlan100 2D3()  ! noop#'/0$ f==\ d + vxland     ,  bridge d   f==\ f==\ !   'l 'fk@: QX`'6,%48\$88\f br100 2D3() ! noop#'/0$ f==\ d + bridge    /u f==\ f==\   ')-*+, c e c !0 " #4 $%&'l )Hq@: QX`'6,%48\K$$88\f8\h$$88\8\~``8L8\floX8\fWW wlp58s0p([X8\f docker0T8\f    qemu-br18\xx8H8\f H8\f @4QYi((H8\f @BhQjjH8\f @ bޔޔH8\f @4rF8\$$88\f8\K XX8H @4rF8\c XX8H @4rF8\z 8t 4rF$ 8\ 8t 4rF$ 8\ 8t 4rF$ 8\ 8t 4rF$ 8\ 8t 4rF$ 8\ $$88\8\ \ \ 8$8\4Ilo 23() ! noqueue#'/0$  FoFo'V'VdFoFo'V'V +'l ^@: QX`'6,%(g(g4(8\4C wlp58s0 23 () ! noqueue#'/0$ п  .i?d .i? +'l i(OX@: QX`'6,% Ud d  4b8\G 88\4C docker0 2D3() ! noqueue# '/0$ BhQ d + bridge    D/u BhQ BhQ   ')-*+, c e c !0 " #4 $%&'l j0@: QX`'6,%4 8\48 nlmon0 23() ! noqueue#'/0$D@: QX`'6,%dd48\ 8H8\4 veth0 2D3() ! noop#'/0$ 6 d + veth%'l yL@: QX`'6,%4,8\4 vxlan100 2D3()  ! noop#'/0$ f==\ d + vxland     ,  bridge d   f==\ f==\ !   'l 'fk@: QX`'6,%48\˅ 88\4 br100 2D3() ! noop#'/0$ f==\ d + bridge    /u f==\ f==\   ')-*+, c e c !0 " #4 $%&'l )Hq@: QX`'6,%48\օ $$88\48\ $$88\8\ ``8L8\4loX8\4WW wlp58s0p([X8\4 docker0T8\4    qemu-br18\ xx8H8\4 H8\4 @4QYi((H8\4 @BhQjjH8\4 @ bޔޔH8\4 @4rF8\ $$88\48\$$88\"8<.X<.D.XW<.   <. <.<. <. <. <. <. <. W<. WW<. W<.    <.    <.    8\"$$88\F8<X<DXW<   < << < < < < < W< WW< W<    <    <    8\R$$88\u8<X<DXW<   < << < < < < < W< WW< W<    <    <    8\hh8t. $ t. @$ t. @ $ t. @$ t. @X$ t. $ t. BhQ$ t. 4rF$ t. 4QY$ t. b $ t. $ t. $ t.  $ t. $ 8\hh8t $ t @$ t @ $ t @$ t @X$ t $ t BhQ$ t 4rF$ t 4QY$ t b $ t $ t $ t  $ t $ 8\$$8.8\$$88\hh8t $ t @$ t @ $ t @$ t @X$ t $ t BhQ$ t 4rF$ t 4QY$ t b $ t $ t $ t  $ t $ 8\V$$88\U $$88\ 8<pX<pDpXW<p   <p <p<p <p <p <p <p <p W<p WW<p W<p    <p    <p    8\ hh8tp $ tp @$ tp @ $ tp @$ tp @X$ tp $ tp BhQ$ tp 4rF$ tp 4QY$ tp b $ tp $ tp $ tp  $ tp $ 8\& $$8pnetlink-packet-route-0.17.0/src/lib.rs000064400000000000000000000004141046102023000156600ustar 00000000000000// SPDX-License-Identifier: MIT #[macro_use] extern crate bitflags; pub mod rtnl; pub use self::rtnl::*; #[cfg(test)] #[macro_use] extern crate lazy_static; #[cfg(test)] #[macro_use] extern crate pretty_assertions; #[macro_use] extern crate netlink_packet_utils; netlink-packet-route-0.17.0/src/rtnl/address/buffer.rs000064400000000000000000000011551046102023000207720ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::DecodeError; use netlink_packet_utils::nla::{NlaBuffer, NlasIterator}; pub const ADDRESS_HEADER_LEN: usize = 8; buffer!(AddressMessageBuffer(ADDRESS_HEADER_LEN) { family: (u8, 0), prefix_len: (u8, 1), flags: (u8, 2), scope: (u8, 3), index: (u32, 4..ADDRESS_HEADER_LEN), payload: (slice, ADDRESS_HEADER_LEN..), }); impl<'a, T: AsRef<[u8]> + ?Sized> AddressMessageBuffer<&'a T> { pub fn nlas( &self, ) -> impl Iterator, DecodeError>> { NlasIterator::new(self.payload()) } } netlink-packet-route-0.17.0/src/rtnl/address/message.rs000064400000000000000000000046001046102023000211430ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; use crate::{nlas::address::Nla, AddressMessageBuffer, ADDRESS_HEADER_LEN}; #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct AddressMessage { pub header: AddressHeader, pub nlas: Vec, } #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct AddressHeader { pub family: u8, pub prefix_len: u8, pub flags: u8, pub scope: u8, pub index: u32, } impl Emitable for AddressHeader { fn buffer_len(&self) -> usize { ADDRESS_HEADER_LEN } fn emit(&self, buffer: &mut [u8]) { let mut packet = AddressMessageBuffer::new(buffer); packet.set_family(self.family); packet.set_prefix_len(self.prefix_len); packet.set_flags(self.flags); packet.set_scope(self.scope); packet.set_index(self.index); } } impl Emitable for AddressMessage { fn buffer_len(&self) -> usize { self.header.buffer_len() + self.nlas.as_slice().buffer_len() } fn emit(&self, buffer: &mut [u8]) { self.header.emit(buffer); self.nlas .as_slice() .emit(&mut buffer[self.header.buffer_len()..]); } } impl> Parseable> for AddressHeader { fn parse(buf: &AddressMessageBuffer) -> Result { Ok(Self { family: buf.family(), prefix_len: buf.prefix_len(), flags: buf.flags(), scope: buf.scope(), index: buf.index(), }) } } impl<'a, T: AsRef<[u8]> + 'a> Parseable> for AddressMessage { fn parse(buf: &AddressMessageBuffer<&'a T>) -> Result { Ok(AddressMessage { header: AddressHeader::parse(buf) .context("failed to parse address message header")?, nlas: Vec::::parse(buf) .context("failed to parse address message NLAs")?, }) } } impl<'a, T: AsRef<[u8]> + 'a> Parseable> for Vec { fn parse(buf: &AddressMessageBuffer<&'a T>) -> Result { let mut nlas = vec![]; for nla_buf in buf.nlas() { nlas.push(Nla::parse(&nla_buf?)?); } Ok(nlas) } } netlink-packet-route-0.17.0/src/rtnl/address/mod.rs000064400000000000000000000002241046102023000202740ustar 00000000000000// SPDX-License-Identifier: MIT mod buffer; pub use self::buffer::*; mod message; pub use self::message::*; pub mod nlas; pub use self::nlas::*; netlink-packet-route-0.17.0/src/rtnl/address/nlas/cache_info.rs000064400000000000000000000023411046102023000225320ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub struct CacheInfo { pub ifa_preferred: i32, pub ifa_valid: i32, pub cstamp: i32, pub tstamp: i32, } pub const ADDRESSS_CACHE_INFO_LEN: usize = 16; buffer!(CacheInfoBuffer(ADDRESSS_CACHE_INFO_LEN) { ifa_preferred: (i32, 0..4), ifa_valid: (i32, 4..8), cstamp: (i32, 8..12), tstamp: (i32, 12..16), }); impl> Parseable> for CacheInfo { fn parse(buf: &CacheInfoBuffer) -> Result { Ok(CacheInfo { ifa_preferred: buf.ifa_preferred(), ifa_valid: buf.ifa_valid(), cstamp: buf.cstamp(), tstamp: buf.tstamp(), }) } } impl Emitable for CacheInfo { fn buffer_len(&self) -> usize { ADDRESSS_CACHE_INFO_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = CacheInfoBuffer::new(buffer); buffer.set_ifa_preferred(self.ifa_preferred); buffer.set_ifa_valid(self.ifa_valid); buffer.set_cstamp(self.cstamp); buffer.set_tstamp(self.tstamp); } } netlink-packet-route-0.17.0/src/rtnl/address/nlas/mod.rs000064400000000000000000000071651046102023000212440ustar 00000000000000// SPDX-License-Identifier: MIT mod cache_info; pub use self::cache_info::*; use std::mem::size_of; use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{self, DefaultNla, NlaBuffer}, parsers::{parse_string, parse_u32}, traits::Parseable, DecodeError, }; use crate::constants::*; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum Nla { Unspec(Vec), Address(Vec), Local(Vec), Label(String), Broadcast(Vec), Anycast(Vec), CacheInfo(Vec), Multicast(Vec), Flags(u32), Other(DefaultNla), } impl nla::Nla for Nla { #[rustfmt::skip] fn value_len(&self) -> usize { use self::Nla::*; match *self { // Vec Unspec(ref bytes) | Address(ref bytes) | Local(ref bytes) | Broadcast(ref bytes) | Anycast(ref bytes) | Multicast(ref bytes) => bytes.len(), // strings: +1 because we need to append a nul byte Label(ref string) => string.as_bytes().len() + 1, // u32 Flags(_) => size_of::(), // Native CacheInfo(ref buffer) => buffer.len(), // Defaults Other(ref attr) => attr.value_len(), } } #[rustfmt::skip] fn emit_value(&self, buffer: &mut [u8]) { use self::Nla::*; match *self { // Vec Unspec(ref bytes) | Address(ref bytes) | Local(ref bytes) | Broadcast(ref bytes) | Anycast(ref bytes) | CacheInfo(ref bytes) | Multicast(ref bytes) => buffer.copy_from_slice(bytes.as_slice()), // String Label(ref string) => { buffer[..string.len()].copy_from_slice(string.as_bytes()); buffer[string.len()] = 0; } // u32 Flags(ref value) => NativeEndian::write_u32(buffer, *value), // Default Other(ref attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { use self::Nla::*; match *self { Unspec(_) => IFA_UNSPEC, Address(_) => IFA_ADDRESS, Local(_) => IFA_LOCAL, Label(_) => IFA_LABEL, Broadcast(_) => IFA_BROADCAST, Anycast(_) => IFA_ANYCAST, CacheInfo(_) => IFA_CACHEINFO, Multicast(_) => IFA_MULTICAST, Flags(_) => IFA_FLAGS, Other(ref nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Nla { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::Nla::*; let payload = buf.value(); Ok(match buf.kind() { IFA_UNSPEC => Unspec(payload.to_vec()), IFA_ADDRESS => Address(payload.to_vec()), IFA_LOCAL => Local(payload.to_vec()), IFA_LABEL => { Label(parse_string(payload).context("invalid IFA_LABEL value")?) } IFA_BROADCAST => Broadcast(payload.to_vec()), IFA_ANYCAST => Anycast(payload.to_vec()), IFA_CACHEINFO => CacheInfo(payload.to_vec()), IFA_MULTICAST => Multicast(payload.to_vec()), IFA_FLAGS => { Flags(parse_u32(payload).context("invalid IFA_FLAGS value")?) } kind => Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } netlink-packet-route-0.17.0/src/rtnl/buffer.rs000064400000000000000000000205771046102023000173560ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ traits::{Parseable, ParseableParametrized}, DecodeError, }; use crate::{ constants::*, AddressHeader, AddressMessage, AddressMessageBuffer, LinkMessage, LinkMessageBuffer, NeighbourMessage, NeighbourMessageBuffer, NeighbourTableMessage, NeighbourTableMessageBuffer, NsidMessage, NsidMessageBuffer, RouteHeader, RouteMessage, RouteMessageBuffer, RtnlMessage, RuleMessage, RuleMessageBuffer, TcMessage, TcMessageBuffer, }; buffer!(RtnlMessageBuffer); impl<'a, T: AsRef<[u8]> + ?Sized> ParseableParametrized, u16> for RtnlMessage { #[rustfmt::skip] fn parse_with_param(buf: &RtnlMessageBuffer<&'a T>, message_type: u16) -> Result { use self::RtnlMessage::*; let message = match message_type { // Link messages RTM_NEWLINK | RTM_GETLINK | RTM_DELLINK | RTM_SETLINK => { let msg = match LinkMessageBuffer::new_checked(&buf.inner()) { Ok(buf) => LinkMessage::parse(&buf).context("invalid link message")?, // HACK: iproute2 sends invalid RTM_GETLINK message, where the header is // limited to the interface family (1 byte) and 3 bytes of padding. Err(e) => { if buf.inner().len() == 4 && message_type == RTM_GETLINK { let mut msg = LinkMessage::default(); msg.header.interface_family = buf.inner()[0]; msg } else { return Err(e); } } }; match message_type { RTM_NEWLINK => NewLink(msg), RTM_GETLINK => GetLink(msg), RTM_DELLINK => DelLink(msg), RTM_SETLINK => SetLink(msg), _ => unreachable!(), } } // Address messages RTM_NEWADDR | RTM_GETADDR | RTM_DELADDR => { let msg = match AddressMessageBuffer::new_checked(&buf.inner()) { Ok(buf) => AddressMessage::parse(&buf).context("invalid link message")?, // HACK: iproute2 sends invalid RTM_GETADDR message, where the header is // limited to the interface family (1 byte) and 3 bytes of padding. Err(e) => { if buf.inner().len() == 4 && message_type == RTM_GETADDR { let mut msg = AddressMessage { header: AddressHeader::default(), nlas: vec![], }; msg.header.family = buf.inner()[0]; msg } else { return Err(e); } } }; match message_type { RTM_NEWADDR => NewAddress(msg), RTM_GETADDR => GetAddress(msg), RTM_DELADDR => DelAddress(msg), _ => unreachable!(), } } // Neighbour messages RTM_NEWNEIGH | RTM_GETNEIGH | RTM_DELNEIGH => { let err = "invalid neighbour message"; let msg = NeighbourMessage::parse(&NeighbourMessageBuffer::new_checked(&buf.inner()).context(err)?).context(err)?; match message_type { RTM_GETNEIGH => GetNeighbour(msg), RTM_NEWNEIGH => NewNeighbour(msg), RTM_DELNEIGH => DelNeighbour(msg), _ => unreachable!(), } } // Neighbour table messages RTM_NEWNEIGHTBL | RTM_GETNEIGHTBL | RTM_SETNEIGHTBL => { let err = "invalid neighbour table message"; let msg = NeighbourTableMessage::parse(&NeighbourTableMessageBuffer::new_checked(&buf.inner()).context(err)?).context(err)?; match message_type { RTM_GETNEIGHTBL => GetNeighbourTable(msg), RTM_NEWNEIGHTBL => NewNeighbourTable(msg), RTM_SETNEIGHTBL => SetNeighbourTable(msg), _ => unreachable!(), } } // Route messages RTM_NEWROUTE | RTM_GETROUTE | RTM_DELROUTE => { let msg = match RouteMessageBuffer::new_checked(&buf.inner()) { Ok(buf) => RouteMessage::parse(&buf).context("invalid route message")?, // HACK: iproute2 sends invalid RTM_GETROUTE message, where the header is // limited to the interface family (1 byte) and 3 bytes of padding. Err(e) => { // Not only does iproute2 sends invalid messages, it's also inconsistent in // doing so: for link and address messages, the length advertised in the // netlink header includes the 3 bytes of padding but it does not seem to // be the case for the route message, hence the buf.length() == 1 check. if (buf.inner().len() == 4 || buf.inner().len() == 1) && message_type == RTM_GETROUTE { let mut msg = RouteMessage { header: RouteHeader::default(), nlas: vec![], }; msg.header.address_family = buf.inner()[0]; msg } else { return Err(e); } } }; match message_type { RTM_NEWROUTE => NewRoute(msg), RTM_GETROUTE => GetRoute(msg), RTM_DELROUTE => DelRoute(msg), _ => unreachable!(), } } RTM_NEWRULE | RTM_GETRULE | RTM_DELRULE => { let err = "invalid fib rule message"; let msg = RuleMessage::parse(&RuleMessageBuffer::new_checked(&buf.inner()).context(err)?).context(err)?; match message_type { RTM_NEWRULE => NewRule(msg), RTM_DELRULE => DelRule(msg), RTM_GETRULE => GetRule(msg), _ => unreachable!() } } // TC Messages RTM_NEWQDISC | RTM_DELQDISC | RTM_GETQDISC | RTM_NEWTCLASS | RTM_DELTCLASS | RTM_GETTCLASS | RTM_NEWTFILTER | RTM_DELTFILTER | RTM_GETTFILTER | RTM_NEWCHAIN | RTM_DELCHAIN | RTM_GETCHAIN => { let err = "invalid tc message"; let msg = TcMessage::parse(&TcMessageBuffer::new_checked(&buf.inner()).context(err)?).context(err)?; match message_type { RTM_NEWQDISC => NewQueueDiscipline(msg), RTM_DELQDISC => DelQueueDiscipline(msg), RTM_GETQDISC => GetQueueDiscipline(msg), RTM_NEWTCLASS => NewTrafficClass(msg), RTM_DELTCLASS => DelTrafficClass(msg), RTM_GETTCLASS => GetTrafficClass(msg), RTM_NEWTFILTER => NewTrafficFilter(msg), RTM_DELTFILTER => DelTrafficFilter(msg), RTM_GETTFILTER => GetTrafficFilter(msg), RTM_NEWCHAIN => NewTrafficChain(msg), RTM_DELCHAIN => DelTrafficChain(msg), RTM_GETCHAIN => GetTrafficChain(msg), _ => unreachable!(), } } // ND ID Messages RTM_NEWNSID | RTM_GETNSID | RTM_DELNSID => { let err = "invalid nsid message"; let msg = NsidMessage::parse(&NsidMessageBuffer::new_checked(&buf.inner()).context(err)?).context(err)?; match message_type { RTM_NEWNSID => NewNsId(msg), RTM_DELNSID => DelNsId(msg), RTM_GETNSID => GetNsId(msg), _ => unreachable!(), } } _ => return Err(format!("Unknown message type: {message_type}").into()), }; Ok(message) } } netlink-packet-route-0.17.0/src/rtnl/constants.rs000064400000000000000000001337431046102023000201210ustar 00000000000000// SPDX-License-Identifier: MIT pub const RTM_BASE: u16 = 16; pub const RTM_NEWLINK: u16 = 16; pub const RTM_DELLINK: u16 = 17; pub const RTM_GETLINK: u16 = 18; pub const RTM_SETLINK: u16 = 19; pub const RTM_NEWADDR: u16 = 20; pub const RTM_DELADDR: u16 = 21; pub const RTM_GETADDR: u16 = 22; pub const RTM_NEWROUTE: u16 = 24; pub const RTM_DELROUTE: u16 = 25; pub const RTM_GETROUTE: u16 = 26; pub const RTM_NEWNEIGH: u16 = 28; pub const RTM_DELNEIGH: u16 = 29; pub const RTM_GETNEIGH: u16 = 30; pub const RTM_NEWRULE: u16 = 32; pub const RTM_DELRULE: u16 = 33; pub const RTM_GETRULE: u16 = 34; pub const RTM_NEWQDISC: u16 = 36; pub const RTM_DELQDISC: u16 = 37; pub const RTM_GETQDISC: u16 = 38; pub const RTM_NEWTCLASS: u16 = 40; pub const RTM_DELTCLASS: u16 = 41; pub const RTM_GETTCLASS: u16 = 42; pub const RTM_NEWTFILTER: u16 = 44; pub const RTM_DELTFILTER: u16 = 45; pub const RTM_GETTFILTER: u16 = 46; pub const RTM_NEWACTION: u16 = 48; pub const RTM_DELACTION: u16 = 49; pub const RTM_GETACTION: u16 = 50; pub const RTM_NEWPREFIX: u16 = 52; pub const RTM_GETMULTICAST: u16 = 58; pub const RTM_GETANYCAST: u16 = 62; pub const RTM_NEWNEIGHTBL: u16 = 64; pub const RTM_GETNEIGHTBL: u16 = 66; pub const RTM_SETNEIGHTBL: u16 = 67; pub const RTM_NEWNDUSEROPT: u16 = 68; pub const RTM_NEWADDRLABEL: u16 = 72; pub const RTM_DELADDRLABEL: u16 = 73; pub const RTM_GETADDRLABEL: u16 = 74; pub const RTM_GETDCB: u16 = 78; pub const RTM_SETDCB: u16 = 79; pub const RTM_NEWNETCONF: u16 = 80; pub const RTM_DELNETCONF: u16 = 81; pub const RTM_GETNETCONF: u16 = 82; pub const RTM_NEWMDB: u16 = 84; pub const RTM_DELMDB: u16 = 85; pub const RTM_GETMDB: u16 = 86; pub const RTM_NEWNSID: u16 = 88; pub const RTM_DELNSID: u16 = 89; pub const RTM_GETNSID: u16 = 90; pub const RTM_NEWSTATS: u16 = 92; pub const RTM_GETSTATS: u16 = 94; pub const RTM_NEWCACHEREPORT: u16 = 96; pub const RTM_NEWCHAIN: u16 = 100; pub const RTM_DELCHAIN: u16 = 101; pub const RTM_GETCHAIN: u16 = 102; pub const RTM_NEWLINKPROP: u16 = 108; pub const RTM_DELLINKPROP: u16 = 109; /// Unknown route pub const RTN_UNSPEC: u8 = 0; /// A gateway or direct route pub const RTN_UNICAST: u8 = 1; /// A local interface route pub const RTN_LOCAL: u8 = 2; /// A local broadcast route (sent as a broadcast) pub const RTN_BROADCAST: u8 = 3; /// A local broadcast route (sent as a unicast) pub const RTN_ANYCAST: u8 = 4; /// A multicast route pub const RTN_MULTICAST: u8 = 5; /// A packet dropping route pub const RTN_BLACKHOLE: u8 = 6; /// An unreachable destination pub const RTN_UNREACHABLE: u8 = 7; /// A packet rejection route pub const RTN_PROHIBIT: u8 = 8; /// Continue routing lookup in another table pub const RTN_THROW: u8 = 9; /// A network address translation rule pub const RTN_NAT: u8 = 10; /// Refer to an external resolver (not implemented) pub const RTN_XRESOLVE: u8 = 11; /// Unknown pub const RTPROT_UNSPEC: u8 = 0; /// Route was learnt by an ICMP redirect pub const RTPROT_REDIRECT: u8 = 1; /// Route was learnt by the kernel pub const RTPROT_KERNEL: u8 = 2; /// Route was learnt during boot pub const RTPROT_BOOT: u8 = 3; /// Route was set statically pub const RTPROT_STATIC: u8 = 4; pub const RTPROT_GATED: u8 = 8; pub const RTPROT_RA: u8 = 9; pub const RTPROT_MRT: u8 = 10; pub const RTPROT_ZEBRA: u8 = 11; pub const RTPROT_BIRD: u8 = 12; pub const RTPROT_DNROUTED: u8 = 13; pub const RTPROT_XORP: u8 = 14; pub const RTPROT_NTK: u8 = 15; pub const RTPROT_DHCP: u8 = 16; pub const RTPROT_MROUTED: u8 = 17; pub const RTPROT_BABEL: u8 = 42; /// The destination is globally valid. pub const RT_SCOPE_UNIVERSE: u8 = 0; /// (IPv6 only) the destination is site local, i.e. it is valid inside this /// site. This is for interior routes in the local autonomous system pub const RT_SCOPE_SITE: u8 = 200; /// The destination is link local pub const RT_SCOPE_LINK: u8 = 253; /// The destination is valid only on this host pub const RT_SCOPE_HOST: u8 = 254; /// Destination doesn't exist pub const RT_SCOPE_NOWHERE: u8 = 255; /// An unspecified routing table pub const RT_TABLE_UNSPEC: u8 = 0; /// A route table introduced for compatibility with old software which do not /// support table IDs greater than 255. See commit `709772e6e065` in the kernel: /// /// ```no_rust /// commit 709772e6e06564ed94ba740de70185ac3d792773 /// Author: Krzysztof Piotr Oledzki /// Date: Tue Jun 10 15:44:49 2008 -0700 /// /// net: Fix routing tables with id > 255 for legacy software /// /// Most legacy software do not like tables > 255 as rtm_table is u8 /// so tb_id is sent &0xff and it is possible to mismatch for example /// table 510 with table 254 (main). /// /// This patch introduces RT_TABLE_COMPAT=252 so the code uses it if /// tb_id > 255. It makes such old applications happy, new /// ones are still able to use RTA_TABLE to get a proper table id. /// /// Signed-off-by: Krzysztof Piotr Oledzki /// Acked-by: Patrick McHardy /// Signed-off-by: David S. Miller /// ``` pub const RT_TABLE_COMPAT: u8 = 252; /// The default routing table. /// /// The default table is empty and has little use. It has been kept when the /// current incarnation of advanced routing has been introduced in Linux 2.1.68 /// after a first tentative using "classes" in Linux 2.1.15. /// # Source /// /// This documentation is taken from [Vincent Bernat's excellent /// blog](https://vincent.bernat.ch/en/blog/2017-ipv4-route-lookup-linux#builtin-tables) pub const RT_TABLE_DEFAULT: u8 = 253; /// The main routing table. /// /// By default, apart from the local ones which are added to the local table, /// routes that are added to this table. pub const RT_TABLE_MAIN: u8 = 254; /// The local table. /// /// This table is populated automatically by the kernel when addresses are /// configured. /// /// On a machine that has `192.168.44.211/24` configured on `wlp58s0`, /// `iproute2` shows the following routes in the local table: /// /// ```no_rust /// $ ip route show table local /// /// broadcast 127.0.0.0 dev lo proto kernel scope link src 127.0.0.1 /// local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1 /// local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1 /// broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1 /// /// broadcast 192.168.44.0 dev wlp58s0 proto kernel scope link src 192.168.44.211 /// local 192.168.44.211 dev wlp58s0 proto kernel scope host src 192.168.44.211 /// broadcast 192.168.44.255 dev wlp58s0 proto kernel scope link src 192.168.44.211 /// ``` /// /// When the IP address `192.168.44.211` was configured on the `wlp58s0` /// interface, the kernel automatically added the appropriate routes: /// /// - a route for `192.168.44.211` for local unicast delivery to the IP address /// - a route for `192.168.44.255` for broadcast delivery to the broadcast /// address /// - a route for `192.168.44.0` for broadcast delivery to the network address /// /// When `127.0.0.1` was configured on the loopback interface, the same kind of /// routes were added to the local table. However, a loopback address receives a /// special treatment and the kernel also adds the whole subnet to the local /// table. /// /// Note that this is similar for IPv6: /// /// ```no_rust /// $ ip -6 route show table local /// local ::1 dev lo proto kernel metric 0 pref medium /// local fe80::7de1:4914:99b7:aa28 dev wlp58s0 proto kernel metric 0 pref medium /// ff00::/8 dev wlp58s0 metric 256 pref medium /// ``` /// /// # Source /// /// This documentation is adapted from [Vincent Bernat's excellent /// blog](https://vincent.bernat.ch/en/blog/2017-ipv4-route-lookup-linux#builtin-tables) pub const RT_TABLE_LOCAL: u8 = 255; /// If the route changes, notify the user via rtnetlink pub const RTM_F_NOTIFY: u32 = 256; /// This route is cloned. Cloned routes are routes coming from the cache instead /// of the FIB. For IPv4, the cache was removed in Linux 3.6 (see [IPv4 route /// lookup on Linux] for more information about IPv4 routing) /// /// [IPv4 route lookup on Linux]: https://vincent.bernat.ch/en/blog/2017-ipv4-route-lookup-linux pub const RTM_F_CLONED: u32 = 512; /// Multipath equalizer (not yet implemented) pub const RTM_F_EQUALIZE: u32 = 1024; /// Prefix addresses pub const RTM_F_PREFIX: u32 = 2048; /// Show the table from which the lookup result comes. Note that before commit /// `c36ba6603a11`, Linux would always hardcode [`RouteMessageHeader.table`] /// (known as `rtmsg.rtm_table` in the kernel) to `RT_TABLE_MAIN`. /// /// [`RouteMessageHeader.table`]: ../struct.RouteMessageHeader.html#structfield.table pub const RTM_F_LOOKUP_TABLE: u32 = 4096; /// Return the full FIB lookup match (see commit /// `b61798130f1be5bff08712308126c2d7ebe390ef`) pub const RTM_F_FIB_MATCH: u32 = 8192; pub const AF_UNSPEC: u16 = libc::AF_UNSPEC as u16; pub const AF_UNIX: u16 = libc::AF_UNIX as u16; // pub const AF_LOCAL: u16 = libc::AF_LOCAL as u16; pub const AF_INET: u16 = libc::AF_INET as u16; pub const AF_AX25: u16 = libc::AF_AX25 as u16; pub const AF_IPX: u16 = libc::AF_IPX as u16; pub const AF_APPLETALK: u16 = libc::AF_APPLETALK as u16; pub const AF_NETROM: u16 = libc::AF_NETROM as u16; pub const AF_BRIDGE: u16 = libc::AF_BRIDGE as u16; pub const AF_ATMPVC: u16 = libc::AF_ATMPVC as u16; pub const AF_X25: u16 = libc::AF_X25 as u16; pub const AF_INET6: u16 = libc::AF_INET6 as u16; pub const AF_ROSE: u16 = libc::AF_ROSE as u16; pub const AF_DECNET: u16 = libc::AF_DECnet as u16; pub const AF_NETBEUI: u16 = libc::AF_NETBEUI as u16; pub const AF_SECURITY: u16 = libc::AF_SECURITY as u16; pub const AF_KEY: u16 = libc::AF_KEY as u16; pub const AF_NETLINK: u16 = libc::AF_NETLINK as u16; // pub const AF_ROUTE: u16 = libc::AF_ROUTE as u16; pub const AF_PACKET: u16 = libc::AF_PACKET as u16; pub const AF_ASH: u16 = libc::AF_ASH as u16; pub const AF_ECONET: u16 = libc::AF_ECONET as u16; pub const AF_ATMSVC: u16 = libc::AF_ATMSVC as u16; pub const AF_RDS: u16 = libc::AF_RDS as u16; pub const AF_SNA: u16 = libc::AF_SNA as u16; pub const AF_IRDA: u16 = libc::AF_IRDA as u16; pub const AF_PPPOX: u16 = libc::AF_PPPOX as u16; pub const AF_WANPIPE: u16 = libc::AF_WANPIPE as u16; pub const AF_LLC: u16 = libc::AF_LLC as u16; pub const AF_CAN: u16 = libc::AF_CAN as u16; pub const AF_TIPC: u16 = libc::AF_TIPC as u16; pub const AF_BLUETOOTH: u16 = libc::AF_BLUETOOTH as u16; pub const AF_IUCV: u16 = libc::AF_IUCV as u16; pub const AF_RXRPC: u16 = libc::AF_RXRPC as u16; pub const AF_ISDN: u16 = libc::AF_ISDN as u16; pub const AF_PHONET: u16 = libc::AF_PHONET as u16; pub const AF_IEEE802154: u16 = libc::AF_IEEE802154 as u16; pub const AF_CAIF: u16 = libc::AF_CAIF as u16; pub const AF_ALG: u16 = libc::AF_ALG as u16; pub const NETNSA_NONE: u16 = 0; pub const NETNSA_NSID: u16 = 1; pub const NETNSA_PID: u16 = 2; pub const NETNSA_FD: u16 = 3; pub const NETNSA_NSID_NOT_ASSIGNED: i32 = -1; /// Neighbour cache entry state: the neighbour has not (yet) been resolved pub const NUD_INCOMPLETE: u16 = 1; /// Neighbour cache entry state: the neighbour entry is valid until its lifetime /// expires pub const NUD_REACHABLE: u16 = 2; /// Neighbour cache entry state: the neighbour entry is valid but suspicious pub const NUD_STALE: u16 = 4; /// Neighbour cache entry state: the validation of this entry is currently /// delayed pub const NUD_DELAY: u16 = 8; /// Neighbour cache entry state: the neighbour entry is being probed pub const NUD_PROBE: u16 = 16; /// Neighbour cache entry state: the validation of this entry has failed pub const NUD_FAILED: u16 = 32; /// Neighbour cache entry state: entry is valid and the kernel will not try to /// validate or refresh it. pub const NUD_NOARP: u16 = 64; /// Neighbour cache entry state: entry is valid forever and can only be removed /// explicitly from userspace. pub const NUD_PERMANENT: u16 = 128; /// Neighbour cache entry state: pseudo state for fresh entries or before /// deleting entries pub const NUD_NONE: u16 = 0; // Neighbour cache entry flags pub const NTF_USE: u8 = 1; pub const NTF_SELF: u8 = 2; pub const NTF_MASTER: u8 = 4; pub const NTF_PROXY: u8 = 8; pub const NTF_EXT_LEARNED: u8 = 16; pub const NTF_OFFLOADED: u8 = 32; pub const NTF_ROUTER: u8 = 128; pub const TCA_UNSPEC: u16 = 0; pub const TCA_KIND: u16 = 1; pub const TCA_OPTIONS: u16 = 2; pub const TCA_STATS: u16 = 3; pub const TCA_XSTATS: u16 = 4; pub const TCA_RATE: u16 = 5; pub const TCA_FCNT: u16 = 6; pub const TCA_STATS2: u16 = 7; pub const TCA_STAB: u16 = 8; pub const TCA_PAD: u16 = 9; pub const TCA_DUMP_INVISIBLE: u16 = 10; pub const TCA_CHAIN: u16 = 11; pub const TCA_HW_OFFLOAD: u16 = 12; pub const TCA_INGRESS_BLOCK: u16 = 13; pub const TCA_EGRESS_BLOCK: u16 = 14; pub const TCA_STATS_UNSPEC: u16 = 0; pub const TCA_STATS_BASIC: u16 = 1; pub const TCA_STATS_RATE_EST: u16 = 2; pub const TCA_STATS_QUEUE: u16 = 3; pub const TCA_STATS_APP: u16 = 4; pub const TCA_STATS_RATE_EST64: u16 = 5; pub const TCA_STATS_PAD: u16 = 6; pub const TCA_STATS_BASIC_HW: u16 = 7; pub const NDTA_UNSPEC: u16 = 0; pub const NDTA_NAME: u16 = 1; pub const NDTA_THRESH1: u16 = 2; pub const NDTA_THRESH2: u16 = 3; pub const NDTA_THRESH3: u16 = 4; pub const NDTA_CONFIG: u16 = 5; pub const NDTA_PARMS: u16 = 6; pub const NDTA_STATS: u16 = 7; pub const NDTA_GC_INTERVAL: u16 = 8; pub const NDTA_PAD: u16 = 9; pub const RTA_UNSPEC: u16 = 0; pub const RTA_DST: u16 = 1; pub const RTA_SRC: u16 = 2; pub const RTA_IIF: u16 = 3; pub const RTA_OIF: u16 = 4; pub const RTA_GATEWAY: u16 = 5; pub const RTA_PRIORITY: u16 = 6; pub const RTA_PREFSRC: u16 = 7; pub const RTA_METRICS: u16 = 8; pub const RTA_MULTIPATH: u16 = 9; pub const RTA_PROTOINFO: u16 = 10; pub const RTA_FLOW: u16 = 11; pub const RTA_CACHEINFO: u16 = 12; pub const RTA_SESSION: u16 = 13; pub const RTA_MP_ALGO: u16 = 14; pub const RTA_TABLE: u16 = 15; pub const RTA_MARK: u16 = 16; pub const RTA_MFC_STATS: u16 = 17; pub const RTA_VIA: u16 = 18; pub const RTA_NEWDST: u16 = 19; pub const RTA_PREF: u16 = 20; pub const RTA_ENCAP_TYPE: u16 = 21; pub const RTA_ENCAP: u16 = 22; pub const RTA_EXPIRES: u16 = 23; pub const RTA_PAD: u16 = 24; pub const RTA_UID: u16 = 25; pub const RTA_TTL_PROPAGATE: u16 = 26; pub const RTAX_UNSPEC: u16 = 0; pub const RTAX_LOCK: u16 = 1; pub const RTAX_MTU: u16 = 2; pub const RTAX_WINDOW: u16 = 3; pub const RTAX_RTT: u16 = 4; pub const RTAX_RTTVAR: u16 = 5; pub const RTAX_SSTHRESH: u16 = 6; pub const RTAX_CWND: u16 = 7; pub const RTAX_ADVMSS: u16 = 8; pub const RTAX_REORDERING: u16 = 9; pub const RTAX_HOPLIMIT: u16 = 10; pub const RTAX_INITCWND: u16 = 11; pub const RTAX_FEATURES: u16 = 12; pub const RTAX_RTO_MIN: u16 = 13; pub const RTAX_INITRWND: u16 = 14; pub const RTAX_QUICKACK: u16 = 15; pub const RTAX_CC_ALGO: u16 = 16; pub const RTAX_FASTOPEN_NO_COOKIE: u16 = 17; pub const IFLA_INFO_UNSPEC: u16 = 0; pub const IFLA_INFO_KIND: u16 = 1; pub const IFLA_INFO_DATA: u16 = 2; pub const IFLA_INFO_XSTATS: u16 = 3; pub const IFLA_INFO_PORT_KIND: u16 = 4; pub const IFLA_INFO_PORT_DATA: u16 = 5; // Bridge flags pub const IFLA_BRIDGE_FLAGS: u16 = 0; pub const BRIDGE_FLAGS_MASTER: u16 = 1; /* Bridge command to/from master */ pub const BRIDGE_FLAGS_SELF: u16 = 2; /* Bridge command to/from lowerdev */ pub const IFLA_BRIDGE_VLAN_INFO: u16 = 2; pub const BRIDGE_VLAN_INFO_MASTER: u16 = 1 << 0; pub const BRIDGE_VLAN_INFO_PVID: u16 = 1 << 1; pub const BRIDGE_VLAN_INFO_UNTAGGED: u16 = 1 << 2; pub const BRIDGE_VLAN_INFO_RANGE_BEGIN: u16 = 1 << 3; pub const BRIDGE_VLAN_INFO_RANGE_END: u16 = 1 << 4; pub const BRIDGE_VLAN_INFO_BRENTRY: u16 = 1 << 5; pub const BRIDGE_VLAN_INFO_ONLY_OPTS: u16 = 1 << 6; pub const IFLA_BR_UNSPEC: u16 = 0; pub const IFLA_BR_FORWARD_DELAY: u16 = 1; pub const IFLA_BR_HELLO_TIME: u16 = 2; pub const IFLA_BR_MAX_AGE: u16 = 3; pub const IFLA_BR_AGEING_TIME: u16 = 4; pub const IFLA_BR_STP_STATE: u16 = 5; pub const IFLA_BR_PRIORITY: u16 = 6; pub const IFLA_BR_VLAN_FILTERING: u16 = 7; pub const IFLA_BR_VLAN_PROTOCOL: u16 = 8; pub const IFLA_BR_GROUP_FWD_MASK: u16 = 9; pub const IFLA_BR_ROOT_ID: u16 = 10; pub const IFLA_BR_BRIDGE_ID: u16 = 11; pub const IFLA_BR_ROOT_PORT: u16 = 12; pub const IFLA_BR_ROOT_PATH_COST: u16 = 13; pub const IFLA_BR_TOPOLOGY_CHANGE: u16 = 14; pub const IFLA_BR_TOPOLOGY_CHANGE_DETECTED: u16 = 15; pub const IFLA_BR_HELLO_TIMER: u16 = 16; pub const IFLA_BR_TCN_TIMER: u16 = 17; pub const IFLA_BR_TOPOLOGY_CHANGE_TIMER: u16 = 18; pub const IFLA_BR_GC_TIMER: u16 = 19; pub const IFLA_BR_GROUP_ADDR: u16 = 20; pub const IFLA_BR_FDB_FLUSH: u16 = 21; pub const IFLA_BR_MCAST_ROUTER: u16 = 22; pub const IFLA_BR_MCAST_SNOOPING: u16 = 23; pub const IFLA_BR_MCAST_QUERY_USE_IFADDR: u16 = 24; pub const IFLA_BR_MCAST_QUERIER: u16 = 25; pub const IFLA_BR_MCAST_HASH_ELASTICITY: u16 = 26; pub const IFLA_BR_MCAST_HASH_MAX: u16 = 27; pub const IFLA_BR_MCAST_LAST_MEMBER_CNT: u16 = 28; pub const IFLA_BR_MCAST_STARTUP_QUERY_CNT: u16 = 29; pub const IFLA_BR_MCAST_LAST_MEMBER_INTVL: u16 = 30; pub const IFLA_BR_MCAST_MEMBERSHIP_INTVL: u16 = 31; pub const IFLA_BR_MCAST_QUERIER_INTVL: u16 = 32; pub const IFLA_BR_MCAST_QUERY_INTVL: u16 = 33; pub const IFLA_BR_MCAST_QUERY_RESPONSE_INTVL: u16 = 34; pub const IFLA_BR_MCAST_STARTUP_QUERY_INTVL: u16 = 35; pub const IFLA_BR_NF_CALL_IPTABLES: u16 = 36; pub const IFLA_BR_NF_CALL_IP6TABLES: u16 = 37; pub const IFLA_BR_NF_CALL_ARPTABLES: u16 = 38; pub const IFLA_BR_VLAN_DEFAULT_PVID: u16 = 39; pub const IFLA_BR_PAD: u16 = 40; pub const IFLA_BR_VLAN_STATS_ENABLED: u16 = 41; pub const IFLA_BR_MCAST_STATS_ENABLED: u16 = 42; pub const IFLA_BR_MCAST_IGMP_VERSION: u16 = 43; pub const IFLA_BR_MCAST_MLD_VERSION: u16 = 44; pub const IFLA_BR_VLAN_STATS_PER_PORT: u16 = 45; pub const IFLA_BR_MULTI_BOOLOPT: u16 = 46; pub const IFLA_BR_MCAST_QUERIER_STATE: u16 = 47; pub const IFLA_MACVLAN_UNSPEC: u16 = 0; pub const IFLA_MACVLAN_MODE: u16 = 1; pub const IFLA_MACVLAN_FLAGS: u16 = 2; pub const IFLA_MACVLAN_MACADDR_MODE: u16 = 3; pub const IFLA_MACVLAN_MACADDR: u16 = 4; pub const IFLA_MACVLAN_MACADDR_DATA: u16 = 5; pub const IFLA_MACVLAN_MACADDR_COUNT: u16 = 6; pub const IFLA_MACVLAN_BC_QUEUE_LEN: u16 = 7; pub const IFLA_MACVLAN_BC_QUEUE_LEN_USED: u16 = 8; pub const IFLA_MACVLAN_BC_CUTOFF: u16 = 9; pub const IFLA_VLAN_UNSPEC: u16 = 0; pub const IFLA_VLAN_ID: u16 = 1; pub const IFLA_VLAN_FLAGS: u16 = 2; pub const IFLA_VLAN_EGRESS_QOS: u16 = 3; pub const IFLA_VLAN_INGRESS_QOS: u16 = 4; pub const IFLA_VLAN_PROTOCOL: u16 = 5; pub const IFLA_VLAN_QOS_UNSPEC: u16 = 0; pub const IFLA_VLAN_QOS_MAPPING: u16 = 1; pub const IFLA_VRF_UNSPEC: u16 = 0; pub const IFLA_VRF_TABLE: u16 = 1; pub const IFLA_IPVLAN_UNSPEC: u16 = 0; pub const IFLA_IPVLAN_MODE: u16 = 1; pub const IFLA_IPVLAN_FLAGS: u16 = 2; pub const IFLA_IPOIB_UNSPEC: u16 = 0; pub const IFLA_IPOIB_PKEY: u16 = 1; pub const IFLA_IPOIB_MODE: u16 = 2; pub const IFLA_IPOIB_UMCAST: u16 = 3; pub const VETH_INFO_UNSPEC: u16 = 0; pub const VETH_INFO_PEER: u16 = 1; pub const IFLA_XFRM_UNSPEC: u16 = 0; pub const IFLA_XFRM_LINK: u16 = 1; pub const IFLA_XFRM_IF_ID: u16 = 2; pub const ARPHRD_NETROM: u16 = 0; pub const ARPHRD_ETHER: u16 = 1; pub const ARPHRD_EETHER: u16 = 2; pub const ARPHRD_AX25: u16 = 3; pub const ARPHRD_PRONET: u16 = 4; pub const ARPHRD_CHAOS: u16 = 5; pub const ARPHRD_IEEE802: u16 = 6; pub const ARPHRD_ARCNET: u16 = 7; pub const ARPHRD_APPLETLK: u16 = 8; pub const ARPHRD_DLCI: u16 = 15; pub const ARPHRD_ATM: u16 = 19; pub const ARPHRD_METRICOM: u16 = 23; pub const ARPHRD_IEEE1394: u16 = 24; pub const ARPHRD_EUI64: u16 = 27; pub const ARPHRD_INFINIBAND: u16 = 32; pub const ARPHRD_SLIP: u16 = 256; pub const ARPHRD_CSLIP: u16 = 257; pub const ARPHRD_SLIP6: u16 = 258; pub const ARPHRD_CSLIP6: u16 = 259; pub const ARPHRD_RSRVD: u16 = 260; pub const ARPHRD_ADAPT: u16 = 264; pub const ARPHRD_ROSE: u16 = 270; pub const ARPHRD_X25: u16 = 271; pub const ARPHRD_HWX25: u16 = 272; pub const ARPHRD_CAN: u16 = 280; pub const ARPHRD_PPP: u16 = 512; pub const ARPHRD_CISCO: u16 = 513; pub const ARPHRD_HDLC: u16 = 513; pub const ARPHRD_LAPB: u16 = 516; pub const ARPHRD_DDCMP: u16 = 517; pub const ARPHRD_RAWHDLC: u16 = 518; pub const ARPHRD_RAWIP: u16 = 519; pub const ARPHRD_TUNNEL: u16 = 768; pub const ARPHRD_TUNNEL6: u16 = 769; pub const ARPHRD_FRAD: u16 = 770; pub const ARPHRD_SKIP: u16 = 771; pub const ARPHRD_LOOPBACK: u16 = 772; pub const ARPHRD_LOCALTLK: u16 = 773; pub const ARPHRD_FDDI: u16 = 774; pub const ARPHRD_BIF: u16 = 775; pub const ARPHRD_SIT: u16 = 776; pub const ARPHRD_IPDDP: u16 = 777; pub const ARPHRD_IPGRE: u16 = 778; pub const ARPHRD_PIMREG: u16 = 779; pub const ARPHRD_HIPPI: u16 = 780; pub const ARPHRD_ASH: u16 = 781; pub const ARPHRD_ECONET: u16 = 782; pub const ARPHRD_IRDA: u16 = 783; pub const ARPHRD_FCPP: u16 = 784; pub const ARPHRD_FCAL: u16 = 785; pub const ARPHRD_FCPL: u16 = 786; pub const ARPHRD_FCFABRIC: u16 = 787; pub const ARPHRD_IEEE802_TR: u16 = 800; pub const ARPHRD_IEEE80211: u16 = 801; pub const ARPHRD_IEEE80211_PRISM: u16 = 802; pub const ARPHRD_IEEE80211_RADIOTAP: u16 = 803; pub const ARPHRD_IEEE802154: u16 = 804; pub const ARPHRD_IEEE802154_MONITOR: u16 = 805; pub const ARPHRD_PHONET: u16 = 820; pub const ARPHRD_PHONET_PIPE: u16 = 821; pub const ARPHRD_CAIF: u16 = 822; pub const ARPHRD_IP6GRE: u16 = 823; pub const ARPHRD_NETLINK: u16 = 824; pub const ARPHRD_6LOWPAN: u16 = 825; pub const ARPHRD_VSOCKMON: u16 = 826; pub const ARPHRD_VOID: u16 = 65535; pub const ARPHRD_NONE: u16 = 65534; pub const IFA_UNSPEC: u16 = 0; pub const IFA_ADDRESS: u16 = 1; pub const IFA_LOCAL: u16 = 2; pub const IFA_LABEL: u16 = 3; pub const IFA_BROADCAST: u16 = 4; pub const IFA_ANYCAST: u16 = 5; pub const IFA_CACHEINFO: u16 = 6; pub const IFA_MULTICAST: u16 = 7; pub const IFA_FLAGS: u16 = 8; pub const IFLA_UNSPEC: u16 = 0; pub const IFLA_ADDRESS: u16 = 1; pub const IFLA_BROADCAST: u16 = 2; pub const IFLA_IFNAME: u16 = 3; pub const IFLA_MTU: u16 = 4; pub const IFLA_LINK: u16 = 5; pub const IFLA_QDISC: u16 = 6; pub const IFLA_STATS: u16 = 7; pub const IFLA_COST: u16 = 8; pub const IFLA_PRIORITY: u16 = 9; pub const IFLA_MASTER: u16 = 10; pub const IFLA_WIRELESS: u16 = 11; pub const IFLA_PROTINFO: u16 = 12; pub const IFLA_TXQLEN: u16 = 13; pub const IFLA_MAP: u16 = 14; pub const IFLA_WEIGHT: u16 = 15; pub const IFLA_OPERSTATE: u16 = 16; pub const IFLA_LINKMODE: u16 = 17; pub const IFLA_LINKINFO: u16 = 18; pub const IFLA_NET_NS_PID: u16 = 19; pub const IFLA_IFALIAS: u16 = 20; pub const IFLA_NUM_VF: u16 = 21; pub const IFLA_VFINFO_LIST: u16 = 22; pub const IFLA_STATS64: u16 = 23; pub const IFLA_VF_PORTS: u16 = 24; pub const IFLA_PORT_SELF: u16 = 25; pub const IFLA_AF_SPEC: u16 = 26; pub const IFLA_GROUP: u16 = 27; pub const IFLA_NET_NS_FD: u16 = 28; pub const IFLA_EXT_MASK: u16 = 29; pub const IFLA_PROMISCUITY: u16 = 30; pub const IFLA_NUM_TX_QUEUES: u16 = 31; pub const IFLA_NUM_RX_QUEUES: u16 = 32; pub const IFLA_CARRIER: u16 = 33; pub const IFLA_PHYS_PORT_ID: u16 = 34; pub const IFLA_CARRIER_CHANGES: u16 = 35; pub const IFLA_PHYS_SWITCH_ID: u16 = 36; pub const IFLA_LINK_NETNSID: u16 = 37; pub const IFLA_PHYS_PORT_NAME: u16 = 38; pub const IFLA_PROTO_DOWN: u16 = 39; pub const IFLA_GSO_MAX_SEGS: u16 = 40; pub const IFLA_GSO_MAX_SIZE: u16 = 41; pub const IFLA_PAD: u16 = 42; pub const IFLA_XDP: u16 = 43; pub const IFLA_EVENT: u16 = 44; pub const IFLA_NEW_NETNSID: u16 = 45; pub const IFLA_IF_NETNSID: u16 = 46; pub const IFLA_CARRIER_UP_COUNT: u16 = 47; pub const IFLA_CARRIER_DOWN_COUNT: u16 = 48; pub const IFLA_NEW_IFINDEX: u16 = 49; pub const IFLA_MIN_MTU: u16 = 50; pub const IFLA_MAX_MTU: u16 = 51; pub const IFLA_PROP_LIST: u16 = 52; pub const IFLA_ALT_IFNAME: u16 = 53; pub const IFLA_PERM_ADDRESS: u16 = 54; pub const IFLA_PROTO_DOWN_REASON: u16 = 55; pub const IFLA_INET_UNSPEC: u16 = 0; pub const IFLA_INET_CONF: u16 = 1; pub const IFLA_INET6_UNSPEC: u16 = 0; pub const IFLA_INET6_FLAGS: u16 = 1; pub const IFLA_INET6_CONF: u16 = 2; pub const IFLA_INET6_STATS: u16 = 3; // pub const IFLA_INET6_MCAST: u16 = 4; pub const IFLA_INET6_CACHEINFO: u16 = 5; pub const IFLA_INET6_ICMP6STATS: u16 = 6; pub const IFLA_INET6_TOKEN: u16 = 7; pub const IFLA_INET6_ADDR_GEN_MODE: u16 = 8; /// Link is up (administratively). pub const IFF_UP: u32 = libc::IFF_UP as u32; /// Link is up and carrier is OK (RFC2863 OPER_UP) pub const IFF_RUNNING: u32 = libc::IFF_RUNNING as u32; /// Link layer is operational pub const IFF_LOWER_UP: u32 = libc::IFF_LOWER_UP as u32; /// Driver signals IFF_DORMANT pub const IFF_DORMANT: u32 = libc::IFF_DORMANT as u32; /// Link supports broadcasting pub const IFF_BROADCAST: u32 = libc::IFF_BROADCAST as u32; /// Link supports multicasting pub const IFF_MULTICAST: u32 = libc::IFF_MULTICAST as u32; /// Link supports multicast routing pub const IFF_ALLMULTI: u32 = libc::IFF_ALLMULTI as u32; /// Tell driver to do debugging (currently unused) pub const IFF_DEBUG: u32 = libc::IFF_DEBUG as u32; /// Link loopback network pub const IFF_LOOPBACK: u32 = libc::IFF_LOOPBACK as u32; /// u32erface is point-to-point link pub const IFF_POINTOPOINT: u32 = libc::IFF_POINTOPOINT as u32; /// ARP is not supported pub const IFF_NOARP: u32 = libc::IFF_NOARP as u32; /// Receive all packets. pub const IFF_PROMISC: u32 = libc::IFF_PROMISC as u32; /// Master of a load balancer (bonding) pub const IFF_MASTER: u32 = libc::IFF_MASTER as u32; /// Link selects port automatically (only used by ARM ethernet) pub const IFF_PORTSEL: u32 = libc::IFF_PORTSEL as u32; /// Driver supports setting media type (only used by ARM ethernet) pub const IFF_AUTOMEDIA: u32 = libc::IFF_AUTOMEDIA as u32; // /// Echo sent packets (testing feature, CAN only) // pub const IFF_ECHO: u32 = libc::IFF_ECHO as u32; // /// Dialup device with changing addresses (unused, BSD compatibility) // pub const IFF_DYNAMIC: u32 = libc::IFF_DYNAMIC as u32; // /// Avoid use of trailers (unused, BSD compatibility) // pub const IFF_NOTRAILERS: u32 = libc::IFF_NOTRAILERS as u32; pub const IF_OPER_UNKNOWN: u8 = 0; pub const IF_OPER_NOTPRESENT: u8 = 1; pub const IF_OPER_DOWN: u8 = 2; pub const IF_OPER_LOWERLAYERDOWN: u8 = 3; pub const IF_OPER_TESTING: u8 = 4; pub const IF_OPER_DORMANT: u8 = 5; pub const IF_OPER_UP: u8 = 6; /// Neighbour cache entry type: unknown type pub const NDA_UNSPEC: u16 = 0; /// Neighbour cache entry type: entry for a network layer destination /// address pub const NDA_DST: u16 = 1; /// Neighbour cache entry type: entry for a link layer destination /// address pub const NDA_LLADDR: u16 = 2; /// Neighbour cache entry type: entry for cache statistics pub const NDA_CACHEINFO: u16 = 3; pub const NDA_PROBES: u16 = 4; pub const NDA_VLAN: u16 = 5; pub const NDA_PORT: u16 = 6; pub const NDA_VNI: u16 = 7; pub const NDA_IFINDEX: u16 = 8; pub const NDA_MASTER: u16 = 9; pub const NDA_LINK_NETNSID: u16 = 10; pub const NDA_SRC_VNI: u16 = 11; /// see `https://github.com/torvalds/linux/blob/master/include/uapi/linux/fib_rules.h` pub const FR_ACT_UNSPEC: u8 = 0; /// Pass to fixed table pub const FR_ACT_TO_TBL: u8 = 1; /// Jump to another rule pub const FR_ACT_GOTO: u8 = 2; /// No operation pub const FR_ACT_NOP: u8 = 3; pub const FR_ACT_RES3: u8 = 4; pub const FR_ACT_RES4: u8 = 5; /// Drop without notification pub const FR_ACT_BLACKHOLE: u8 = 6; /// Drop with `ENETUNREACH` pub const FR_ACT_UNREACHABLE: u8 = 7; /// Drop with `EACCES` pub const FR_ACT_PROHIBIT: u8 = 8; pub const FRA_UNSPEC: u16 = 0; /// Destination address pub const FRA_DST: u16 = 1; /// Source address pub const FRA_SRC: u16 = 2; /// Interface name pub const FRA_IIFNAME: u16 = 3; /// Target to jump to pub const FRA_GOTO: u16 = 4; pub const FRA_UNUSED2: u16 = 5; /// priority/preference pub const FRA_PRIORITY: u16 = 6; pub const FRA_UNUSED3: u16 = 7; pub const FRA_UNUSED4: u16 = 8; pub const FRA_UNUSED5: u16 = 9; /// mark pub const FRA_FWMARK: u16 = 10; /// flow/class id pub const FRA_FLOW: u16 = 11; pub const FRA_TUN_ID: u16 = 12; pub const FRA_SUPPRESS_IFGROUP: u16 = 13; pub const FRA_SUPPRESS_PREFIXLEN: u16 = 14; /// Extended table id pub const FRA_TABLE: u16 = 15; /// mask for netfilter mark pub const FRA_FWMASK: u16 = 16; pub const FRA_OIFNAME: u16 = 17; pub const FRA_PAD: u16 = 18; /// iif or oif is l3mdev goto its table pub const FRA_L3MDEV: u16 = 19; /// UID range pub const FRA_UID_RANGE: u16 = 20; /// Originator of the rule pub const FRA_PROTOCOL: u16 = 21; /// IP protocol pub const FRA_IP_PROTO: u16 = 22; /// Source port pub const FRA_SPORT_RANGE: u16 = 23; /// Destination port pub const FRA_DPORT_RANGE: u16 = 24; pub const FIB_RULE_PERMANENT: u32 = 1; pub const FIB_RULE_INVERT: u32 = 2; pub const FIB_RULE_UNRESOLVED: u32 = 4; pub const FIB_RULE_IIF_DETACHED: u32 = 8; pub const FIB_RULE_DEV_DETACHED: u32 = FIB_RULE_IIF_DETACHED; pub const FIB_RULE_OIF_DETACHED: u32 = 10; /// try to find source address in routing lookups pub const FIB_RULE_FIND_SADDR: u32 = 10000; // pub const MACVLAN_FLAG_NOPROMISC: int = 1; // pub const IPVLAN_F_PRIVATE: int = 1; // pub const IPVLAN_F_VEPA: int = 2; // pub const MAX_VLAN_LIST_LEN: int = 1; // pub const PORT_PROFILE_MAX: int = 40; // pub const PORT_UUID_MAX: int = 16; // pub const PORT_SELF_VF: int = -1; // pub const XDP_FLAGS_UPDATE_IF_NOEXIST: int = 1; // pub const XDP_FLAGS_SKB_MODE: int = 2; // pub const XDP_FLAGS_DRV_MODE: int = 4; // pub const XDP_FLAGS_HW_MODE: int = 8; // pub const XDP_FLAGS_MODES: int = 14; // pub const XDP_FLAGS_MASK: int = 15; pub const IFA_F_SECONDARY: u32 = 1; pub const IFA_F_TEMPORARY: u32 = 1; pub const IFA_F_NODAD: u32 = 2; pub const IFA_F_OPTIMISTIC: u32 = 4; pub const IFA_F_DADFAILED: u32 = 8; pub const IFA_F_HOMEADDRESS: u32 = 16; pub const IFA_F_DEPRECATED: u32 = 32; pub const IFA_F_TENTATIVE: u32 = 64; pub const IFA_F_PERMANENT: u32 = 128; pub const IFA_F_MANAGETEMPADDR: u32 = 256; pub const IFA_F_NOPREFIXROUTE: u32 = 512; pub const IFA_F_MCAUTOJOIN: u32 = 1024; pub const IFA_F_STABLE_PRIVACY: u32 = 2048; // pub const RTNL_FAMILY_IPMR: int = 128; // pub const RTNL_FAMILY_IP6MR: int = 129; // pub const RTNL_FAMILY_MAX: int = 129; // pub const RTA_ALIGNTO: int = 4; // pub const RTNH_F_DEAD: u8 = 1; pub const RTNH_F_PERVASIVE: u8 = 2; pub const RTNH_F_ONLINK: u8 = 4; pub const RTNH_F_OFFLOAD: u8 = 8; pub const RTNH_F_LINKDOWN: u8 = 16; pub const RTNH_F_UNRESOLVED: u8 = 32; // pub const RTNH_COMPARE_MASK: int = 25; // pub const RTNH_ALIGNTO: int = 4; // pub const RTNETLINK_HAVE_PEERINFO: int = 1; // pub const RTAX_FEATURE_ECN: int = 1; // pub const RTAX_FEATURE_SACK: int = 2; // pub const RTAX_FEATURE_TIMESTAMP: int = 4; // pub const RTAX_FEATURE_ALLFRAG: int = 8; // pub const RTAX_FEATURE_MASK: int = 15; pub const TCM_IFINDEX_MAGIC_BLOCK: u32 = 0xffff_ffff; // pub const TCA_FLAG_LARGE_DUMP_ON: int = 1; pub const RTEXT_FILTER_VF: u32 = 1; pub const RTEXT_FILTER_BRVLAN: u32 = 2; pub const RTEXT_FILTER_BRVLAN_COMPRESSED: u32 = 4; pub const RTEXT_FILTER_SKIP_STATS: u32 = 8; // pub const ARPOP_REQUEST: int = 1; // pub const ARPOP_REPLY: int = 2; // // pub const IN6_ADDR_GEN_MODE_EUI64: int = 0; // pub const IN6_ADDR_GEN_MODE_NONE: int = 1; // pub const IN6_ADDR_GEN_MODE_STABLE_PRIVACY: int = 2; // pub const IN6_ADDR_GEN_MODE_RANDOM: int = 3; // // pub const BRIDGE_MODE_UNSPEC: int = 0; // pub const BRIDGE_MODE_HAIRPIN: int = 1; // // pub const IFLA_BRPORT_UNSPEC: int = 0; // pub const IFLA_BRPORT_STATE: int = 1; // pub const IFLA_BRPORT_PRIORITY: int = 2; // pub const IFLA_BRPORT_COST: int = 3; // pub const IFLA_BRPORT_MODE: int = 4; // pub const IFLA_BRPORT_GUARD: int = 5; // pub const IFLA_BRPORT_PROTECT: int = 6; // pub const IFLA_BRPORT_FAST_LEAVE: int = 7; // pub const IFLA_BRPORT_LEARNING: int = 8; // pub const IFLA_BRPORT_UNICAST_FLOOD: int = 9; // pub const IFLA_BRPORT_PROXYARP: int = 10; // pub const IFLA_BRPORT_LEARNING_SYNC: int = 11; // pub const IFLA_BRPORT_PROXYARP_WIFI: int = 12; // pub const IFLA_BRPORT_ROOT_ID: int = 13; // pub const IFLA_BRPORT_BRIDGE_ID: int = 14; // pub const IFLA_BRPORT_DESIGNATED_PORT: int = 15; // pub const IFLA_BRPORT_DESIGNATED_COST: int = 16; // pub const IFLA_BRPORT_ID: int = 17; // pub const IFLA_BRPORT_NO: int = 18; // pub const IFLA_BRPORT_TOPOLOGY_CHANGE_ACK: int = 19; // pub const IFLA_BRPORT_CONFIG_PENDING: int = 20; // pub const IFLA_BRPORT_MESSAGE_AGE_TIMER: int = 21; // pub const IFLA_BRPORT_FORWARD_DELAY_TIMER: int = 22; // pub const IFLA_BRPORT_HOLD_TIMER: int = 23; // pub const IFLA_BRPORT_FLUSH: int = 24; // pub const IFLA_BRPORT_MULTICAST_ROUTER: int = 25; // pub const IFLA_BRPORT_PAD: int = 26; // pub const IFLA_BRPORT_MCAST_FLOOD: int = 27; // pub const IFLA_BRPORT_MCAST_TO_UCAST: int = 28; // pub const IFLA_BRPORT_VLAN_TUNNEL: int = 29; // pub const IFLA_BRPORT_BCAST_FLOOD: int = 30; // pub const IFLA_BRPORT_GROUP_FWD_MASK: int = 31; // pub const IFLA_BRPORT_NEIGH_SUPPRESS: int = 32; // // pub const IFLA_VLAN_QOS_UNSPEC: int = 0; // pub const IFLA_VLAN_QOS_MAPPING: int = 1; // // pub const IFLA_MACVLAN_UNSPEC: int = 0; // pub const IFLA_MACVLAN_MODE: int = 1; // pub const IFLA_MACVLAN_FLAGS: int = 2; // pub const IFLA_MACVLAN_MACADDR_MODE: int = 3; // pub const IFLA_MACVLAN_MACADDR: int = 4; // pub const IFLA_MACVLAN_MACADDR_DATA: int = 5; // pub const IFLA_MACVLAN_MACADDR_COUNT: int = 6; // // Available MACVLAN MODES pub const MACVLAN_MODE_PRIVATE: u32 = 1; pub const MACVLAN_MODE_VEPA: u32 = 2; pub const MACVLAN_MODE_BRIDGE: u32 = 4; pub const MACVLAN_MODE_PASSTHRU: u32 = 8; pub const MACVLAN_MODE_SOURCE: u32 = 16; // // pub const MACVLAN_MACADDR_ADD: int = 0; // pub const MACVLAN_MACADDR_DEL: int = 1; // pub const MACVLAN_MACADDR_FLUSH: int = 2; // pub const MACVLAN_MACADDR_SET: int = 3; // // pub const IFLA_VRF_UNSPEC: int = 0; // pub const IFLA_VRF_TABLE: int = 1; // // pub const IFLA_VRF_PORT_UNSPEC: int = 0; // pub const IFLA_VRF_PORT_TABLE: int = 1; // // pub const IFLA_MACSEC_UNSPEC: int = 0; // pub const IFLA_MACSEC_SCI: int = 1; // pub const IFLA_MACSEC_PORT: int = 2; // pub const IFLA_MACSEC_ICV_LEN: int = 3; // pub const IFLA_MACSEC_CIPHER_SUITE: int = 4; // pub const IFLA_MACSEC_WINDOW: int = 5; // pub const IFLA_MACSEC_ENCODING_SA: int = 6; // pub const IFLA_MACSEC_ENCRYPT: int = 7; // pub const IFLA_MACSEC_PROTECT: int = 8; // pub const IFLA_MACSEC_INC_SCI: int = 9; // pub const IFLA_MACSEC_ES: int = 10; // pub const IFLA_MACSEC_SCB: int = 11; // pub const IFLA_MACSEC_REPLAY_PROTECT: int = 12; // pub const IFLA_MACSEC_VALIDATION: int = 13; // pub const IFLA_MACSEC_PAD: int = 14; // // pub const MACSEC_VALIDATE_DISABLED: int = 0; // pub const MACSEC_VALIDATE_CHECK: int = 1; // pub const MACSEC_VALIDATE_STRICT: int = 2; // pub const MACSEC_VALIDATE_MAX: int = 2; // // pub const IFLA_IPVLAN_UNSPEC: int = 0; // pub const IFLA_IPVLAN_MODE: int = 1; // pub const IFLA_IPVLAN_FLAGS: int = 2; // // pub const IPVLAN_MODE_L2: int = 0; // pub const IPVLAN_MODE_L3: int = 1; // pub const IPVLAN_MODE_L3S: int = 2; // pub const IPVLAN_MODE_MAX: int = 3; // // FROM https://elixir.bootlin.com/linux/v5.9.8/source/include/uapi/linux/if_link.h#L531 pub const IFLA_VXLAN_UNSPEC: u16 = 0; pub const IFLA_VXLAN_ID: u16 = 1; pub const IFLA_VXLAN_GROUP: u16 = 2; pub const IFLA_VXLAN_LINK: u16 = 3; pub const IFLA_VXLAN_LOCAL: u16 = 4; pub const IFLA_VXLAN_TTL: u16 = 5; pub const IFLA_VXLAN_TOS: u16 = 6; pub const IFLA_VXLAN_LEARNING: u16 = 7; pub const IFLA_VXLAN_AGEING: u16 = 8; pub const IFLA_VXLAN_LIMIT: u16 = 9; pub const IFLA_VXLAN_PORT_RANGE: u16 = 10; pub const IFLA_VXLAN_PROXY: u16 = 11; pub const IFLA_VXLAN_RSC: u16 = 12; pub const IFLA_VXLAN_L2MISS: u16 = 13; pub const IFLA_VXLAN_L3MISS: u16 = 14; pub const IFLA_VXLAN_PORT: u16 = 15; pub const IFLA_VXLAN_GROUP6: u16 = 16; pub const IFLA_VXLAN_LOCAL6: u16 = 17; pub const IFLA_VXLAN_UDP_CSUM: u16 = 18; pub const IFLA_VXLAN_UDP_ZERO_CSUM6_TX: u16 = 19; pub const IFLA_VXLAN_UDP_ZERO_CSUM6_RX: u16 = 20; pub const IFLA_VXLAN_REMCSUM_TX: u16 = 21; pub const IFLA_VXLAN_REMCSUM_RX: u16 = 22; pub const IFLA_VXLAN_GBP: u16 = 23; pub const IFLA_VXLAN_REMCSUM_NOPARTIAL: u16 = 24; pub const IFLA_VXLAN_COLLECT_METADATA: u16 = 25; pub const IFLA_VXLAN_LABEL: u16 = 26; pub const IFLA_VXLAN_GPE: u16 = 27; pub const IFLA_VXLAN_TTL_INHERIT: u16 = 28; pub const IFLA_VXLAN_DF: u16 = 29; pub const __IFLA_VXLAN_MAX: u16 = 30; // // pub const IFLA_GENEVE_UNSPEC: int = 0; // pub const IFLA_GENEVE_ID: int = 1; // pub const IFLA_GENEVE_REMOTE: int = 2; // pub const IFLA_GENEVE_TTL: int = 3; // pub const IFLA_GENEVE_TOS: int = 4; // pub const IFLA_GENEVE_PORT: int = 5; // pub const IFLA_GENEVE_COLLECT_METADATA: int = 6; // pub const IFLA_GENEVE_REMOTE6: int = 7; // pub const IFLA_GENEVE_UDP_CSUM: int = 8; // pub const IFLA_GENEVE_UDP_ZERO_CSUM6_TX: int = 9; // pub const IFLA_GENEVE_UDP_ZERO_CSUM6_RX: int = 10; // pub const IFLA_GENEVE_LABEL: int = 11; // // pub const IFLA_PPP_UNSPEC: int = 0; // pub const IFLA_PPP_DEV_FD: int = 1; // // pub const GTP_ROLE_GGSN: int = 0; // pub const GTP_ROLE_SGSN: int = 1; // // pub const IFLA_GTP_UNSPEC: int = 0; // pub const IFLA_GTP_FD0: int = 1; // pub const IFLA_GTP_FD1: int = 2; // pub const IFLA_GTP_PDP_HASHSIZE: int = 3; // pub const IFLA_GTP_ROLE: int = 4; pub const IFLA_BOND_UNSPEC: u16 = 0; pub const IFLA_BOND_MODE: u16 = 1; pub const IFLA_BOND_ACTIVE_PORT: u16 = 2; pub const IFLA_BOND_MIIMON: u16 = 3; pub const IFLA_BOND_UPDELAY: u16 = 4; pub const IFLA_BOND_DOWNDELAY: u16 = 5; pub const IFLA_BOND_USE_CARRIER: u16 = 6; pub const IFLA_BOND_ARP_INTERVAL: u16 = 7; pub const IFLA_BOND_ARP_IP_TARGET: u16 = 8; pub const IFLA_BOND_ARP_VALIDATE: u16 = 9; pub const IFLA_BOND_ARP_ALL_TARGETS: u16 = 10; pub const IFLA_BOND_PRIMARY: u16 = 11; pub const IFLA_BOND_PRIMARY_RESELECT: u16 = 12; pub const IFLA_BOND_FAIL_OVER_MAC: u16 = 13; pub const IFLA_BOND_XMIT_HASH_POLICY: u16 = 14; pub const IFLA_BOND_RESEND_IGMP: u16 = 15; pub const IFLA_BOND_NUM_PEER_NOTIF: u16 = 16; pub const IFLA_BOND_ALL_PORTS_ACTIVE: u16 = 17; pub const IFLA_BOND_MIN_LINKS: u16 = 18; pub const IFLA_BOND_LP_INTERVAL: u16 = 19; pub const IFLA_BOND_PACKETS_PER_PORT: u16 = 20; pub const IFLA_BOND_AD_LACP_RATE: u16 = 21; pub const IFLA_BOND_AD_SELECT: u16 = 22; pub const IFLA_BOND_AD_INFO: u16 = 23; pub const IFLA_BOND_AD_ACTOR_SYS_PRIO: u16 = 24; pub const IFLA_BOND_AD_USER_PORT_KEY: u16 = 25; pub const IFLA_BOND_AD_ACTOR_SYSTEM: u16 = 26; pub const IFLA_BOND_TLB_DYNAMIC_LB: u16 = 27; pub const IFLA_BOND_PEER_NOTIF_DELAY: u16 = 28; pub const IFLA_BOND_AD_LACP_ACTIVE: u16 = 29; pub const IFLA_BOND_MISSED_MAX: u16 = 30; pub const IFLA_BOND_NS_IP6_TARGET: u16 = 31; pub const IFLA_BOND_AD_INFO_UNSPEC: u16 = 0; pub const IFLA_BOND_AD_INFO_AGGREGATOR: u16 = 1; pub const IFLA_BOND_AD_INFO_NUM_PORTS: u16 = 2; pub const IFLA_BOND_AD_INFO_ACTOR_KEY: u16 = 3; pub const IFLA_BOND_AD_INFO_PARTNER_KEY: u16 = 4; pub const IFLA_BOND_AD_INFO_PARTNER_MAC: u16 = 5; pub const IFLA_BOND_PORT_UNSPEC: u16 = 0; pub const IFLA_BOND_PORT_STATE: u16 = 1; pub const IFLA_BOND_PORT_MII_STATUS: u16 = 2; pub const IFLA_BOND_PORT_LINK_FAILURE_COUNT: u16 = 3; pub const IFLA_BOND_PORT_PERM_HWADDR: u16 = 4; pub const IFLA_BOND_PORT_QUEUE_ID: u16 = 5; pub const IFLA_BOND_PORT_AD_AGGREGATOR_ID: u16 = 6; pub const IFLA_BOND_PORT_AD_ACTOR_OPER_PORT_STATE: u16 = 7; pub const IFLA_BOND_PORT_AD_PARTNER_OPER_PORT_STATE: u16 = 8; pub const IFLA_BOND_PORT_PRIO: u16 = 9; pub const IFLA_BOND_PORT_STATE_ACTIVE: u8 = 0; pub const IFLA_BOND_PORT_STATE_BACKUP: u8 = 1; pub const IFLA_BOND_PORT_MII_STATUS_UP: u8 = 0; pub const IFLA_BOND_PORT_MII_STATUS_GOING_DOWN: u8 = 1; pub const IFLA_BOND_PORT_MII_STATUS_DOWN: u8 = 2; pub const IFLA_BOND_PORT_MII_STATUS_GOING_BACK: u8 = 3; // pub const IFLA_VF_INFO_UNSPEC: int = 0; // pub const IFLA_VF_INFO: int = 1; // // pub const IFLA_VF_UNSPEC: int = 0; // pub const IFLA_VF_MAC: int = 1; // pub const IFLA_VF_VLAN: int = 2; // pub const IFLA_VF_TX_RATE: int = 3; // pub const IFLA_VF_SPOOFCHK: int = 4; // pub const IFLA_VF_LINK_STATE: int = 5; // pub const IFLA_VF_RATE: int = 6; // pub const IFLA_VF_RSS_QUERY_EN: int = 7; // pub const IFLA_VF_STATS: int = 8; // pub const IFLA_VF_TRUST: int = 9; // pub const IFLA_VF_IB_NODE_GUID: int = 10; // pub const IFLA_VF_IB_PORT_GUID: int = 11; // pub const IFLA_VF_VLAN_LIST: int = 12; // // pub const IFLA_VF_VLAN_INFO_UNSPEC: int = 0; // pub const IFLA_VF_VLAN_INFO: int = 1; // // pub const NDUSEROPT_UNSPEC: int = 0; // pub const NDUSEROPT_SRCADDR: int = 1; // pub const RTNLGRP_NONE: u32 = 0; pub const RTNLGRP_LINK: u32 = 1; pub const RTNLGRP_NOTIFY: u32 = 2; pub const RTNLGRP_NEIGH: u32 = 3; pub const RTNLGRP_TC: u32 = 4; pub const RTNLGRP_IPV4_IFADDR: u32 = 5; pub const RTNLGRP_IPV4_MROUTE: u32 = 6; pub const RTNLGRP_IPV4_ROUTE: u32 = 7; pub const RTNLGRP_IPV4_RULE: u32 = 8; pub const RTNLGRP_IPV6_IFADDR: u32 = 9; pub const RTNLGRP_IPV6_MROUTE: u32 = 10; pub const RTNLGRP_IPV6_ROUTE: u32 = 11; pub const RTNLGRP_IPV6_IFINFO: u32 = 12; pub const RTNLGRP_DECNET_IFADDR: u32 = 13; pub const RTNLGRP_NOP2: u32 = 14; pub const RTNLGRP_DECNET_ROUTE: u32 = 15; pub const RTNLGRP_DECNET_RULE: u32 = 16; pub const RTNLGRP_NOP4: u32 = 17; pub const RTNLGRP_IPV6_PREFIX: u32 = 18; pub const RTNLGRP_IPV6_RULE: u32 = 19; pub const RTNLGRP_ND_USEROPT: u32 = 20; pub const RTNLGRP_PHONET_IFADDR: u32 = 21; pub const RTNLGRP_PHONET_ROUTE: u32 = 22; pub const RTNLGRP_DCB: u32 = 23; pub const RTNLGRP_IPV4_NETCONF: u32 = 24; pub const RTNLGRP_IPV6_NETCONF: u32 = 25; pub const RTNLGRP_MDB: u32 = 26; pub const RTNLGRP_MPLS_ROUTE: u32 = 27; pub const RTNLGRP_NSID: u32 = 28; pub const RTNLGRP_MPLS_NETCONF: u32 = 29; pub const RTNLGRP_IPV4_MROUTE_R: u32 = 30; pub const RTNLGRP_IPV6_MROUTE_R: u32 = 31; // // pub const IFLA_VF_LINK_STATE_AUTO: int = 0; // pub const IFLA_VF_LINK_STATE_ENABLE: int = 1; // pub const IFLA_VF_LINK_STATE_DISABLE: int = 2; // // pub const IFLA_VF_STATS_RX_PACKETS: int = 0; // pub const IFLA_VF_STATS_TX_PACKETS: int = 1; // pub const IFLA_VF_STATS_RX_BYTES: int = 2; // pub const IFLA_VF_STATS_TX_BYTES: int = 3; // pub const IFLA_VF_STATS_BROADCAST: int = 4; // pub const IFLA_VF_STATS_MULTICAST: int = 5; // pub const IFLA_VF_STATS_PAD: int = 6; // pub const IFLA_VF_STATS_RX_DROPPED: int = 7; // pub const IFLA_VF_STATS_TX_DROPPED: int = 8; // // pub const IFLA_VF_PORT_UNSPEC: int = 0; // pub const IFLA_VF_PORT: int = 1; // // pub const IFLA_PORT_UNSPEC: int = 0; // pub const IFLA_PORT_VF: int = 1; // pub const IFLA_PORT_PROFILE: int = 2; // pub const IFLA_PORT_VSI_TYPE: int = 3; // pub const IFLA_PORT_INSTANCE_UUID: int = 4; // pub const IFLA_PORT_HOST_UUID: int = 5; // pub const IFLA_PORT_REQUEST: int = 6; // pub const IFLA_PORT_RESPONSE: int = 7; // // pub const PORT_REQUEST_PREASSOCIATE: int = 0; // pub const PORT_REQUEST_PREASSOCIATE_RR: int = 1; // pub const PORT_REQUEST_ASSOCIATE: int = 2; // pub const PORT_REQUEST_DISASSOCIATE: int = 3; // // pub const PORT_VDP_RESPONSE_SUCCESS: int = 0; // pub const PORT_VDP_RESPONSE_INVALID_FORMAT: int = 1; // pub const PORT_VDP_RESPONSE_INSUFFICIENT_RESOURCES: int = 2; // pub const PORT_VDP_RESPONSE_UNUSED_VTID: int = 3; // pub const PORT_VDP_RESPONSE_VTID_VIOLATION: int = 4; // pub const PORT_VDP_RESPONSE_VTID_VERSION_VIOALTION: int = 5; // pub const PORT_VDP_RESPONSE_OUT_OF_SYNC: int = 6; // pub const PORT_PROFILE_RESPONSE_SUCCESS: int = 256; // pub const PORT_PROFILE_RESPONSE_INPROGRESS: int = 257; // pub const PORT_PROFILE_RESPONSE_INVALID: int = 258; // pub const PORT_PROFILE_RESPONSE_BADSTATE: int = 259; // pub const PORT_PROFILE_RESPONSE_INSUFFICIENT_RESOURCES: int = 260; // pub const PORT_PROFILE_RESPONSE_ERROR: int = 261; // // pub const IFLA_IPOIB_UNSPEC: int = 0; // pub const IFLA_IPOIB_PKEY: int = 1; // pub const IFLA_IPOIB_MODE: int = 2; // pub const IFLA_IPOIB_UMCAST: int = 3; // // pub const IPOIB_MODE_DATAGRAM: int = 0; // pub const IPOIB_MODE_CONNECTED: int = 1; // // pub const IFLA_HSR_UNSPEC: int = 0; // pub const IFLA_HSR_PORT1: int = 1; // pub const IFLA_HSR_PORT2: int = 2; // pub const IFLA_HSR_MULTICAST_SPEC: int = 3; // pub const IFLA_HSR_SUPERVISION_ADDR: int = 4; // pub const IFLA_HSR_SEQ_NR: int = 5; // pub const IFLA_HSR_VERSION: int = 6; // // pub const IFLA_STATS_UNSPEC: int = 0; // pub const IFLA_STATS_LINK_64: int = 1; // pub const IFLA_STATS_LINK_XSTATS: int = 2; // pub const IFLA_STATS_LINK_XSTATS_PORT: int = 3; // pub const IFLA_STATS_LINK_OFFLOAD_XSTATS: int = 4; // pub const IFLA_STATS_AF_SPEC: int = 5; // // pub const LINK_XSTATS_TYPE_UNSPEC: int = 0; // pub const LINK_XSTATS_TYPE_BRIDGE: int = 1; // // pub const IFLA_OFFLOAD_XSTATS_UNSPEC: int = 0; // pub const IFLA_OFFLOAD_XSTATS_CPU_HIT: int = 1; // pub const XDP_ATTACHED_NONE: u8 = 0; pub const XDP_ATTACHED_DRV: u8 = 1; pub const XDP_ATTACHED_SKB: u8 = 2; pub const XDP_ATTACHED_HW: u8 = 3; pub const XDP_ATTACHED_MULTI: u8 = 4; pub const IFLA_XDP_UNSPEC: u32 = 0; pub const IFLA_XDP_FD: u32 = 1; pub const IFLA_XDP_ATTACHED: u32 = 2; pub const IFLA_XDP_FLAGS: u32 = 3; pub const IFLA_XDP_PROG_ID: u32 = 4; pub const IFLA_XDP_DRV_PROG_ID: u32 = 5; pub const IFLA_XDP_SKB_PROG_ID: u32 = 6; pub const IFLA_XDP_HW_PROG_ID: u32 = 7; pub const IFLA_XDP_EXPECTED_FD: u32 = 8; // pub const IFLA_EVENT_NONE: int = 0; // pub const IFLA_EVENT_REBOOT: int = 1; // pub const IFLA_EVENT_FEATURES: int = 2; // pub const IFLA_EVENT_BONDING_FAILOVER: int = 3; // pub const IFLA_EVENT_NOTIFY_PEERS: int = 4; // pub const IFLA_EVENT_IGMP_RESEND: int = 5; // pub const IFLA_EVENT_BONDING_OPTIONS: int = 6; // // pub const NDTPA_UNSPEC: int = 0; // pub const NDTPA_IFINDEX: int = 1; // pub const NDTPA_REFCNT: int = 2; // pub const NDTPA_REACHABLE_TIME: int = 3; // pub const NDTPA_BASE_REACHABLE_TIME: int = 4; // pub const NDTPA_RETRANS_TIME: int = 5; // pub const NDTPA_GC_STALETIME: int = 6; // pub const NDTPA_DELAY_PROBE_TIME: int = 7; // pub const NDTPA_QUEUE_LEN: int = 8; // pub const NDTPA_APP_PROBES: int = 9; // pub const NDTPA_UCAST_PROBES: int = 10; // pub const NDTPA_MCAST_PROBES: int = 11; // pub const NDTPA_ANYCAST_DELAY: int = 12; // pub const NDTPA_PROXY_DELAY: int = 13; // pub const NDTPA_PROXY_QLEN: int = 14; // pub const NDTPA_LOCKTIME: int = 15; // pub const NDTPA_QUEUE_LENBYTES: int = 16; // pub const NDTPA_MCAST_REPROBES: int = 17; // pub const NDTPA_PAD: int = 18; // // #[allow(overflowing_literals)] // pub const RT_TABLE_MAX: int = 0xffff_ffff; // // pub const PREFIX_UNSPEC: int = 0; // pub const PREFIX_ADDRESS: int = 1; // pub const PREFIX_CACHEINFO: int = 2; pub const LWTUNNEL_ENCAP_NONE: u16 = 0; pub const LWTUNNEL_ENCAP_MPLS: u16 = 1; pub const LWTUNNEL_ENCAP_IP: u16 = 2; pub const LWTUNNEL_ENCAP_ILA: u16 = 3; pub const LWTUNNEL_ENCAP_IP6: u16 = 4; pub const LWTUNNEL_ENCAP_SEG6: u16 = 5; pub const LWTUNNEL_ENCAP_BPF: u16 = 6; pub const LWTUNNEL_ENCAP_SEG6_LOCAL: u16 = 7; pub const LWTUNNEL_ENCAP_RPL: u16 = 8; pub const MPLS_IPTUNNEL_UNSPEC: u16 = 0; pub const MPLS_IPTUNNEL_DST: u16 = 1; pub const MPLS_IPTUNNEL_TTL: u16 = 2; // Available MACVTAP MODES pub const MACVTAP_MODE_PRIVATE: u32 = 1; pub const MACVTAP_MODE_VEPA: u32 = 2; pub const MACVTAP_MODE_BRIDGE: u32 = 4; pub const MACVTAP_MODE_PASSTHRU: u32 = 8; pub const MACVTAP_MODE_SOURCE: u32 = 16; netlink-packet-route-0.17.0/src/rtnl/link/buffer.rs000064400000000000000000000012351046102023000203010ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::DecodeError; use netlink_packet_utils::nla::{NlaBuffer, NlasIterator}; pub const LINK_HEADER_LEN: usize = 16; buffer!(LinkMessageBuffer(LINK_HEADER_LEN) { interface_family: (u8, 0), reserved_1: (u8, 1), link_layer_type: (u16, 2..4), link_index: (u32, 4..8), flags: (u32, 8..12), change_mask: (u32, 12..LINK_HEADER_LEN), payload: (slice, LINK_HEADER_LEN..), }); impl<'a, T: AsRef<[u8]> + ?Sized> LinkMessageBuffer<&'a T> { pub fn nlas( &self, ) -> impl Iterator, DecodeError>> { NlasIterator::new(self.payload()) } } netlink-packet-route-0.17.0/src/rtnl/link/header.rs000064400000000000000000000051741046102023000202660ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; use crate::{LinkMessageBuffer, LINK_HEADER_LEN}; /// High level representation of `RTM_GETLINK`, `RTM_SETLINK`, `RTM_NEWLINK` and /// `RTM_DELLINK` messages headers. /// /// These headers have the following structure: /// /// ```no_rust /// 0 8 16 24 32 /// +----------------+----------------+----------------+----------------+ /// |interface family| reserved | link layer type | /// +----------------+----------------+----------------+----------------+ /// | link index | /// +----------------+----------------+----------------+----------------+ /// | flags | /// +----------------+----------------+----------------+----------------+ /// | change mask | /// +----------------+----------------+----------------+----------------+ /// ``` /// /// `LinkHeader` exposes all these fields except for the "reserved" one. #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct LinkHeader { /// Address family: one of the `AF_*` constants. pub interface_family: u8, /// Link index. pub index: u32, /// Link type. It should be set to one of the `ARPHRD_*` /// constants. The most common value is `ARPHRD_ETHER` for /// Ethernet. pub link_layer_type: u16, /// State of the link, described by a combinations of `IFF_*` /// constants, for instance `IFF_UP | IFF_LOWER_UP`. pub flags: u32, /// Change mask for the `flags` field. Reserved, it should be set /// to `0xffff_ffff`. pub change_mask: u32, } impl Emitable for LinkHeader { fn buffer_len(&self) -> usize { LINK_HEADER_LEN } fn emit(&self, buffer: &mut [u8]) { let mut packet = LinkMessageBuffer::new(buffer); packet.set_interface_family(self.interface_family); packet.set_link_index(self.index); packet.set_change_mask(self.change_mask); packet.set_link_layer_type(self.link_layer_type); packet.set_flags(self.flags); } } impl> Parseable> for LinkHeader { fn parse(buf: &LinkMessageBuffer) -> Result { Ok(Self { interface_family: buf.interface_family(), link_layer_type: buf.link_layer_type(), index: buf.link_index(), change_mask: buf.change_mask(), flags: buf.flags(), }) } } netlink-packet-route-0.17.0/src/rtnl/link/message.rs000064400000000000000000000206141046102023000204560ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ traits::{Emitable, Parseable, ParseableParametrized}, DecodeError, }; use crate::{nlas::link::Nla, LinkHeader, LinkMessageBuffer}; #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct LinkMessage { pub header: LinkHeader, pub nlas: Vec, } impl Emitable for LinkMessage { fn buffer_len(&self) -> usize { self.header.buffer_len() + self.nlas.as_slice().buffer_len() } fn emit(&self, buffer: &mut [u8]) { self.header.emit(buffer); self.nlas .as_slice() .emit(&mut buffer[self.header.buffer_len()..]); } } impl<'a, T: AsRef<[u8]> + 'a> Parseable> for LinkMessage { fn parse(buf: &LinkMessageBuffer<&'a T>) -> Result { let header = LinkHeader::parse(buf) .context("failed to parse link message header")?; let interface_family = header.interface_family; let nlas = Vec::::parse_with_param(buf, interface_family) .context("failed to parse link message NLAs")?; Ok(LinkMessage { header, nlas }) } } impl<'a, T: AsRef<[u8]> + 'a> ParseableParametrized, u16> for Vec { fn parse_with_param( buf: &LinkMessageBuffer<&'a T>, family: u16, ) -> Result { let mut nlas = vec![]; for nla_buf in buf.nlas() { nlas.push(Nla::parse_with_param(&nla_buf?, family)?); } Ok(nlas) } } impl<'a, T: AsRef<[u8]> + 'a> ParseableParametrized, u8> for Vec { fn parse_with_param( buf: &LinkMessageBuffer<&'a T>, family: u8, ) -> Result { Vec::::parse_with_param(buf, u16::from(family)) } } #[cfg(test)] mod test { use crate::{ constants::*, nlas::link::{Nla, State}, LinkHeader, LinkMessage, LinkMessageBuffer, }; use netlink_packet_utils::traits::{Emitable, ParseableParametrized}; #[rustfmt::skip] static HEADER: [u8; 96] = [ 0x00, // interface family 0x00, // reserved 0x04, 0x03, // link layer type 772 = loopback 0x01, 0x00, 0x00, 0x00, // interface index = 1 // Note: in the wireshark capture, the thrid byte is 0x01 // but that does not correpond to any of the IFF_ flags... 0x49, 0x00, 0x00, 0x00, // device flags: UP, LOOPBACK, RUNNING, LOWERUP 0x00, 0x00, 0x00, 0x00, // reserved 2 (aka device change flag) // nlas 0x07, 0x00, 0x03, 0x00, 0x6c, 0x6f, 0x00, // device name L=7,T=3,V=lo 0x00, // padding 0x08, 0x00, 0x0d, 0x00, 0xe8, 0x03, 0x00, 0x00, // TxQueue length L=8,T=13,V=1000 0x05, 0x00, 0x10, 0x00, 0x00, // OperState L=5,T=16,V=0 (unknown) 0x00, 0x00, 0x00, // padding 0x05, 0x00, 0x11, 0x00, 0x00, // Link mode L=5,T=17,V=0 0x00, 0x00, 0x00, // padding 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, // MTU L=8,T=4,V=65536 0x08, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, // Group L=8,T=27,V=9 0x08, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, // Promiscuity L=8,T=30,V=0 0x08, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, // Number of Tx Queues L=8,T=31,V=1 0x08, 0x00, 0x28, 0x00, 0xff, 0xff, 0x00, 0x00, // Maximum GSO segment count L=8,T=40,V=65536 0x08, 0x00, 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, // Maximum GSO size L=8,T=41,V=65536 ]; #[test] fn packet_header_read() { let packet = LinkMessageBuffer::new(&HEADER[0..16]); assert_eq!(packet.interface_family(), 0); assert_eq!(packet.reserved_1(), 0); assert_eq!(packet.link_layer_type(), ARPHRD_LOOPBACK); assert_eq!(packet.link_index(), 1); assert_eq!(packet.flags(), IFF_UP | IFF_LOOPBACK | IFF_RUNNING); assert_eq!(packet.change_mask(), 0); } #[test] fn packet_header_build() { let mut buf = vec![0xff; 16]; { let mut packet = LinkMessageBuffer::new(&mut buf); packet.set_interface_family(0); packet.set_reserved_1(0); packet.set_link_layer_type(ARPHRD_LOOPBACK); packet.set_link_index(1); packet.set_flags(IFF_UP | IFF_LOOPBACK | IFF_RUNNING); packet.set_change_mask(0); } assert_eq!(&buf[..], &HEADER[0..16]); } #[test] fn packet_nlas_read() { let packet = LinkMessageBuffer::new(&HEADER[..]); assert_eq!(packet.nlas().count(), 10); let mut nlas = packet.nlas(); // device name L=7,T=3,V=lo let nla = nlas.next().unwrap().unwrap(); nla.check_buffer_length().unwrap(); assert_eq!(nla.length(), 7); assert_eq!(nla.kind(), 3); assert_eq!(nla.value(), &[0x6c, 0x6f, 0x00]); let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap(); assert_eq!(parsed, Nla::IfName(String::from("lo"))); // TxQueue length L=8,T=13,V=1000 let nla = nlas.next().unwrap().unwrap(); nla.check_buffer_length().unwrap(); assert_eq!(nla.length(), 8); assert_eq!(nla.kind(), 13); assert_eq!(nla.value(), &[0xe8, 0x03, 0x00, 0x00]); let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap(); assert_eq!(parsed, Nla::TxQueueLen(1000)); // OperState L=5,T=16,V=0 (unknown) let nla = nlas.next().unwrap().unwrap(); nla.check_buffer_length().unwrap(); assert_eq!(nla.length(), 5); assert_eq!(nla.kind(), 16); assert_eq!(nla.value(), &[0x00]); let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap(); assert_eq!(parsed, Nla::OperState(State::Unknown)); // Link mode L=5,T=17,V=0 let nla = nlas.next().unwrap().unwrap(); nla.check_buffer_length().unwrap(); assert_eq!(nla.length(), 5); assert_eq!(nla.kind(), 17); assert_eq!(nla.value(), &[0x00]); let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap(); assert_eq!(parsed, Nla::Mode(0)); // MTU L=8,T=4,V=65536 let nla = nlas.next().unwrap().unwrap(); nla.check_buffer_length().unwrap(); assert_eq!(nla.length(), 8); assert_eq!(nla.kind(), 4); assert_eq!(nla.value(), &[0x00, 0x00, 0x01, 0x00]); let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap(); assert_eq!(parsed, Nla::Mtu(65_536)); // 0x00, 0x00, 0x00, 0x00, // Group L=8,T=27,V=9 let nla = nlas.next().unwrap().unwrap(); nla.check_buffer_length().unwrap(); assert_eq!(nla.length(), 8); assert_eq!(nla.kind(), 27); assert_eq!(nla.value(), &[0x00, 0x00, 0x00, 0x00]); let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap(); assert_eq!(parsed, Nla::Group(0)); // Promiscuity L=8,T=30,V=0 let nla = nlas.next().unwrap().unwrap(); nla.check_buffer_length().unwrap(); assert_eq!(nla.length(), 8); assert_eq!(nla.kind(), 30); assert_eq!(nla.value(), &[0x00, 0x00, 0x00, 0x00]); let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap(); assert_eq!(parsed, Nla::Promiscuity(0)); // Number of Tx Queues L=8,T=31,V=1 // 0x01, 0x00, 0x00, 0x00 let nla = nlas.next().unwrap().unwrap(); nla.check_buffer_length().unwrap(); assert_eq!(nla.length(), 8); assert_eq!(nla.kind(), 31); assert_eq!(nla.value(), &[0x01, 0x00, 0x00, 0x00]); let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap(); assert_eq!(parsed, Nla::NumTxQueues(1)); } #[test] fn emit() { let header = LinkHeader { link_layer_type: ARPHRD_LOOPBACK, index: 1, flags: IFF_UP | IFF_LOOPBACK | IFF_RUNNING | IFF_LOWER_UP, ..Default::default() }; let nlas = vec![ Nla::IfName("lo".into()), Nla::TxQueueLen(1000), Nla::OperState(State::Unknown), Nla::Mode(0), Nla::Mtu(0x1_0000), Nla::Group(0), Nla::Promiscuity(0), Nla::NumTxQueues(1), Nla::GsoMaxSegs(0xffff), Nla::GsoMaxSize(0x1_0000), ]; let packet = LinkMessage { header, nlas }; let mut buf = [0; 96]; assert_eq!(packet.buffer_len(), 96); packet.emit(&mut buf[..]); } } netlink-packet-route-0.17.0/src/rtnl/link/mod.rs000064400000000000000000000002071046102023000176050ustar 00000000000000// SPDX-License-Identifier: MIT mod buffer; mod header; mod message; pub mod nlas; pub use self::{buffer::*, header::*, message::*}; netlink-packet-route-0.17.0/src/rtnl/link/nlas/af_spec_bridge.rs000064400000000000000000000060561046102023000227070ustar 00000000000000// SPDX-License-Identifier: MIT use std::convert::TryFrom; use anyhow::Context; use netlink_packet_utils::{ nla::{self, DefaultNla, NlaBuffer}, parsers::parse_u16, traits::Parseable, DecodeError, }; use crate::constants::*; use byteorder::{ByteOrder, NativeEndian}; #[derive(Clone, Eq, PartialEq, Debug)] #[non_exhaustive] pub enum AfSpecBridge { Flags(u16), VlanInfo(BridgeVlanInfo), Other(DefaultNla), } impl nla::Nla for AfSpecBridge { fn value_len(&self) -> usize { use self::AfSpecBridge::*; match *self { VlanInfo(_) => 4, Flags(_) => 2, Other(ref nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::AfSpecBridge::*; match *self { Flags(value) => NativeEndian::write_u16(buffer, value), VlanInfo(ref info) => { buffer[..4].copy_from_slice(<[u8; 4]>::from(info).as_slice()) } Other(ref nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::AfSpecBridge::*; match *self { Flags(_) => IFLA_BRIDGE_FLAGS, VlanInfo(_) => IFLA_BRIDGE_VLAN_INFO, Other(ref nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for AfSpecBridge { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::AfSpecBridge::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_BRIDGE_VLAN_INFO => VlanInfo( BridgeVlanInfo::try_from(payload) .context("Invalid IFLA_BRIDGE_VLAN_INFO value")?, ), IFLA_BRIDGE_FLAGS => Flags( parse_u16(payload) .context("invalid IFLA_BRIDGE_FLAGS value")?, ), kind => Other( DefaultNla::parse(buf) .context(format!("Unknown NLA type {kind}"))?, ), }) } } #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] #[non_exhaustive] pub struct BridgeVlanInfo { pub flags: u16, pub vid: u16, } impl From<&BridgeVlanInfo> for [u8; 4] { fn from(d: &BridgeVlanInfo) -> Self { let mut ret = [0u8; 4]; NativeEndian::write_u16(&mut ret[0..2], d.flags); NativeEndian::write_u16(&mut ret[2..4], d.vid); ret } } impl TryFrom<&[u8]> for BridgeVlanInfo { type Error = DecodeError; fn try_from(raw: &[u8]) -> Result { if raw.len() == 4 { Ok(Self { flags: parse_u16(&raw[0..2]).context(format!( "Invalid IFLA_BRIDGE_VLAN_INFO value: {raw:?}" ))?, vid: parse_u16(&raw[2..4]).context(format!( "Invalid IFLA_BRIDGE_VLAN_INFO value: {raw:?}" ))?, }) } else { Err(DecodeError::from(format!( "Invalid IFLA_BRIDGE_VLAN_INFO value, expecting [u8;4], but got {raw:?}" ))) } } } netlink-packet-route-0.17.0/src/rtnl/link/nlas/af_spec_inet.rs000064400000000000000000000207641046102023000224140ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{self, DefaultNla, NlaBuffer, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; use super::{inet, inet6}; use crate::constants::*; #[derive(Clone, Eq, PartialEq, Debug)] #[non_exhaustive] pub enum AfSpecInet { Unspec(Vec), Unix(Vec), Ax25(Vec), Ipx(Vec), AppleTalk(Vec), Netrom(Vec), Bridge(Vec), AtmPvc(Vec), X25(Vec), Inet(Vec), Inet6(Vec), Rose(Vec), DecNet(Vec), NetbEui(Vec), Security(Vec), Key(Vec), Netlink(Vec), Packet(Vec), Ash(Vec), EcoNet(Vec), AtmSvc(Vec), Rds(Vec), Sna(Vec), Irda(Vec), Pppox(Vec), WanPipe(Vec), Llc(Vec), Can(Vec), Tipc(Vec), Bluetooth(Vec), Iucv(Vec), RxRpc(Vec), Isdn(Vec), Phonet(Vec), Ieee802154(Vec), Caif(Vec), Alg(Vec), Other(DefaultNla), } impl nla::Nla for AfSpecInet { #[rustfmt::skip] fn value_len(&self) -> usize { use self::AfSpecInet::*; match *self { Unspec(ref bytes) | Unix(ref bytes) | Ax25(ref bytes) | Ipx(ref bytes) | AppleTalk(ref bytes) | Netrom(ref bytes) | Bridge(ref bytes) | AtmPvc(ref bytes) | X25(ref bytes) | Rose(ref bytes) | DecNet(ref bytes) | NetbEui(ref bytes) | Security(ref bytes) | Key(ref bytes) | Netlink(ref bytes) | Packet(ref bytes) | Ash(ref bytes) | EcoNet(ref bytes) | AtmSvc(ref bytes) | Rds(ref bytes) | Sna(ref bytes) | Irda(ref bytes) | Pppox(ref bytes) | WanPipe(ref bytes) | Llc(ref bytes) | Can(ref bytes) | Tipc(ref bytes) | Bluetooth(ref bytes) | Iucv(ref bytes) | RxRpc(ref bytes) | Isdn(ref bytes) | Phonet(ref bytes) | Ieee802154(ref bytes) | Caif(ref bytes) | Alg(ref bytes) => bytes.len(), Inet6(ref nlas) => nlas.as_slice().buffer_len(), Inet(ref nlas) => nlas.as_slice().buffer_len(), Other(ref nla) => nla.value_len(), } } #[rustfmt::skip] fn emit_value(&self, buffer: &mut [u8]) { use self::AfSpecInet::*; match *self { Unspec(ref bytes) | Unix(ref bytes) | Ax25(ref bytes) | Ipx(ref bytes) | AppleTalk(ref bytes) | Netrom(ref bytes) | Bridge(ref bytes) | AtmPvc(ref bytes) | X25(ref bytes) | Rose(ref bytes) | DecNet(ref bytes) | NetbEui(ref bytes) | Security(ref bytes) | Key(ref bytes) | Netlink(ref bytes) | Packet(ref bytes) | Ash(ref bytes) | EcoNet(ref bytes) | AtmSvc(ref bytes) | Rds(ref bytes) | Sna(ref bytes) | Irda(ref bytes) | Pppox(ref bytes) | WanPipe(ref bytes) | Llc(ref bytes) | Can(ref bytes) | Tipc(ref bytes) | Bluetooth(ref bytes) | Iucv(ref bytes) | RxRpc(ref bytes) | Isdn(ref bytes) | Phonet(ref bytes) | Ieee802154(ref bytes) | Caif(ref bytes) | Alg(ref bytes) => buffer[..bytes.len()].copy_from_slice(bytes.as_slice()), Inet6(ref nlas) => nlas.as_slice().emit(buffer), Inet(ref nlas) => nlas.as_slice().emit(buffer), Other(ref nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::AfSpecInet::*; match *self { Inet(_) => AF_INET, Unspec(_) => AF_UNSPEC, Unix(_) => AF_UNIX, Ax25(_) => AF_AX25, Ipx(_) => AF_IPX, AppleTalk(_) => AF_APPLETALK, Netrom(_) => AF_NETROM, Bridge(_) => AF_BRIDGE, AtmPvc(_) => AF_ATMPVC, X25(_) => AF_X25, Inet6(_) => AF_INET6, Rose(_) => AF_ROSE, DecNet(_) => AF_DECNET, NetbEui(_) => AF_NETBEUI, Security(_) => AF_SECURITY, Key(_) => AF_KEY, Netlink(_) => AF_NETLINK, Packet(_) => AF_PACKET, Ash(_) => AF_ASH, EcoNet(_) => AF_ECONET, AtmSvc(_) => AF_ATMSVC, Rds(_) => AF_RDS, Sna(_) => AF_SNA, Irda(_) => AF_IRDA, Pppox(_) => AF_PPPOX, WanPipe(_) => AF_WANPIPE, Llc(_) => AF_LLC, Can(_) => AF_CAN, Tipc(_) => AF_TIPC, Bluetooth(_) => AF_BLUETOOTH, Iucv(_) => AF_IUCV, RxRpc(_) => AF_RXRPC, Isdn(_) => AF_ISDN, Phonet(_) => AF_PHONET, Ieee802154(_) => AF_IEEE802154, Caif(_) => AF_CAIF, Alg(_) => AF_ALG, Other(ref nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for AfSpecInet { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::AfSpecInet::*; let payload = buf.value(); Ok(match buf.kind() { AF_UNSPEC => Unspec(payload.to_vec()), AF_INET => { let mut nlas = vec![]; for nla in NlasIterator::new(payload) { let nla = nla.context("invalid AF_INET value")?; nlas.push( inet::Inet::parse(&nla) .context("invalid AF_INET value")?, ); } Inet(nlas) } AF_INET6 => { let mut nlas = vec![]; for nla in NlasIterator::new(payload) { let nla = nla.context("invalid AF_INET6 value")?; nlas.push( inet6::Inet6::parse(&nla) .context("invalid AF_INET6 value")?, ); } Inet6(nlas) } AF_UNIX => Unix(payload.to_vec()), AF_AX25 => Ax25(payload.to_vec()), AF_IPX => Ipx(payload.to_vec()), AF_APPLETALK => AppleTalk(payload.to_vec()), AF_NETROM => Netrom(payload.to_vec()), AF_BRIDGE => Bridge(payload.to_vec()), AF_ATMPVC => AtmPvc(payload.to_vec()), AF_X25 => X25(payload.to_vec()), AF_ROSE => Rose(payload.to_vec()), AF_DECNET => DecNet(payload.to_vec()), AF_NETBEUI => NetbEui(payload.to_vec()), AF_SECURITY => Security(payload.to_vec()), AF_KEY => Key(payload.to_vec()), AF_NETLINK => Netlink(payload.to_vec()), AF_PACKET => Packet(payload.to_vec()), AF_ASH => Ash(payload.to_vec()), AF_ECONET => EcoNet(payload.to_vec()), AF_ATMSVC => AtmSvc(payload.to_vec()), AF_RDS => Rds(payload.to_vec()), AF_SNA => Sna(payload.to_vec()), AF_IRDA => Irda(payload.to_vec()), AF_PPPOX => Pppox(payload.to_vec()), AF_WANPIPE => WanPipe(payload.to_vec()), AF_LLC => Llc(payload.to_vec()), AF_CAN => Can(payload.to_vec()), AF_TIPC => Tipc(payload.to_vec()), AF_BLUETOOTH => Bluetooth(payload.to_vec()), AF_IUCV => Iucv(payload.to_vec()), AF_RXRPC => RxRpc(payload.to_vec()), AF_ISDN => Isdn(payload.to_vec()), AF_PHONET => Phonet(payload.to_vec()), AF_IEEE802154 => Ieee802154(payload.to_vec()), AF_CAIF => Caif(payload.to_vec()), AF_ALG => Alg(payload.to_vec()), kind => Other( DefaultNla::parse(buf) .context(format!("Unknown NLA type {kind}"))?, ), }) } } netlink-packet-route-0.17.0/src/rtnl/link/nlas/bond.rs000064400000000000000000000403311046102023000207070ustar 00000000000000// SPDX-License-Identifier: MIT use std::{ net::{IpAddr, Ipv4Addr, Ipv6Addr}, ops::Deref, }; use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{Nla, NlaBuffer, NlasIterator}, parsers::{parse_ip, parse_mac, parse_u16, parse_u32, parse_u8}, traits::{Emitable, Parseable}, DecodeError, }; use crate::constants::*; #[derive(Debug, Clone, Eq, PartialEq)] #[non_exhaustive] pub enum BondAdInfo { Aggregator(u16), NumPorts(u16), ActorKey(u16), PartnerKey(u16), PartnerMac([u8; 6]), } impl Nla for BondAdInfo { fn value_len(&self) -> usize { use self::BondAdInfo::*; match self { Aggregator(_) | NumPorts(_) | ActorKey(_) | PartnerKey(_) => 2, PartnerMac(_) => 6, } } fn kind(&self) -> u16 { use self::BondAdInfo::*; match self { Aggregator(_) => IFLA_BOND_AD_INFO_AGGREGATOR, NumPorts(_) => IFLA_BOND_AD_INFO_NUM_PORTS, ActorKey(_) => IFLA_BOND_AD_INFO_ACTOR_KEY, PartnerKey(_) => IFLA_BOND_AD_INFO_PARTNER_KEY, PartnerMac(_) => IFLA_BOND_AD_INFO_PARTNER_MAC, } } fn emit_value(&self, buffer: &mut [u8]) { use self::BondAdInfo::*; match self { Aggregator(d) | NumPorts(d) | ActorKey(d) | PartnerKey(d) => { NativeEndian::write_u16(buffer, *d) } PartnerMac(mac) => buffer.copy_from_slice(mac), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for BondAdInfo { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::BondAdInfo::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_BOND_AD_INFO_AGGREGATOR => Aggregator( parse_u16(payload) .context("invalid IFLA_BOND_AD_INFO_AGGREGATOR value")?, ), IFLA_BOND_AD_INFO_NUM_PORTS => NumPorts( parse_u16(payload) .context("invalid IFLA_BOND_AD_INFO_NUM_PORTS value")?, ), IFLA_BOND_AD_INFO_ACTOR_KEY => ActorKey( parse_u16(payload) .context("invalid IFLA_BOND_AD_INFO_ACTOR_KEY value")?, ), IFLA_BOND_AD_INFO_PARTNER_KEY => PartnerKey( parse_u16(payload) .context("invalid IFLA_BOND_AD_INFO_PARTNER_KEY value")?, ), IFLA_BOND_AD_INFO_PARTNER_MAC => PartnerMac( parse_mac(payload) .context("invalid IFLA_BOND_AD_INFO_PARTNER_MAC value")?, ), _ => return Err(format!("unknown NLA type {}", buf.kind()).into()), }) } } // Some attributes (ARP_IP_TARGET, NS_IP6_TARGET) contain a nested // list of IP addresses, where each element uses the index as NLA kind // and the address as value. InfoBond exposes vectors of IP addresses, // and we use this struct for serialization. struct BondIpAddrNla { index: u16, addr: IpAddr, } struct BondIpAddrNlaList(Vec); impl Deref for BondIpAddrNlaList { type Target = Vec; fn deref(&self) -> &Self::Target { &self.0 } } impl From<&Vec> for BondIpAddrNlaList { fn from(addrs: &Vec) -> Self { let mut nlas = Vec::new(); for (i, addr) in addrs.iter().enumerate() { let nla = BondIpAddrNla { index: i as u16, addr: IpAddr::V4(*addr), }; nlas.push(nla); } BondIpAddrNlaList(nlas) } } impl From<&Vec> for BondIpAddrNlaList { fn from(addrs: &Vec) -> Self { let mut nlas = Vec::new(); for (i, addr) in addrs.iter().enumerate() { let nla = BondIpAddrNla { index: i as u16, addr: IpAddr::V6(*addr), }; nlas.push(nla); } BondIpAddrNlaList(nlas) } } impl Nla for BondIpAddrNla { fn value_len(&self) -> usize { if self.addr.is_ipv4() { 4 } else { 16 } } fn emit_value(&self, buffer: &mut [u8]) { match self.addr { IpAddr::V4(addr) => buffer.copy_from_slice(&addr.octets()), IpAddr::V6(addr) => buffer.copy_from_slice(&addr.octets()), } } fn kind(&self) -> u16 { self.index } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoBond { Mode(u8), ActivePort(u32), MiiMon(u32), UpDelay(u32), DownDelay(u32), UseCarrier(u8), ArpInterval(u32), ArpIpTarget(Vec), ArpValidate(u32), ArpAllTargets(u32), Primary(u32), PrimaryReselect(u8), FailOverMac(u8), XmitHashPolicy(u8), ResendIgmp(u32), NumPeerNotif(u8), AllPortsActive(u8), MinLinks(u32), LpInterval(u32), PacketsPerPort(u32), AdLacpRate(u8), AdSelect(u8), AdInfo(Vec), AdActorSysPrio(u16), AdUserPortKey(u16), AdActorSystem([u8; 6]), TlbDynamicLb(u8), PeerNotifDelay(u32), AdLacpActive(u8), MissedMax(u8), NsIp6Target(Vec), } impl Nla for InfoBond { #[rustfmt::skip] fn value_len(&self) -> usize { use self::InfoBond::*; match *self { Mode(_) | UseCarrier(_) | PrimaryReselect(_) | FailOverMac(_) | XmitHashPolicy(_) | NumPeerNotif(_) | AllPortsActive(_) | AdLacpActive(_) | AdLacpRate(_) | AdSelect(_) | TlbDynamicLb(_) | MissedMax(_) => 1, AdActorSysPrio(_) | AdUserPortKey(_) => 2, ActivePort(_) | MiiMon(_) | UpDelay(_) | DownDelay(_) | ArpInterval(_) | ArpValidate(_) | ArpAllTargets(_) | Primary(_) | ResendIgmp(_) | MinLinks(_) | LpInterval(_) | PacketsPerPort(_) | PeerNotifDelay(_) => 4, ArpIpTarget(ref addrs) => { BondIpAddrNlaList::from(addrs).as_slice().buffer_len() }, NsIp6Target(ref addrs) => { BondIpAddrNlaList::from(addrs).as_slice().buffer_len() }, AdActorSystem(_) => 6, AdInfo(ref infos) => infos.as_slice().buffer_len(), } } #[rustfmt::skip] fn emit_value(&self, buffer: &mut [u8]) { use self::InfoBond::*; match self { Mode(value) | UseCarrier(value) | PrimaryReselect(value) | FailOverMac(value) | XmitHashPolicy(value) | NumPeerNotif(value) | AllPortsActive(value) | AdLacpActive(value) | AdLacpRate(value) | AdSelect(value) | TlbDynamicLb(value) | MissedMax(value) => buffer[0] = *value, AdActorSysPrio(value) | AdUserPortKey(value) => NativeEndian::write_u16(buffer, *value), ActivePort(value) | MiiMon(value) | UpDelay(value) | DownDelay(value) | ArpInterval(value) | ArpValidate(value) | ArpAllTargets(value) | Primary(value) | ResendIgmp(value) | MinLinks(value) | LpInterval(value) | PacketsPerPort(value) | PeerNotifDelay(value) => NativeEndian::write_u32(buffer, *value), AdActorSystem(bytes) => buffer.copy_from_slice(bytes), ArpIpTarget(addrs) => { BondIpAddrNlaList::from(addrs).as_slice().emit(buffer) }, NsIp6Target(addrs) => { BondIpAddrNlaList::from(addrs).as_slice().emit(buffer) }, AdInfo(infos) => infos.as_slice().emit(buffer), } } fn kind(&self) -> u16 { use self::InfoBond::*; match self { Mode(_) => IFLA_BOND_MODE, ActivePort(_) => IFLA_BOND_ACTIVE_PORT, MiiMon(_) => IFLA_BOND_MIIMON, UpDelay(_) => IFLA_BOND_UPDELAY, DownDelay(_) => IFLA_BOND_DOWNDELAY, UseCarrier(_) => IFLA_BOND_USE_CARRIER, ArpInterval(_) => IFLA_BOND_ARP_INTERVAL, ArpIpTarget(_) => IFLA_BOND_ARP_IP_TARGET, ArpValidate(_) => IFLA_BOND_ARP_VALIDATE, ArpAllTargets(_) => IFLA_BOND_ARP_ALL_TARGETS, Primary(_) => IFLA_BOND_PRIMARY, PrimaryReselect(_) => IFLA_BOND_PRIMARY_RESELECT, FailOverMac(_) => IFLA_BOND_FAIL_OVER_MAC, XmitHashPolicy(_) => IFLA_BOND_XMIT_HASH_POLICY, ResendIgmp(_) => IFLA_BOND_RESEND_IGMP, NumPeerNotif(_) => IFLA_BOND_NUM_PEER_NOTIF, AllPortsActive(_) => IFLA_BOND_ALL_PORTS_ACTIVE, MinLinks(_) => IFLA_BOND_MIN_LINKS, LpInterval(_) => IFLA_BOND_LP_INTERVAL, PacketsPerPort(_) => IFLA_BOND_PACKETS_PER_PORT, AdLacpRate(_) => IFLA_BOND_AD_LACP_RATE, AdSelect(_) => IFLA_BOND_AD_SELECT, AdInfo(_) => IFLA_BOND_AD_INFO, AdActorSysPrio(_) => IFLA_BOND_AD_ACTOR_SYS_PRIO, AdUserPortKey(_) => IFLA_BOND_AD_USER_PORT_KEY, AdActorSystem(_) => IFLA_BOND_AD_ACTOR_SYSTEM, TlbDynamicLb(_) => IFLA_BOND_TLB_DYNAMIC_LB, PeerNotifDelay(_) => IFLA_BOND_PEER_NOTIF_DELAY, AdLacpActive(_) => IFLA_BOND_AD_LACP_ACTIVE, MissedMax(_) => IFLA_BOND_MISSED_MAX, NsIp6Target(_) => IFLA_BOND_NS_IP6_TARGET, } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoBond { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoBond::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_BOND_MODE => Mode( parse_u8(payload).context("invalid IFLA_BOND_MODE value")?, ), IFLA_BOND_ACTIVE_PORT => ActivePort( parse_u32(payload) .context("invalid IFLA_BOND_ACTIVE_PORT value")?, ), IFLA_BOND_MIIMON => MiiMon( parse_u32(payload).context("invalid IFLA_BOND_MIIMON value")?, ), IFLA_BOND_UPDELAY => UpDelay( parse_u32(payload) .context("invalid IFLA_BOND_UPDELAY value")?, ), IFLA_BOND_DOWNDELAY => DownDelay( parse_u32(payload) .context("invalid IFLA_BOND_DOWNDELAY value")?, ), IFLA_BOND_USE_CARRIER => UseCarrier( parse_u8(payload) .context("invalid IFLA_BOND_USE_CARRIER value")?, ), IFLA_BOND_ARP_INTERVAL => ArpInterval( parse_u32(payload) .context("invalid IFLA_BOND_ARP_INTERVAL value")?, ), IFLA_BOND_ARP_IP_TARGET => { let mut addrs = Vec::::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context("invalid IFLA_BOND_ARP_IP_TARGET value")?; if let Ok(IpAddr::V4(addr)) = parse_ip(nla.value()) { addrs.push(addr); } } ArpIpTarget(addrs) } IFLA_BOND_ARP_VALIDATE => ArpValidate( parse_u32(payload) .context("invalid IFLA_BOND_ARP_VALIDATE value")?, ), IFLA_BOND_ARP_ALL_TARGETS => ArpAllTargets( parse_u32(payload) .context("invalid IFLA_BOND_ARP_ALL_TARGETS value")?, ), IFLA_BOND_PRIMARY => Primary( parse_u32(payload) .context("invalid IFLA_BOND_PRIMARY value")?, ), IFLA_BOND_PRIMARY_RESELECT => PrimaryReselect( parse_u8(payload) .context("invalid IFLA_BOND_PRIMARY_RESELECT value")?, ), IFLA_BOND_FAIL_OVER_MAC => FailOverMac( parse_u8(payload) .context("invalid IFLA_BOND_FAIL_OVER_MAC value")?, ), IFLA_BOND_XMIT_HASH_POLICY => XmitHashPolicy( parse_u8(payload) .context("invalid IFLA_BOND_XMIT_HASH_POLICY value")?, ), IFLA_BOND_RESEND_IGMP => ResendIgmp( parse_u32(payload) .context("invalid IFLA_BOND_RESEND_IGMP value")?, ), IFLA_BOND_NUM_PEER_NOTIF => NumPeerNotif( parse_u8(payload) .context("invalid IFLA_BOND_NUM_PEER_NOTIF value")?, ), IFLA_BOND_ALL_PORTS_ACTIVE => AllPortsActive( parse_u8(payload) .context("invalid IFLA_BOND_ALL_PORTS_ACTIVE value")?, ), IFLA_BOND_MIN_LINKS => MinLinks( parse_u32(payload) .context("invalid IFLA_BOND_MIN_LINKS value")?, ), IFLA_BOND_LP_INTERVAL => LpInterval( parse_u32(payload) .context("invalid IFLA_BOND_LP_INTERVAL value")?, ), IFLA_BOND_PACKETS_PER_PORT => PacketsPerPort( parse_u32(payload) .context("invalid IFLA_BOND_PACKETS_PER_PORT value")?, ), IFLA_BOND_AD_LACP_RATE => AdLacpRate( parse_u8(payload) .context("invalid IFLA_BOND_AD_LACP_RATE value")?, ), IFLA_BOND_AD_SELECT => AdSelect( parse_u8(payload) .context("invalid IFLA_BOND_AD_SELECT value")?, ), IFLA_BOND_AD_INFO => { let mut infos = Vec::new(); let err = "failed to parse IFLA_BOND_AD_INFO"; for nla in NlasIterator::new(payload) { let nla = &nla.context(err)?; let info = BondAdInfo::parse(nla).context(err)?; infos.push(info); } AdInfo(infos) } IFLA_BOND_AD_ACTOR_SYS_PRIO => AdActorSysPrio( parse_u16(payload) .context("invalid IFLA_BOND_AD_ACTOR_SYS_PRIO value")?, ), IFLA_BOND_AD_USER_PORT_KEY => AdUserPortKey( parse_u16(payload) .context("invalid IFLA_BOND_AD_USER_PORT_KEY value")?, ), IFLA_BOND_AD_ACTOR_SYSTEM => AdActorSystem( parse_mac(payload) .context("invalid IFLA_BOND_AD_ACTOR_SYSTEM value")?, ), IFLA_BOND_TLB_DYNAMIC_LB => TlbDynamicLb( parse_u8(payload) .context("invalid IFLA_BOND_TLB_DYNAMIC_LB value")?, ), IFLA_BOND_PEER_NOTIF_DELAY => PeerNotifDelay( parse_u32(payload) .context("invalid IFLA_BOND_PEER_NOTIF_DELAY value")?, ), IFLA_BOND_AD_LACP_ACTIVE => AdLacpActive( parse_u8(payload) .context("invalid IFLA_BOND_AD_LACP_ACTIVE value")?, ), IFLA_BOND_MISSED_MAX => MissedMax( parse_u8(payload) .context("invalid IFLA_BOND_MISSED_MAX value")?, ), IFLA_BOND_NS_IP6_TARGET => { let mut addrs = Vec::::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context("invalid IFLA_BOND_NS_IP6_TARGET value")?; if let Ok(IpAddr::V6(addr)) = parse_ip(nla.value()) { addrs.push(addr); } } NsIp6Target(addrs) } _ => return Err(format!("unknown NLA type {}", buf.kind()).into()), }) } } netlink-packet-route-0.17.0/src/rtnl/link/nlas/bond_port.rs000064400000000000000000000121771046102023000217620ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::{parse_i32, parse_u16, parse_u32, parse_u8}, traits::Parseable, DecodeError, }; use crate::constants::*; #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] pub enum BondPortState { Active, Backup, Other(u8), } impl From for BondPortState { fn from(value: u8) -> Self { use self::BondPortState::*; match value { IFLA_BOND_PORT_STATE_ACTIVE => Active, IFLA_BOND_PORT_STATE_BACKUP => Backup, _ => Other(value), } } } impl From for u8 { fn from(value: BondPortState) -> Self { use self::BondPortState::*; match value { Active => IFLA_BOND_PORT_STATE_ACTIVE, Backup => IFLA_BOND_PORT_STATE_BACKUP, Other(other) => other, } } } #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] pub enum MiiStatus { Up, GoingDown, Down, GoingBack, Other(u8), } impl From for MiiStatus { fn from(value: u8) -> Self { use self::MiiStatus::*; match value { IFLA_BOND_PORT_MII_STATUS_UP => Up, IFLA_BOND_PORT_MII_STATUS_GOING_DOWN => GoingDown, IFLA_BOND_PORT_MII_STATUS_DOWN => Down, IFLA_BOND_PORT_MII_STATUS_GOING_BACK => GoingBack, _ => Other(value), } } } impl From for u8 { fn from(value: MiiStatus) -> Self { use self::MiiStatus::*; match value { Up => IFLA_BOND_PORT_MII_STATUS_UP, GoingDown => IFLA_BOND_PORT_MII_STATUS_GOING_DOWN, Down => IFLA_BOND_PORT_MII_STATUS_DOWN, GoingBack => IFLA_BOND_PORT_MII_STATUS_GOING_BACK, Other(other) => other, } } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoBondPort { LinkFailureCount(u32), MiiStatus(MiiStatus), PermHwaddr(Vec), Prio(i32), QueueId(u16), BondPortState(BondPortState), Other(DefaultNla), } impl Nla for InfoBondPort { #[rustfmt::skip] fn value_len(&self) -> usize { use self::InfoBondPort::*; match self { QueueId(_) => 2, LinkFailureCount(_) | Prio(_) => 4, PermHwaddr(ref bytes) => bytes.len(), MiiStatus(_) => 1, BondPortState(_) => 1, Other(nla) => nla.value_len(), } } #[rustfmt::skip] fn emit_value(&self, buffer: &mut [u8]) { use self::InfoBondPort::*; match self { QueueId(ref value) => NativeEndian::write_u16(buffer, *value), PermHwaddr(ref bytes) => buffer.copy_from_slice(bytes.as_slice()), Prio(ref value) => NativeEndian::write_i32(buffer, *value), LinkFailureCount(value) => NativeEndian::write_u32(buffer, *value), MiiStatus(state) => buffer[0] = (*state).into(), BondPortState(state) => buffer[0] = (*state).into(), Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::InfoBondPort::*; match self { LinkFailureCount(_) => IFLA_BOND_PORT_LINK_FAILURE_COUNT, MiiStatus(_) => IFLA_BOND_PORT_MII_STATUS, PermHwaddr(_) => IFLA_BOND_PORT_PERM_HWADDR, Prio(_) => IFLA_BOND_PORT_PRIO, QueueId(_) => IFLA_BOND_PORT_QUEUE_ID, BondPortState(_) => IFLA_BOND_PORT_STATE, Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoBondPort { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoBondPort::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_BOND_PORT_LINK_FAILURE_COUNT => { LinkFailureCount(parse_u32(payload).context( "invalid IFLA_BOND_PORT_LINK_FAILURE_COUNT value", )?) } IFLA_BOND_PORT_MII_STATUS => MiiStatus( parse_u8(payload) .context("invalid IFLA_BOND_PORT_MII_STATUS value")? .into(), ), IFLA_BOND_PORT_PERM_HWADDR => PermHwaddr(payload.to_vec()), IFLA_BOND_PORT_PRIO => Prio( parse_i32(payload) .context("invalid IFLA_BOND_PORT_PRIO value")?, ), IFLA_BOND_PORT_QUEUE_ID => QueueId( parse_u16(payload) .context("invalid IFLA_BOND_PORT_QUEUE_ID value")?, ), IFLA_BOND_PORT_STATE => BondPortState( parse_u8(payload) .context("invalid IFLA_BOND_PORT_STATE value")? .into(), ), kind => Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } netlink-packet-route-0.17.0/src/rtnl/link/nlas/bridge.rs000064400000000000000000000625121046102023000212260ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use anyhow::Context; use byteorder::{BigEndian, ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator, NLA_F_NESTED}, parsers::{ parse_ip, parse_mac, parse_u16, parse_u16_be, parse_u32, parse_u64, parse_u8, }, traits::{Emitable, Parseable}, DecodeError, }; use crate::constants::*; const BRIDGE_QUERIER_IP_ADDRESS: u16 = 1; const BRIDGE_QUERIER_IP_PORT: u16 = 2; const BRIDGE_QUERIER_IP_OTHER_TIMER: u16 = 3; // const BRIDGE_QUERIER_PAD: u16 = 4; const BRIDGE_QUERIER_IPV6_ADDRESS: u16 = 5; const BRIDGE_QUERIER_IPV6_PORT: u16 = 6; const BRIDGE_QUERIER_IPV6_OTHER_TIMER: u16 = 7; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoBridge { Unspec(Vec), GroupAddr([u8; 6]), // FIXME: what type is this? putting Vec for now but it might // be a boolean actually FdbFlush(Vec), Pad(Vec), HelloTimer(u64), TcnTimer(u64), TopologyChangeTimer(u64), GcTimer(u64), MulticastMembershipInterval(u64), MulticastQuerierInterval(u64), MulticastQueryInterval(u64), MulticastQueryResponseInterval(u64), MulticastLastMemberInterval(u64), MulticastStartupQueryInterval(u64), ForwardDelay(u32), HelloTime(u32), MaxAge(u32), AgeingTime(u32), StpState(u32), MulticastHashElasticity(u32), MulticastHashMax(u32), MulticastLastMemberCount(u32), MulticastStartupQueryCount(u32), RootPathCost(u32), Priority(u16), VlanProtocol(u16), GroupFwdMask(u16), RootId((u16, [u8; 6])), BridgeId((u16, [u8; 6])), RootPort(u16), VlanDefaultPvid(u16), VlanFiltering(u8), TopologyChange(u8), TopologyChangeDetected(u8), MulticastRouter(u8), MulticastSnooping(u8), MulticastQueryUseIfaddr(u8), MulticastQuerier(u8), NfCallIpTables(u8), NfCallIp6Tables(u8), NfCallArpTables(u8), VlanStatsEnabled(u8), MulticastStatsEnabled(u8), MulticastIgmpVersion(u8), MulticastMldVersion(u8), VlanStatsPerHost(u8), MultiBoolOpt(u64), MulticastQuerierState(Vec), Other(DefaultNla), } impl Nla for InfoBridge { #[rustfmt::skip] fn value_len(&self) -> usize { use self::InfoBridge::*; match self { Unspec(bytes) | FdbFlush(bytes) | Pad(bytes) => bytes.len(), HelloTimer(_) | TcnTimer(_) | TopologyChangeTimer(_) | GcTimer(_) | MulticastMembershipInterval(_) | MulticastQuerierInterval(_) | MulticastQueryInterval(_) | MulticastQueryResponseInterval(_) | MulticastLastMemberInterval(_) | MulticastStartupQueryInterval(_) => 8, ForwardDelay(_) | HelloTime(_) | MaxAge(_) | AgeingTime(_) | StpState(_) | MulticastHashElasticity(_) | MulticastHashMax(_) | MulticastLastMemberCount(_) | MulticastStartupQueryCount(_) | RootPathCost(_) => 4, Priority(_) | VlanProtocol(_) | GroupFwdMask(_) | RootPort(_) | VlanDefaultPvid(_) => 2, RootId(_) | BridgeId(_) | MultiBoolOpt(_) => 8, GroupAddr(_) => 6, VlanFiltering(_) | TopologyChange(_) | TopologyChangeDetected(_) | MulticastRouter(_) | MulticastSnooping(_) | MulticastQueryUseIfaddr(_) | MulticastQuerier(_) | NfCallIpTables(_) | NfCallIp6Tables(_) | NfCallArpTables(_) | VlanStatsEnabled(_) | MulticastStatsEnabled(_) | MulticastIgmpVersion(_) | MulticastMldVersion(_) | VlanStatsPerHost(_) => 1, MulticastQuerierState(ref nlas) => nlas.as_slice().buffer_len(), Other(nla) => nla.value_len(), } } #[rustfmt::skip] fn emit_value(&self, buffer: &mut [u8]) { use self::InfoBridge::*; match self { Unspec(ref bytes) | FdbFlush(ref bytes) | Pad(ref bytes) => buffer.copy_from_slice(bytes), HelloTimer(ref value) | TcnTimer(ref value) | TopologyChangeTimer(ref value) | GcTimer(ref value) | MulticastMembershipInterval(ref value) | MulticastQuerierInterval(ref value) | MulticastQueryInterval(ref value) | MulticastQueryResponseInterval(ref value) | MulticastLastMemberInterval(ref value) | MulticastStartupQueryInterval(ref value) | MultiBoolOpt(ref value) => NativeEndian::write_u64(buffer, *value), ForwardDelay(ref value) | HelloTime(ref value) | MaxAge(ref value) | AgeingTime(ref value) | StpState(ref value) | MulticastHashElasticity(ref value) | MulticastHashMax(ref value) | MulticastLastMemberCount(ref value) | MulticastStartupQueryCount(ref value) | RootPathCost(ref value) => NativeEndian::write_u32(buffer, *value), Priority(ref value) | GroupFwdMask(ref value) | RootPort(ref value) | VlanDefaultPvid(ref value) => NativeEndian::write_u16(buffer, *value), VlanProtocol(ref value) => BigEndian::write_u16(buffer, *value), RootId((ref priority, ref address)) | BridgeId((ref priority, ref address)) => { NativeEndian::write_u16(buffer, *priority); buffer[2..].copy_from_slice(&address[..]); } GroupAddr(ref value) => buffer.copy_from_slice(&value[..]), VlanFiltering(ref value) | TopologyChange(ref value) | TopologyChangeDetected(ref value) | MulticastRouter(ref value) | MulticastSnooping(ref value) | MulticastQueryUseIfaddr(ref value) | MulticastQuerier(ref value) | NfCallIpTables(ref value) | NfCallIp6Tables(ref value) | NfCallArpTables(ref value) | VlanStatsEnabled(ref value) | MulticastStatsEnabled(ref value) | MulticastIgmpVersion(ref value) | MulticastMldVersion(ref value) | VlanStatsPerHost(ref value) => buffer[0] = *value, MulticastQuerierState(ref nlas) => nlas.as_slice().emit(buffer), Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::InfoBridge::*; match self { Unspec(_) => IFLA_BR_UNSPEC, GroupAddr(_) => IFLA_BR_GROUP_ADDR, FdbFlush(_) => IFLA_BR_FDB_FLUSH, Pad(_) => IFLA_BR_PAD, HelloTimer(_) => IFLA_BR_HELLO_TIMER, TcnTimer(_) => IFLA_BR_TCN_TIMER, TopologyChangeTimer(_) => IFLA_BR_TOPOLOGY_CHANGE_TIMER, GcTimer(_) => IFLA_BR_GC_TIMER, MulticastMembershipInterval(_) => IFLA_BR_MCAST_MEMBERSHIP_INTVL, MulticastQuerierInterval(_) => IFLA_BR_MCAST_QUERIER_INTVL, MulticastQueryInterval(_) => IFLA_BR_MCAST_QUERY_INTVL, MulticastQueryResponseInterval(_) => { IFLA_BR_MCAST_QUERY_RESPONSE_INTVL } ForwardDelay(_) => IFLA_BR_FORWARD_DELAY, HelloTime(_) => IFLA_BR_HELLO_TIME, MaxAge(_) => IFLA_BR_MAX_AGE, AgeingTime(_) => IFLA_BR_AGEING_TIME, StpState(_) => IFLA_BR_STP_STATE, MulticastHashElasticity(_) => IFLA_BR_MCAST_HASH_ELASTICITY, MulticastHashMax(_) => IFLA_BR_MCAST_HASH_MAX, MulticastLastMemberCount(_) => IFLA_BR_MCAST_LAST_MEMBER_CNT, MulticastStartupQueryCount(_) => IFLA_BR_MCAST_STARTUP_QUERY_CNT, MulticastLastMemberInterval(_) => IFLA_BR_MCAST_LAST_MEMBER_INTVL, MulticastStartupQueryInterval(_) => { IFLA_BR_MCAST_STARTUP_QUERY_INTVL } RootPathCost(_) => IFLA_BR_ROOT_PATH_COST, Priority(_) => IFLA_BR_PRIORITY, VlanProtocol(_) => IFLA_BR_VLAN_PROTOCOL, GroupFwdMask(_) => IFLA_BR_GROUP_FWD_MASK, RootId(_) => IFLA_BR_ROOT_ID, BridgeId(_) => IFLA_BR_BRIDGE_ID, RootPort(_) => IFLA_BR_ROOT_PORT, VlanDefaultPvid(_) => IFLA_BR_VLAN_DEFAULT_PVID, VlanFiltering(_) => IFLA_BR_VLAN_FILTERING, TopologyChange(_) => IFLA_BR_TOPOLOGY_CHANGE, TopologyChangeDetected(_) => IFLA_BR_TOPOLOGY_CHANGE_DETECTED, MulticastRouter(_) => IFLA_BR_MCAST_ROUTER, MulticastSnooping(_) => IFLA_BR_MCAST_SNOOPING, MulticastQueryUseIfaddr(_) => IFLA_BR_MCAST_QUERY_USE_IFADDR, MulticastQuerier(_) => IFLA_BR_MCAST_QUERIER, NfCallIpTables(_) => IFLA_BR_NF_CALL_IPTABLES, NfCallIp6Tables(_) => IFLA_BR_NF_CALL_IP6TABLES, NfCallArpTables(_) => IFLA_BR_NF_CALL_ARPTABLES, VlanStatsEnabled(_) => IFLA_BR_VLAN_STATS_ENABLED, MulticastStatsEnabled(_) => IFLA_BR_MCAST_STATS_ENABLED, MulticastIgmpVersion(_) => IFLA_BR_MCAST_IGMP_VERSION, MulticastMldVersion(_) => IFLA_BR_MCAST_MLD_VERSION, VlanStatsPerHost(_) => IFLA_BR_VLAN_STATS_PER_PORT, MultiBoolOpt(_) => IFLA_BR_MULTI_BOOLOPT, MulticastQuerierState(_) => { IFLA_BR_MCAST_QUERIER_STATE | NLA_F_NESTED } Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoBridge { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoBridge::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_BR_UNSPEC => Unspec(payload.to_vec()), IFLA_BR_FDB_FLUSH => FdbFlush(payload.to_vec()), IFLA_BR_PAD => Pad(payload.to_vec()), IFLA_BR_HELLO_TIMER => HelloTimer( parse_u64(payload) .context("invalid IFLA_BR_HELLO_TIMER value")?, ), IFLA_BR_TCN_TIMER => TcnTimer( parse_u64(payload) .context("invalid IFLA_BR_TCN_TIMER value")?, ), IFLA_BR_TOPOLOGY_CHANGE_TIMER => TopologyChangeTimer( parse_u64(payload) .context("invalid IFLA_BR_TOPOLOGY_CHANGE_TIMER value")?, ), IFLA_BR_GC_TIMER => GcTimer( parse_u64(payload).context("invalid IFLA_BR_GC_TIMER value")?, ), IFLA_BR_MCAST_LAST_MEMBER_INTVL => MulticastLastMemberInterval( parse_u64(payload) .context("invalid IFLA_BR_MCAST_LAST_MEMBER_INTVL value")?, ), IFLA_BR_MCAST_MEMBERSHIP_INTVL => MulticastMembershipInterval( parse_u64(payload) .context("invalid IFLA_BR_MCAST_MEMBERSHIP_INTVL value")?, ), IFLA_BR_MCAST_QUERIER_INTVL => MulticastQuerierInterval( parse_u64(payload) .context("invalid IFLA_BR_MCAST_QUERIER_INTVL value")?, ), IFLA_BR_MCAST_QUERY_INTVL => MulticastQueryInterval( parse_u64(payload) .context("invalid IFLA_BR_MCAST_QUERY_INTVL value")?, ), IFLA_BR_MCAST_QUERY_RESPONSE_INTVL => { MulticastQueryResponseInterval(parse_u64(payload).context( "invalid IFLA_BR_MCAST_QUERY_RESPONSE_INTVL value", )?) } IFLA_BR_MCAST_STARTUP_QUERY_INTVL => { MulticastStartupQueryInterval(parse_u64(payload).context( "invalid IFLA_BR_MCAST_STARTUP_QUERY_INTVL value", )?) } IFLA_BR_FORWARD_DELAY => ForwardDelay( parse_u32(payload) .context("invalid IFLA_BR_FORWARD_DELAY value")?, ), IFLA_BR_HELLO_TIME => HelloTime( parse_u32(payload) .context("invalid IFLA_BR_HELLO_TIME value")?, ), IFLA_BR_MAX_AGE => MaxAge( parse_u32(payload).context("invalid IFLA_BR_MAX_AGE value")?, ), IFLA_BR_AGEING_TIME => AgeingTime( parse_u32(payload) .context("invalid IFLA_BR_AGEING_TIME value")?, ), IFLA_BR_STP_STATE => StpState( parse_u32(payload) .context("invalid IFLA_BR_STP_STATE value")?, ), IFLA_BR_MCAST_HASH_ELASTICITY => MulticastHashElasticity( parse_u32(payload) .context("invalid IFLA_BR_MCAST_HASH_ELASTICITY value")?, ), IFLA_BR_MCAST_HASH_MAX => MulticastHashMax( parse_u32(payload) .context("invalid IFLA_BR_MCAST_HASH_MAX value")?, ), IFLA_BR_MCAST_LAST_MEMBER_CNT => MulticastLastMemberCount( parse_u32(payload) .context("invalid IFLA_BR_MCAST_LAST_MEMBER_CNT value")?, ), IFLA_BR_MCAST_STARTUP_QUERY_CNT => MulticastStartupQueryCount( parse_u32(payload) .context("invalid IFLA_BR_MCAST_STARTUP_QUERY_CNT value")?, ), IFLA_BR_ROOT_PATH_COST => RootPathCost( parse_u32(payload) .context("invalid IFLA_BR_ROOT_PATH_COST value")?, ), IFLA_BR_PRIORITY => Priority( parse_u16(payload).context("invalid IFLA_BR_PRIORITY value")?, ), IFLA_BR_VLAN_PROTOCOL => VlanProtocol( parse_u16_be(payload) .context("invalid IFLA_BR_VLAN_PROTOCOL value")?, ), IFLA_BR_GROUP_FWD_MASK => GroupFwdMask( parse_u16(payload) .context("invalid IFLA_BR_GROUP_FWD_MASK value")?, ), IFLA_BR_ROOT_ID | IFLA_BR_BRIDGE_ID => { if payload.len() != 8 { return Err( "invalid IFLA_BR_ROOT_ID or IFLA_BR_BRIDGE_ID value" .into(), ); } let priority = NativeEndian::read_u16(&payload[..2]); let address = parse_mac(&payload[2..]).context( "invalid IFLA_BR_ROOT_ID or IFLA_BR_BRIDGE_ID value", )?; match buf.kind() { IFLA_BR_ROOT_ID => RootId((priority, address)), IFLA_BR_BRIDGE_ID => BridgeId((priority, address)), _ => unreachable!(), } } IFLA_BR_GROUP_ADDR => GroupAddr( parse_mac(payload) .context("invalid IFLA_BR_GROUP_ADDR value")?, ), IFLA_BR_ROOT_PORT => RootPort( parse_u16(payload) .context("invalid IFLA_BR_ROOT_PORT value")?, ), IFLA_BR_VLAN_DEFAULT_PVID => VlanDefaultPvid( parse_u16(payload) .context("invalid IFLA_BR_VLAN_DEFAULT_PVID value")?, ), IFLA_BR_VLAN_FILTERING => VlanFiltering( parse_u8(payload) .context("invalid IFLA_BR_VLAN_FILTERING value")?, ), IFLA_BR_TOPOLOGY_CHANGE => TopologyChange( parse_u8(payload) .context("invalid IFLA_BR_TOPOLOGY_CHANGE value")?, ), IFLA_BR_TOPOLOGY_CHANGE_DETECTED => { TopologyChangeDetected(parse_u8(payload).context( "invalid IFLA_BR_TOPOLOGY_CHANGE_DETECTED value", )?) } IFLA_BR_MCAST_ROUTER => MulticastRouter( parse_u8(payload) .context("invalid IFLA_BR_MCAST_ROUTER value")?, ), IFLA_BR_MCAST_SNOOPING => MulticastSnooping( parse_u8(payload) .context("invalid IFLA_BR_MCAST_SNOOPING value")?, ), IFLA_BR_MCAST_QUERY_USE_IFADDR => MulticastQueryUseIfaddr( parse_u8(payload) .context("invalid IFLA_BR_MCAST_QUERY_USE_IFADDR value")?, ), IFLA_BR_MCAST_QUERIER => MulticastQuerier( parse_u8(payload) .context("invalid IFLA_BR_MCAST_QUERIER value")?, ), IFLA_BR_NF_CALL_IPTABLES => NfCallIpTables( parse_u8(payload) .context("invalid IFLA_BR_NF_CALL_IPTABLES value")?, ), IFLA_BR_NF_CALL_IP6TABLES => NfCallIp6Tables( parse_u8(payload) .context("invalid IFLA_BR_NF_CALL_IP6TABLES value")?, ), IFLA_BR_NF_CALL_ARPTABLES => NfCallArpTables( parse_u8(payload) .context("invalid IFLA_BR_NF_CALL_ARPTABLES value")?, ), IFLA_BR_VLAN_STATS_ENABLED => VlanStatsEnabled( parse_u8(payload) .context("invalid IFLA_BR_VLAN_STATS_ENABLED value")?, ), IFLA_BR_MCAST_STATS_ENABLED => MulticastStatsEnabled( parse_u8(payload) .context("invalid IFLA_BR_MCAST_STATS_ENABLED value")?, ), IFLA_BR_MCAST_IGMP_VERSION => MulticastIgmpVersion( parse_u8(payload) .context("invalid IFLA_BR_MCAST_IGMP_VERSION value")?, ), IFLA_BR_MCAST_MLD_VERSION => MulticastMldVersion( parse_u8(payload) .context("invalid IFLA_BR_MCAST_MLD_VERSION value")?, ), IFLA_BR_VLAN_STATS_PER_PORT => VlanStatsPerHost( parse_u8(payload) .context("invalid IFLA_BR_VLAN_STATS_PER_PORT value")?, ), IFLA_BR_MULTI_BOOLOPT => MultiBoolOpt( parse_u64(payload) .context("invalid IFLA_BR_MULTI_BOOLOPT value")?, ), IFLA_BR_MCAST_QUERIER_STATE => { let mut v = Vec::new(); let err = "failed to parse IFLA_BR_MCAST_QUERIER_STATE"; for nla in NlasIterator::new(payload) { let nla = &nla.context(err)?; let parsed = BridgeQuerierState::parse(nla).context(err)?; v.push(parsed); } MulticastQuerierState(v) } _ => Other(DefaultNla::parse(buf).context( "invalid link info bridge NLA value (unknown type)", )?), }) } } #[derive(Debug, Clone, Eq, PartialEq)] #[non_exhaustive] pub enum BridgeQuerierState { Ipv4Address(Ipv4Addr), Ipv4Port(u32), Ipv4OtherTimer(u64), Ipv6Address(Ipv6Addr), Ipv6Port(u32), Ipv6OtherTimer(u64), Other(DefaultNla), } impl Nla for BridgeQuerierState { fn value_len(&self) -> usize { use self::BridgeQuerierState::*; match self { Ipv4Address(_) => 4, Ipv6Address(_) => 16, Ipv4Port(_) | Ipv6Port(_) => 4, Ipv4OtherTimer(_) | Ipv6OtherTimer(_) => 8, Other(nla) => nla.value_len(), } } fn kind(&self) -> u16 { use self::BridgeQuerierState::*; match self { Ipv4Address(_) => BRIDGE_QUERIER_IP_ADDRESS, Ipv4Port(_) => BRIDGE_QUERIER_IP_PORT, Ipv4OtherTimer(_) => BRIDGE_QUERIER_IP_OTHER_TIMER, Ipv6Address(_) => BRIDGE_QUERIER_IPV6_ADDRESS, Ipv6Port(_) => BRIDGE_QUERIER_IPV6_PORT, Ipv6OtherTimer(_) => BRIDGE_QUERIER_IPV6_OTHER_TIMER, Other(nla) => nla.kind(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::BridgeQuerierState::*; match self { Ipv4Port(d) | Ipv6Port(d) => NativeEndian::write_u32(buffer, *d), Ipv4OtherTimer(d) | Ipv6OtherTimer(d) => { NativeEndian::write_u64(buffer, *d) } Ipv4Address(addr) => buffer.copy_from_slice(&addr.octets()), Ipv6Address(addr) => buffer.copy_from_slice(&addr.octets()), Other(nla) => nla.emit_value(buffer), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for BridgeQuerierState { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::BridgeQuerierState::*; let payload = buf.value(); Ok(match buf.kind() { BRIDGE_QUERIER_IP_ADDRESS => match parse_ip(payload) { Ok(IpAddr::V4(addr)) => Ipv4Address(addr), Ok(v) => { return Err(DecodeError::from(format!( "Invalid BRIDGE_QUERIER_IP_ADDRESS, \ expecting IPv4 address, but got {v}" ))) } Err(e) => { return Err(DecodeError::from(format!( "Invalid BRIDGE_QUERIER_IP_ADDRESS {e}" ))) } }, BRIDGE_QUERIER_IPV6_ADDRESS => match parse_ip(payload) { Ok(IpAddr::V6(addr)) => Ipv6Address(addr), Ok(v) => { return Err(DecodeError::from(format!( "Invalid BRIDGE_QUERIER_IPV6_ADDRESS, \ expecting IPv6 address, but got {v}" ))); } Err(e) => { return Err(DecodeError::from(format!( "Invalid BRIDGE_QUERIER_IPV6_ADDRESS {e}" ))); } }, BRIDGE_QUERIER_IP_PORT => Ipv4Port( parse_u32(payload) .context("invalid BRIDGE_QUERIER_IP_PORT value")?, ), BRIDGE_QUERIER_IPV6_PORT => Ipv6Port( parse_u32(payload) .context("invalid BRIDGE_QUERIER_IPV6_PORT value")?, ), BRIDGE_QUERIER_IP_OTHER_TIMER => Ipv4OtherTimer( parse_u64(payload) .context("invalid BRIDGE_QUERIER_IP_OTHER_TIMER value")?, ), BRIDGE_QUERIER_IPV6_OTHER_TIMER => Ipv6OtherTimer( parse_u64(payload) .context("invalid BRIDGE_QUERIER_IPV6_OTHER_TIMER value")?, ), kind => Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } #[cfg(test)] mod tests { use netlink_packet_utils::{ nla::NlaBuffer, traits::{Emitable, Parseable}, }; use super::{BridgeQuerierState, InfoBridge}; #[rustfmt::skip] // This is capture of nlmon of `ip -d link show br0` after: // ip link set br0 type bridge mcast_snooping 1 // ip link set br0 type bridge mcast_querier 1 // ip link set br0 type bridge mcast_stats_enabled 1 const BR_MCAST_QUERIER_STATE_DUMP: [u8; 32] = [ 0x20, 0x00, // len: 32 0x2f, 0x80, // IFLA_BR_MCAST_QUERIER_STATE | NLA_F_NESTED 0x08, 0x00, // len: 8 0x01, 0x00, // BRIDGE_QUERIER_IP_ADDRESS 0x00, 0x00, 0x00, 0x00, // 0.0.0.0 0x14, 0x00, // len: 20 0x05, 0x00, // BRIDGE_QUERIER_IPV6_ADDRESS 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x23, 0x45, 0xff, 0xfe, 0x67, 0x89, 0x1c, // fe80::223:45ff:fe67:891c ]; #[test] fn test_br_multicast_querier_state_parse() { let expected = vec![ BridgeQuerierState::Ipv4Address("0.0.0.0".parse().unwrap()), BridgeQuerierState::Ipv6Address( "fe80::223:45ff:fe67:891c".parse().unwrap(), ), ]; let nla = NlaBuffer::new_checked(&BR_MCAST_QUERIER_STATE_DUMP[..]).unwrap(); let parsed = if let InfoBridge::MulticastQuerierState(s) = InfoBridge::parse(&nla).unwrap() { s } else { panic!("Failed for parse IFLA_BR_MCAST_QUERIER_STATE") }; assert_eq!(parsed, expected); } #[test] fn test_br_multicast_querier_state_emit() { let mut expected = [0u8; 32]; InfoBridge::MulticastQuerierState(vec![ BridgeQuerierState::Ipv4Address("0.0.0.0".parse().unwrap()), BridgeQuerierState::Ipv6Address( "fe80::223:45ff:fe67:891c".parse().unwrap(), ), ]) .emit(&mut expected); assert_eq!(expected, BR_MCAST_QUERIER_STATE_DUMP); } } netlink-packet-route-0.17.0/src/rtnl/link/nlas/inet/dev_conf.rs000064400000000000000000000140031046102023000225040ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; pub const DEV_CONF_LEN: usize = 124; buffer!(InetDevConfBuffer(DEV_CONF_LEN) { forwarding: (i32, 0..4), mc_forwarding: (i32, 4..8), proxy_arp: (i32, 8..12), accept_redirects: (i32, 12..16), secure_redirects: (i32, 16..20), send_redirects: (i32, 20..24), shared_media: (i32, 24..28), rp_filter: (i32, 28..32), accept_source_route: (i32, 32..36), bootp_relay: (i32, 36..40), log_martians: (i32, 40..44), tag: (i32, 44..48), arpfilter: (i32, 48..52), medium_id: (i32, 52..56), noxfrm: (i32, 56..60), nopolicy: (i32, 60..64), force_igmp_version: (i32, 64..68), arp_announce: (i32, 68..72), arp_ignore: (i32, 72..76), promote_secondaries: (i32, 76..80), arp_accept: (i32, 80..84), arp_notify: (i32, 84..88), accept_local: (i32, 88..92), src_vmark: (i32, 92..96), proxy_arp_pvlan: (i32, 96..100), route_localnet: (i32, 100..104), igmpv2_unsolicited_report_interval: (i32, 104..108), igmpv3_unsolicited_report_interval: (i32, 108..112), ignore_routes_with_linkdown: (i32, 112..116), drop_unicast_in_l2_multicast: (i32, 116..120), drop_gratuitous_arp: (i32, 120..124), }); #[derive(Clone, Copy, Eq, PartialEq, Debug)] #[non_exhaustive] pub struct InetDevConf { pub forwarding: i32, pub mc_forwarding: i32, pub proxy_arp: i32, pub accept_redirects: i32, pub secure_redirects: i32, pub send_redirects: i32, pub shared_media: i32, pub rp_filter: i32, pub accept_source_route: i32, pub bootp_relay: i32, pub log_martians: i32, pub tag: i32, pub arpfilter: i32, pub medium_id: i32, pub noxfrm: i32, pub nopolicy: i32, pub force_igmp_version: i32, pub arp_announce: i32, pub arp_ignore: i32, pub promote_secondaries: i32, pub arp_accept: i32, pub arp_notify: i32, pub accept_local: i32, pub src_vmark: i32, pub proxy_arp_pvlan: i32, pub route_localnet: i32, pub igmpv2_unsolicited_report_interval: i32, pub igmpv3_unsolicited_report_interval: i32, pub ignore_routes_with_linkdown: i32, pub drop_unicast_in_l2_multicast: i32, pub drop_gratuitous_arp: i32, } impl> Parseable> for InetDevConf { fn parse(buf: &InetDevConfBuffer) -> Result { Ok(Self { forwarding: buf.forwarding(), mc_forwarding: buf.mc_forwarding(), proxy_arp: buf.proxy_arp(), accept_redirects: buf.accept_redirects(), secure_redirects: buf.secure_redirects(), send_redirects: buf.send_redirects(), shared_media: buf.shared_media(), rp_filter: buf.rp_filter(), accept_source_route: buf.accept_source_route(), bootp_relay: buf.bootp_relay(), log_martians: buf.log_martians(), tag: buf.tag(), arpfilter: buf.arpfilter(), medium_id: buf.medium_id(), noxfrm: buf.noxfrm(), nopolicy: buf.nopolicy(), force_igmp_version: buf.force_igmp_version(), arp_announce: buf.arp_announce(), arp_ignore: buf.arp_ignore(), promote_secondaries: buf.promote_secondaries(), arp_accept: buf.arp_accept(), arp_notify: buf.arp_notify(), accept_local: buf.accept_local(), src_vmark: buf.src_vmark(), proxy_arp_pvlan: buf.proxy_arp_pvlan(), route_localnet: buf.route_localnet(), igmpv2_unsolicited_report_interval: buf .igmpv2_unsolicited_report_interval(), igmpv3_unsolicited_report_interval: buf .igmpv3_unsolicited_report_interval(), ignore_routes_with_linkdown: buf.ignore_routes_with_linkdown(), drop_unicast_in_l2_multicast: buf.drop_unicast_in_l2_multicast(), drop_gratuitous_arp: buf.drop_gratuitous_arp(), }) } } impl Emitable for InetDevConf { fn buffer_len(&self) -> usize { DEV_CONF_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = InetDevConfBuffer::new(buffer); buffer.set_forwarding(self.forwarding); buffer.set_mc_forwarding(self.mc_forwarding); buffer.set_proxy_arp(self.proxy_arp); buffer.set_accept_redirects(self.accept_redirects); buffer.set_secure_redirects(self.secure_redirects); buffer.set_send_redirects(self.send_redirects); buffer.set_shared_media(self.shared_media); buffer.set_rp_filter(self.rp_filter); buffer.set_accept_source_route(self.accept_source_route); buffer.set_bootp_relay(self.bootp_relay); buffer.set_log_martians(self.log_martians); buffer.set_tag(self.tag); buffer.set_arpfilter(self.arpfilter); buffer.set_medium_id(self.medium_id); buffer.set_noxfrm(self.noxfrm); buffer.set_nopolicy(self.nopolicy); buffer.set_force_igmp_version(self.force_igmp_version); buffer.set_arp_announce(self.arp_announce); buffer.set_arp_ignore(self.arp_ignore); buffer.set_promote_secondaries(self.promote_secondaries); buffer.set_arp_accept(self.arp_accept); buffer.set_arp_notify(self.arp_notify); buffer.set_accept_local(self.accept_local); buffer.set_src_vmark(self.src_vmark); buffer.set_proxy_arp_pvlan(self.proxy_arp_pvlan); buffer.set_route_localnet(self.route_localnet); buffer.set_igmpv2_unsolicited_report_interval( self.igmpv2_unsolicited_report_interval, ); buffer.set_igmpv3_unsolicited_report_interval( self.igmpv3_unsolicited_report_interval, ); buffer .set_ignore_routes_with_linkdown(self.ignore_routes_with_linkdown); buffer.set_drop_unicast_in_l2_multicast( self.drop_unicast_in_l2_multicast, ); buffer.set_drop_gratuitous_arp(self.drop_gratuitous_arp); } } netlink-packet-route-0.17.0/src/rtnl/link/nlas/inet/mod.rs000064400000000000000000000034341046102023000215060ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use crate::constants::{IFLA_INET_CONF, IFLA_INET_UNSPEC}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, traits::Parseable, DecodeError, }; mod dev_conf; pub use self::dev_conf::*; #[derive(Clone, Eq, PartialEq, Debug)] #[non_exhaustive] pub enum Inet { DevConf(Vec), Unspec(Vec), Other(DefaultNla), } impl Nla for Inet { fn value_len(&self) -> usize { use self::Inet::*; match *self { Unspec(ref bytes) => bytes.len(), DevConf(_) => DEV_CONF_LEN, Other(ref nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::Inet::*; match *self { Unspec(ref bytes) => { buffer[..bytes.len()].copy_from_slice(bytes.as_slice()) } DevConf(ref dev_conf) => { buffer[..dev_conf.len()].copy_from_slice(dev_conf.as_slice()) } Other(ref nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::Inet::*; match *self { Unspec(_) => IFLA_INET_UNSPEC, DevConf(_) => IFLA_INET_CONF, Other(ref nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Inet { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::Inet::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_INET_UNSPEC => Unspec(payload.to_vec()), IFLA_INET_CONF => DevConf(payload.to_vec()), kind => Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } netlink-packet-route-0.17.0/src/rtnl/link/nlas/inet6/cache.rs000064400000000000000000000024761046102023000220650ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; #[derive(Clone, Copy, Eq, PartialEq, Debug)] #[non_exhaustive] pub struct Inet6CacheInfo { pub max_reasm_len: i32, pub tstamp: i32, pub reachable_time: i32, pub retrans_time: i32, } pub const LINK_INET6_CACHE_INFO_LEN: usize = 16; buffer!(Inet6CacheInfoBuffer(LINK_INET6_CACHE_INFO_LEN) { max_reasm_len: (i32, 0..4), tstamp: (i32, 4..8), reachable_time: (i32, 8..12), retrans_time: (i32, 12..16), }); impl> Parseable> for Inet6CacheInfo { fn parse(buf: &Inet6CacheInfoBuffer) -> Result { Ok(Self { max_reasm_len: buf.max_reasm_len(), tstamp: buf.tstamp(), reachable_time: buf.reachable_time(), retrans_time: buf.retrans_time(), }) } } impl Emitable for Inet6CacheInfo { fn buffer_len(&self) -> usize { LINK_INET6_CACHE_INFO_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = Inet6CacheInfoBuffer::new(buffer); buffer.set_max_reasm_len(self.max_reasm_len); buffer.set_tstamp(self.tstamp); buffer.set_reachable_time(self.reachable_time); buffer.set_retrans_time(self.retrans_time); } } netlink-packet-route-0.17.0/src/rtnl/link/nlas/inet6/dev_conf.rs000064400000000000000000000237241046102023000226040ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; pub const LINK_INET6_DEV_CONF_LEN: usize = 204; buffer!(Inet6DevConfBuffer(LINK_INET6_DEV_CONF_LEN) { forwarding: (i32, 0..4), hoplimit: (i32, 4..8), mtu6: (i32, 8..12), accept_ra: (i32, 12..16), accept_redirects: (i32, 16..20), autoconf: (i32, 20..24), dad_transmits: (i32, 24..28), rtr_solicits: (i32, 28..32), rtr_solicit_interval: (i32, 32..36), rtr_solicit_delay: (i32, 36..40), use_tempaddr: (i32, 40..44), temp_valid_lft: (i32, 44..48), temp_prefered_lft: (i32, 48..52), regen_max_retry: (i32, 52..56), max_desync_factor: (i32, 56..60), max_addresses: (i32, 60..64), force_mld_version: (i32, 64..68), accept_ra_defrtr: (i32, 68..72), accept_ra_pinfo: (i32, 72..76), accept_ra_rtr_pref: (i32, 76..80), rtr_probe_interval: (i32, 80..84), accept_ra_rt_info_max_plen: (i32, 84..88), proxy_ndp: (i32, 88..92), optimistic_dad: (i32, 92..96), accept_source_route: (i32, 96..100), mc_forwarding: (i32, 100..104), disable_ipv6: (i32, 104..108), accept_dad: (i32, 108..112), force_tllao: (i32, 112..116), ndisc_notify: (i32, 116..120), mldv1_unsolicited_report_interval: (i32, 120..124), mldv2_unsolicited_report_interval: (i32, 124..128), suppress_frag_ndisc: (i32, 128..132), accept_ra_from_local: (i32, 132..136), use_optimistic: (i32, 136..140), accept_ra_mtu: (i32, 140..144), stable_secret: (i32, 144..148), use_oif_addrs_only: (i32, 148..152), accept_ra_min_hop_limit: (i32, 152..156), ignore_routes_with_linkdown: (i32, 156..160), drop_unicast_in_l2_multicast: (i32, 160..164), drop_unsolicited_na: (i32, 164..168), keep_addr_on_down: (i32, 168..172), rtr_solicit_max_interval: (i32, 172..176), seg6_enabled: (i32, 176..180), seg6_require_hmac: (i32, 180..184), enhanced_dad: (i32, 184..188), addr_gen_mode: (i32, 188..192), disable_policy: (i32, 192..196), accept_ra_rt_info_min_plen: (i32, 196..200), ndisc_tclass: (i32, 200..204), }); impl> Parseable> for Inet6DevConf { fn parse(buf: &Inet6DevConfBuffer) -> Result { Ok(Self { forwarding: buf.forwarding(), hoplimit: buf.hoplimit(), mtu6: buf.mtu6(), accept_ra: buf.accept_ra(), accept_redirects: buf.accept_redirects(), autoconf: buf.autoconf(), dad_transmits: buf.dad_transmits(), rtr_solicits: buf.rtr_solicits(), rtr_solicit_interval: buf.rtr_solicit_interval(), rtr_solicit_delay: buf.rtr_solicit_delay(), use_tempaddr: buf.use_tempaddr(), temp_valid_lft: buf.temp_valid_lft(), temp_prefered_lft: buf.temp_prefered_lft(), regen_max_retry: buf.regen_max_retry(), max_desync_factor: buf.max_desync_factor(), max_addresses: buf.max_addresses(), force_mld_version: buf.force_mld_version(), accept_ra_defrtr: buf.accept_ra_defrtr(), accept_ra_pinfo: buf.accept_ra_pinfo(), accept_ra_rtr_pref: buf.accept_ra_rtr_pref(), rtr_probe_interval: buf.rtr_probe_interval(), accept_ra_rt_info_max_plen: buf.accept_ra_rt_info_max_plen(), proxy_ndp: buf.proxy_ndp(), optimistic_dad: buf.optimistic_dad(), accept_source_route: buf.accept_source_route(), mc_forwarding: buf.mc_forwarding(), disable_ipv6: buf.disable_ipv6(), accept_dad: buf.accept_dad(), force_tllao: buf.force_tllao(), ndisc_notify: buf.ndisc_notify(), mldv1_unsolicited_report_interval: buf .mldv1_unsolicited_report_interval(), mldv2_unsolicited_report_interval: buf .mldv2_unsolicited_report_interval(), suppress_frag_ndisc: buf.suppress_frag_ndisc(), accept_ra_from_local: buf.accept_ra_from_local(), use_optimistic: buf.use_optimistic(), accept_ra_mtu: buf.accept_ra_mtu(), stable_secret: buf.stable_secret(), use_oif_addrs_only: buf.use_oif_addrs_only(), accept_ra_min_hop_limit: buf.accept_ra_min_hop_limit(), ignore_routes_with_linkdown: buf.ignore_routes_with_linkdown(), drop_unicast_in_l2_multicast: buf.drop_unicast_in_l2_multicast(), drop_unsolicited_na: buf.drop_unsolicited_na(), keep_addr_on_down: buf.keep_addr_on_down(), rtr_solicit_max_interval: buf.rtr_solicit_max_interval(), seg6_enabled: buf.seg6_enabled(), seg6_require_hmac: buf.seg6_require_hmac(), enhanced_dad: buf.enhanced_dad(), addr_gen_mode: buf.addr_gen_mode(), disable_policy: buf.disable_policy(), accept_ra_rt_info_min_plen: buf.accept_ra_rt_info_min_plen(), ndisc_tclass: buf.ndisc_tclass(), }) } } impl Emitable for Inet6DevConf { fn buffer_len(&self) -> usize { LINK_INET6_DEV_CONF_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = Inet6DevConfBuffer::new(buffer); buffer.set_forwarding(self.forwarding); buffer.set_hoplimit(self.hoplimit); buffer.set_mtu6(self.mtu6); buffer.set_accept_ra(self.accept_ra); buffer.set_accept_redirects(self.accept_redirects); buffer.set_autoconf(self.autoconf); buffer.set_dad_transmits(self.dad_transmits); buffer.set_rtr_solicits(self.rtr_solicits); buffer.set_rtr_solicit_interval(self.rtr_solicit_interval); buffer.set_rtr_solicit_delay(self.rtr_solicit_delay); buffer.set_use_tempaddr(self.use_tempaddr); buffer.set_temp_valid_lft(self.temp_valid_lft); buffer.set_temp_prefered_lft(self.temp_prefered_lft); buffer.set_regen_max_retry(self.regen_max_retry); buffer.set_max_desync_factor(self.max_desync_factor); buffer.set_max_addresses(self.max_addresses); buffer.set_force_mld_version(self.force_mld_version); buffer.set_accept_ra_defrtr(self.accept_ra_defrtr); buffer.set_accept_ra_pinfo(self.accept_ra_pinfo); buffer.set_accept_ra_rtr_pref(self.accept_ra_rtr_pref); buffer.set_rtr_probe_interval(self.rtr_probe_interval); buffer.set_accept_ra_rt_info_max_plen(self.accept_ra_rt_info_max_plen); buffer.set_proxy_ndp(self.proxy_ndp); buffer.set_optimistic_dad(self.optimistic_dad); buffer.set_accept_source_route(self.accept_source_route); buffer.set_mc_forwarding(self.mc_forwarding); buffer.set_disable_ipv6(self.disable_ipv6); buffer.set_accept_dad(self.accept_dad); buffer.set_force_tllao(self.force_tllao); buffer.set_ndisc_notify(self.ndisc_notify); buffer.set_mldv1_unsolicited_report_interval( self.mldv1_unsolicited_report_interval, ); buffer.set_mldv2_unsolicited_report_interval( self.mldv2_unsolicited_report_interval, ); buffer.set_suppress_frag_ndisc(self.suppress_frag_ndisc); buffer.set_accept_ra_from_local(self.accept_ra_from_local); buffer.set_use_optimistic(self.use_optimistic); buffer.set_accept_ra_mtu(self.accept_ra_mtu); buffer.set_stable_secret(self.stable_secret); buffer.set_use_oif_addrs_only(self.use_oif_addrs_only); buffer.set_accept_ra_min_hop_limit(self.accept_ra_min_hop_limit); buffer .set_ignore_routes_with_linkdown(self.ignore_routes_with_linkdown); buffer.set_drop_unicast_in_l2_multicast( self.drop_unicast_in_l2_multicast, ); buffer.set_drop_unsolicited_na(self.drop_unsolicited_na); buffer.set_keep_addr_on_down(self.keep_addr_on_down); buffer.set_rtr_solicit_max_interval(self.rtr_solicit_max_interval); buffer.set_seg6_enabled(self.seg6_enabled); buffer.set_seg6_require_hmac(self.seg6_require_hmac); buffer.set_enhanced_dad(self.enhanced_dad); buffer.set_addr_gen_mode(self.addr_gen_mode); buffer.set_disable_policy(self.disable_policy); buffer.set_accept_ra_rt_info_min_plen(self.accept_ra_rt_info_min_plen); buffer.set_ndisc_tclass(self.ndisc_tclass); } } #[derive(Clone, Copy, Eq, PartialEq, Debug)] #[non_exhaustive] pub struct Inet6DevConf { pub forwarding: i32, pub hoplimit: i32, pub mtu6: i32, pub accept_ra: i32, pub accept_redirects: i32, pub autoconf: i32, pub dad_transmits: i32, pub rtr_solicits: i32, pub rtr_solicit_interval: i32, pub rtr_solicit_delay: i32, pub use_tempaddr: i32, pub temp_valid_lft: i32, pub temp_prefered_lft: i32, pub regen_max_retry: i32, pub max_desync_factor: i32, pub max_addresses: i32, pub force_mld_version: i32, pub accept_ra_defrtr: i32, pub accept_ra_pinfo: i32, pub accept_ra_rtr_pref: i32, pub rtr_probe_interval: i32, pub accept_ra_rt_info_max_plen: i32, pub proxy_ndp: i32, pub optimistic_dad: i32, pub accept_source_route: i32, pub mc_forwarding: i32, pub disable_ipv6: i32, pub accept_dad: i32, pub force_tllao: i32, pub ndisc_notify: i32, pub mldv1_unsolicited_report_interval: i32, pub mldv2_unsolicited_report_interval: i32, pub suppress_frag_ndisc: i32, pub accept_ra_from_local: i32, pub use_optimistic: i32, pub accept_ra_mtu: i32, pub stable_secret: i32, pub use_oif_addrs_only: i32, pub accept_ra_min_hop_limit: i32, pub ignore_routes_with_linkdown: i32, pub drop_unicast_in_l2_multicast: i32, pub drop_unsolicited_na: i32, pub keep_addr_on_down: i32, pub rtr_solicit_max_interval: i32, pub seg6_enabled: i32, pub seg6_require_hmac: i32, pub enhanced_dad: i32, pub addr_gen_mode: i32, pub disable_policy: i32, pub accept_ra_rt_info_min_plen: i32, pub ndisc_tclass: i32, } netlink-packet-route-0.17.0/src/rtnl/link/nlas/inet6/icmp6_stats.rs000064400000000000000000000027041046102023000232500ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; #[derive(Clone, Copy, Eq, PartialEq, Debug)] #[non_exhaustive] pub struct Icmp6Stats { pub num: i64, pub in_msgs: i64, pub in_errors: i64, pub out_msgs: i64, pub out_errors: i64, pub csum_errors: i64, } pub const ICMP6_STATS_LEN: usize = 48; buffer!(Icmp6StatsBuffer(ICMP6_STATS_LEN) { num: (i64, 0..8), in_msgs: (i64, 8..16), in_errors: (i64, 16..24), out_msgs: (i64, 24..32), out_errors: (i64, 32..40), csum_errors: (i64, 40..48), }); impl> Parseable> for Icmp6Stats { fn parse(buf: &Icmp6StatsBuffer) -> Result { Ok(Self { num: buf.num(), in_msgs: buf.in_msgs(), in_errors: buf.in_errors(), out_msgs: buf.out_msgs(), out_errors: buf.out_errors(), csum_errors: buf.csum_errors(), }) } } impl Emitable for Icmp6Stats { fn buffer_len(&self) -> usize { ICMP6_STATS_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = Icmp6StatsBuffer::new(buffer); buffer.set_num(self.num); buffer.set_in_msgs(self.in_msgs); buffer.set_in_errors(self.in_errors); buffer.set_out_msgs(self.out_msgs); buffer.set_out_errors(self.out_errors); buffer.set_csum_errors(self.csum_errors); } } netlink-packet-route-0.17.0/src/rtnl/link/nlas/inet6/mod.rs000064400000000000000000000073001046102023000215700ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use crate::constants::*; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::{parse_ipv6, parse_u32, parse_u8}, traits::Parseable, DecodeError, }; mod cache; pub use self::cache::*; mod dev_conf; pub use self::dev_conf::*; mod icmp6_stats; pub use self::icmp6_stats::*; mod stats; pub use self::stats::*; #[derive(Clone, Eq, PartialEq, Debug)] #[non_exhaustive] pub enum Inet6 { Flags(u32), CacheInfo(Vec), DevConf(Vec), Unspec(Vec), Stats(Vec), IcmpStats(Vec), Token([u8; 16]), AddrGenMode(u8), Other(DefaultNla), } impl Nla for Inet6 { fn value_len(&self) -> usize { use self::Inet6::*; match *self { Unspec(ref bytes) => bytes.len(), CacheInfo(ref cache_info) => cache_info.len(), DevConf(ref dev_conf) => dev_conf.len(), Stats(ref stats) => stats.len(), IcmpStats(ref icmp_stats) => icmp_stats.len(), Flags(_) => 4, Token(_) => 16, AddrGenMode(_) => 1, Other(ref nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::Inet6::*; match *self { Unspec(ref bytes) => buffer.copy_from_slice(bytes.as_slice()), Flags(ref value) => NativeEndian::write_u32(buffer, *value), CacheInfo(ref cache_info) => { buffer.copy_from_slice(cache_info.as_slice()) } DevConf(ref bytes) => buffer.copy_from_slice(bytes.as_slice()), Stats(ref inet6_stats) => { buffer.copy_from_slice(inet6_stats.as_slice()) } IcmpStats(ref icmp6_stats) => { buffer.copy_from_slice(icmp6_stats.as_slice()) } Token(ref ipv6) => buffer.copy_from_slice(&ipv6[..]), AddrGenMode(value) => buffer[0] = value, Other(ref nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::Inet6::*; match *self { Unspec(_) => IFLA_INET6_UNSPEC, Flags(_) => IFLA_INET6_FLAGS, CacheInfo(_) => IFLA_INET6_CACHEINFO, DevConf(_) => IFLA_INET6_CONF, Stats(_) => IFLA_INET6_STATS, IcmpStats(_) => IFLA_INET6_ICMP6STATS, Token(_) => IFLA_INET6_TOKEN, AddrGenMode(_) => IFLA_INET6_ADDR_GEN_MODE, Other(ref nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Inet6 { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::Inet6::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_INET6_UNSPEC => Unspec(payload.to_vec()), IFLA_INET6_FLAGS => Flags( parse_u32(payload).context("invalid IFLA_INET6_FLAGS value")?, ), IFLA_INET6_CACHEINFO => CacheInfo(payload.to_vec()), IFLA_INET6_CONF => DevConf(payload.to_vec()), IFLA_INET6_STATS => Stats(payload.to_vec()), IFLA_INET6_ICMP6STATS => IcmpStats(payload.to_vec()), IFLA_INET6_TOKEN => Token( parse_ipv6(payload) .context("invalid IFLA_INET6_TOKEN value")?, ), IFLA_INET6_ADDR_GEN_MODE => AddrGenMode( parse_u8(payload) .context("invalid IFLA_INET6_ADDR_GEN_MODE value")?, ), kind => Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } netlink-packet-route-0.17.0/src/rtnl/link/nlas/inet6/stats.rs000064400000000000000000000145201046102023000221510ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; pub const INET6_STATS_LEN: usize = 288; buffer!(Inet6StatsBuffer(INET6_STATS_LEN) { num: (i64, 0..8), in_pkts: (i64, 8..16), in_octets: (i64, 16..24), in_delivers: (i64, 24..32), out_forw_datagrams: (i64, 32..40), out_pkts: (i64, 40..48), out_octets: (i64, 48..56), in_hdr_errors: (i64, 56..64), in_too_big_errors: (i64, 64..72), in_no_routes: (i64, 72..80), in_addr_errors: (i64, 80..88), in_unknown_protos: (i64, 88..96), in_truncated_pkts: (i64, 96..104), in_discards: (i64, 104..112), out_discards: (i64, 112..120), out_no_routes: (i64, 120..128), reasm_timeout: (i64, 128..136), reasm_reqds: (i64, 136..144), reasm_oks: (i64, 144..152), reasm_fails: (i64, 152..160), frag_oks: (i64, 160..168), frag_fails: (i64, 168..176), frag_creates: (i64, 176..184), in_mcast_pkts: (i64, 184..192), out_mcast_pkts: (i64, 192..200), in_bcast_pkts: (i64, 200..208), out_bcast_pkts: (i64, 208..216), in_mcast_octets: (i64, 216..224), out_mcast_octets: (i64, 224..232), in_bcast_octets: (i64, 232..240), out_bcast_octets: (i64, 240..248), in_csum_errors: (i64, 248..256), in_no_ect_pkts: (i64, 256..264), in_ect1_pkts: (i64, 264..272), in_ect0_pkts: (i64, 272..280), in_ce_pkts: (i64, 280..288), }); #[derive(Clone, Copy, Eq, PartialEq, Debug)] #[non_exhaustive] pub struct Inet6Stats { pub num: i64, pub in_pkts: i64, pub in_octets: i64, pub in_delivers: i64, pub out_forw_datagrams: i64, pub out_pkts: i64, pub out_octets: i64, pub in_hdr_errors: i64, pub in_too_big_errors: i64, pub in_no_routes: i64, pub in_addr_errors: i64, pub in_unknown_protos: i64, pub in_truncated_pkts: i64, pub in_discards: i64, pub out_discards: i64, pub out_no_routes: i64, pub reasm_timeout: i64, pub reasm_reqds: i64, pub reasm_oks: i64, pub reasm_fails: i64, pub frag_oks: i64, pub frag_fails: i64, pub frag_creates: i64, pub in_mcast_pkts: i64, pub out_mcast_pkts: i64, pub in_bcast_pkts: i64, pub out_bcast_pkts: i64, pub in_mcast_octets: i64, pub out_mcast_octets: i64, pub in_bcast_octets: i64, pub out_bcast_octets: i64, pub in_csum_errors: i64, pub in_no_ect_pkts: i64, pub in_ect1_pkts: i64, pub in_ect0_pkts: i64, pub in_ce_pkts: i64, } impl> Parseable> for Inet6Stats { fn parse(buf: &Inet6StatsBuffer) -> Result { Ok(Self { num: buf.num(), in_pkts: buf.in_pkts(), in_octets: buf.in_octets(), in_delivers: buf.in_delivers(), out_forw_datagrams: buf.out_forw_datagrams(), out_pkts: buf.out_pkts(), out_octets: buf.out_octets(), in_hdr_errors: buf.in_hdr_errors(), in_too_big_errors: buf.in_too_big_errors(), in_no_routes: buf.in_no_routes(), in_addr_errors: buf.in_addr_errors(), in_unknown_protos: buf.in_unknown_protos(), in_truncated_pkts: buf.in_truncated_pkts(), in_discards: buf.in_discards(), out_discards: buf.out_discards(), out_no_routes: buf.out_no_routes(), reasm_timeout: buf.reasm_timeout(), reasm_reqds: buf.reasm_reqds(), reasm_oks: buf.reasm_oks(), reasm_fails: buf.reasm_fails(), frag_oks: buf.frag_oks(), frag_fails: buf.frag_fails(), frag_creates: buf.frag_creates(), in_mcast_pkts: buf.in_mcast_pkts(), out_mcast_pkts: buf.out_mcast_pkts(), in_bcast_pkts: buf.in_bcast_pkts(), out_bcast_pkts: buf.out_bcast_pkts(), in_mcast_octets: buf.in_mcast_octets(), out_mcast_octets: buf.out_mcast_octets(), in_bcast_octets: buf.in_bcast_octets(), out_bcast_octets: buf.out_bcast_octets(), in_csum_errors: buf.in_csum_errors(), in_no_ect_pkts: buf.in_no_ect_pkts(), in_ect1_pkts: buf.in_ect1_pkts(), in_ect0_pkts: buf.in_ect0_pkts(), in_ce_pkts: buf.in_ce_pkts(), }) } } impl Emitable for Inet6Stats { fn buffer_len(&self) -> usize { INET6_STATS_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = Inet6StatsBuffer::new(buffer); buffer.set_num(self.num); buffer.set_in_pkts(self.in_pkts); buffer.set_in_octets(self.in_octets); buffer.set_in_delivers(self.in_delivers); buffer.set_out_forw_datagrams(self.out_forw_datagrams); buffer.set_out_pkts(self.out_pkts); buffer.set_out_octets(self.out_octets); buffer.set_in_hdr_errors(self.in_hdr_errors); buffer.set_in_too_big_errors(self.in_too_big_errors); buffer.set_in_no_routes(self.in_no_routes); buffer.set_in_addr_errors(self.in_addr_errors); buffer.set_in_unknown_protos(self.in_unknown_protos); buffer.set_in_truncated_pkts(self.in_truncated_pkts); buffer.set_in_discards(self.in_discards); buffer.set_out_discards(self.out_discards); buffer.set_out_no_routes(self.out_no_routes); buffer.set_reasm_timeout(self.reasm_timeout); buffer.set_reasm_reqds(self.reasm_reqds); buffer.set_reasm_oks(self.reasm_oks); buffer.set_reasm_fails(self.reasm_fails); buffer.set_frag_oks(self.frag_oks); buffer.set_frag_fails(self.frag_fails); buffer.set_frag_creates(self.frag_creates); buffer.set_in_mcast_pkts(self.in_mcast_pkts); buffer.set_out_mcast_pkts(self.out_mcast_pkts); buffer.set_in_bcast_pkts(self.in_bcast_pkts); buffer.set_out_bcast_pkts(self.out_bcast_pkts); buffer.set_in_mcast_octets(self.in_mcast_octets); buffer.set_out_mcast_octets(self.out_mcast_octets); buffer.set_in_bcast_octets(self.in_bcast_octets); buffer.set_out_bcast_octets(self.out_bcast_octets); buffer.set_in_csum_errors(self.in_csum_errors); buffer.set_in_no_ect_pkts(self.in_no_ect_pkts); buffer.set_in_ect1_pkts(self.in_ect1_pkts); buffer.set_in_ect0_pkts(self.in_ect0_pkts); buffer.set_in_ce_pkts(self.in_ce_pkts); } } netlink-packet-route-0.17.0/src/rtnl/link/nlas/link_infos.rs000064400000000000000000002523371046102023000221330ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, parsers::{ parse_i32, parse_mac, parse_string, parse_u16, parse_u16_be, parse_u32, parse_u8, }, traits::{Emitable, Parseable}, DecodeError, }; use super::{bond::InfoBond, bond_port::InfoBondPort, bridge::InfoBridge}; use crate::{constants::*, LinkMessage, LinkMessageBuffer}; const DUMMY: &str = "dummy"; const IFB: &str = "ifb"; const BRIDGE: &str = "bridge"; const TUN: &str = "tun"; const NLMON: &str = "nlmon"; const VLAN: &str = "vlan"; const VETH: &str = "veth"; const VXLAN: &str = "vxlan"; const BOND: &str = "bond"; const IPVLAN: &str = "ipvlan"; const MACVLAN: &str = "macvlan"; const MACVTAP: &str = "macvtap"; const GRETAP: &str = "gretap"; const IP6GRETAP: &str = "ip6gretap"; const IPIP: &str = "ipip"; const SIT: &str = "sit"; const GRE: &str = "gre"; const IP6GRE: &str = "ip6gre"; const VTI: &str = "vti"; const VRF: &str = "vrf"; const GTP: &str = "gtp"; const IPOIB: &str = "ipoib"; const WIREGUARD: &str = "wireguard"; const XFRM: &str = "xfrm"; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum Info { Unspec(Vec), Xstats(Vec), Kind(InfoKind), Data(InfoData), PortKind(InfoPortKind), PortData(InfoPortData), } impl Nla for Info { #[rustfmt::skip] fn value_len(&self) -> usize { use self::Info::*; match self { Unspec(ref bytes) | Xstats(ref bytes) => bytes.len(), Kind(ref nla) => nla.value_len(), Data(ref nla) => nla.value_len(), PortKind(ref nla) => nla.value_len(), PortData(ref nla) => nla.value_len(), } } #[rustfmt::skip] fn emit_value(&self, buffer: &mut [u8]) { use self::Info::*; match self { Unspec(ref bytes) | Xstats(ref bytes) => buffer.copy_from_slice(bytes), Kind(ref nla) => nla.emit_value(buffer), Data(ref nla) => nla.emit_value(buffer), PortKind(ref nla) => nla.emit_value(buffer), PortData(ref nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::Info::*; match self { Unspec(_) => IFLA_INFO_UNSPEC, Xstats(_) => IFLA_INFO_XSTATS, PortKind(_) => IFLA_INFO_PORT_KIND, PortData(_) => IFLA_INFO_PORT_DATA, Kind(_) => IFLA_INFO_KIND, Data(_) => IFLA_INFO_DATA, } } } pub(crate) struct VecInfo(pub(crate) Vec); // We cannot `impl Parseable<_> for Info` because some attributes // depend on each other. To parse IFLA_INFO_DATA we first need to // parse the preceding IFLA_INFO_KIND for example. // // Moreover, with cannot `impl Parseable for Vec` due to the // orphan rule: `Parseable` and `Vec<_>` are both defined outside of // this crate. Thus, we create this internal VecInfo struct that wraps // `Vec` and allows us to circumvent the orphan rule. // // The downside is that this impl will not be exposed. impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecInfo { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut res = Vec::new(); let nlas = NlasIterator::new(buf.into_inner()); let mut link_info_kind: Option = None; let mut link_info_port_kind: Option = None; for nla in nlas { let nla = nla?; match nla.kind() { IFLA_INFO_UNSPEC => { res.push(Info::Unspec(nla.value().to_vec())) } IFLA_INFO_XSTATS => { res.push(Info::Xstats(nla.value().to_vec())) } IFLA_INFO_PORT_KIND => { let parsed = InfoPortKind::parse(&nla)?; res.push(Info::PortKind(parsed.clone())); link_info_port_kind = Some(parsed); } IFLA_INFO_PORT_DATA => { if let Some(link_info_port_kind) = link_info_port_kind { let payload = nla.value(); let info_port_data = match link_info_port_kind { InfoPortKind::Bond => { let mut v = Vec::new(); let err = "failed to parse IFLA_INFO_PORT_DATA (IFLA_INFO_PORT_KIND is 'bond')"; for nla in NlasIterator::new(payload) { let nla = &nla.context(err)?; let parsed = InfoBondPort::parse(nla) .context(err)?; v.push(parsed); } InfoPortData::BondPort(v) } InfoPortKind::Other(_) => { InfoPortData::Other(payload.to_vec()) } }; res.push(Info::PortData(info_port_data)); } else { return Err("IFLA_INFO_PORT_DATA is not preceded by an IFLA_INFO_PORT_KIND".into()); } link_info_port_kind = None; } IFLA_INFO_KIND => { let parsed = InfoKind::parse(&nla)?; res.push(Info::Kind(parsed.clone())); link_info_kind = Some(parsed); } IFLA_INFO_DATA => { if let Some(link_info_kind) = link_info_kind { let payload = nla.value(); let info_data = match link_info_kind { InfoKind::Dummy => { InfoData::Dummy(payload.to_vec()) } InfoKind::Ifb => InfoData::Ifb(payload.to_vec()), InfoKind::Bridge => { let mut v = Vec::new(); let err = "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'bridge')"; for nla in NlasIterator::new(payload) { let nla = &nla.context(err)?; let parsed = InfoBridge::parse(nla).context(err)?; v.push(parsed); } InfoData::Bridge(v) } InfoKind::Vlan => { let mut v = Vec::new(); let err = "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'vlan')"; for nla in NlasIterator::new(payload) { let nla = &nla.context(err)?; let parsed = InfoVlan::parse(nla).context(err)?; v.push(parsed); } InfoData::Vlan(v) } InfoKind::Tun => InfoData::Tun(payload.to_vec()), InfoKind::Nlmon => { InfoData::Nlmon(payload.to_vec()) } InfoKind::Veth => { let err = "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'veth')"; let nla_buf = NlaBuffer::new_checked(&payload) .context(err)?; let parsed = VethInfo::parse(&nla_buf).context(err)?; InfoData::Veth(parsed) } InfoKind::Vxlan => { let mut v = Vec::new(); let err = "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'vxlan')"; for nla in NlasIterator::new(payload) { let nla = &nla.context(err)?; let parsed = InfoVxlan::parse(nla).context(err)?; v.push(parsed); } InfoData::Vxlan(v) } InfoKind::Bond => { let mut v = Vec::new(); let err = "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'bond')"; for nla in NlasIterator::new(payload) { let nla = &nla.context(err)?; let parsed = InfoBond::parse(nla).context(err)?; v.push(parsed); } InfoData::Bond(v) } InfoKind::IpVlan => { let mut v = Vec::new(); let err = "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'ipvlan')"; for nla in NlasIterator::new(payload) { let nla = &nla.context(err)?; let parsed = InfoIpVlan::parse(nla).context(err)?; v.push(parsed); } InfoData::IpVlan(v) } InfoKind::MacVlan => { let mut v = Vec::new(); let err = "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'macvlan')"; for nla in NlasIterator::new(payload) { let nla = &nla.context(err)?; let parsed = InfoMacVlan::parse(nla).context(err)?; v.push(parsed); } InfoData::MacVlan(v) } InfoKind::MacVtap => { let mut v = Vec::new(); let err = "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'macvtap')"; for nla in NlasIterator::new(payload) { let nla = &nla.context(err)?; let parsed = InfoMacVtap::parse(nla).context(err)?; v.push(parsed); } InfoData::MacVtap(v) } InfoKind::GreTap => { InfoData::GreTap(payload.to_vec()) } InfoKind::GreTap6 => { InfoData::GreTap6(payload.to_vec()) } InfoKind::IpTun => { InfoData::IpTun(payload.to_vec()) } InfoKind::SitTun => { InfoData::SitTun(payload.to_vec()) } InfoKind::GreTun => { InfoData::GreTun(payload.to_vec()) } InfoKind::GreTun6 => { InfoData::GreTun6(payload.to_vec()) } InfoKind::Vti => InfoData::Vti(payload.to_vec()), InfoKind::Vrf => { let mut v = Vec::new(); let err = "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'vrf')"; for nla in NlasIterator::new(payload) { let nla = &nla.context(err)?; let parsed = InfoVrf::parse(nla).context(err)?; v.push(parsed); } InfoData::Vrf(v) } InfoKind::Gtp => InfoData::Gtp(payload.to_vec()), InfoKind::Ipoib => { let mut v = Vec::new(); let err = "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'ipoib')"; for nla in NlasIterator::new(payload) { let nla = &nla.context(err)?; let parsed = InfoIpoib::parse(nla).context(err)?; v.push(parsed); } InfoData::Ipoib(v) } InfoKind::Wireguard => { InfoData::Wireguard(payload.to_vec()) } InfoKind::Other(_) => { InfoData::Other(payload.to_vec()) } InfoKind::Xfrm => { let mut v = Vec::new(); let err = "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'Xfrm')"; for nla in NlasIterator::new(payload) { let nla = &nla.context(err)?; let parsed = InfoXfrmTun::parse(nla).context(err)?; v.push(parsed); } InfoData::Xfrm(v) } }; res.push(Info::Data(info_data)); } else { return Err("IFLA_INFO_DATA is not preceded by an IFLA_INFO_KIND".into()); } link_info_kind = None; } _ => { return Err( format!("unknown NLA type {}", nla.kind()).into() ) } } } Ok(VecInfo(res)) } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoData { Bridge(Vec), Tun(Vec), Nlmon(Vec), Vlan(Vec), Dummy(Vec), Ifb(Vec), Veth(VethInfo), Vxlan(Vec), Bond(Vec), IpVlan(Vec), MacVlan(Vec), MacVtap(Vec), GreTap(Vec), GreTap6(Vec), IpTun(Vec), SitTun(Vec), GreTun(Vec), GreTun6(Vec), Vti(Vec), Vrf(Vec), Gtp(Vec), Ipoib(Vec), Wireguard(Vec), Xfrm(Vec), Other(Vec), } impl Nla for InfoData { #[rustfmt::skip] fn value_len(&self) -> usize { use self::InfoData::*; match self { Bond(ref nlas) => nlas.as_slice().buffer_len(), Bridge(ref nlas) => nlas.as_slice().buffer_len(), Vlan(ref nlas) => nlas.as_slice().buffer_len(), Veth(ref msg) => msg.buffer_len(), IpVlan(ref nlas) => nlas.as_slice().buffer_len(), Ipoib(ref nlas) => nlas.as_slice().buffer_len(), MacVlan(ref nlas) => nlas.as_slice().buffer_len(), MacVtap(ref nlas) => nlas.as_slice().buffer_len(), Vrf(ref nlas) => nlas.as_slice().buffer_len(), Vxlan(ref nlas) => nlas.as_slice().buffer_len(), Xfrm(ref nlas) => nlas.as_slice().buffer_len(), Dummy(ref bytes) | Tun(ref bytes) | Nlmon(ref bytes) | Ifb(ref bytes) | GreTap(ref bytes) | GreTap6(ref bytes) | IpTun(ref bytes) | SitTun(ref bytes) | GreTun(ref bytes) | GreTun6(ref bytes) | Vti(ref bytes) | Gtp(ref bytes) | Wireguard(ref bytes) | Other(ref bytes) => bytes.len(), } } #[rustfmt::skip] fn emit_value(&self, buffer: &mut [u8]) { use self::InfoData::*; match self { Bond(ref nlas) => nlas.as_slice().emit(buffer), Bridge(ref nlas) => nlas.as_slice().emit(buffer), Vlan(ref nlas) => nlas.as_slice().emit(buffer), Veth(ref msg) => msg.emit(buffer), IpVlan(ref nlas) => nlas.as_slice().emit(buffer), Ipoib(ref nlas) => nlas.as_slice().emit(buffer), MacVlan(ref nlas) => nlas.as_slice().emit(buffer), MacVtap(ref nlas) => nlas.as_slice().emit(buffer), Vrf(ref nlas) => nlas.as_slice().emit(buffer), Vxlan(ref nlas) => nlas.as_slice().emit(buffer), Xfrm(ref nlas) => nlas.as_slice().emit(buffer), Dummy(ref bytes) | Tun(ref bytes) | Nlmon(ref bytes) | Ifb(ref bytes) | GreTap(ref bytes) | GreTap6(ref bytes) | IpTun(ref bytes) | SitTun(ref bytes) | GreTun(ref bytes) | GreTun6(ref bytes) | Vti(ref bytes) | Gtp(ref bytes) | Wireguard(ref bytes) | Other(ref bytes) => buffer.copy_from_slice(bytes), } } fn kind(&self) -> u16 { IFLA_INFO_DATA } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoPortData { BondPort(Vec), Other(Vec), } impl Nla for InfoPortData { #[rustfmt::skip] fn value_len(&self) -> usize { use self::InfoPortData::*; match self { BondPort(ref nlas) => nlas.as_slice().buffer_len(), Other(ref bytes) => bytes.len(), } } #[rustfmt::skip] fn emit_value(&self, buffer: &mut [u8]) { use self::InfoPortData::*; match self { BondPort(ref nlas) => nlas.as_slice().emit(buffer), Other(ref bytes) => buffer.copy_from_slice(bytes), } } fn kind(&self) -> u16 { IFLA_INFO_PORT_DATA } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoKind { Dummy, Ifb, Bridge, Tun, Nlmon, Vlan, Veth, Vxlan, Bond, IpVlan, MacVlan, MacVtap, GreTap, GreTap6, IpTun, SitTun, GreTun, GreTun6, Vti, Vrf, Gtp, Ipoib, Wireguard, Xfrm, Other(String), } impl Nla for InfoKind { fn value_len(&self) -> usize { use self::InfoKind::*; let len = match *self { Dummy => DUMMY.len(), Ifb => IFB.len(), Bridge => BRIDGE.len(), Tun => TUN.len(), Nlmon => NLMON.len(), Vlan => VLAN.len(), Veth => VETH.len(), Vxlan => VXLAN.len(), Bond => BOND.len(), IpVlan => IPVLAN.len(), MacVlan => MACVLAN.len(), MacVtap => MACVTAP.len(), GreTap => GRETAP.len(), GreTap6 => IP6GRETAP.len(), IpTun => IPIP.len(), SitTun => SIT.len(), GreTun => GRE.len(), GreTun6 => IP6GRE.len(), Vti => VTI.len(), Vrf => VRF.len(), Gtp => GTP.len(), Ipoib => IPOIB.len(), Wireguard => WIREGUARD.len(), Xfrm => XFRM.len(), Other(ref s) => s.len(), }; len + 1 } fn emit_value(&self, buffer: &mut [u8]) { use self::InfoKind::*; let s = match *self { Dummy => DUMMY, Ifb => IFB, Bridge => BRIDGE, Tun => TUN, Nlmon => NLMON, Vlan => VLAN, Veth => VETH, Vxlan => VXLAN, Bond => BOND, IpVlan => IPVLAN, MacVlan => MACVLAN, MacVtap => MACVTAP, GreTap => GRETAP, GreTap6 => IP6GRETAP, IpTun => IPIP, SitTun => SIT, GreTun => GRE, GreTun6 => IP6GRE, Vti => VTI, Vrf => VRF, Gtp => GTP, Ipoib => IPOIB, Wireguard => WIREGUARD, Xfrm => XFRM, Other(ref s) => s.as_str(), }; buffer[..s.len()].copy_from_slice(s.as_bytes()); buffer[s.len()] = 0; } fn kind(&self) -> u16 { IFLA_INFO_KIND } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoKind { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoKind::*; if buf.kind() != IFLA_INFO_KIND { return Err(format!( "failed to parse IFLA_INFO_KIND: NLA type is {}", buf.kind() ) .into()); } let s = parse_string(buf.value()) .context("invalid IFLA_INFO_KIND value")?; Ok(match s.as_str() { DUMMY => Dummy, IFB => Ifb, BRIDGE => Bridge, TUN => Tun, NLMON => Nlmon, VLAN => Vlan, VETH => Veth, VXLAN => Vxlan, BOND => Bond, IPVLAN => IpVlan, MACVLAN => MacVlan, MACVTAP => MacVtap, GRETAP => GreTap, IP6GRETAP => GreTap6, IPIP => IpTun, SIT => SitTun, GRE => GreTun, IP6GRE => GreTun6, VTI => Vti, VRF => Vrf, GTP => Gtp, IPOIB => Ipoib, WIREGUARD => Wireguard, _ => Other(s), }) } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoPortKind { Bond, Other(String), } impl Nla for InfoPortKind { fn value_len(&self) -> usize { use self::InfoPortKind::*; let len = match *self { Bond => BOND.len(), Other(ref s) => s.len(), }; len + 1 } fn emit_value(&self, buffer: &mut [u8]) { use self::InfoPortKind::*; let s = match *self { Bond => BOND, Other(ref s) => s.as_str(), }; buffer[..s.len()].copy_from_slice(s.as_bytes()); buffer[s.len()] = 0; } fn kind(&self) -> u16 { IFLA_INFO_PORT_KIND } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoPortKind { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoPortKind::*; if buf.kind() != IFLA_INFO_PORT_KIND { return Err(format!( "failed to parse IFLA_INFO_PORT_KIND: NLA type is {}", buf.kind() ) .into()); } let s = parse_string(buf.value()) .context("invalid IFLA_INFO_PORT_KIND value")?; Ok(match s.as_str() { BOND => Bond, _ => Other(s), }) } } // https://elixir.bootlin.com/linux/v5.9.8/source/drivers/net/vxlan.c#L3332 #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoVxlan { Unspec(Vec), Id(u32), Group(Vec), Group6(Vec), Link(u32), Local(Vec), Local6(Vec), Tos(u8), Ttl(u8), Label(u32), Learning(u8), Ageing(u32), Limit(u32), PortRange((u16, u16)), Proxy(u8), Rsc(u8), L2Miss(u8), L3Miss(u8), CollectMetadata(u8), Port(u16), UDPCsum(u8), UDPZeroCsumTX(u8), UDPZeroCsumRX(u8), RemCsumTX(u8), RemCsumRX(u8), Gbp(u8), Gpe(u8), RemCsumNoPartial(u8), TtlInherit(u8), Df(u8), } impl Nla for InfoVxlan { #[rustfmt::skip] fn value_len(&self) -> usize { use self::InfoVxlan::*; match *self { Tos(_) | Ttl(_) | Learning(_) | Proxy(_) | Rsc(_) | L2Miss(_) | L3Miss(_) | CollectMetadata(_) | UDPCsum(_) | UDPZeroCsumTX(_) | UDPZeroCsumRX(_) | RemCsumTX(_) | RemCsumRX(_) | Gbp(_) | Gpe(_) | RemCsumNoPartial(_) | TtlInherit(_) | Df(_) => 1, Port(_) => 2, Id(_) | Label(_) | Link(_) | Ageing(_) | Limit(_) | PortRange(_) => 4, Local(ref bytes) | Local6(ref bytes) | Group(ref bytes) | Group6(ref bytes) | Unspec(ref bytes) => bytes.len(), } } #[rustfmt::skip] fn emit_value(&self, buffer: &mut [u8]) { use self::InfoVxlan::*; match self { Unspec(ref bytes) => buffer.copy_from_slice(bytes), Id(ref value) | Label(ref value) | Link(ref value) | Ageing(ref value) | Limit(ref value) => NativeEndian::write_u32(buffer, *value), Tos(ref value) | Ttl(ref value) | Learning (ref value) | Proxy(ref value) | Rsc(ref value) | L2Miss(ref value) | L3Miss(ref value) | CollectMetadata(ref value) | UDPCsum(ref value) | UDPZeroCsumTX(ref value) | UDPZeroCsumRX(ref value) | RemCsumTX(ref value) | RemCsumRX(ref value) | Gbp(ref value) | Gpe(ref value) | RemCsumNoPartial(ref value) | TtlInherit(ref value) | Df(ref value) => buffer[0] = *value, Local(ref value) | Group(ref value) | Group6(ref value) | Local6(ref value) => buffer.copy_from_slice(value.as_slice()), Port(ref value) => NativeEndian::write_u16(buffer, *value), PortRange(ref range) => { NativeEndian::write_u16(buffer, range.0); NativeEndian::write_u16(buffer, range.1) } } } fn kind(&self) -> u16 { use self::InfoVxlan::*; match self { Id(_) => IFLA_VXLAN_ID, Group(_) => IFLA_VXLAN_GROUP, Group6(_) => IFLA_VXLAN_GROUP6, Link(_) => IFLA_VXLAN_LINK, Local(_) => IFLA_VXLAN_LOCAL, Local6(_) => IFLA_VXLAN_LOCAL6, Tos(_) => IFLA_VXLAN_TOS, Ttl(_) => IFLA_VXLAN_TTL, Label(_) => IFLA_VXLAN_LABEL, Learning(_) => IFLA_VXLAN_LEARNING, Ageing(_) => IFLA_VXLAN_AGEING, Limit(_) => IFLA_VXLAN_LIMIT, PortRange(_) => IFLA_VXLAN_PORT_RANGE, Proxy(_) => IFLA_VXLAN_PROXY, Rsc(_) => IFLA_VXLAN_RSC, L2Miss(_) => IFLA_VXLAN_L2MISS, L3Miss(_) => IFLA_VXLAN_L3MISS, CollectMetadata(_) => IFLA_VXLAN_COLLECT_METADATA, Port(_) => IFLA_VXLAN_PORT, UDPCsum(_) => IFLA_VXLAN_UDP_CSUM, UDPZeroCsumTX(_) => IFLA_VXLAN_UDP_ZERO_CSUM6_TX, UDPZeroCsumRX(_) => IFLA_VXLAN_UDP_ZERO_CSUM6_RX, RemCsumTX(_) => IFLA_VXLAN_REMCSUM_TX, RemCsumRX(_) => IFLA_VXLAN_REMCSUM_RX, Gbp(_) => IFLA_VXLAN_GBP, Gpe(_) => IFLA_VXLAN_GPE, RemCsumNoPartial(_) => IFLA_VXLAN_REMCSUM_NOPARTIAL, TtlInherit(_) => IFLA_VXLAN_TTL_INHERIT, Df(_) => IFLA_VXLAN_DF, Unspec(_) => IFLA_VXLAN_UNSPEC, } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoVxlan { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoVxlan::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_VLAN_UNSPEC => Unspec(payload.to_vec()), IFLA_VXLAN_ID => { Id(parse_u32(payload).context("invalid IFLA_VXLAN_ID value")?) } IFLA_VXLAN_GROUP => Group(payload.to_vec()), IFLA_VXLAN_GROUP6 => Group6(payload.to_vec()), IFLA_VXLAN_LINK => Link( parse_u32(payload).context("invalid IFLA_VXLAN_LINK value")?, ), IFLA_VXLAN_LOCAL => Local(payload.to_vec()), IFLA_VXLAN_LOCAL6 => Local6(payload.to_vec()), IFLA_VXLAN_TOS => { Tos(parse_u8(payload) .context("invalid IFLA_VXLAN_TOS value")?) } IFLA_VXLAN_TTL => { Ttl(parse_u8(payload) .context("invalid IFLA_VXLAN_TTL value")?) } IFLA_VXLAN_LABEL => Label( parse_u32(payload).context("invalid IFLA_VXLAN_LABEL value")?, ), IFLA_VXLAN_LEARNING => Learning( parse_u8(payload) .context("invalid IFLA_VXLAN_LEARNING value")?, ), IFLA_VXLAN_AGEING => Ageing( parse_u32(payload) .context("invalid IFLA_VXLAN_AGEING value")?, ), IFLA_VXLAN_LIMIT => Limit( parse_u32(payload).context("invalid IFLA_VXLAN_LIMIT value")?, ), IFLA_VXLAN_PROXY => Proxy( parse_u8(payload).context("invalid IFLA_VXLAN_PROXY value")?, ), IFLA_VXLAN_RSC => { Rsc(parse_u8(payload) .context("invalid IFLA_VXLAN_RSC value")?) } IFLA_VXLAN_L2MISS => L2Miss( parse_u8(payload).context("invalid IFLA_VXLAN_L2MISS value")?, ), IFLA_VXLAN_L3MISS => L3Miss( parse_u8(payload).context("invalid IFLA_VXLAN_L3MISS value")?, ), IFLA_VXLAN_COLLECT_METADATA => CollectMetadata( parse_u8(payload) .context("invalid IFLA_VXLAN_COLLECT_METADATA value")?, ), IFLA_VXLAN_PORT_RANGE => { let err = "invalid IFLA_VXLAN_PORT value"; if payload.len() != 4 { return Err(err.into()); } let low = parse_u16(&payload[0..2]).context(err)?; let high = parse_u16(&payload[2..]).context(err)?; PortRange((low, high)) } IFLA_VXLAN_PORT => Port( parse_u16_be(payload) .context("invalid IFLA_VXLAN_PORT value")?, ), IFLA_VXLAN_UDP_CSUM => UDPCsum( parse_u8(payload) .context("invalid IFLA_VXLAN_UDP_CSUM value")?, ), IFLA_VXLAN_UDP_ZERO_CSUM6_TX => UDPZeroCsumTX( parse_u8(payload) .context("invalid IFLA_VXLAN_UDP_ZERO_CSUM6_TX value")?, ), IFLA_VXLAN_UDP_ZERO_CSUM6_RX => UDPZeroCsumRX( parse_u8(payload) .context("invalid IFLA_VXLAN_UDP_ZERO_CSUM6_RX value")?, ), IFLA_VXLAN_REMCSUM_TX => RemCsumTX( parse_u8(payload) .context("invalid IFLA_VXLAN_REMCSUM_TX value")?, ), IFLA_VXLAN_REMCSUM_RX => RemCsumRX( parse_u8(payload) .context("invalid IFLA_VXLAN_REMCSUM_RX value")?, ), IFLA_VXLAN_DF => { Df(parse_u8(payload).context("invalid IFLA_VXLAN_DF value")?) } IFLA_VXLAN_GBP => { Gbp(parse_u8(payload) .context("invalid IFLA_VXLAN_GBP value")?) } IFLA_VXLAN_GPE => { Gpe(parse_u8(payload) .context("invalid IFLA_VXLAN_GPE value")?) } IFLA_VXLAN_REMCSUM_NOPARTIAL => RemCsumNoPartial( parse_u8(payload) .context("invalid IFLA_VXLAN_REMCSUM_NO_PARTIAL")?, ), IFLA_VXLAN_TTL_INHERIT => TtlInherit( parse_u8(payload) .context("invalid IFLA_VXLAN_TTL_INHERIT value")?, ), __IFLA_VXLAN_MAX => Unspec(payload.to_vec()), _ => return Err(format!("unknown NLA type {}", buf.kind()).into()), }) } } // https://elixir.bootlin.com/linux/latest/source/net/8021q/vlan_netlink.c#L21 #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoVlan { Unspec(Vec), Id(u16), Flags((u32, u32)), EgressQos(Vec), IngressQos(Vec), Protocol(u16), } impl Nla for InfoVlan { #[rustfmt::skip] fn value_len(&self) -> usize { use self::InfoVlan::*; match self { Id(_) | Protocol(_) => 2, Flags(_) => 8, Unspec(bytes) => bytes.len(), EgressQos(mappings) | IngressQos(mappings) => mappings.as_slice().buffer_len(), } } #[rustfmt::skip] fn emit_value(&self, buffer: &mut [u8]) { use self::InfoVlan::*; match self { Unspec(ref bytes) => buffer.copy_from_slice(bytes), EgressQos(ref mappings) | IngressQos(ref mappings) => mappings.as_slice().emit(buffer), Id(ref value) | Protocol(ref value) => NativeEndian::write_u16(buffer, *value), Flags(ref flags) => { NativeEndian::write_u32(buffer, flags.0); NativeEndian::write_u32(buffer, flags.1) } } } fn kind(&self) -> u16 { use self::InfoVlan::*; match self { Unspec(_) => IFLA_VLAN_UNSPEC, Id(_) => IFLA_VLAN_ID, Flags(_) => IFLA_VLAN_FLAGS, EgressQos(_) => IFLA_VLAN_EGRESS_QOS, IngressQos(_) => IFLA_VLAN_INGRESS_QOS, Protocol(_) => IFLA_VLAN_PROTOCOL, } } } fn parse_mappings(payload: &[u8]) -> Result, DecodeError> { let mut mappings = Vec::new(); for nla in NlasIterator::new(payload) { let nla = nla?; let parsed = VlanQosMapping::parse(&nla)?; mappings.push(parsed); } Ok(mappings) } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoVlan { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoVlan::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_VLAN_UNSPEC => Unspec(payload.to_vec()), IFLA_VLAN_ID => { Id(parse_u16(payload).context("invalid IFLA_VLAN_ID value")?) } IFLA_VLAN_FLAGS => { let err = "invalid IFLA_VLAN_FLAGS value"; if payload.len() != 8 { return Err(err.into()); } let flags = parse_u32(&payload[0..4]).context(err)?; let mask = parse_u32(&payload[4..]).context(err)?; Flags((flags, mask)) } IFLA_VLAN_EGRESS_QOS => EgressQos( parse_mappings(payload) .context("failed to parse IFLA_VLAN_EGRESS_QOS")?, ), IFLA_VLAN_INGRESS_QOS => IngressQos( parse_mappings(payload) .context("failed to parse IFLA_VLAN_INGRESS_QOS")?, ), IFLA_VLAN_PROTOCOL => Protocol( parse_u16_be(payload) .context("invalid IFLA_VLAN_PROTOCOL value")?, ), _ => return Err(format!("unknown NLA type {}", buf.kind()).into()), }) } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoIpoib { Unspec(Vec), Pkey(u16), Mode(u16), UmCast(u16), Other(DefaultNla), } impl Nla for InfoIpoib { fn value_len(&self) -> usize { use self::InfoIpoib::*; match self { Unspec(bytes) => bytes.len(), Pkey(_) | Mode(_) | UmCast(_) => 2, Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::InfoIpoib::*; match self { Unspec(bytes) => buffer.copy_from_slice(bytes.as_slice()), Pkey(value) => NativeEndian::write_u16(buffer, *value), Mode(value) => NativeEndian::write_u16(buffer, *value), UmCast(value) => NativeEndian::write_u16(buffer, *value), Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::InfoIpoib::*; match self { Unspec(_) => IFLA_IPOIB_UNSPEC, Pkey(_) => IFLA_IPOIB_PKEY, Mode(_) => IFLA_IPOIB_MODE, UmCast(_) => IFLA_IPOIB_UMCAST, Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoIpoib { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoIpoib::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_IPOIB_UNSPEC => Unspec(payload.to_vec()), IFLA_IPOIB_PKEY => Pkey( parse_u16(payload).context("invalid IFLA_IPOIB_PKEY value")?, ), IFLA_IPOIB_MODE => Mode( parse_u16(payload).context("invalid IFLA_IPOIB_MODE value")?, ), IFLA_IPOIB_UMCAST => UmCast( parse_u16(payload) .context("invalid IFLA_IPOIB_UMCAST value")?, ), kind => Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum VethInfo { Unspec(Vec), Peer(LinkMessage), Other(DefaultNla), } impl Nla for VethInfo { fn value_len(&self) -> usize { use self::VethInfo::*; match *self { Unspec(ref bytes) => bytes.len(), Peer(ref message) => message.buffer_len(), Other(ref attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::VethInfo::*; match *self { Unspec(ref bytes) => buffer.copy_from_slice(bytes.as_slice()), Peer(ref message) => message.emit(buffer), Other(ref attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { use self::VethInfo::*; match *self { Unspec(_) => VETH_INFO_UNSPEC, Peer(_) => VETH_INFO_PEER, Other(ref attr) => attr.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VethInfo { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::VethInfo::*; let payload = buf.value(); Ok(match buf.kind() { VETH_INFO_UNSPEC => Unspec(payload.to_vec()), VETH_INFO_PEER => { let err = "failed to parse veth link info"; let buffer = LinkMessageBuffer::new_checked(&payload).context(err)?; Peer(LinkMessage::parse(&buffer).context(err)?) } kind => Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoIpVlan { Unspec(Vec), Mode(u16), Flags(u16), Other(DefaultNla), } impl Nla for InfoIpVlan { fn value_len(&self) -> usize { use self::InfoIpVlan::*; match self { Unspec(bytes) => bytes.len(), Mode(_) | Flags(_) => 2, Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::InfoIpVlan::*; match self { Unspec(bytes) => buffer.copy_from_slice(bytes.as_slice()), Mode(value) => NativeEndian::write_u16(buffer, *value), Flags(value) => NativeEndian::write_u16(buffer, *value), Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::InfoIpVlan::*; match self { Unspec(_) => IFLA_IPVLAN_UNSPEC, Mode(_) => IFLA_IPVLAN_MODE, Flags(_) => IFLA_IPVLAN_FLAGS, Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoIpVlan { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoIpVlan::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_IPVLAN_UNSPEC => Unspec(payload.to_vec()), IFLA_IPVLAN_MODE => Mode( parse_u16(payload).context("invalid IFLA_IPVLAN_MODE value")?, ), IFLA_IPVLAN_FLAGS => Flags( parse_u16(payload) .context("invalid IFLA_IPVLAN_FLAGS value")?, ), kind => Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoXfrmTun { Unspec(Vec), Link(u32), IfId(u32), Other(DefaultNla), } impl Nla for InfoXfrmTun { fn value_len(&self) -> usize { use self::InfoXfrmTun::*; match self { Unspec(bytes) => bytes.len(), Link(_) => 4, IfId(_) => 4, Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::InfoXfrmTun::*; match self { Unspec(bytes) => buffer.copy_from_slice(bytes.as_slice()), Link(value) => NativeEndian::write_u32(buffer, *value), IfId(value) => NativeEndian::write_u32(buffer, *value), Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::InfoXfrmTun::*; match self { Unspec(_) => IFLA_XFRM_UNSPEC, Link(_) => IFLA_XFRM_LINK, IfId(_) => IFLA_XFRM_IF_ID, Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoXfrmTun { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoXfrmTun::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_XFRM_UNSPEC => Unspec(payload.to_vec()), IFLA_XFRM_LINK => Link( parse_u32(payload).context("invalid IFLA_XFRM_IF_ID value")?, ), IFLA_XFRM_IF_ID => IfId( parse_u32(payload).context("invalid IFLA_XFRM_IF_ID value")?, ), kind => Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoVrf { TableId(u32), Other(DefaultNla), } impl Nla for InfoVrf { fn value_len(&self) -> usize { use self::InfoVrf::*; match self { TableId(_) => 4, Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::InfoVrf::*; match self { TableId(value) => NativeEndian::write_u32(buffer, *value), Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::InfoVrf::*; match self { TableId(_) => IFLA_VRF_TABLE, Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoVrf { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoVrf::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_VRF_TABLE => TableId( parse_u32(payload).context("invalid IFLA_VRF_TABLE value")?, ), kind => Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoMacVlan { Unspec(Vec), Mode(u32), Flags(u16), MacAddrMode(u32), MacAddr([u8; 6]), MacAddrData(Vec), MacAddrCount(u32), BcQueueLen(u32), BcQueueLenUsed(u32), BcCutoff(i32), Other(DefaultNla), } impl Nla for InfoMacVlan { fn value_len(&self) -> usize { use self::InfoMacVlan::*; match self { Unspec(bytes) => bytes.len(), Mode(_) => 4, Flags(_) => 2, MacAddrMode(_) => 4, MacAddr(_) => 6, MacAddrData(ref nlas) => nlas.as_slice().buffer_len(), MacAddrCount(_) => 4, BcQueueLen(_) => 4, BcQueueLenUsed(_) => 4, BcCutoff(_) => 4, Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::InfoMacVlan::*; match self { Unspec(bytes) => buffer.copy_from_slice(bytes.as_slice()), Mode(value) => NativeEndian::write_u32(buffer, *value), Flags(value) => NativeEndian::write_u16(buffer, *value), MacAddrMode(value) => NativeEndian::write_u32(buffer, *value), MacAddr(bytes) => buffer.copy_from_slice(bytes), MacAddrData(ref nlas) => nlas.as_slice().emit(buffer), MacAddrCount(value) => NativeEndian::write_u32(buffer, *value), BcQueueLen(value) => NativeEndian::write_u32(buffer, *value), BcQueueLenUsed(value) => NativeEndian::write_u32(buffer, *value), BcCutoff(value) => NativeEndian::write_i32(buffer, *value), Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::InfoMacVlan::*; match self { Unspec(_) => IFLA_MACVLAN_UNSPEC, Mode(_) => IFLA_MACVLAN_MODE, Flags(_) => IFLA_MACVLAN_FLAGS, MacAddrMode(_) => IFLA_MACVLAN_MACADDR_MODE, MacAddr(_) => IFLA_MACVLAN_MACADDR, MacAddrData(_) => IFLA_MACVLAN_MACADDR_DATA, MacAddrCount(_) => IFLA_MACVLAN_MACADDR_COUNT, BcQueueLen(_) => IFLA_MACVLAN_BC_QUEUE_LEN, BcQueueLenUsed(_) => IFLA_MACVLAN_BC_QUEUE_LEN_USED, BcCutoff(_) => IFLA_MACVLAN_BC_CUTOFF, Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoMacVlan { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoMacVlan::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_MACVLAN_UNSPEC => Unspec(payload.to_vec()), IFLA_MACVLAN_MODE => Mode( parse_u32(payload) .context("invalid IFLA_MACVLAN_MODE value")?, ), IFLA_MACVLAN_FLAGS => Flags( parse_u16(payload) .context("invalid IFLA_MACVLAN_FLAGS value")?, ), IFLA_MACVLAN_MACADDR_MODE => MacAddrMode( parse_u32(payload) .context("invalid IFLA_MACVLAN_MACADDR_MODE value")?, ), IFLA_MACVLAN_MACADDR => MacAddr( parse_mac(payload) .context("invalid IFLA_MACVLAN_MACADDR value")?, ), IFLA_MACVLAN_MACADDR_DATA => { let mut mac_data = Vec::new(); let err = "failed to parse IFLA_MACVLAN_MACADDR_DATA"; for nla in NlasIterator::new(payload) { let nla = &nla.context(err)?; let parsed = InfoMacVlan::parse(nla).context(err)?; mac_data.push(parsed); } MacAddrData(mac_data) } IFLA_MACVLAN_MACADDR_COUNT => MacAddrCount( parse_u32(payload) .context("invalid IFLA_MACVLAN_MACADDR_COUNT value")?, ), IFLA_MACVLAN_BC_QUEUE_LEN => BcQueueLen( parse_u32(payload) .context("invalid IFLA_MACVLAN_BC_QUEUE_LEN value")?, ), IFLA_MACVLAN_BC_QUEUE_LEN_USED => BcQueueLenUsed( parse_u32(payload) .context("invalid IFLA_MACVLAN_BC_QUEUE_LEN_USED value")?, ), IFLA_MACVLAN_BC_CUTOFF => BcCutoff( parse_i32(payload) .context("invalid IFLA_MACVLAN_BC_CUTOFF value")?, ), kind => Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoMacVtap { Unspec(Vec), Mode(u32), Flags(u16), MacAddrMode(u32), MacAddr([u8; 6]), MacAddrData(Vec), MacAddrCount(u32), BcQueueLen(u32), BcQueueLenUsed(u32), BcCutoff(i32), Other(DefaultNla), } impl Nla for InfoMacVtap { fn value_len(&self) -> usize { use self::InfoMacVtap::*; match self { Unspec(bytes) => bytes.len(), Mode(_) => 4, Flags(_) => 2, MacAddrMode(_) => 4, MacAddr(_) => 6, MacAddrData(ref nlas) => nlas.as_slice().buffer_len(), MacAddrCount(_) => 4, BcQueueLen(_) => 4, BcQueueLenUsed(_) => 4, BcCutoff(_) => 4, Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::InfoMacVtap::*; match self { Unspec(bytes) => buffer.copy_from_slice(bytes.as_slice()), Mode(value) => NativeEndian::write_u32(buffer, *value), Flags(value) => NativeEndian::write_u16(buffer, *value), MacAddrMode(value) => NativeEndian::write_u32(buffer, *value), MacAddr(bytes) => buffer.copy_from_slice(bytes), MacAddrData(ref nlas) => nlas.as_slice().emit(buffer), MacAddrCount(value) => NativeEndian::write_u32(buffer, *value), BcQueueLen(value) => NativeEndian::write_u32(buffer, *value), BcQueueLenUsed(value) => NativeEndian::write_u32(buffer, *value), BcCutoff(value) => NativeEndian::write_i32(buffer, *value), Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::InfoMacVtap::*; match self { Unspec(_) => IFLA_MACVLAN_UNSPEC, Mode(_) => IFLA_MACVLAN_MODE, Flags(_) => IFLA_MACVLAN_FLAGS, MacAddrMode(_) => IFLA_MACVLAN_MACADDR_MODE, MacAddr(_) => IFLA_MACVLAN_MACADDR, MacAddrData(_) => IFLA_MACVLAN_MACADDR_DATA, MacAddrCount(_) => IFLA_MACVLAN_MACADDR_COUNT, BcQueueLen(_) => IFLA_MACVLAN_BC_QUEUE_LEN, BcQueueLenUsed(_) => IFLA_MACVLAN_BC_QUEUE_LEN_USED, BcCutoff(_) => IFLA_MACVLAN_BC_CUTOFF, Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoMacVtap { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoMacVtap::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_MACVLAN_UNSPEC => Unspec(payload.to_vec()), IFLA_MACVLAN_MODE => Mode( parse_u32(payload) .context("invalid IFLA_MACVLAN_MODE value")?, ), IFLA_MACVLAN_FLAGS => Flags( parse_u16(payload) .context("invalid IFLA_MACVLAN_FLAGS value")?, ), IFLA_MACVLAN_MACADDR_MODE => MacAddrMode( parse_u32(payload) .context("invalid IFLA_MACVLAN_MACADDR_MODE value")?, ), IFLA_MACVLAN_MACADDR => MacAddr( parse_mac(payload) .context("invalid IFLA_MACVLAN_MACADDR value")?, ), IFLA_MACVLAN_MACADDR_DATA => { let mut mac_data = Vec::new(); let err = "failed to parse IFLA_MACVLAN_MACADDR_DATA"; for nla in NlasIterator::new(payload) { let nla = &nla.context(err)?; let parsed = InfoMacVtap::parse(nla).context(err)?; mac_data.push(parsed); } MacAddrData(mac_data) } IFLA_MACVLAN_MACADDR_COUNT => MacAddrCount( parse_u32(payload) .context("invalid IFLA_MACVLAN_MACADDR_COUNT value")?, ), IFLA_MACVLAN_BC_QUEUE_LEN => BcQueueLen( parse_u32(payload) .context("invalid IFLA_MACVLAN_BC_QUEUE_LEN value")?, ), IFLA_MACVLAN_BC_QUEUE_LEN_USED => BcQueueLenUsed( parse_u32(payload) .context("invalid IFLA_MACVLAN_BC_QUEUE_LEN_USED value")?, ), IFLA_MACVLAN_BC_CUTOFF => BcCutoff( parse_i32(payload) .context("invalid IFLA_MACVLAN_BC_CUTOFF value")?, ), kind => Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum VlanQosMapping { Unspec(Vec), Mapping { from: u32, to: u32 }, Other(DefaultNla), } impl Nla for VlanQosMapping { fn value_len(&self) -> usize { match self { VlanQosMapping::Unspec(bytes) => bytes.len(), VlanQosMapping::Mapping { .. } => 8, VlanQosMapping::Other(nla) => nla.value_len(), } } fn kind(&self) -> u16 { match self { VlanQosMapping::Unspec(_) => IFLA_VLAN_QOS_UNSPEC, VlanQosMapping::Mapping { .. } => IFLA_VLAN_QOS_MAPPING, VlanQosMapping::Other(nla) => nla.kind(), } } fn emit_value(&self, buffer: &mut [u8]) { use VlanQosMapping::*; match self { Unspec(payload) => buffer.copy_from_slice(payload), Mapping { from, to } => { NativeEndian::write_u32(buffer, *from); NativeEndian::write_u32(&mut buffer[4..], *to); } Other(nla) => nla.emit_value(buffer), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VlanQosMapping { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use VlanQosMapping::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_VLAN_QOS_UNSPEC => Unspec(payload.to_vec()), IFLA_VLAN_QOS_MAPPING => Mapping { from: parse_u32(&payload[..4]) .context("expected u32 from value")?, to: parse_u32(&payload[4..]) .context("expected u32 to value")?, }, kind => Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } #[cfg(test)] mod tests { use super::*; use crate::{ nlas::link::{bond::*, Nla}, LinkHeader, LinkMessage, }; use netlink_packet_utils::traits::Emitable; use std::net::{Ipv4Addr, Ipv6Addr}; #[rustfmt::skip] static BRIDGE: [u8; 424] = [ 0x0b, 0x00, // L = 11 0x01, 0x00, // T = 1 (IFLA_INFO_KIND) 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x00, // V = "bridge" 0x00, // padding 0x9c, 0x01, // L = 412 0x02, 0x00, // T = 2 (IFLA_INFO_DATA) 0x0c, 0x00, // L = 12 0x10, 0x00, // T = 16 (IFLA_BR_HELLO_TIMER) 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V = 35 0x0c, 0x00, // L = 12 0x11, 0x00, // T = 17 (IFLA_BR_TCN_TIMER) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V = 0 0x0c, 0x00, // L = 12 0x12, 0x00, // T = 18 (IFLA_BR_TOPOLOGY_CHANGE_TIMER) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V = 0 0x0c, 0x00, // L = 12 0x13, 0x00, // T = 19 (IFLA_BR_GC_TIMER) 0xb5, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V = 14261 (0x37b5) 0x08, 0x00, // L = 8 0x01, 0x00, // T = 1 (IFLA_BR_FORWARD_DELAY) 0xc7, 0x00, 0x00, 0x00, // V = 199 0x08, 0x00, // L = 8 0x02, 0x00, // T = 2 (IFLA_BR_HELLO_TIME) 0xc7, 0x00, 0x00, 0x00, // V = 199 0x08, 0x00, // L = 8 0x03, 0x00, // T = 3 (IFLA_BR_MAX_AGE) 0xcf, 0x07, 0x00, 0x00, // V = 1999 (0x07cf) 0x08, 0x00, // L = 8 0x04, 0x00, // T = 4 (IFLA_BR_AGEING_TIME) 0x2f, 0x75, 0x00, 0x00, // V = 29999 (0x752f) 0x08, 0x00, // L = 8 0x05, 0x00, // T = 5 (IFLA_BR_STP_STATE) 0x01, 0x00, 0x00, 0x00, // V = 1 0x06, 0x00, // L = 6 0x06, 0x00, // T = 6 (IFLA_BR_PRIORITY) 0x00, 0x80, // V = 32768 (0x8000) 0x00, 0x00, // Padding 0x05, 0x00, // L = 5 0x07, 0x00, // T = 7 (IFLA_BR_VLAN_FILTERING) 0x00, // V = 0 0x00, 0x00, 0x00, // Padding 0x06, 0x00, // L = 6 0x09, 0x00, // T = 9 (IFLA_BR_GROUP_FWD_MASK) 0x00, 0x00, // V = 0 0x00, 0x00, // Padding 0x0c, 0x00, // L = 12 0x0b, 0x00, // T = 11 (IFLA_BR_BRIDGE_ID) 0x80, 0x00, // V (priority) = 128 (0x80) 0x52, 0x54, 0x00, 0xd7, 0x19, 0x3e, // V (address) = 52:54:00:d7:19:3e 0x0c, 0x00, // L = 12 0x0a, 0x00, // T = 10 (IFLA_BR_ROOT_ID) 0x80, 0x00, // V (priority) = 128 (0x80) 0x52, 0x54, 0x00, 0xd7, 0x19, 0x3e, // V (address) = 52:54:00:d7:19:3e 0x06, 0x00, // L = 6 0x0c, 0x00, // T = 12 (IFLA_BR_ROOT_PORT) 0x00, 0x00, // V = 0 0x00, 0x00, // Padding 0x08, 0x00, // L = 8 0x0d, 0x00, // T = 13 (IFLA_BR_ROOT_PATH_COST) 0x00, 0x00, 0x00, 0x00, // V = 0 0x05, 0x00, // L = 5 0x0e, 0x00, // T = 14 (IFLA_BR_TOPOLOGY_CHANGE) 0x00, // V = 0 0x00, 0x00, 0x00, // Padding 0x05, 0x00, // L = 5 0x0f, 0x00, // T = 15 (IFLA_BR_TOPOLOGY_CHANGE_DETECTED) 0x00, // V = 0 0x00, 0x00, 0x00, // Padding 0x0a, 0x00, // L = 10 0x14, 0x00, // T = 20 (IFLA_BR_GROUP_ADDR) 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00, // V = 01:80:c2:00:00:00 0x00, 0x00, // Padding 0x06, 0x00, // L = 6 0x08, 0x00, // T = 8 (IFLA_BR_VLAN_PROTOCOL) 0x81, 0x00, // V = 33024 (big-endian) 0x00, 0x00, // Padding 0x06, 0x00, // L = 6 0x27, 0x00, // T = 39 (IFLA_BR_VLAN_DEFAULT_PVID) 0x01, 0x00, // V = 1 0x00, 0x00, // Padding 0x05, 0x00, // L = 5 0x29, 0x00, // T = 41 (IFLA_BR_VLAN_STATS_ENABLED) 0x00, // V = 0 0x00, 0x00, 0x00, // Padding 0x05, 0x00, // L = 5 0x16, 0x00, // T = 22 (IFLA_BR_MCAST_ROUTER) 0x01, // V = 1 0x00, 0x00, 0x00, // Padding 0x05, 0x00, // L = 5 0x17, 0x00, // T = 23 (IFLA_BR_MCAST_SNOOPING) 0x01, // V = 1 0x00, 0x00, 0x00, // Padding 0x05, 0x00, // L = 5 0x18, 0x00, // T = 24 (IFLA_BR_MCAST_QUERY_USE_IFADDR) 0x00, // V = 0 0x00, 0x00, 0x00, // Padding 0x05, 0x00, // L = 5 0x19, 0x00, // T = 25 (IFLA_BR_MCAST_QUERIER) 0x00, // V = 0 0x00, 0x00, 0x00, // Padding 0x05, 0x00, // L = 5 0x2a, 0x00, // T = 42 (IFLA_BR_MCAST_STATS_ENABLED) 0x00, // V = 0 0x00, 0x00, 0x00, // Padding 0x08, 0x00, // L = 8 0x1a, 0x00, // T = 26 (IFLA_BR_MCAST_HASH_ELASTICITY) 0x04, 0x00, 0x00, 0x00, // V = 4 0x08, 0x00, // L = 8 0x1b, 0x00, // T = 27 (IFLA_BR_MCAST_HASH_MAX) 0x00, 0x02, 0x00, 0x00, // V = 512 (0x0200) 0x08, 0x00, // L = 8 0x1c, 0x00, // T = 28 (IFLA_BR_MCAST_LAST_MEMBER_CNT) 0x02, 0x00, 0x00, 0x00, // V = 2 0x08, 0x00, // L = 8 0x1d, 0x00, // T = 29 (IFLA_BR_MCAST_STARTUP_QUERY_CNT) 0x02, 0x00, 0x00, 0x00, // V = 2 0x05, 0x00, // L = 5 0x2b, 0x00, // T = 43 (IFLA_BR_MCAST_IGMP_VERSION) 0x02, // V = 2 0x00, 0x00, 0x00, // Padding 0x05, 0x00, // L = 5 0x2c, 0x00, // T = 44 (IFLA_BR_MCAST_MLD_VERSION) 0x01, // V = 1 0x00, 0x00, 0x00, // Padding 0x0c, 0x00, // L = 12 0x1e, 0x00, // T = 30 (IFLA_BR_MCAST_LAST_MEMBER_INTVL) 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V = 99 0x0c, 0x00, // L = 12 0x1f, 0x00, // T = 31 (IFLA_BR_MCAST_MEMBERSHIP_INTVL) 0x8f, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V = 25999 (0x658f) 0x0c, 0x00, // L = 12 0x20, 0x00, // T = 32 (IFLA_BR_MCAST_QUERIER_INTVL) 0x9b, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V = 25499 (0x639b) 0x0c, 0x00, // L = 12 0x21, 0x00, // T = 33 (IFLA_BR_MCAST_QUERY_INTVL) 0xd3, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V = 12499 (0x30d3) 0x0c, 0x00, // L = 12 0x22, 0x00, // T = 34 (IFLA_BR_MCAST_QUERY_RESPONSE_INTVL) 0xe7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V = 999 (0x03e7) 0x0c, 0x00, // L = 12 0x23, 0x00, // T = 35 (IFLA_BR_MCAST_STARTUP_QUERY_INTVL) 0x34, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V = 3124 (0x0c34) 0x05, 0x00, // L = 5 0x24, 0x00, // T = 36 (IFLA_BR_NF_CALL_IPTABLES) 0x00, // V = 0 0x00, 0x00, 0x00, // Padding 0x05, 0x00, // L = 5 0x25, 0x00, // T = 37 (IFLA_BR_NF_CALL_IP6TABLES) 0x00, // V = 0 0x00, 0x00, 0x00, // Padding 0x05, 0x00, // L = 5 0x26, 0x00, // T = 38 (IFLA_BR_NF_CALL_ARPTABLES) 0x00, // V = 0 0x00, 0x00, 0x00, // Padding 0x05, 0x00, // L = 5 0x2d, 0x00, // T = 45 (IFLA_BR_VLAN_STATS_PER_PORT) 0x01, // V = 1 0x00, 0x00, 0x00, // Padding 0x0c, 0x00, // L = 12 0x2e, 0x00, // T = 46 (IFLA_BR_MULTI_BOOLOPT) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V = 0 ]; lazy_static! { static ref BRIDGE_INFO: Vec = vec![ InfoBridge::HelloTimer(35), InfoBridge::TcnTimer(0), InfoBridge::TopologyChangeTimer(0), InfoBridge::GcTimer(14261), InfoBridge::ForwardDelay(199), InfoBridge::HelloTime(199), InfoBridge::MaxAge(1999), InfoBridge::AgeingTime(29999), InfoBridge::StpState(1), InfoBridge::Priority(0x8000), InfoBridge::VlanFiltering(0), InfoBridge::GroupFwdMask(0), InfoBridge::BridgeId((128, [0x52, 0x54, 0x00, 0xd7, 0x19, 0x3e])), InfoBridge::RootId((128, [0x52, 0x54, 0x00, 0xd7, 0x19, 0x3e])), InfoBridge::RootPort(0), InfoBridge::RootPathCost(0), InfoBridge::TopologyChange(0), InfoBridge::TopologyChangeDetected(0), InfoBridge::GroupAddr([0x01, 0x80, 0xc2, 0x00, 0x00, 0x00]), InfoBridge::VlanProtocol(33024), InfoBridge::VlanDefaultPvid(1), InfoBridge::VlanStatsEnabled(0), InfoBridge::MulticastRouter(1), InfoBridge::MulticastSnooping(1), InfoBridge::MulticastQueryUseIfaddr(0), InfoBridge::MulticastQuerier(0), InfoBridge::MulticastStatsEnabled(0), InfoBridge::MulticastHashElasticity(4), InfoBridge::MulticastHashMax(512), InfoBridge::MulticastLastMemberCount(2), InfoBridge::MulticastStartupQueryCount(2), InfoBridge::MulticastIgmpVersion(2), InfoBridge::MulticastMldVersion(1), InfoBridge::MulticastLastMemberInterval(99), InfoBridge::MulticastMembershipInterval(25999), InfoBridge::MulticastQuerierInterval(25499), InfoBridge::MulticastQueryInterval(12499), InfoBridge::MulticastQueryResponseInterval(999), InfoBridge::MulticastStartupQueryInterval(3124), InfoBridge::NfCallIpTables(0), InfoBridge::NfCallIp6Tables(0), InfoBridge::NfCallArpTables(0), InfoBridge::VlanStatsPerHost(1), InfoBridge::MultiBoolOpt(0), ]; } #[test] fn parse_info_kind() { let info_kind_nla = NlaBuffer::new_checked(&BRIDGE[..12]).unwrap(); let parsed = InfoKind::parse(&info_kind_nla).unwrap(); assert_eq!(parsed, InfoKind::Bridge); } #[test] fn parse_info_bridge() { let nlas = NlasIterator::new(&BRIDGE[16..]); for nla in nlas.map(|nla| nla.unwrap()) { InfoBridge::parse(&nla).unwrap(); } } #[rustfmt::skip] #[test] fn parse_veth_info() { let data = [0x08, 0x00, // length = 8 0x01, 0x00, // type = 1 = IFLA_INFO_KIND 0x76, 0x65, 0x74, 0x68, // VETH 0x30, 0x00, // length = 48 0x02, 0x00, // type = IFLA_INFO_DATA 0x2c, 0x00, // length = 44 0x01, 0x00, // type = VETH_INFO_PEER // The data a NEWLINK message 0x00, // interface family 0x00, // padding 0x00, 0x00, // link layer type 0x00, 0x00, 0x00, 0x00, // link index 0x00, 0x00, 0x00, 0x00, // flags 0x00, 0x00, 0x00, 0x00, // flags change mask // NLA 0x10, 0x00, // length = 16 0x03, 0x00, // type = IFLA_IFNAME 0x76, 0x65, 0x74, 0x68, 0x63, 0x30, 0x65, 0x36, 0x30, 0x64, 0x36, 0x00, // NLA 0x08, 0x00, // length = 8 0x0d, 0x00, // type = IFLA_TXQLEN 0x00, 0x00, 0x00, 0x00]; let nla = NlaBuffer::new_checked(&data[..]).unwrap(); let parsed = VecInfo::parse(&nla).unwrap().0; let expected = vec![ Info::Kind(InfoKind::Veth), Info::Data(InfoData::Veth(VethInfo::Peer(LinkMessage { header: LinkHeader { interface_family: 0, index: 0, link_layer_type: ARPHRD_NETROM, flags: 0, change_mask: 0, }, nlas: vec![ Nla::IfName("vethc0e60d6".to_string()), Nla::TxQueueLen(0), ], }))), ]; assert_eq!(expected, parsed); } #[rustfmt::skip] #[test] fn parse_info_bondport() { let data = [0x09, 0x00, // length 0x04, 0x00, // IFLA_INFO_PORT_KIND 0x62, 0x6f, 0x6e, 0x64, 0x00, // V = "bond\0" 0x00, 0x00, 0x00, // padding 0x14, 0x00, // length = 20 0x05, 0x00, // IFLA_INFO_PORT_DATA 0x06, 0x00, // length 0x05, 0x00, // IFLA_BOND_PORT_QUEUE_ID 0x00, 0x00, // 0 0x00, 0x00, // padding 0x08, 0x00, // length 0x09, 0x00, // IFLA_BOND_PORT_PRIO 0x32, 0x00, 0x00, 0x00]; let nla = NlaBuffer::new_checked(&data[..]).unwrap(); let parsed = VecInfo::parse(&nla).unwrap().0; let expected = vec![ Info::PortKind(InfoPortKind::Bond), Info::PortData(InfoPortData::BondPort(vec![InfoBondPort::QueueId(0), InfoBondPort::Prio(50), ])), ]; assert_eq!(expected, parsed); } #[rustfmt::skip] #[test] fn parse_info_bond() { let data = [0x08, 0x00, // length 0x01, 0x00, // IFLA_INFO_KIND 0x62, 0x6f, 0x6e, 0x64, // "bond" 0x80, 0x00, // length 0x02, 0x00, // IFLA_INFO_DATA 0x05, 0x00, // length 0x01, 0x00, // IFLA_BOND_MODE 0x04, // 4 (802.3ad) 0x00, 0x00, 0x00, // padding 0x08, 0x00, // length 0x03, 0x00, // IFLA_BOND_MIIMON 0x32, 0x00, 0x00, 0x00, // 50 0x08, 0x00, // length 0x04, 0x00, // IFLA_BOND_UPDELAY 0x64, 0x00, 0x00, 0x00, // 100 0x08, 0x00, // length 0x05, 0x00, // IFLA_BOND_DOWNDELAY 0x64, 0x00, 0x00, 0x00, // 100 0x14, 0x00, // length 0x08, 0x00, // IFLA_BOND_ARP_IP_TARGET 0x08, 0x00, // length 0x00, 0x00, // entry #0 0x01, 0x02, 0x03, 0x04, // 1.2.3.4 0x08, 0x00, // length 0x01, 0x00, // entry #1 0x09, 0x09, 0x09, 0x09, // 9.9.9.9 0x18, 0x00, // length 0x1f, 0x00, // IFLA_BOND_NS_IP6_TARGET 0x14, 0x00, // length 0x00, 0x00, // entry #0 0xfd, 0x01, 0x00, 0x00, // fd01::1 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, // length 0x1c, 0x00, // IFLA_BOND_PEER_NOTIF_DELAY 0xc8, 0x00, 0x00, 0x00, // 200 0x08, 0x00, // length 0x12, 0x00, // IFLA_BOND_MIN_LINKS 0x03, 0x00, 0x00, 0x00, // 3 0x20, 0x00, // length 0x17, 0x00, // IFLA_BOND_AD_INFO 0x06, 0x00, // length 0x01, 0x00, // IFLA_BOND_AD_INFO_AGGREGATOR 0x10, 0x00, // 16 0x00, 0x00, // padding 0x06, 0x00, // length 0x02, 0x00, // IFLA_BOND_AD_INFO_NUM_PORTS 0x02, 0x00, // 2 0x00, 0x00, // padding 0x0a, 0x00, // length 0x05, 0x00, // IFLA_BOND_AD_INFO_PARTNER_MAC 0x00, 0x11, 0x22, // 00:11:22:33:44:55 0x33, 0x44, 0x55, 0x00, 0x00]; let nla = NlaBuffer::new_checked(&data[..]).unwrap(); let parsed = VecInfo::parse(&nla).unwrap().0; let expected = vec![ Info::Kind(InfoKind::Bond), Info::Data(InfoData::Bond(vec![InfoBond::Mode(4), InfoBond::MiiMon(50), InfoBond::UpDelay(100), InfoBond::DownDelay(100), InfoBond::ArpIpTarget(vec!(Ipv4Addr::new(1, 2, 3, 4), Ipv4Addr::new(9, 9, 9, 9))), InfoBond::NsIp6Target(vec!(Ipv6Addr::new(0xfd01, 0, 0, 0, 0, 0, 0, 1))), InfoBond::PeerNotifDelay(200), InfoBond::MinLinks(3), InfoBond::AdInfo(vec!(BondAdInfo::Aggregator(16), BondAdInfo::NumPorts(2), BondAdInfo::PartnerMac([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]))), ])), ]; assert_eq!(expected, parsed); } #[rustfmt::skip] static IPVLAN: [u8; 32] = [ 0x0b, 0x00, // length = 11 0x01, 0x00, // type = 1 = IFLA_INFO_KIND 0x69, 0x70, 0x76, 0x6c, 0x61, 0x6e, 0x00, // V = "ipvlan\0" 0x00, // padding 0x14, 0x00, // length = 20 0x02, 0x00, // type = 2 = IFLA_INFO_DATA 0x06, 0x00, // length = 6 0x01, 0x00, // type = 1 = IFLA_IPVLAN_MODE 0x01, 0x00, // l3 0x00, 0x00, // padding 0x06, 0x00, // length = 6 0x02, 0x00, // type = 2 = IFLA_IPVLAN_FLAGS 0x02, 0x00, // vepa flag 0x00, 0x00, // padding ]; lazy_static! { static ref IPVLAN_INFO: Vec = vec![ InfoIpVlan::Mode(1), // L3 InfoIpVlan::Flags(2), // vepa flag ]; } #[test] fn parse_info_ipvlan() { let nla = NlaBuffer::new_checked(&IPVLAN[..]).unwrap(); let parsed = VecInfo::parse(&nla).unwrap().0; let expected = vec![ Info::Kind(InfoKind::IpVlan), Info::Data(InfoData::IpVlan(IPVLAN_INFO.clone())), ]; assert_eq!(expected, parsed); } #[test] fn emit_info_ipvlan() { let nlas = vec![ Info::Kind(InfoKind::IpVlan), Info::Data(InfoData::IpVlan(IPVLAN_INFO.clone())), ]; assert_eq!(nlas.as_slice().buffer_len(), 32); let mut vec = vec![0xff; 32]; nlas.as_slice().emit(&mut vec); assert_eq!(&vec[..], &IPVLAN[..]); } #[rustfmt::skip] static MACVLAN: [u8; 24] = [ 0x0c, 0x00, // length = 12 0x01, 0x00, // type = 1 = IFLA_INFO_KIND 0x6d, 0x61, 0x63, 0x76, 0x6c, 0x61, 0x6e, 0x00, // V = "macvlan\0" 0x0c, 0x00, // length = 12 0x02, 0x00, // type = 2 = IFLA_INFO_DATA 0x08, 0x00, // length = 8 0x01, 0x00, // type = IFLA_MACVLAN_MODE 0x04, 0x00, 0x00, 0x00, // V = 4 = bridge ]; lazy_static! { static ref MACVLAN_INFO: Vec = vec![ InfoMacVlan::Mode(4), // bridge ]; } #[test] fn parse_info_macvlan() { let nla = NlaBuffer::new_checked(&MACVLAN[..]).unwrap(); let parsed = VecInfo::parse(&nla).unwrap().0; let expected = vec![ Info::Kind(InfoKind::MacVlan), Info::Data(InfoData::MacVlan(MACVLAN_INFO.clone())), ]; assert_eq!(expected, parsed); } #[test] fn emit_info_macvlan() { let nlas = vec![ Info::Kind(InfoKind::MacVlan), Info::Data(InfoData::MacVlan(MACVLAN_INFO.clone())), ]; assert_eq!(nlas.as_slice().buffer_len(), 24); let mut vec = vec![0xff; 24]; nlas.as_slice().emit(&mut vec); assert_eq!(&vec[..], &MACVLAN[..]); } #[rustfmt::skip] static MACVLAN_BC: [u8; 48] = [ 0x0c, 0x00, // length = 12 0x01, 0x00, // type = 1 = IFLA_INFO_KIND 0x6d, 0x61, 0x63, 0x76, 0x6c, 0x61, 0x6e, 0x00, // V = "macvlan\0" 0x24, 0x00, // length = 36 0x02, 0x00, // type = 2 = IFLA_INFO_DATA 0x08, 0x00, // length = 8 0x01, 0x00, // type = IFLA_MACVLAN_MODE 0x02, 0x00, 0x00, 0x00, // V = 2 = vepa 0x08, 0x00, // length = 8 0x07, 0x00, // type = IFLA_MACVLAN_BC_QUEUE_LEN 0xe8, 0x03, 0x00, 0x00, // value 1000 0x08, 0x00, // length = 8 0x08, 0x00, // type = IFLA_MACVLAN_BC_QUEUE_LEN_USED 0xe8, 0x03, 0x00, 0x00, // value 1000 0x08, 0x00, // length = 8 0x09, 0x00, // type = IFLA_MACVLAN_BC_CUTOFF 0xff, 0xff, 0xff, 0xff, // value = -1 (signed two's complement) ]; lazy_static! { static ref MACVLAN_INFO_BC: Vec = vec![ InfoMacVlan::Mode(2), // vepa InfoMacVlan::BcQueueLen(1000), InfoMacVlan::BcQueueLenUsed(1000), InfoMacVlan::BcCutoff(-1), ]; } #[test] fn parse_info_macvlan_bc() { let nla = NlaBuffer::new_checked(&MACVLAN_BC[..]).unwrap(); let parsed = VecInfo::parse(&nla).unwrap().0; let expected = vec![ Info::Kind(InfoKind::MacVlan), Info::Data(InfoData::MacVlan(MACVLAN_INFO_BC.clone())), ]; assert_eq!(expected, parsed); } #[test] fn emit_info_macvlan_bc() { let nlas = vec![ Info::Kind(InfoKind::MacVlan), Info::Data(InfoData::MacVlan(MACVLAN_INFO_BC.clone())), ]; assert_eq!(nlas.as_slice().buffer_len(), 48); let mut vec = vec![0xff; 48]; nlas.as_slice().emit(&mut vec); assert_eq!(&vec[..], &MACVLAN_BC[..]); } lazy_static! { static ref XFRMTUN_INFO: Vec = vec![ InfoXfrmTun::IfId(4), // ifid ]; } #[rustfmt::skip] static XFRMTUN: [u8; 24] = [ 9, 0, 1, 0, 120,102,114,109, 0, 0, 0, 0, 12, 0, 2, 0, 8, 0, 2, 0, 4, 0, 0, 0 ]; #[test] fn emit_info_xfrmtun() { let nlas = vec![ Info::Kind(InfoKind::Xfrm), Info::Data(InfoData::Xfrm(XFRMTUN_INFO.clone())), ]; assert_eq!(nlas.as_slice().buffer_len(), 24); let mut vec = vec![0xff; 24]; nlas.as_slice().emit(&mut vec); assert_eq!(&vec[..], &XFRMTUN[..]); } #[rustfmt::skip] static MACVLAN_SOURCE_SET: [u8; 84] = [ 0x0c, 0x00, // length = 12 0x01, 0x00, // type = 1 = IFLA_INFO_KIND 0x6d, 0x61, 0x63, 0x76, 0x6c, 0x61, 0x6e, 0x00, // V = "macvlan\0" 0x48, 0x00, // length = 72 0x02, 0x00, // type = 2 = IFLA_INFO_DATA 0x08, 0x00, // length = 8 0x03, 0x00, // type = 3 = IFLA_MACVLAN_MACADDR_MODE 0x03, 0x00, 0x00, 0x00, // V = 3 = set 0x34, 0x00, // length = 52 0x05, 0x00, // type = 5 = IFLA_MACVLAN_MACADDR_DATA 0x0a, 0x00, // length = 10 0x04, 0x00, // type = 4 = IFLA_MACVLAN_MACADDR 0x22, 0xf5, 0x54, 0x09, 0x88, 0xd7, // V = mac address 0x00, 0x00, // padding 0x0a, 0x00, // length = 10 0x04, 0x00, // type = 4 = IFLA_MACVLAN_MACADDR 0x22, 0xf5, 0x54, 0x09, 0x99, 0x32, // V = mac address 0x00, 0x00, // padding 0x0a, 0x00, // length = 10 0x04, 0x00, // type = 4 = IFLA_MACVLAN_MACADDR 0x22, 0xf5, 0x54, 0x09, 0x87, 0x45, // V = mac address 0x00, 0x00, // padding 0x0a, 0x00, // length = 10 0x04, 0x00, // type = 4 = IFLA_MACVLAN_MACADDR 0x22, 0xf5, 0x54, 0x09, 0x11, 0x45, // V = mac address 0x00, 0x00, // padding 0x08, 0x00, // length = 8 0x01, 0x00, // Type = 1 = IFLA_MACVLAN_MODE 0x10, 0x00, 0x00, 0x00, // V = 16 = source ]; lazy_static! { static ref MACVLAN_SOURCE_SET_INFO: Vec = vec![ InfoMacVlan::MacAddrMode(3), // set InfoMacVlan::MacAddrData(vec![ InfoMacVlan::MacAddr([0x22, 0xf5, 0x54, 0x09, 0x88, 0xd7,]), InfoMacVlan::MacAddr([0x22, 0xf5, 0x54, 0x09, 0x99, 0x32,]), InfoMacVlan::MacAddr([0x22, 0xf5, 0x54, 0x09, 0x87, 0x45,]), InfoMacVlan::MacAddr([0x22, 0xf5, 0x54, 0x09, 0x11, 0x45,]), ]), InfoMacVlan::Mode(16), // source ]; } #[test] fn parse_info_macvlan_source_set() { let nla = NlaBuffer::new_checked(&MACVLAN_SOURCE_SET[..]).unwrap(); let parsed = VecInfo::parse(&nla).unwrap().0; let expected = vec![ Info::Kind(InfoKind::MacVlan), Info::Data(InfoData::MacVlan(MACVLAN_SOURCE_SET_INFO.clone())), ]; assert_eq!(expected, parsed); } #[test] fn emit_info_macvlan_source_set() { let nlas = vec![ Info::Kind(InfoKind::MacVlan), Info::Data(InfoData::MacVlan(MACVLAN_SOURCE_SET_INFO.clone())), ]; assert_eq!(nlas.as_slice().buffer_len(), 84); let mut vec = vec![0xff; 84]; nlas.as_slice().emit(&mut vec); assert_eq!(&vec[..], &MACVLAN_SOURCE_SET[..]); } #[test] fn parse() { let nla = NlaBuffer::new_checked(&BRIDGE[..]).unwrap(); let parsed = VecInfo::parse(&nla).unwrap().0; assert_eq!(parsed.len(), 2); assert_eq!(parsed[0], Info::Kind(InfoKind::Bridge)); if let Info::Data(InfoData::Bridge(nlas)) = parsed[1].clone() { assert_eq!(nlas.len(), BRIDGE_INFO.len()); for (expected, parsed) in BRIDGE_INFO.iter().zip(nlas) { assert_eq!(*expected, parsed); } } else { panic!( "expected Info::Data(InfoData::Bridge(_) got {:?}", parsed[1] ) } } #[test] fn emit() { let nlas = vec![ Info::Kind(InfoKind::Bridge), Info::Data(InfoData::Bridge(BRIDGE_INFO.clone())), ]; assert_eq!(nlas.as_slice().buffer_len(), 424); let mut vec = vec![0xff; 424]; nlas.as_slice().emit(&mut vec); assert_eq!(&vec[..], &BRIDGE[..]); } #[rustfmt::skip] static VLAN: [u8; 68] = [ 0x09, 0x00, // length = 9 0x01, 0x00, // type = 1 = IFLA_INFO_KIND 0x76, 0x6c, 0x61, 0x6e, 0x00, // V = "vlan\0" 0x00, 0x00, 0x00, // padding 0x38, 0x00, // length = 56 0x02, 0x00, // type = 2 = IFLA_INFO_DATA 0x06, 0x00, // length - 6 0x01, 0x00, // type = 1 = IFLA_VLAN_ID 0x4b, 0x00, // id = 0x4b = 75 0x00, 0x00, // padding 0x10, 0x00, // length = 16 0x03, 0x00, // type = 3 = IFLA_VLAN_EGRESS_QOS_MAPPING 0x0c, 0x00, // length = 12 0x01, 0x00, // type = 1 = IFLA_VLAN_QOS_MAPPING 0x03, 0x00, 0x00, 0x00, // from = 3 0x04, 0x00, 0x00, 0x00, // to = 4 0x1c, 0x00, // length = 44 0x04, 0x00, // type = 4 = IFLA_VLAN_INGRESS_QOS_MAPPING 0x0c, 0x00, // length = 12 0x01, 0x00, // type = 1 = IFLA_VLAN_QOS_MAPPING 0x00, 0x00, 0x00, 0x00, // from = 0 0x01, 0x00, 0x00, 0x00, // to = 1 0x0c, 0x00, // length = 12 0x01, 0x00, // type = 1 = IFLA_VLAN_QOS_MAPPING 0x01, 0x00, 0x00, 0x00, // from = 1 0x02, 0x00, 0x00, 0x00, // to = 2 ]; lazy_static! { static ref VLAN_INFO: Vec = vec![ InfoVlan::Id(75), InfoVlan::EgressQos(vec![VlanQosMapping::Mapping { from: 3, to: 4 }]), InfoVlan::IngressQos(vec![ VlanQosMapping::Mapping { from: 0, to: 1 }, VlanQosMapping::Mapping { from: 1, to: 2 } ]), ]; } #[test] fn parse_info_vlan() { let nla = NlaBuffer::new_checked(&VLAN[..]).unwrap(); let parsed = VecInfo::parse(&nla).unwrap().0; let expected = vec![ Info::Kind(InfoKind::Vlan), Info::Data(InfoData::Vlan(VLAN_INFO.clone())), ]; assert_eq!(expected, parsed); } #[test] fn emit_info_vlan() { let nlas = vec![ Info::Kind(InfoKind::Vlan), Info::Data(InfoData::Vlan(VLAN_INFO.clone())), ]; assert_eq!(nlas.as_slice().buffer_len(), VLAN.len()); let mut vec = vec![0xff; VLAN.len()]; nlas.as_slice().emit(&mut vec); assert_eq!(&vec[..], &VLAN[..]); } } netlink-packet-route-0.17.0/src/rtnl/link/nlas/link_state.rs000064400000000000000000000030211046102023000221150ustar 00000000000000// SPDX-License-Identifier: MIT use crate::constants::*; #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] pub enum State { /// Status can't be determined Unknown, /// Some component is missing NotPresent, /// Down Down, /// Down due to state of lower layer LowerLayerDown, /// In some test mode Testing, /// Not up but pending an external event Dormant, /// Up, ready to send packets Up, /// Unrecognized value. This should go away when `TryFrom` is stable in /// Rust // FIXME: there's not point in having this. When TryFrom is stable we'll // remove it Other(u8), } impl From for State { fn from(value: u8) -> Self { use self::State::*; match value { IF_OPER_UNKNOWN => Unknown, IF_OPER_NOTPRESENT => NotPresent, IF_OPER_DOWN => Down, IF_OPER_LOWERLAYERDOWN => LowerLayerDown, IF_OPER_TESTING => Testing, IF_OPER_DORMANT => Dormant, IF_OPER_UP => Up, _ => Other(value), } } } impl From for u8 { fn from(value: State) -> Self { use self::State::*; match value { Unknown => IF_OPER_UNKNOWN, NotPresent => IF_OPER_NOTPRESENT, Down => IF_OPER_DOWN, LowerLayerDown => IF_OPER_LOWERLAYERDOWN, Testing => IF_OPER_TESTING, Dormant => IF_OPER_DORMANT, Up => IF_OPER_UP, Other(other) => other, } } } netlink-packet-route-0.17.0/src/rtnl/link/nlas/link_xdp.rs000064400000000000000000000273751046102023000216120ustar 00000000000000// SPDX-License-Identifier: MIT use std::{convert::TryFrom, mem::size_of, os::fd::RawFd}; use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, parsers::{parse_i32, parse_u32}, DecodeError, Parseable, }; use crate::constants::*; #[non_exhaustive] #[derive(Debug, PartialEq, Eq, Clone)] pub enum Xdp { Fd(RawFd), Attached(XdpAttached), Flags(u32), ProgId(u32), DrvProgId(u32), SkbProgId(u32), HwProgId(u32), ExpectedFd(u32), Other(DefaultNla), } impl Nla for Xdp { #[rustfmt::skip] fn value_len(&self) -> usize { use self::Xdp::*; match self { Fd(_) => size_of::(), Attached(_) => size_of::(), Flags(_) => size_of::(), ProgId(_) => size_of::(), DrvProgId(_) => size_of::(), SkbProgId(_) => size_of::(), HwProgId(_) => size_of::(), ExpectedFd(_) => size_of::(), Other(nla) => nla.value_len() } } #[rustfmt::skip] fn emit_value(&self, buffer: &mut [u8]) { use self::Xdp::*; match self { Fd(ref value) => NativeEndian::write_i32(buffer, *value), Attached(ref value) => buffer[0] = value.as_u8(), Flags(ref value) => NativeEndian::write_u32(buffer, *value), ProgId(ref value) => NativeEndian::write_u32(buffer, *value), DrvProgId(ref value) => NativeEndian::write_u32(buffer, *value), SkbProgId(ref value) => NativeEndian::write_u32(buffer, *value), HwProgId(ref value) => NativeEndian::write_u32(buffer, *value), ExpectedFd(ref value) => NativeEndian::write_u32(buffer, *value), Other(ref nla) => nla.emit_value(buffer) } } fn kind(&self) -> u16 { use self::Xdp::*; match self { Fd(_) => IFLA_XDP_FD as u16, Attached(_) => IFLA_XDP_ATTACHED as u16, Flags(_) => IFLA_XDP_FLAGS as u16, ProgId(_) => IFLA_XDP_PROG_ID as u16, DrvProgId(_) => IFLA_XDP_DRV_PROG_ID as u16, SkbProgId(_) => IFLA_XDP_SKB_PROG_ID as u16, HwProgId(_) => IFLA_XDP_HW_PROG_ID as u16, ExpectedFd(_) => IFLA_XDP_EXPECTED_FD as u16, Other(nla) => nla.kind(), } } } pub(crate) struct VecXdp(pub(crate) Vec); // These NLAs are nested, meaning they are NLAs that contain NLAs. These NLAs // can contain more nested NLAs nla->type // IFLA_XDP // nla->len // nla->data[] // <- You are here == Vec // nla->data[0].type <- nla.kind() // nla->data[0].len impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecXdp { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut res = Vec::new(); let nlas = NlasIterator::new(buf.into_inner()); for nla in nlas { let nla = nla?; match nla.kind() as u32 { IFLA_XDP_FD => res.push(Xdp::Fd( parse_i32(nla.value()) .context("invalid IFLA_XDP_FD value")?, )), IFLA_XDP_ATTACHED => res.push(Xdp::Attached( XdpAttached::try_from(nla.value()[0]) .context("invalid IFLA_XDP_ATTACHED value")?, )), IFLA_XDP_FLAGS => res.push(Xdp::Flags( parse_u32(nla.value()) .context("invalid IFLA_XDP_FLAGS value")?, )), IFLA_XDP_PROG_ID => res.push(Xdp::ProgId( parse_u32(nla.value()) .context("invalid IFLA_XDP_PROG_ID value")?, )), IFLA_XDP_DRV_PROG_ID => res.push(Xdp::DrvProgId( parse_u32(nla.value()) .context("invalid IFLA_XDP_PROG_ID value")?, )), IFLA_XDP_SKB_PROG_ID => res.push(Xdp::SkbProgId( parse_u32(nla.value()) .context("invalid IFLA_XDP_PROG_ID value")?, )), IFLA_XDP_HW_PROG_ID => res.push(Xdp::HwProgId( parse_u32(nla.value()) .context("invalid IFLA_XDP_PROG_ID value")?, )), IFLA_XDP_EXPECTED_FD => res.push(Xdp::ExpectedFd( parse_u32(nla.value()) .context("invalid IFLA_XDP_PROG_ID value")?, )), _ => res .push(Xdp::Other(DefaultNla::parse(&nla).context( format!("unknown NLA type {}", nla.kind()), )?)), } } Ok(VecXdp(res)) } } #[non_exhaustive] #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum XdpAttached { /// XDP_ATTACHED_NONE None, /// XDP_ATTACHED_DRV Driver, /// XDP_ATTACHED_SKB SocketBuffer, /// XDP_ATTACHED_HW Hardware, /// XDP_ATTACHED_MULTI Multiple, /// This crate is unaware of the attachment type the kernel is reporting Other(u8), } impl TryFrom for XdpAttached { type Error = DecodeError; fn try_from(value: u8) -> Result { match value { XDP_ATTACHED_NONE => Ok(XdpAttached::None), XDP_ATTACHED_DRV => Ok(XdpAttached::Driver), XDP_ATTACHED_SKB => Ok(XdpAttached::SocketBuffer), XDP_ATTACHED_HW => Ok(XdpAttached::Hardware), XDP_ATTACHED_MULTI => Ok(XdpAttached::Multiple), _ => Ok(XdpAttached::Other(value)), } } } impl XdpAttached { fn as_u8(&self) -> u8 { match self { XdpAttached::None => XDP_ATTACHED_NONE, XdpAttached::Driver => XDP_ATTACHED_DRV, XdpAttached::SocketBuffer => XDP_ATTACHED_SKB, XdpAttached::Hardware => XDP_ATTACHED_HW, XdpAttached::Multiple => XDP_ATTACHED_MULTI, XdpAttached::Other(other) => *other, } } } #[cfg(test)] mod tests { use netlink_packet_utils::Emitable; use super::*; #[rustfmt::skip] static ATTACHED: [u8; 48] = [ 0x05, 0x00, // length = 5 0x02, 0x00, // type = 2 = IFLA_XDP_ATTACHED 0x00, 0x00, // none = XDP_ATTACHED_NONE 0x00, 0x00, // padding 0x05, 0x00, // length = 5 0x02, 0x00, // type = 2 = IFLA_XDP_ATTACHED 0x01, 0x00, // driver = XDP_ATTACHED_DRV 0x00, 0x00, // padding 0x05, 0x00, // length = 5 0x02, 0x00, // type = 2 = IFLA_XDP_ATTACHED 0x02, 0x00, // skb = XDP_ATTACHED_SKB 0x00, 0x00, // padding 0x05, 0x00, // length = 5 0x02, 0x00, // type = 2 = IFLA_XDP_ATTACHED 0x03, 0x00, // hw = XDP_ATTACHED_HW 0x00, 0x00, // padding 0x05, 0x00, // length = 5 0x02, 0x00, // type = 2 = IFLA_XDP_ATTACHED 0x04, 0x00, // multi = XDP_ATTACHED_MULTI 0x00, 0x00, // padding 0x05, 0x00, // length = 5 0x02, 0x00, // type = 2 = IFLA_XDP_ATTACHED 0xfc, 0x00, // other = random number = 252 0x00, 0x00, // padding ]; #[test] fn parse_xdp_attached() { let nla = NlaBuffer::new_checked(&ATTACHED[..]).unwrap(); let parsed = VecXdp::parse(&nla).unwrap().0; let expected = vec![ Xdp::Attached(XdpAttached::None), Xdp::Attached(XdpAttached::Driver), Xdp::Attached(XdpAttached::SocketBuffer), Xdp::Attached(XdpAttached::Hardware), Xdp::Attached(XdpAttached::Multiple), Xdp::Attached(XdpAttached::Other(252)), ]; assert_eq!(expected, parsed); } #[test] fn emit_xdp_attached() { // None let nlas = vec![Xdp::Attached(XdpAttached::None)]; assert_eq!(nlas.as_slice().buffer_len(), 8); let mut vec = vec![0xff; 8]; nlas.as_slice().emit(&mut vec); assert_eq!(&vec[..], &ATTACHED[..8]); // Driver let nlas = vec![Xdp::Attached(XdpAttached::Driver)]; assert_eq!(nlas.as_slice().buffer_len(), 8); let mut vec = vec![0xff; 8]; nlas.as_slice().emit(&mut vec); assert_eq!(&vec[..], &ATTACHED[8..16]); // SocketBuffer/skb let nlas = vec![Xdp::Attached(XdpAttached::SocketBuffer)]; assert_eq!(nlas.as_slice().buffer_len(), 8); let mut vec = vec![0xff; 8]; nlas.as_slice().emit(&mut vec); assert_eq!(&vec[..], &ATTACHED[16..24]); // Hardware let nlas = vec![Xdp::Attached(XdpAttached::Hardware)]; assert_eq!(nlas.as_slice().buffer_len(), 8); let mut vec = vec![0xff; 8]; nlas.as_slice().emit(&mut vec); assert_eq!(&vec[..], &ATTACHED[24..32]); // Multiple let nlas = vec![Xdp::Attached(XdpAttached::Multiple)]; assert_eq!(nlas.as_slice().buffer_len(), 8); let mut vec = vec![0xff; 8]; nlas.as_slice().emit(&mut vec); assert_eq!(&vec[..], &ATTACHED[32..40]); // Multiple let nlas = vec![Xdp::Attached(XdpAttached::Other(252))]; assert_eq!(nlas.as_slice().buffer_len(), 8); let mut vec = vec![0xff; 8]; nlas.as_slice().emit(&mut vec); assert_eq!(&vec[..], &ATTACHED[40..48]); } #[rustfmt::skip] static XDP: [u8; 72] = [ 0x08, 0x00, // length = 8 0x01, 0x00, // type = 1 = IFLA_XDP_FD 0xA0, 0x74, 0x00, 0x00, // 29856 0x08, 0x00, // length = 8 0x03, 0x00, // type = 3 = IFLA_XDP_FLAGS 0x00, 0x00, 0x00, 0x00, // empty 0x08, 0x00, // length = 8 0x04, 0x00, // type = 4 = IFLA_XDP_PROG_ID 0x67, 0x00, 0x00, 0x00, // 103 0x08, 0x00, // length = 8 0x05, 0x00, // type = 5 = IFLA_XDP_DRV_PROG_ID 0x65, 0x00, 0x00, 0x00, // 101 0x08, 0x00, // length = 8 0x06, 0x00, // type = 6 = IFLA_XDP_DRV_SKB_ID 0x65, 0x00, 0x00, 0x00, // 101 0x08, 0x00, // length = 8 0x07, 0x00, // type = 7 = IFLA_XDP_DRV_HW_ID 0x65, 0x00, 0x00, 0x00, // 101 0x08, 0x00, // length = 8 0x08, 0x00, // type = 8 = IFLA_XDP_DRV_EXPECTED_FD 0xA1, 0x74, 0x00, 0x00, // 29857 0x08, 0x00, // length = 8 0xfc, 0x00, // type = 252 = random number/unknown type 0xA1, 0x74, 0x00, 0x00, // 29857 0x06, 0x00, // length = 6 0xfb, 0x00, // type = 251 = random number/unknown type 0xaa, 0xab, // 29857 0x00, 0x00, // padding ]; #[test] fn parse_xdp() { let nla = NlaBuffer::new_checked(&XDP[..]).unwrap(); let parsed = VecXdp::parse(&nla).unwrap().0; let expected = vec![ Xdp::Fd(29856), Xdp::Flags(0), Xdp::ProgId(103), Xdp::DrvProgId(101), Xdp::SkbProgId(101), Xdp::HwProgId(101), Xdp::ExpectedFd(29857), Xdp::Other( DefaultNla::parse(&NlaBuffer::new(&XDP[56..64])).unwrap(), ), Xdp::Other(DefaultNla::parse(&NlaBuffer::new(&XDP[64..])).unwrap()), ]; assert_eq!(expected, parsed); } #[test] fn emit_xdp() { let nlas = vec![ Xdp::Fd(29856), Xdp::Flags(0), Xdp::ProgId(103), Xdp::DrvProgId(101), Xdp::SkbProgId(101), Xdp::HwProgId(101), Xdp::ExpectedFd(29857), Xdp::Other( DefaultNla::parse(&NlaBuffer::new(&XDP[56..64])).unwrap(), ), Xdp::Other(DefaultNla::parse(&NlaBuffer::new(&XDP[64..])).unwrap()), ]; assert_eq!(nlas.as_slice().buffer_len(), XDP.len()); let mut vec = vec![0xff; XDP.len()]; nlas.as_slice().emit(&mut vec); assert_eq!(&vec[..], &XDP[..]); } } netlink-packet-route-0.17.0/src/rtnl/link/nlas/map.rs000064400000000000000000000025461046102023000205500ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; pub const LINK_MAP_LEN: usize = 28; buffer!(MapBuffer(LINK_MAP_LEN) { memory_start: (u64, 0..8), memory_end: (u64, 8..16), base_address: (u64, 16..24), irq: (u16, 24..26), dma: (u8, 26), port: (u8, 27), }); #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] pub struct Map { pub memory_start: u64, pub memory_end: u64, pub base_address: u64, pub irq: u16, pub dma: u8, pub port: u8, } impl> Parseable> for Map { fn parse(buf: &MapBuffer) -> Result { Ok(Self { memory_start: buf.memory_start(), memory_end: buf.memory_end(), base_address: buf.base_address(), irq: buf.irq(), dma: buf.dma(), port: buf.port(), }) } } impl Emitable for Map { fn buffer_len(&self) -> usize { LINK_MAP_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = MapBuffer::new(buffer); buffer.set_memory_start(self.memory_start); buffer.set_memory_end(self.memory_end); buffer.set_base_address(self.base_address); buffer.set_irq(self.irq); buffer.set_dma(self.dma); buffer.set_port(self.port); } } netlink-packet-route-0.17.0/src/rtnl/link/nlas/mod.rs000064400000000000000000000513721046102023000205530ustar 00000000000000// SPDX-License-Identifier: MIT mod inet; pub use self::inet::*; mod inet6; pub use self::inet6::*; mod af_spec_inet; pub use self::af_spec_inet::*; mod af_spec_bridge; pub use self::af_spec_bridge::*; mod link_infos; pub use self::link_infos::*; mod bond; pub use self::bond::*; mod bond_port; pub use self::bond_port::*; mod bridge; pub use self::bridge::*; mod prop_list; pub use self::prop_list::*; mod map; pub use self::map::*; mod stats; pub use self::stats::*; mod stats64; pub use self::stats64::*; mod link_state; pub use self::link_state::*; mod link_xdp; pub use self::link_xdp::*; #[cfg(test)] mod tests; use std::os::unix::io::RawFd; use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{self, DefaultNla, NlaBuffer, NlasIterator, NLA_F_NESTED}, parsers::{parse_i32, parse_string, parse_u32, parse_u8}, traits::{Emitable, Parseable, ParseableParametrized}, DecodeError, }; use crate::constants::*; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum Nla { // Vec Unspec(Vec), Cost(Vec), Priority(Vec), Weight(Vec), VfInfoList(Vec), VfPorts(Vec), PortSelf(Vec), PhysPortId(Vec), PhysSwitchId(Vec), Pad(Vec), Xdp(Vec), Event(Vec), NewNetnsId(Vec), IfNetnsId(Vec), CarrierUpCount(Vec), CarrierDownCount(Vec), NewIfIndex(Vec), Info(Vec), Wireless(Vec), ProtoInfo(Vec), /// A list of properties for the device. For additional context see the /// related linux kernel threads[1][1],[2][2]. In particular /// see [this message][defining message] from the first thread /// describing the design. /// /// [1]: https://lwn.net/ml/netdev/20190719110029.29466-1-jiri@resnulli.us/ /// [2]: https://lwn.net/ml/netdev/20190930094820.11281-1-jiri@resnulli.us/ /// [defining message]: https://lwn.net/ml/netdev/20190913145012.GB2276@nanopsycho.orion/ PropList(Vec), /// `protodown` is a mechanism that allows protocols to hold an interface /// down. This field is used to specify the reason why it is held down. /// For additional context see the related linux kernel /// threads[1][1],[2][2]. /// /// [1]: https://lwn.net/ml/netdev/1595877677-45849-1-git-send-email-roopa%40cumulusnetworks.com/ /// [2]: https://lwn.net/ml/netdev/1596242041-14347-1-git-send-email-roopa%40cumulusnetworks.com/ ProtoDownReason(Vec), // mac address (use to be [u8; 6] but it turns out MAC != HW address, for // instance for IP over GRE where it's an IPv4!) Address(Vec), Broadcast(Vec), /// Permanent hardware address of the device. The provides the same /// information as the ethtool ioctl interface. PermAddress(Vec), // string // FIXME: for empty string, should we encode the NLA as \0 or should we // not set a payload? It seems that for certain attriutes, this // matter: https://elixir.bootlin.com/linux/v4.17-rc5/source/net/core/rtnetlink.c#L1660 IfName(String), Qdisc(String), IfAlias(String), PhysPortName(String), /// Alternate name for the device. /// For additional context see the related linux kernel /// threads[1][1],[2][2]. /// /// [1]: https://lwn.net/ml/netdev/20190719110029.29466-1-jiri@resnulli.us/ /// [2]: https://lwn.net/ml/netdev/20190930094820.11281-1-jiri@resnulli.us/ AltIfName(String), // byte Mode(u8), Carrier(u8), ProtoDown(u8), // u32 Mtu(u32), Link(u32), Master(u32), TxQueueLen(u32), NetNsPid(u32), NumVf(u32), Group(u32), NetNsFd(RawFd), ExtMask(u32), Promiscuity(u32), NumTxQueues(u32), NumRxQueues(u32), CarrierChanges(u32), GsoMaxSegs(u32), GsoMaxSize(u32), /// The minimum MTU for the device. /// For additional context see the related [linux kernel message][1]. /// /// [1]: https://lwn.net/ml/netdev/20180727204323.19408-3-sthemmin%40microsoft.com/ MinMtu(u32), /// The maximum MTU for the device. /// For additional context see the related [linux kernel message][1]. /// /// [1]: https://lwn.net/ml/netdev/20180727204323.19408-3-sthemmin%40microsoft.com/ MaxMtu(u32), // i32 NetnsId(i32), // custom OperState(State), Stats(Vec), Stats64(Vec), Map(Vec), // AF_SPEC (the type of af_spec depends on the interface family of the // message) AfSpecInet(Vec), AfSpecBridge(Vec), //AfSpecBridge(Vec), AfSpecUnknown(Vec), Other(DefaultNla), } impl nla::Nla for Nla { #[rustfmt::skip] fn value_len(&self) -> usize { use self::Nla::*; match *self { // Vec Unspec(ref bytes) | Cost(ref bytes) | Priority(ref bytes) | Weight(ref bytes) | VfInfoList(ref bytes) | VfPorts(ref bytes) | PortSelf(ref bytes) | PhysPortId(ref bytes) | PhysSwitchId(ref bytes) | Pad(ref bytes) | Event(ref bytes) | NewNetnsId(ref bytes) | IfNetnsId(ref bytes) | Wireless(ref bytes) | ProtoInfo(ref bytes) | CarrierUpCount(ref bytes) | CarrierDownCount(ref bytes) | NewIfIndex(ref bytes) | Address(ref bytes) | Broadcast(ref bytes) | PermAddress(ref bytes) | AfSpecUnknown(ref bytes) | Map(ref bytes) | ProtoDownReason(ref bytes) => bytes.len(), // strings: +1 because we need to append a nul byte IfName(ref string) | Qdisc(ref string) | IfAlias(ref string) | PhysPortName(ref string) | AltIfName(ref string) => string.as_bytes().len() + 1, // u8 Mode(_) | Carrier(_) | ProtoDown(_) => 1, // u32 and i32 Mtu(_) | Link(_) | Master(_) | TxQueueLen(_) | NetNsPid(_) | NumVf(_) | Group(_) | NetNsFd(_) | ExtMask(_) | Promiscuity(_) | NumTxQueues(_) | NumRxQueues(_) | CarrierChanges(_) | GsoMaxSegs(_) | GsoMaxSize(_) | NetnsId(_) | MinMtu(_) | MaxMtu(_) => 4, // Defaults OperState(_) => 1, Stats(_) => LINK_STATS_LEN, Stats64(_) => LINK_STATS64_LEN, Info(ref nlas) => nlas.as_slice().buffer_len(), Xdp(ref nlas) => nlas.as_slice().buffer_len(), PropList(ref nlas) => nlas.as_slice().buffer_len(), AfSpecInet(ref nlas) => nlas.as_slice().buffer_len(), AfSpecBridge(ref nlas) => nlas.as_slice().buffer_len(), Other(ref attr) => attr.value_len(), } } #[rustfmt::skip] fn emit_value(&self, buffer: &mut [u8]) { use self::Nla::*; match *self { // Vec Unspec(ref bytes) | Cost(ref bytes) | Priority(ref bytes) | Weight(ref bytes) | VfInfoList(ref bytes) | VfPorts(ref bytes) | PortSelf(ref bytes) | PhysPortId(ref bytes) | PhysSwitchId(ref bytes) | Wireless(ref bytes) | ProtoInfo(ref bytes) | Pad(ref bytes) | Event(ref bytes) | NewNetnsId(ref bytes) | IfNetnsId(ref bytes) | CarrierUpCount(ref bytes) | CarrierDownCount(ref bytes) | NewIfIndex(ref bytes) // mac address (could be [u8; 6] or [u8; 4] for example. Not sure if we should have // a separate type for them | Address(ref bytes) | Broadcast(ref bytes) | PermAddress(ref bytes) | AfSpecUnknown(ref bytes) | Stats(ref bytes) | Stats64(ref bytes) | Map(ref bytes) | ProtoDownReason(ref bytes) => buffer.copy_from_slice(bytes.as_slice()), // String IfName(ref string) | Qdisc(ref string) | IfAlias(ref string) | PhysPortName(ref string) | AltIfName(ref string) => { buffer[..string.len()].copy_from_slice(string.as_bytes()); buffer[string.len()] = 0; } // u8 Mode(ref val) | Carrier(ref val) | ProtoDown(ref val) => buffer[0] = *val, // u32 Mtu(ref value) | Link(ref value) | Master(ref value) | TxQueueLen(ref value) | NetNsPid(ref value) | NumVf(ref value) | Group(ref value) | ExtMask(ref value) | Promiscuity(ref value) | NumTxQueues(ref value) | NumRxQueues(ref value) | CarrierChanges(ref value) | GsoMaxSegs(ref value) | GsoMaxSize(ref value) | MinMtu(ref value) | MaxMtu(ref value) => NativeEndian::write_u32(buffer, *value), NetnsId(ref value) | NetNsFd(ref value) => NativeEndian::write_i32(buffer, *value), OperState(state) => buffer[0] = state.into(), Info(ref nlas) => nlas.as_slice().emit(buffer), Xdp(ref nlas) => nlas.as_slice().emit(buffer), PropList(ref nlas) => nlas.as_slice().emit(buffer), AfSpecInet(ref nlas) => nlas.as_slice().emit(buffer), AfSpecBridge(ref nlas) => nlas.as_slice().emit(buffer), // default nlas Other(ref attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { use self::Nla::*; match *self { // Vec Unspec(_) => IFLA_UNSPEC, Cost(_) => IFLA_COST, Priority(_) => IFLA_PRIORITY, Weight(_) => IFLA_WEIGHT, VfInfoList(_) => IFLA_VFINFO_LIST, VfPorts(_) => IFLA_VF_PORTS, PortSelf(_) => IFLA_PORT_SELF, PhysPortId(_) => IFLA_PHYS_PORT_ID, PhysSwitchId(_) => IFLA_PHYS_SWITCH_ID, Info(_) => IFLA_LINKINFO, Wireless(_) => IFLA_WIRELESS, ProtoInfo(_) => IFLA_PROTINFO, Pad(_) => IFLA_PAD, Xdp(_) => IFLA_XDP, Event(_) => IFLA_EVENT, NewNetnsId(_) => IFLA_NEW_NETNSID, IfNetnsId(_) => IFLA_IF_NETNSID, CarrierUpCount(_) => IFLA_CARRIER_UP_COUNT, CarrierDownCount(_) => IFLA_CARRIER_DOWN_COUNT, NewIfIndex(_) => IFLA_NEW_IFINDEX, PropList(_) => IFLA_PROP_LIST | NLA_F_NESTED, ProtoDownReason(_) => IFLA_PROTO_DOWN_REASON, // Mac address Address(_) => IFLA_ADDRESS, Broadcast(_) => IFLA_BROADCAST, PermAddress(_) => IFLA_PERM_ADDRESS, // String IfName(_) => IFLA_IFNAME, Qdisc(_) => IFLA_QDISC, IfAlias(_) => IFLA_IFALIAS, PhysPortName(_) => IFLA_PHYS_PORT_NAME, AltIfName(_) => IFLA_ALT_IFNAME, // u8 Mode(_) => IFLA_LINKMODE, Carrier(_) => IFLA_CARRIER, ProtoDown(_) => IFLA_PROTO_DOWN, // u32 Mtu(_) => IFLA_MTU, Link(_) => IFLA_LINK, Master(_) => IFLA_MASTER, TxQueueLen(_) => IFLA_TXQLEN, NetNsPid(_) => IFLA_NET_NS_PID, NumVf(_) => IFLA_NUM_VF, Group(_) => IFLA_GROUP, NetNsFd(_) => IFLA_NET_NS_FD, ExtMask(_) => IFLA_EXT_MASK, Promiscuity(_) => IFLA_PROMISCUITY, NumTxQueues(_) => IFLA_NUM_TX_QUEUES, NumRxQueues(_) => IFLA_NUM_RX_QUEUES, CarrierChanges(_) => IFLA_CARRIER_CHANGES, GsoMaxSegs(_) => IFLA_GSO_MAX_SEGS, GsoMaxSize(_) => IFLA_GSO_MAX_SIZE, MinMtu(_) => IFLA_MIN_MTU, MaxMtu(_) => IFLA_MAX_MTU, // i32 NetnsId(_) => IFLA_LINK_NETNSID, // custom OperState(_) => IFLA_OPERSTATE, Map(_) => IFLA_MAP, Stats(_) => IFLA_STATS, Stats64(_) => IFLA_STATS64, AfSpecInet(_) | AfSpecBridge(_) | AfSpecUnknown(_) => IFLA_AF_SPEC, Other(ref attr) => attr.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> ParseableParametrized, u16> for Nla { fn parse_with_param( buf: &NlaBuffer<&'a T>, interface_family: u16, ) -> Result { use Nla::*; let payload = buf.value(); Ok(match buf.kind() { // Vec IFLA_UNSPEC => Unspec(payload.to_vec()), IFLA_COST => Cost(payload.to_vec()), IFLA_PRIORITY => Priority(payload.to_vec()), IFLA_WEIGHT => Weight(payload.to_vec()), IFLA_VFINFO_LIST => VfInfoList(payload.to_vec()), IFLA_VF_PORTS => VfPorts(payload.to_vec()), IFLA_PORT_SELF => PortSelf(payload.to_vec()), IFLA_PHYS_PORT_ID => PhysPortId(payload.to_vec()), IFLA_PHYS_SWITCH_ID => PhysSwitchId(payload.to_vec()), IFLA_WIRELESS => Wireless(payload.to_vec()), IFLA_PROTINFO => ProtoInfo(payload.to_vec()), IFLA_PAD => Pad(payload.to_vec()), IFLA_EVENT => Event(payload.to_vec()), IFLA_NEW_NETNSID => NewNetnsId(payload.to_vec()), IFLA_IF_NETNSID => IfNetnsId(payload.to_vec()), IFLA_CARRIER_UP_COUNT => CarrierUpCount(payload.to_vec()), IFLA_CARRIER_DOWN_COUNT => CarrierDownCount(payload.to_vec()), IFLA_NEW_IFINDEX => NewIfIndex(payload.to_vec()), IFLA_PROP_LIST => { let error_msg = "invalid IFLA_PROP_LIST value"; let mut nlas = vec![]; for nla in NlasIterator::new(payload) { let nla = &nla.context(error_msg)?; let parsed = Prop::parse(nla).context(error_msg)?; nlas.push(parsed); } PropList(nlas) } IFLA_PROTO_DOWN_REASON => ProtoDownReason(payload.to_vec()), // HW address (we parse them as Vec for now, because for IP over // GRE, the HW address is an IP instead of a MAC for // example IFLA_ADDRESS => Address(payload.to_vec()), IFLA_BROADCAST => Broadcast(payload.to_vec()), IFLA_PERM_ADDRESS => PermAddress(payload.to_vec()), // String IFLA_IFNAME => IfName( parse_string(payload).context("invalid IFLA_IFNAME value")?, ), IFLA_QDISC => Qdisc( parse_string(payload).context("invalid IFLA_QDISC value")?, ), IFLA_IFALIAS => IfAlias( parse_string(payload).context("invalid IFLA_IFALIAS value")?, ), IFLA_PHYS_PORT_NAME => PhysPortName( parse_string(payload) .context("invalid IFLA_PHYS_PORT_NAME value")?, ), IFLA_ALT_IFNAME => AltIfName( parse_string(payload) .context("invalid IFLA_ALT_IFNAME value")?, ), // u8 IFLA_LINKMODE => { Mode(parse_u8(payload).context("invalid IFLA_LINKMODE value")?) } IFLA_CARRIER => Carrier( parse_u8(payload).context("invalid IFLA_CARRIER value")?, ), IFLA_PROTO_DOWN => ProtoDown( parse_u8(payload).context("invalid IFLA_PROTO_DOWN value")?, ), IFLA_MTU => { Mtu(parse_u32(payload).context("invalid IFLA_MTU value")?) } IFLA_LINK => { Link(parse_u32(payload).context("invalid IFLA_LINK value")?) } IFLA_MASTER => { Master(parse_u32(payload).context("invalid IFLA_MASTER value")?) } IFLA_TXQLEN => TxQueueLen( parse_u32(payload).context("invalid IFLA_TXQLEN value")?, ), IFLA_NET_NS_PID => NetNsPid( parse_u32(payload).context("invalid IFLA_NET_NS_PID value")?, ), IFLA_NUM_VF => { NumVf(parse_u32(payload).context("invalid IFLA_NUM_VF value")?) } IFLA_GROUP => { Group(parse_u32(payload).context("invalid IFLA_GROUP value")?) } IFLA_NET_NS_FD => NetNsFd( parse_i32(payload).context("invalid IFLA_NET_NS_FD value")?, ), IFLA_EXT_MASK => ExtMask( parse_u32(payload).context("invalid IFLA_EXT_MASK value")?, ), IFLA_PROMISCUITY => Promiscuity( parse_u32(payload).context("invalid IFLA_PROMISCUITY value")?, ), IFLA_NUM_TX_QUEUES => NumTxQueues( parse_u32(payload) .context("invalid IFLA_NUM_TX_QUEUES value")?, ), IFLA_NUM_RX_QUEUES => NumRxQueues( parse_u32(payload) .context("invalid IFLA_NUM_RX_QUEUES value")?, ), IFLA_CARRIER_CHANGES => CarrierChanges( parse_u32(payload) .context("invalid IFLA_CARRIER_CHANGES value")?, ), IFLA_GSO_MAX_SEGS => GsoMaxSegs( parse_u32(payload) .context("invalid IFLA_GSO_MAX_SEGS value")?, ), IFLA_GSO_MAX_SIZE => GsoMaxSize( parse_u32(payload) .context("invalid IFLA_GSO_MAX_SIZE value")?, ), IFLA_MIN_MTU => MinMtu( parse_u32(payload).context("invalid IFLA_MIN_MTU value")?, ), IFLA_MAX_MTU => MaxMtu( parse_u32(payload).context("invalid IFLA_MAX_MTU value")?, ), IFLA_LINK_NETNSID => NetnsId( parse_i32(payload) .context("invalid IFLA_LINK_NETNSID value")?, ), IFLA_OPERSTATE => OperState( parse_u8(payload) .context("invalid IFLA_OPERSTATE value")? .into(), ), IFLA_MAP => Map(payload.to_vec()), IFLA_STATS => Stats(payload.to_vec()), IFLA_STATS64 => Stats64(payload.to_vec()), IFLA_AF_SPEC => match interface_family { AF_INET | AF_INET6 | AF_UNSPEC => { let mut nlas = vec![]; let err = "invalid IFLA_AF_SPEC value"; for nla in NlasIterator::new(payload) { let nla = nla.context(err)?; nlas.push( af_spec_inet::AfSpecInet::parse(&nla) .context(err)?, ); } AfSpecInet(nlas) } AF_BRIDGE => { let mut nlas = vec![]; let err = "invalid IFLA_AF_SPEC value for AF_BRIDGE"; for nla in NlasIterator::new(payload) { let nla = nla.context(err)?; nlas.push( af_spec_bridge::AfSpecBridge::parse(&nla) .context(err)?, ); } AfSpecBridge(nlas) } _ => AfSpecUnknown(payload.to_vec()), }, IFLA_LINKINFO => { let err = "invalid IFLA_LINKINFO value"; let buf = NlaBuffer::new_checked(payload).context(err)?; Info(VecInfo::parse(&buf).context(err)?.0) } IFLA_XDP => { let err = "invalid IFLA_XDP value"; let buf = NlaBuffer::new_checked(payload).context(err)?; Xdp(VecXdp::parse(&buf).context(err)?.0) } kind => Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } netlink-packet-route-0.17.0/src/rtnl/link/nlas/prop_list.rs000064400000000000000000000031771046102023000220070ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::parse_string, traits::Parseable, DecodeError, }; use crate::constants::*; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum Prop { AltIfName(String), Other(DefaultNla), } impl Nla for Prop { #[rustfmt::skip] fn value_len(&self) -> usize { use self::Prop::*; match self { AltIfName(ref string) => string.as_bytes().len() + 1, Other(nla) => nla.value_len() } } #[rustfmt::skip] fn emit_value(&self, buffer: &mut [u8]) { use self::Prop::*; match self { AltIfName(ref string) => { buffer[..string.len()].copy_from_slice(string.as_bytes()); buffer[string.len()] = 0; }, Other(nla) => nla.emit_value(buffer) } } fn kind(&self) -> u16 { use self::Prop::*; match self { AltIfName(_) => IFLA_ALT_IFNAME, Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Prop { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { IFLA_ALT_IFNAME => Prop::AltIfName( parse_string(payload) .context("invalid IFLA_ALT_IFNAME value")?, ), kind => Prop::Other( DefaultNla::parse(buf) .context(format!("Unknown NLA type {kind}"))?, ), }) } } netlink-packet-route-0.17.0/src/rtnl/link/nlas/stats.rs000064400000000000000000000117401046102023000211250ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] pub struct Stats { /// total packets received pub rx_packets: u32, /// total packets transmitted pub tx_packets: u32, /// total bytes received pub rx_bytes: u32, /// total bytes transmitted pub tx_bytes: u32, /// bad packets received pub rx_errors: u32, /// packet transmit problems pub tx_errors: u32, /// no space in linux buffers pub rx_dropped: u32, /// no space available in linux pub tx_dropped: u32, /// multicast packets received pub multicast: u32, pub collisions: u32, // detailed rx_errors pub rx_length_errors: u32, /// receiver ring buff overflow pub rx_over_errors: u32, /// received packets with crc error pub rx_crc_errors: u32, /// received frame alignment errors pub rx_frame_errors: u32, /// recv'r fifo overrun pub rx_fifo_errors: u32, /// receiver missed packet pub rx_missed_errors: u32, // detailed tx_errors pub tx_aborted_errors: u32, pub tx_carrier_errors: u32, pub tx_fifo_errors: u32, pub tx_heartbeat_errors: u32, pub tx_window_errors: u32, // for cslip etc pub rx_compressed: u32, pub tx_compressed: u32, /// dropped, no handler found pub rx_nohandler: u32, } pub const LINK_STATS_LEN: usize = 96; buffer!(StatsBuffer(LINK_STATS_LEN) { rx_packets: (u32, 0..4), tx_packets: (u32, 4..8), rx_bytes: (u32, 8..12), tx_bytes: (u32, 12..16), rx_errors: (u32, 16..20), tx_errors: (u32, 20..24), rx_dropped: (u32, 24..28), tx_dropped: (u32, 28..32), multicast: (u32, 32..36), collisions: (u32, 36..40), rx_length_errors: (u32, 40..44), rx_over_errors: (u32, 44..48), rx_crc_errors: (u32, 48..52), rx_frame_errors: (u32, 52..56), rx_fifo_errors: (u32, 56..60), rx_missed_errors: (u32, 60..64), tx_aborted_errors: (u32, 64..68), tx_carrier_errors: (u32, 68..72), tx_fifo_errors: (u32, 72..76), tx_heartbeat_errors: (u32, 76..80), tx_window_errors: (u32, 80..84), rx_compressed: (u32, 84..88), tx_compressed: (u32, 88..92), rx_nohandler: (u32, 92..96), }); impl> Parseable> for Stats { fn parse(buf: &StatsBuffer) -> Result { Ok(Self { rx_packets: buf.rx_packets(), tx_packets: buf.tx_packets(), rx_bytes: buf.rx_bytes(), tx_bytes: buf.tx_bytes(), rx_errors: buf.rx_errors(), tx_errors: buf.tx_errors(), rx_dropped: buf.rx_dropped(), tx_dropped: buf.tx_dropped(), multicast: buf.multicast(), collisions: buf.collisions(), rx_length_errors: buf.rx_length_errors(), rx_over_errors: buf.rx_over_errors(), rx_crc_errors: buf.rx_crc_errors(), rx_frame_errors: buf.rx_frame_errors(), rx_fifo_errors: buf.rx_fifo_errors(), rx_missed_errors: buf.rx_missed_errors(), tx_aborted_errors: buf.tx_aborted_errors(), tx_carrier_errors: buf.tx_carrier_errors(), tx_fifo_errors: buf.tx_fifo_errors(), tx_heartbeat_errors: buf.tx_heartbeat_errors(), tx_window_errors: buf.tx_window_errors(), rx_compressed: buf.rx_compressed(), tx_compressed: buf.tx_compressed(), rx_nohandler: buf.rx_nohandler(), }) } } impl Emitable for Stats { fn buffer_len(&self) -> usize { LINK_STATS_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = StatsBuffer::new(buffer); buffer.set_rx_packets(self.rx_packets); buffer.set_tx_packets(self.tx_packets); buffer.set_rx_bytes(self.rx_bytes); buffer.set_tx_bytes(self.tx_bytes); buffer.set_rx_errors(self.rx_errors); buffer.set_tx_errors(self.tx_errors); buffer.set_rx_dropped(self.rx_dropped); buffer.set_tx_dropped(self.tx_dropped); buffer.set_multicast(self.multicast); buffer.set_collisions(self.collisions); buffer.set_rx_length_errors(self.rx_length_errors); buffer.set_rx_over_errors(self.rx_over_errors); buffer.set_rx_crc_errors(self.rx_crc_errors); buffer.set_rx_frame_errors(self.rx_frame_errors); buffer.set_rx_fifo_errors(self.rx_fifo_errors); buffer.set_rx_missed_errors(self.rx_missed_errors); buffer.set_tx_aborted_errors(self.tx_aborted_errors); buffer.set_tx_carrier_errors(self.tx_carrier_errors); buffer.set_tx_fifo_errors(self.tx_fifo_errors); buffer.set_tx_heartbeat_errors(self.tx_heartbeat_errors); buffer.set_tx_window_errors(self.tx_window_errors); buffer.set_rx_compressed(self.rx_compressed); buffer.set_tx_compressed(self.tx_compressed); buffer.set_rx_nohandler(self.rx_nohandler); } } netlink-packet-route-0.17.0/src/rtnl/link/nlas/stats64.rs000064400000000000000000000120151046102023000212730ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; pub const LINK_STATS64_LEN: usize = 192; buffer!(Stats64Buffer(LINK_STATS64_LEN) { rx_packets: (u64, 0..8), tx_packets: (u64, 8..16), rx_bytes: (u64, 16..24), tx_bytes: (u64, 24..32), rx_errors: (u64, 32..40), tx_errors: (u64, 40..48), rx_dropped: (u64, 48..56), tx_dropped: (u64, 56..64), multicast: (u64, 64..72), collisions: (u64, 72..80), rx_length_errors: (u64, 80..88), rx_over_errors: (u64, 88..96), rx_crc_errors: (u64, 96..104), rx_frame_errors: (u64, 104..112), rx_fifo_errors: (u64, 112..120), rx_missed_errors: (u64, 120..128), tx_aborted_errors: (u64, 128..136), tx_carrier_errors: (u64, 136..144), tx_fifo_errors: (u64, 144..152), tx_heartbeat_errors: (u64, 152..160), tx_window_errors: (u64, 160..168), rx_compressed: (u64, 168..176), tx_compressed: (u64, 176..184), rx_nohandler: (u64, 184..192), }); #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] pub struct Stats64 { /// total packets received pub rx_packets: u64, /// total packets transmitted pub tx_packets: u64, /// total bytes received pub rx_bytes: u64, /// total bytes transmitted pub tx_bytes: u64, /// bad packets received pub rx_errors: u64, /// packet transmit problems pub tx_errors: u64, /// no space in linux buffers pub rx_dropped: u64, /// no space available in linux pub tx_dropped: u64, /// multicast packets received pub multicast: u64, pub collisions: u64, // detailed rx_errors pub rx_length_errors: u64, /// receiver ring buff overflow pub rx_over_errors: u64, /// received packets with crc error pub rx_crc_errors: u64, /// received frame alignment errors pub rx_frame_errors: u64, /// recv'r fifo overrun pub rx_fifo_errors: u64, /// receiver missed packet pub rx_missed_errors: u64, // detailed tx_errors pub tx_aborted_errors: u64, pub tx_carrier_errors: u64, pub tx_fifo_errors: u64, pub tx_heartbeat_errors: u64, pub tx_window_errors: u64, // for cslip etc pub rx_compressed: u64, pub tx_compressed: u64, /// dropped, no handler found pub rx_nohandler: u64, } impl> Parseable> for Stats64 { fn parse(buf: &Stats64Buffer) -> Result { Ok(Self { rx_packets: buf.rx_packets(), tx_packets: buf.tx_packets(), rx_bytes: buf.rx_bytes(), tx_bytes: buf.tx_bytes(), rx_errors: buf.rx_errors(), tx_errors: buf.tx_errors(), rx_dropped: buf.rx_dropped(), tx_dropped: buf.tx_dropped(), multicast: buf.multicast(), collisions: buf.collisions(), rx_length_errors: buf.rx_length_errors(), rx_over_errors: buf.rx_over_errors(), rx_crc_errors: buf.rx_crc_errors(), rx_frame_errors: buf.rx_frame_errors(), rx_fifo_errors: buf.rx_fifo_errors(), rx_missed_errors: buf.rx_missed_errors(), tx_aborted_errors: buf.tx_aborted_errors(), tx_carrier_errors: buf.tx_carrier_errors(), tx_fifo_errors: buf.tx_fifo_errors(), tx_heartbeat_errors: buf.tx_heartbeat_errors(), tx_window_errors: buf.tx_window_errors(), rx_compressed: buf.rx_compressed(), tx_compressed: buf.tx_compressed(), rx_nohandler: buf.rx_nohandler(), }) } } impl Emitable for Stats64 { fn buffer_len(&self) -> usize { LINK_STATS64_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = Stats64Buffer::new(buffer); buffer.set_rx_packets(self.rx_packets); buffer.set_tx_packets(self.tx_packets); buffer.set_rx_bytes(self.rx_bytes); buffer.set_tx_bytes(self.tx_bytes); buffer.set_rx_errors(self.rx_errors); buffer.set_tx_errors(self.tx_errors); buffer.set_rx_dropped(self.rx_dropped); buffer.set_tx_dropped(self.tx_dropped); buffer.set_multicast(self.multicast); buffer.set_collisions(self.collisions); buffer.set_rx_length_errors(self.rx_length_errors); buffer.set_rx_over_errors(self.rx_over_errors); buffer.set_rx_crc_errors(self.rx_crc_errors); buffer.set_rx_frame_errors(self.rx_frame_errors); buffer.set_rx_fifo_errors(self.rx_fifo_errors); buffer.set_rx_missed_errors(self.rx_missed_errors); buffer.set_tx_aborted_errors(self.tx_aborted_errors); buffer.set_tx_carrier_errors(self.tx_carrier_errors); buffer.set_tx_fifo_errors(self.tx_fifo_errors); buffer.set_tx_heartbeat_errors(self.tx_heartbeat_errors); buffer.set_tx_window_errors(self.tx_window_errors); buffer.set_rx_compressed(self.rx_compressed); buffer.set_tx_compressed(self.tx_compressed); buffer.set_rx_nohandler(self.rx_nohandler); } } netlink-packet-route-0.17.0/src/rtnl/link/nlas/tests.rs000064400000000000000000000410161046102023000211300ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{nla::Nla, DecodeError}; use super::*; // https://lists.infradead.org/pipermail/libnl/2015-November/002034.html // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/if_link.h#L89 #[rustfmt::skip] static BYTES: [u8; 748] = [ // AF_SPEC (L=748, T=26) 0xec, 0x02, 0x1a, 0x00, // AF_INET (L=132, T=2) 0x84, 0x00, 0x02, 0x00, // IFLA_INET_CONF (L=128, T=1) 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, // 1 forwarding 0x00, 0x00, 0x00, 0x00, // 2 mc_forwarding 0x00, 0x00, 0x00, 0x00, // 3 proxy_arp 0x01, 0x00, 0x00, 0x00, // 4 accept_redirects 0x01, 0x00, 0x00, 0x00, // 5 secure_redirects 0x01, 0x00, 0x00, 0x00, // 6 send_redirects 0x01, 0x00, 0x00, 0x00, // 7 shared_media 0x00, 0x00, 0x00, 0x00, // 8 rp_filter 0x01, 0x00, 0x00, 0x00, // 9 accept_source_route 0x00, 0x00, 0x00, 0x00, // 10 bootp_relay (40 bytes) 0x00, 0x00, 0x00, 0x00, // 11 log_martians 0x00, 0x00, 0x00, 0x00, // 12 tag 0x00, 0x00, 0x00, 0x00, // 13 arpfilter 0x00, 0x00, 0x00, 0x00, // 14 medium_id 0x01, 0x00, 0x00, 0x00, // 15 noxfrm 0x01, 0x00, 0x00, 0x00, // 16 nopolicy 0x00, 0x00, 0x00, 0x00, // 17 force_igmp_version 0x00, 0x00, 0x00, 0x00, // 18 arp_announce 0x00, 0x00, 0x00, 0x00, // 19 arp_ignore 0x00, 0x00, 0x00, 0x00, // 20 promote_secondaries (80 bytes) 0x00, 0x00, 0x00, 0x00, // 21 arp_accept 0x00, 0x00, 0x00, 0x00, // 22 arp_notify 0x00, 0x00, 0x00, 0x00, // 23 accept_local 0x00, 0x00, 0x00, 0x00, // 24 src_vmark 0x00, 0x00, 0x00, 0x00, // 25 proxy_arp_pvlan 0x00, 0x00, 0x00, 0x00, // 26 route_localnet 0x10, 0x27, 0x00, 0x00, // 27 igmpv2_unsolicited_report_interval 0xe8, 0x03, 0x00, 0x00, // 28 igmpv3_unsolicited_report_interval 0x00, 0x00, 0x00, 0x00, // 29 ignore_routes_with_linkdown 0x00, 0x00, 0x00, 0x00, // 30 drop_unicast_in_l2_multicast (120 bytes) 0x00, 0x00, 0x00, 0x00, // 31 drop_gratuitous_arp // AF_INET6 (L=612, T=10) 0x64, 0x02, 0x0a, 0x00, // IFLA_INET6_FLAGS (L=8,T=1) 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, // IFLA_INET6_CACHEINFO (L=20, T=5) 0x14, 0x00, 0x05, 0x00, 0xff, 0xff, 0x00, 0x00, // max_reasm_len 0xaf, 0x00, 0x00, 0x00, // tstamp 0x82, 0x64, 0x00, 0x00, // reachable_time 0xe8, 0x03, 0x00, 0x00, // retrans_time // IFLA_INET6_CONF (L=208, T=2) 0xd0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // forwarding 0x40, 0x00, 0x00, 0x00, // hoplimit 0x00, 0x00, 0x01, 0x00, // mtu6 0x01, 0x00, 0x00, 0x00, // accept_ra 0x01, 0x00, 0x00, 0x00, // accept_redirects 0x01, 0x00, 0x00, 0x00, // autoconf 0x01, 0x00, 0x00, 0x00, // dad_transmits 0xff, 0xff, 0xff, 0xff, // rtr_solicits 0xa0, 0x0f, 0x00, 0x00, // rtr_solicit_interval 0xe8, 0x03, 0x00, 0x00, // rtr_solicit_delay 0xff, 0xff, 0xff, 0xff, // use_tempaddr 0x80, 0x3a, 0x09, 0x00, // temp_valid_lft 0x80, 0x51, 0x01, 0x00, // temp_prefered_lft 0x03, 0x00, 0x00, 0x00, // regen_max_retry 0x58, 0x02, 0x00, 0x00, // max_desync_factor 0x10, 0x00, 0x00, 0x00, // max_addresses 0x00, 0x00, 0x00, 0x00, // force_mld_version 0x01, 0x00, 0x00, 0x00, // accept_ra_defrtr 0x01, 0x00, 0x00, 0x00, // accept_ra_pinfo 0x01, 0x00, 0x00, 0x00, // accept_ra_rtr_pref 0x60, 0xea, 0x00, 0x00, // rtr_probe_interval 0x00, 0x00, 0x00, 0x00, // accept_ra_rt_info_max_plen 0x00, 0x00, 0x00, 0x00, // proxy_ndp 0x00, 0x00, 0x00, 0x00, // optimistic_dad 0x00, 0x00, 0x00, 0x00, // accept_source_route 0x00, 0x00, 0x00, 0x00, // mc_forwarding 0x00, 0x00, 0x00, 0x00, // disable_ipv6 0xff, 0xff, 0xff, 0xff, // accept_dad 0x00, 0x00, 0x00, 0x00, // force_tllao 0x00, 0x00, 0x00, 0x00, // ndisc_notify 0x10, 0x27, 0x00, 0x00, // mldv1_unsolicited_report_interval 0xe8, 0x03, 0x00, 0x00, // mldv2_unsolicited_report_interval 0x01, 0x00, 0x00, 0x00, // suppress_frag_ndisc 0x00, 0x00, 0x00, 0x00, // accept_ra_from_local 0x00, 0x00, 0x00, 0x00, // use_optimistic 0x01, 0x00, 0x00, 0x00, // accept_ra_mtu 0x00, 0x00, 0x00, 0x00, // stable_secret 0x00, 0x00, 0x00, 0x00, // use_oif_addrs_only 0x01, 0x00, 0x00, 0x00, // accept_ra_min_hop_limit 0x00, 0x00, 0x00, 0x00, // ignore_routes_with_linkdown 0x00, 0x00, 0x00, 0x00, // drop_unicast_in_l2_multicast 0x00, 0x00, 0x00, 0x00, // drop_unsolicited_na 0x00, 0x00, 0x00, 0x00, // keep_addr_on_down 0x80, 0xee, 0x36, 0x00, // rtr_solicit_max_interval 0x00, 0x00, 0x00, 0x00, // seg6_enabled 0x00, 0x00, 0x00, 0x00, // seg6_require_hmac 0x01, 0x00, 0x00, 0x00, // enhanced_dad 0x00, 0x00, 0x00, 0x00, // addr_gen_mode 0x00, 0x00, 0x00, 0x00, // disable_policy 0x00, 0x00, 0x00, 0x00, // accept_ra_rt_info_min_plen 0x00, 0x00, 0x00, 0x00, // ndisc_tclass // IFLA_INET6_STATS (L=292, T=3) 0x24, 0x01, 0x03, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1 num 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 2 in_pkts 0xa4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 3 in_octets 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 4 in_delivers 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 5 out_forw_datagrams 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 6 out_pkts 0xa4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 7 out_octets 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8 in_hdr_errors 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9 in_too_big_errors 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 10 in_no_routes (40 bytes) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 11 in_addr_errors 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 12 in_unknown_protos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 13 in_truncated_pkts 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 14 in_discards 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 15 out_discards 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 16 out_no_routes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 17 reasm_timeout 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 18 reasm_reqds 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 19 reasm_oks 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 20 reasm_fails (80 bytes) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 21 frag_oks 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 22 frag_fails 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 23 frag_creates 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 24 in_mcast_pkts 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 25 out_mcast_pkts 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 26 in_bcast_pkts 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 27 out_bcast_pkts 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 28 in_mcast_octets 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 29 out_mcast_octets 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 30 in_bcast_octets (120 bytes) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 31 out_bcast_octets 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 32 in_csum_errors 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 33 in_no_ect_pkts 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 34 in_ect1_pkts 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 35 in_ect0_pkts 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 36 in_ce_pkts // IFLA_INET6_ICMP6STATS (L=52, T=6) 0x34, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // num 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // in_msgs 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // in_errors 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // out_msgs 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // out_errors 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // csum_errors // IFLA_INET6_TOKEN (L=20, T=7) 0x14, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // IFLA_INET6_ADDR_GEN_MODE (L=5, T=8) 0x05, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00]; lazy_static! { static ref BUFFER: NlaBuffer<&'static [u8]> = NlaBuffer::new_checked(&BYTES[..]).unwrap(); } fn get_nlas( ) -> impl Iterator, DecodeError>> { NlasIterator::new(BUFFER.value()) } fn get_byte_buffer(nla: &dyn Emitable) -> Vec { let mut buf = vec![0u8; nla.buffer_len()]; nla.emit(&mut buf); buf } lazy_static! { static ref PARSED_AF_INET6: AfSpecInet = AfSpecInet::Inet6(vec![ Inet6::Flags(2147483648), Inet6::CacheInfo(get_byte_buffer(&Inet6CacheInfo { max_reasm_len: 65535, tstamp: 175, reachable_time: 25730, retrans_time: 1000, })), Inet6::DevConf(get_byte_buffer(&Inet6DevConf { forwarding: 0, hoplimit: 64, mtu6: 65536, accept_ra: 1, accept_redirects: 1, autoconf: 1, dad_transmits: 1, rtr_solicits: -1, rtr_solicit_interval: 4000, rtr_solicit_delay: 1000, use_tempaddr: -1, temp_valid_lft: 604800, temp_prefered_lft: 86400, regen_max_retry: 3, max_desync_factor: 600, max_addresses: 16, force_mld_version: 0, accept_ra_defrtr: 1, accept_ra_pinfo: 1, accept_ra_rtr_pref: 1, rtr_probe_interval: 60000, accept_ra_rt_info_max_plen: 0, proxy_ndp: 0, optimistic_dad: 0, accept_source_route: 0, mc_forwarding: 0, disable_ipv6: 0, accept_dad: -1, force_tllao: 0, ndisc_notify: 0, mldv1_unsolicited_report_interval: 10000, mldv2_unsolicited_report_interval: 1000, suppress_frag_ndisc: 1, accept_ra_from_local: 0, use_optimistic: 0, accept_ra_mtu: 1, stable_secret: 0, use_oif_addrs_only: 0, accept_ra_min_hop_limit: 1, ignore_routes_with_linkdown: 0, drop_unicast_in_l2_multicast: 0, drop_unsolicited_na: 0, keep_addr_on_down: 0, rtr_solicit_max_interval: 3600000, seg6_enabled: 0, seg6_require_hmac: 0, enhanced_dad: 1, addr_gen_mode: 0, disable_policy: 0, accept_ra_rt_info_min_plen: 0, ndisc_tclass: 0, })), Inet6::Stats(get_byte_buffer(&Inet6Stats { num: 36, in_pkts: 6, in_octets: 420, in_delivers: 6, out_forw_datagrams: 0, out_pkts: 6, out_octets: 420, in_hdr_errors: 0, in_too_big_errors: 0, in_no_routes: 2, in_addr_errors: 0, in_unknown_protos: 0, in_truncated_pkts: 0, in_discards: 0, out_discards: 0, out_no_routes: 0, reasm_timeout: 0, reasm_reqds: 0, reasm_oks: 0, reasm_fails: 0, frag_oks: 0, frag_fails: 0, frag_creates: 0, in_mcast_pkts: 0, out_mcast_pkts: 0, in_bcast_pkts: 0, out_bcast_pkts: 0, in_mcast_octets: 0, out_mcast_octets: 0, in_bcast_octets: 0, out_bcast_octets: 0, in_csum_errors: 0, in_no_ect_pkts: 6, in_ect1_pkts: 0, in_ect0_pkts: 0, in_ce_pkts: 0, })), Inet6::IcmpStats(get_byte_buffer(&Icmp6Stats { num: 6, in_msgs: 0, in_errors: 0, out_msgs: 0, out_errors: 0, csum_errors: 0, })), Inet6::Token([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), Inet6::AddrGenMode(0), ]); } lazy_static! { static ref PARSED_AF_INET: AfSpecInet = AfSpecInet::Inet(vec![Inet::DevConf(get_byte_buffer(&InetDevConf { forwarding: 1, mc_forwarding: 0, proxy_arp: 0, accept_redirects: 1, secure_redirects: 1, send_redirects: 1, shared_media: 1, rp_filter: 0, accept_source_route: 1, bootp_relay: 0, log_martians: 0, tag: 0, arpfilter: 0, medium_id: 0, noxfrm: 1, nopolicy: 1, force_igmp_version: 0, arp_announce: 0, arp_ignore: 0, promote_secondaries: 0, arp_accept: 0, arp_notify: 0, accept_local: 0, src_vmark: 0, proxy_arp_pvlan: 0, route_localnet: 0, igmpv2_unsolicited_report_interval: 10000, igmpv3_unsolicited_report_interval: 1000, ignore_routes_with_linkdown: 0, drop_unicast_in_l2_multicast: 0, drop_gratuitous_arp: 0, }))]); } #[test] fn af_spec_header() { assert_eq!(BUFFER.length(), 748); assert_eq!(BUFFER.kind(), IFLA_AF_SPEC); } #[test] fn parse_af_inet() { let mut nlas = get_nlas(); // take the first nla let inet_buf = nlas.next().unwrap().unwrap(); // buffer checks assert_eq!(inet_buf.length(), 132); assert_eq!(inet_buf.kind(), AF_INET); assert_eq!(inet_buf.value().len(), 128); // parsing check let parsed = AfSpecInet::parse(&inet_buf).unwrap(); assert_eq!(parsed, *PARSED_AF_INET); } #[test] fn emit_af_inet() { let mut bytes = [0xff; 132]; // Note: the value is a Vec of nlas, so the padding is automatically added // for each nla. assert_eq!(PARSED_AF_INET.value_len(), 128); assert_eq!(PARSED_AF_INET.buffer_len(), 128 + 4); PARSED_AF_INET.emit(&mut bytes[..]); let buf = NlaBuffer::new_checked(&bytes[..]).unwrap(); let mut nlas = get_nlas(); let expected_buf = nlas.next().unwrap().unwrap(); assert_eq!(expected_buf.kind(), buf.kind()); assert_eq!(expected_buf.length(), buf.length()); assert_eq!(expected_buf.value(), buf.value()); } #[test] fn emit_af_inet6() { let mut bytes = vec![0xff; 612]; // Note: the value is a Vec of nlas, so the padding is automatically added // for each nla. assert_eq!(PARSED_AF_INET6.value_len(), 608); assert_eq!(PARSED_AF_INET6.buffer_len(), 608 + 4); PARSED_AF_INET6.emit(&mut bytes[..]); let buf = NlaBuffer::new_checked(&bytes[..]).unwrap(); let mut nlas = get_nlas(); let _ = nlas.next(); let expected_buf = nlas.next().unwrap().unwrap(); assert_eq!(expected_buf.kind(), buf.kind()); assert_eq!(expected_buf.length(), buf.length()); assert_eq!(expected_buf.value(), buf.value()); } #[test] fn parse_af_inet6() { let mut nlas = get_nlas(); // take the first nla let _ = nlas.next().unwrap(); let inet6_buf = nlas.next().unwrap().unwrap(); assert_eq!(inet6_buf.length(), 612); assert_eq!(inet6_buf.kind(), AF_INET6); assert_eq!(inet6_buf.value().len(), 608); let parsed = AfSpecInet::parse(&inet6_buf).unwrap(); assert_eq!(parsed, *PARSED_AF_INET6); // Normally this is the end of the nla iterator assert!(nlas.next().is_none()); } netlink-packet-route-0.17.0/src/rtnl/message.rs000064400000000000000000000255701046102023000175270ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, ParseableParametrized}, DecodeError, }; use netlink_packet_core::{ NetlinkDeserializable, NetlinkHeader, NetlinkPayload, NetlinkSerializable, }; use crate::{ constants::*, AddressMessage, LinkMessage, NeighbourMessage, NeighbourTableMessage, NsidMessage, RouteMessage, RtnlMessageBuffer, RuleMessage, TcMessage, }; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum RtnlMessage { NewLink(LinkMessage), DelLink(LinkMessage), GetLink(LinkMessage), SetLink(LinkMessage), NewLinkProp(LinkMessage), DelLinkProp(LinkMessage), NewAddress(AddressMessage), DelAddress(AddressMessage), GetAddress(AddressMessage), NewNeighbour(NeighbourMessage), GetNeighbour(NeighbourMessage), DelNeighbour(NeighbourMessage), NewNeighbourTable(NeighbourTableMessage), GetNeighbourTable(NeighbourTableMessage), SetNeighbourTable(NeighbourTableMessage), NewRoute(RouteMessage), DelRoute(RouteMessage), GetRoute(RouteMessage), NewQueueDiscipline(TcMessage), DelQueueDiscipline(TcMessage), GetQueueDiscipline(TcMessage), NewTrafficClass(TcMessage), DelTrafficClass(TcMessage), GetTrafficClass(TcMessage), NewTrafficFilter(TcMessage), DelTrafficFilter(TcMessage), GetTrafficFilter(TcMessage), NewTrafficChain(TcMessage), DelTrafficChain(TcMessage), GetTrafficChain(TcMessage), NewNsId(NsidMessage), DelNsId(NsidMessage), GetNsId(NsidMessage), NewRule(RuleMessage), DelRule(RuleMessage), GetRule(RuleMessage), } impl RtnlMessage { pub fn is_new_link(&self) -> bool { matches!(self, RtnlMessage::NewLink(_)) } pub fn is_del_link(&self) -> bool { matches!(self, RtnlMessage::DelLink(_)) } pub fn is_get_link(&self) -> bool { matches!(self, RtnlMessage::GetLink(_)) } pub fn is_set_link(&self) -> bool { matches!(self, RtnlMessage::SetLink(_)) } pub fn is_new_address(&self) -> bool { matches!(self, RtnlMessage::NewAddress(_)) } pub fn is_del_address(&self) -> bool { matches!(self, RtnlMessage::DelAddress(_)) } pub fn is_get_address(&self) -> bool { matches!(self, RtnlMessage::GetAddress(_)) } pub fn is_get_neighbour(&self) -> bool { matches!(self, RtnlMessage::GetNeighbour(_)) } pub fn is_new_route(&self) -> bool { matches!(self, RtnlMessage::NewRoute(_)) } pub fn is_new_neighbour(&self) -> bool { matches!(self, RtnlMessage::NewNeighbour(_)) } pub fn is_get_route(&self) -> bool { matches!(self, RtnlMessage::GetRoute(_)) } pub fn is_del_neighbour(&self) -> bool { matches!(self, RtnlMessage::DelNeighbour(_)) } pub fn is_new_neighbour_table(&self) -> bool { matches!(self, RtnlMessage::NewNeighbourTable(_)) } pub fn is_get_neighbour_table(&self) -> bool { matches!(self, RtnlMessage::GetNeighbourTable(_)) } pub fn is_set_neighbour_table(&self) -> bool { matches!(self, RtnlMessage::SetNeighbourTable(_)) } pub fn is_del_route(&self) -> bool { matches!(self, RtnlMessage::DelRoute(_)) } pub fn is_new_qdisc(&self) -> bool { matches!(self, RtnlMessage::NewQueueDiscipline(_)) } pub fn is_del_qdisc(&self) -> bool { matches!(self, RtnlMessage::DelQueueDiscipline(_)) } pub fn is_get_qdisc(&self) -> bool { matches!(self, RtnlMessage::GetQueueDiscipline(_)) } pub fn is_new_class(&self) -> bool { matches!(self, RtnlMessage::NewTrafficClass(_)) } pub fn is_del_class(&self) -> bool { matches!(self, RtnlMessage::DelTrafficClass(_)) } pub fn is_get_class(&self) -> bool { matches!(self, RtnlMessage::GetTrafficClass(_)) } pub fn is_new_filter(&self) -> bool { matches!(self, RtnlMessage::NewTrafficFilter(_)) } pub fn is_del_filter(&self) -> bool { matches!(self, RtnlMessage::DelTrafficFilter(_)) } pub fn is_get_filter(&self) -> bool { matches!(self, RtnlMessage::GetTrafficFilter(_)) } pub fn is_new_chain(&self) -> bool { matches!(self, RtnlMessage::NewTrafficChain(_)) } pub fn is_del_chain(&self) -> bool { matches!(self, RtnlMessage::DelTrafficChain(_)) } pub fn is_get_chain(&self) -> bool { matches!(self, RtnlMessage::GetTrafficChain(_)) } pub fn is_new_nsid(&self) -> bool { matches!(self, RtnlMessage::NewNsId(_)) } pub fn is_get_nsid(&self) -> bool { matches!(self, RtnlMessage::GetNsId(_)) } pub fn is_del_nsid(&self) -> bool { matches!(self, RtnlMessage::DelNsId(_)) } pub fn is_get_rule(&self) -> bool { matches!(self, RtnlMessage::GetRule(_)) } pub fn is_new_rule(&self) -> bool { matches!(self, RtnlMessage::NewRule(_)) } pub fn is_del_rule(&self) -> bool { matches!(self, RtnlMessage::DelRule(_)) } pub fn message_type(&self) -> u16 { use self::RtnlMessage::*; match self { NewLink(_) => RTM_NEWLINK, DelLink(_) => RTM_DELLINK, GetLink(_) => RTM_GETLINK, SetLink(_) => RTM_SETLINK, NewLinkProp(_) => RTM_NEWLINKPROP, DelLinkProp(_) => RTM_DELLINKPROP, NewAddress(_) => RTM_NEWADDR, DelAddress(_) => RTM_DELADDR, GetAddress(_) => RTM_GETADDR, GetNeighbour(_) => RTM_GETNEIGH, NewNeighbour(_) => RTM_NEWNEIGH, DelNeighbour(_) => RTM_DELNEIGH, GetNeighbourTable(_) => RTM_GETNEIGHTBL, NewNeighbourTable(_) => RTM_NEWNEIGHTBL, SetNeighbourTable(_) => RTM_SETNEIGHTBL, NewRoute(_) => RTM_NEWROUTE, DelRoute(_) => RTM_DELROUTE, GetRoute(_) => RTM_GETROUTE, NewQueueDiscipline(_) => RTM_NEWQDISC, DelQueueDiscipline(_) => RTM_DELQDISC, GetQueueDiscipline(_) => RTM_GETQDISC, NewTrafficClass(_) => RTM_NEWTCLASS, DelTrafficClass(_) => RTM_DELTCLASS, GetTrafficClass(_) => RTM_GETTCLASS, NewTrafficFilter(_) => RTM_NEWTFILTER, DelTrafficFilter(_) => RTM_DELTFILTER, GetTrafficFilter(_) => RTM_GETTFILTER, NewTrafficChain(_) => RTM_NEWCHAIN, DelTrafficChain(_) => RTM_DELCHAIN, GetTrafficChain(_) => RTM_GETCHAIN, GetNsId(_) => RTM_GETNSID, NewNsId(_) => RTM_NEWNSID, DelNsId(_) => RTM_DELNSID, GetRule(_) => RTM_GETRULE, NewRule(_) => RTM_NEWRULE, DelRule(_) => RTM_DELRULE, } } } impl Emitable for RtnlMessage { #[rustfmt::skip] fn buffer_len(&self) -> usize { use self::RtnlMessage::*; match self { | NewLink(ref msg) | DelLink(ref msg) | GetLink(ref msg) | SetLink(ref msg) | NewLinkProp(ref msg) | DelLinkProp(ref msg) => msg.buffer_len(), | NewAddress(ref msg) | DelAddress(ref msg) | GetAddress(ref msg) => msg.buffer_len(), | NewNeighbour(ref msg) | GetNeighbour(ref msg) | DelNeighbour(ref msg) => msg.buffer_len(), | NewNeighbourTable(ref msg) | GetNeighbourTable(ref msg) | SetNeighbourTable(ref msg) => msg.buffer_len(), | NewRoute(ref msg) | DelRoute(ref msg) | GetRoute(ref msg) => msg.buffer_len(), | NewQueueDiscipline(ref msg) | DelQueueDiscipline(ref msg) | GetQueueDiscipline(ref msg) | NewTrafficClass(ref msg) | DelTrafficClass(ref msg) | GetTrafficClass(ref msg) | NewTrafficFilter(ref msg) | DelTrafficFilter(ref msg) | GetTrafficFilter(ref msg) | NewTrafficChain(ref msg) | DelTrafficChain(ref msg) | GetTrafficChain(ref msg) => msg.buffer_len(), | NewNsId(ref msg) | DelNsId(ref msg) | GetNsId(ref msg) => msg.buffer_len(), | NewRule(ref msg) | DelRule(ref msg) | GetRule(ref msg) => msg.buffer_len() } } #[rustfmt::skip] fn emit(&self, buffer: &mut [u8]) { use self::RtnlMessage::*; match self { | NewLink(ref msg) | DelLink(ref msg) | GetLink(ref msg) | SetLink(ref msg) | NewLinkProp(ref msg) | DelLinkProp(ref msg) => msg.emit(buffer), | NewAddress(ref msg) | DelAddress(ref msg) | GetAddress(ref msg) => msg.emit(buffer), | GetNeighbour(ref msg) | NewNeighbour(ref msg) | DelNeighbour(ref msg) => msg.emit(buffer), | GetNeighbourTable(ref msg) | NewNeighbourTable(ref msg) | SetNeighbourTable(ref msg) => msg.emit(buffer), | NewRoute(ref msg) | DelRoute(ref msg) | GetRoute(ref msg) => msg.emit(buffer), | NewQueueDiscipline(ref msg) | DelQueueDiscipline(ref msg) | GetQueueDiscipline(ref msg) | NewTrafficClass(ref msg) | DelTrafficClass(ref msg) | GetTrafficClass(ref msg) | NewTrafficFilter(ref msg) | DelTrafficFilter(ref msg) | GetTrafficFilter(ref msg) | NewTrafficChain(ref msg) | DelTrafficChain(ref msg) | GetTrafficChain(ref msg) => msg.emit(buffer), | NewNsId(ref msg) | DelNsId(ref msg) | GetNsId(ref msg) => msg.emit(buffer), | NewRule(ref msg) | DelRule(ref msg) | GetRule(ref msg) => msg.emit(buffer) } } } impl NetlinkSerializable for RtnlMessage { fn message_type(&self) -> u16 { self.message_type() } fn buffer_len(&self) -> usize { ::buffer_len(self) } fn serialize(&self, buffer: &mut [u8]) { self.emit(buffer) } } impl NetlinkDeserializable for RtnlMessage { type Error = DecodeError; fn deserialize( header: &NetlinkHeader, payload: &[u8], ) -> Result { let buf = RtnlMessageBuffer::new(payload); match RtnlMessage::parse_with_param(&buf, header.message_type) { Err(e) => Err(e), Ok(message) => Ok(message), } } } impl From for NetlinkPayload { fn from(message: RtnlMessage) -> Self { NetlinkPayload::InnerMessage(message) } } netlink-packet-route-0.17.0/src/rtnl/mod.rs000064400000000000000000000025011046102023000166470ustar 00000000000000// SPDX-License-Identifier: MIT pub mod address; pub use address::{ AddressHeader, AddressMessage, AddressMessageBuffer, ADDRESS_HEADER_LEN, }; pub mod link; pub use link::{LinkHeader, LinkMessage, LinkMessageBuffer, LINK_HEADER_LEN}; pub mod neighbour; pub use neighbour::{ NeighbourHeader, NeighbourMessage, NeighbourMessageBuffer, NEIGHBOUR_HEADER_LEN, }; pub mod neighbour_table; pub use neighbour_table::{ NeighbourTableHeader, NeighbourTableMessage, NeighbourTableMessageBuffer, NEIGHBOUR_TABLE_HEADER_LEN, }; pub mod nsid; pub use nsid::{NsidHeader, NsidMessage, NsidMessageBuffer, NSID_HEADER_LEN}; pub mod route; pub use route::{ RouteFlags, RouteHeader, RouteMessage, RouteMessageBuffer, ROUTE_HEADER_LEN, }; pub mod rule; pub use rule::{RuleHeader, RuleMessage, RuleMessageBuffer, RULE_HEADER_LEN}; pub mod tc; pub use tc::{TcHeader, TcMessage, TcMessageBuffer, TC_HEADER_LEN}; pub mod constants; pub use self::constants::*; mod buffer; pub use self::buffer::*; mod message; pub use self::message::*; pub mod nlas { pub use super::{ address::nlas as address, link::nlas as link, neighbour::nlas as neighbour, neighbour_table::nlas as neighbour_table, nsid::nlas as nsid, route::nlas as route, rule::nlas as rule, tc::nlas as tc, }; } #[cfg(test)] mod test; netlink-packet-route-0.17.0/src/rtnl/neighbour/buffer.rs000064400000000000000000000011511046102023000213230ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::DecodeError; use netlink_packet_utils::nla::{NlaBuffer, NlasIterator}; pub const NEIGHBOUR_HEADER_LEN: usize = 12; buffer!(NeighbourMessageBuffer(NEIGHBOUR_HEADER_LEN) { family: (u8, 0), ifindex: (u32, 4..8), state: (u16, 8..10), flags: (u8, 10), ntype: (u8, 11), payload:(slice, NEIGHBOUR_HEADER_LEN..), }); impl<'a, T: AsRef<[u8]> + ?Sized> NeighbourMessageBuffer<&'a T> { pub fn nlas( &self, ) -> impl Iterator, DecodeError>> { NlasIterator::new(self.payload()) } } netlink-packet-route-0.17.0/src/rtnl/neighbour/header.rs000064400000000000000000000041651046102023000213120ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; use crate::{NeighbourMessageBuffer, NEIGHBOUR_HEADER_LEN}; /// Neighbour headers have the following structure: /// /// ```no_rust /// 0 8 16 24 32 /// +----------------+----------------+----------------+----------------+ /// | family | padding | /// +----------------+----------------+----------------+----------------+ /// | link index | /// +----------------+----------------+----------------+----------------+ /// | state | flags | ntype | /// +----------------+----------------+----------------+----------------+ /// ``` /// /// `NeighbourHeader` exposes all these fields. #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct NeighbourHeader { pub family: u8, pub ifindex: u32, /// Neighbour cache entry state. It should be set to one of the /// `NUD_*` constants pub state: u16, /// Neighbour cache entry flags. It should be set to a combination /// of the `NTF_*` constants pub flags: u8, /// Neighbour cache entry type. It should be set to one of the /// `NDA_*` constants. pub ntype: u8, } impl> Parseable> for NeighbourHeader { fn parse(buf: &NeighbourMessageBuffer) -> Result { Ok(Self { family: buf.family(), ifindex: buf.ifindex(), state: buf.state(), flags: buf.flags(), ntype: buf.ntype(), }) } } impl Emitable for NeighbourHeader { fn buffer_len(&self) -> usize { NEIGHBOUR_HEADER_LEN } fn emit(&self, buffer: &mut [u8]) { let mut packet = NeighbourMessageBuffer::new(buffer); packet.set_family(self.family); packet.set_ifindex(self.ifindex); packet.set_state(self.state); packet.set_flags(self.flags); packet.set_ntype(self.ntype); } } netlink-packet-route-0.17.0/src/rtnl/neighbour/message.rs000064400000000000000000000067401046102023000215070ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; use crate::{nlas::neighbour::Nla, NeighbourHeader, NeighbourMessageBuffer}; #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct NeighbourMessage { pub header: NeighbourHeader, pub nlas: Vec, } impl Emitable for NeighbourMessage { fn buffer_len(&self) -> usize { self.header.buffer_len() + self.nlas.as_slice().buffer_len() } fn emit(&self, buffer: &mut [u8]) { self.header.emit(buffer); self.nlas .as_slice() .emit(&mut buffer[self.header.buffer_len()..]); } } impl<'a, T: AsRef<[u8]> + 'a> Parseable> for NeighbourMessage { fn parse(buf: &NeighbourMessageBuffer<&'a T>) -> Result { Ok(NeighbourMessage { header: NeighbourHeader::parse(buf) .context("failed to parse neighbour message header")?, nlas: Vec::::parse(buf) .context("failed to parse neighbour message NLAs")?, }) } } impl<'a, T: AsRef<[u8]> + 'a> Parseable> for Vec { fn parse(buf: &NeighbourMessageBuffer<&'a T>) -> Result { let mut nlas = vec![]; for nla_buf in buf.nlas() { nlas.push(Nla::parse(&nla_buf?)?); } Ok(nlas) } } #[cfg(test)] mod test { use crate::{ constants::*, NeighbourHeader, NeighbourMessage, NeighbourMessageBuffer, }; use netlink_packet_utils::traits::Emitable; // 0020 0a 00 00 00 02 00 00 00 02 00 80 01 14 00 01 00 // 0030 2a 02 80 10 66 d5 00 00 f6 90 ea ff fe 00 2d 83 // 0040 0a 00 02 00 f4 90 ea 00 2d 83 00 00 08 00 04 00 // 0050 01 00 00 00 14 00 03 00 00 00 00 00 00 00 00 00 // 0060 00 00 00 00 02 00 00 00 #[rustfmt::skip] static HEADER: [u8; 12] = [ 0x0a, // interface family (inet6) 0xff, 0xff, 0xff, // padding 0x01, 0x00, 0x00, 0x00, // interface index = 1 0x02, 0x00, // state NUD_REACHABLE 0x80, // flags NTF_PROXY 0x01 // ntype // nlas // will add some once I've got them parsed out. ]; #[test] fn packet_header_read() { let packet = NeighbourMessageBuffer::new(&HEADER[0..12]); assert_eq!(packet.family(), AF_INET6 as u8); assert_eq!(packet.ifindex(), 1); assert_eq!(packet.state(), NUD_REACHABLE); assert_eq!(packet.flags(), NTF_ROUTER); assert_eq!(packet.ntype(), NDA_DST as u8); } #[test] fn packet_header_build() { let mut buf = vec![0xff; 12]; { let mut packet = NeighbourMessageBuffer::new(&mut buf); packet.set_family(AF_INET6 as u8); packet.set_ifindex(1); packet.set_state(NUD_REACHABLE); packet.set_flags(NTF_ROUTER); packet.set_ntype(NDA_DST as u8); } assert_eq!(&buf[..], &HEADER[0..12]); } #[test] fn emit() { let header = NeighbourHeader { family: AF_INET6 as u8, ifindex: 1, state: NUD_REACHABLE, flags: NTF_ROUTER, ntype: NDA_DST as u8, }; let nlas = vec![]; let packet = NeighbourMessage { header, nlas }; let mut buf = [0; 12]; assert_eq!(packet.buffer_len(), 12); packet.emit(&mut buf[..]); } } netlink-packet-route-0.17.0/src/rtnl/neighbour/mod.rs000064400000000000000000000002201046102023000206250ustar 00000000000000// SPDX-License-Identifier: MIT mod buffer; mod header; mod message; pub mod nlas; pub use self::{buffer::*, header::*, message::*, nlas::*}; netlink-packet-route-0.17.0/src/rtnl/neighbour/nlas/cache_info.rs000064400000000000000000000022471046102023000230740ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] pub struct CacheInfo { pub confirmed: u32, pub used: u32, pub updated: u32, pub refcnt: u32, } pub const NEIGHBOUR_CACHE_INFO_LEN: usize = 16; buffer!(CacheInfoBuffer(NEIGHBOUR_CACHE_INFO_LEN) { confirmed: (u32, 0..4), used: (u32, 4..8), updated: (u32, 8..12), refcnt: (u32, 12..16), }); impl> Parseable> for CacheInfo { fn parse(buf: &CacheInfoBuffer) -> Result { Ok(Self { confirmed: buf.confirmed(), used: buf.used(), updated: buf.updated(), refcnt: buf.refcnt(), }) } } impl Emitable for CacheInfo { fn buffer_len(&self) -> usize { NEIGHBOUR_CACHE_INFO_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = CacheInfoBuffer::new(buffer); buffer.set_confirmed(self.confirmed); buffer.set_used(self.used); buffer.set_updated(self.updated); buffer.set_refcnt(self.refcnt); } } netlink-packet-route-0.17.0/src/rtnl/neighbour/nlas/mod.rs000064400000000000000000000070661046102023000216010ustar 00000000000000// SPDX-License-Identifier: MIT mod cache_info; pub use self::cache_info::*; use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{self, DefaultNla, NlaBuffer}, parsers::{parse_u16, parse_u32}, traits::Parseable, DecodeError, }; use crate::constants::*; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum Nla { Unspec(Vec), Destination(Vec), LinkLocalAddress(Vec), CacheInfo(Vec), Probes(Vec), Vlan(u16), Port(Vec), Vni(u32), IfIndex(u32), Master(Vec), LinkNetNsId(Vec), SourceVni(u32), Other(DefaultNla), } impl nla::Nla for Nla { #[rustfmt::skip] fn value_len(&self) -> usize { use self::Nla::*; match *self { Unspec(ref bytes) | Destination(ref bytes) | LinkLocalAddress(ref bytes) | Probes(ref bytes) | Port(ref bytes) | Master(ref bytes) | CacheInfo(ref bytes) | LinkNetNsId(ref bytes) => bytes.len(), Vlan(_) => 2, Vni(_) | IfIndex(_) | SourceVni(_) => 4, Other(ref attr) => attr.value_len(), } } #[rustfmt::skip] fn emit_value(&self, buffer: &mut [u8]) { use self::Nla::*; match *self { Unspec(ref bytes) | Destination(ref bytes) | LinkLocalAddress(ref bytes) | Probes(ref bytes) | Port(ref bytes) | Master(ref bytes) | CacheInfo(ref bytes) | LinkNetNsId(ref bytes) => buffer.copy_from_slice(bytes.as_slice()), Vlan(ref value) => NativeEndian::write_u16(buffer, *value), Vni(ref value) | IfIndex(ref value) | SourceVni(ref value) => NativeEndian::write_u32(buffer, *value), Other(ref attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { use self::Nla::*; match *self { Unspec(_) => NDA_UNSPEC, Destination(_) => NDA_DST, LinkLocalAddress(_) => NDA_LLADDR, CacheInfo(_) => NDA_CACHEINFO, Probes(_) => NDA_PROBES, Vlan(_) => NDA_VLAN, Port(_) => NDA_PORT, Vni(_) => NDA_VNI, IfIndex(_) => NDA_IFINDEX, Master(_) => NDA_MASTER, LinkNetNsId(_) => NDA_LINK_NETNSID, SourceVni(_) => NDA_SRC_VNI, Other(ref nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Nla { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::Nla::*; let payload = buf.value(); Ok(match buf.kind() { NDA_UNSPEC => Unspec(payload.to_vec()), NDA_DST => Destination(payload.to_vec()), NDA_LLADDR => LinkLocalAddress(payload.to_vec()), NDA_CACHEINFO => CacheInfo(payload.to_vec()), NDA_PROBES => Probes(payload.to_vec()), NDA_VLAN => Vlan(parse_u16(payload)?), NDA_PORT => Port(payload.to_vec()), NDA_VNI => Vni(parse_u32(payload)?), NDA_IFINDEX => IfIndex(parse_u32(payload)?), NDA_MASTER => Master(payload.to_vec()), NDA_LINK_NETNSID => LinkNetNsId(payload.to_vec()), NDA_SRC_VNI => SourceVni(parse_u32(payload)?), _ => Other( DefaultNla::parse(buf) .context("invalid link NLA value (unknown type)")?, ), }) } } netlink-packet-route-0.17.0/src/rtnl/neighbour_table/buffer.rs000064400000000000000000000010511046102023000224710ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::DecodeError; use netlink_packet_utils::nla::{NlaBuffer, NlasIterator}; pub const NEIGHBOUR_TABLE_HEADER_LEN: usize = 4; buffer!(NeighbourTableMessageBuffer(NEIGHBOUR_TABLE_HEADER_LEN) { family: (u8, 0), payload: (slice, NEIGHBOUR_TABLE_HEADER_LEN..), }); impl<'a, T: AsRef<[u8]> + ?Sized> NeighbourTableMessageBuffer<&'a T> { pub fn nlas( &self, ) -> impl Iterator, DecodeError>> { NlasIterator::new(self.payload()) } } netlink-packet-route-0.17.0/src/rtnl/neighbour_table/header.rs000064400000000000000000000015171046102023000224570ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; use super::buffer::{NeighbourTableMessageBuffer, NEIGHBOUR_TABLE_HEADER_LEN}; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct NeighbourTableHeader { pub family: u8, } impl> Parseable> for NeighbourTableHeader { fn parse( buf: &NeighbourTableMessageBuffer, ) -> Result { Ok(Self { family: buf.family(), }) } } impl Emitable for NeighbourTableHeader { fn buffer_len(&self) -> usize { NEIGHBOUR_TABLE_HEADER_LEN } fn emit(&self, buffer: &mut [u8]) { let mut packet = NeighbourTableMessageBuffer::new(buffer); packet.set_family(self.family); } } netlink-packet-route-0.17.0/src/rtnl/neighbour_table/message.rs000064400000000000000000000027761046102023000226630ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; use crate::{ nlas::neighbour_table::Nla, NeighbourTableHeader, NeighbourTableMessageBuffer, }; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct NeighbourTableMessage { pub header: NeighbourTableHeader, pub nlas: Vec, } impl Emitable for NeighbourTableMessage { fn buffer_len(&self) -> usize { self.header.buffer_len() + self.nlas.as_slice().buffer_len() } fn emit(&self, buffer: &mut [u8]) { self.header.emit(buffer); self.nlas.as_slice().emit(buffer); } } impl<'a, T: AsRef<[u8]> + 'a> Parseable> for NeighbourTableMessage { fn parse( buf: &NeighbourTableMessageBuffer<&'a T>, ) -> Result { Ok(NeighbourTableMessage { header: NeighbourTableHeader::parse(buf) .context("failed to parse neighbour table message header")?, nlas: Vec::::parse(buf) .context("failed to parse neighbour table message NLAs")?, }) } } impl<'a, T: AsRef<[u8]> + 'a> Parseable> for Vec { fn parse( buf: &NeighbourTableMessageBuffer<&'a T>, ) -> Result { let mut nlas = vec![]; for nla_buf in buf.nlas() { nlas.push(Nla::parse(&nla_buf?)?); } Ok(nlas) } } netlink-packet-route-0.17.0/src/rtnl/neighbour_table/mod.rs000064400000000000000000000002201046102023000217740ustar 00000000000000// SPDX-License-Identifier: MIT mod buffer; mod header; mod message; pub mod nlas; pub use self::{buffer::*, header::*, message::*, nlas::*}; netlink-packet-route-0.17.0/src/rtnl/neighbour_table/nlas/config.rs000064400000000000000000000035601046102023000234310ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] pub struct Config { pub key_len: u16, pub entry_size: u16, pub entries: u32, pub last_flush: u32, pub last_rand: u32, pub hash_rand: u32, pub hash_mask: u32, pub hash_chain_gc: u32, pub proxy_qlen: u32, } pub const CONFIG_LEN: usize = 32; buffer!(ConfigBuffer(CONFIG_LEN) { key_len: (u16, 0..2), entry_size: (u16, 2..4), entries: (u32, 4..8), last_flush: (u32, 8..12), last_rand: (u32, 12..16), hash_rand: (u32, 16..20), hash_mask: (u32, 20..24), hash_chain_gc: (u32, 24..28), proxy_qlen: (u32, 28..32), }); impl> Parseable> for Config { fn parse(buf: &ConfigBuffer) -> Result { Ok(Self { key_len: buf.key_len(), entry_size: buf.entry_size(), entries: buf.entries(), last_flush: buf.last_flush(), last_rand: buf.last_rand(), hash_rand: buf.hash_rand(), hash_mask: buf.hash_mask(), hash_chain_gc: buf.hash_chain_gc(), proxy_qlen: buf.proxy_qlen(), }) } } impl Emitable for Config { fn buffer_len(&self) -> usize { CONFIG_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = ConfigBuffer::new(buffer); buffer.set_key_len(self.key_len); buffer.set_entry_size(self.entry_size); buffer.set_entries(self.entries); buffer.set_last_flush(self.last_flush); buffer.set_last_rand(self.last_rand); buffer.set_hash_rand(self.hash_rand); buffer.set_hash_mask(self.hash_mask); buffer.set_hash_chain_gc(self.hash_chain_gc); buffer.set_proxy_qlen(self.proxy_qlen); } } netlink-packet-route-0.17.0/src/rtnl/neighbour_table/nlas/mod.rs000064400000000000000000000070351046102023000227440ustar 00000000000000// SPDX-License-Identifier: MIT mod config; pub use config::*; mod stats; pub use stats::*; use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{self, DefaultNla, NlaBuffer}, parsers::{parse_string, parse_u32, parse_u64}, traits::Parseable, DecodeError, }; use crate::constants::*; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum Nla { Unspec(Vec), // FIXME: parse this nla Parms(Vec), Name(String), Threshold1(u32), Threshold2(u32), Threshold3(u32), Config(Vec), Stats(Vec), GcInterval(u64), Other(DefaultNla), } impl nla::Nla for Nla { #[rustfmt::skip] fn value_len(&self) -> usize { use self::Nla::*; match *self { Unspec(ref bytes) | Parms(ref bytes) | Config(ref bytes) | Stats(ref bytes)=> bytes.len(), // strings: +1 because we need to append a nul byte Name(ref s) => s.len() + 1, Threshold1(_) | Threshold2(_) | Threshold3(_) => 4, GcInterval(_) => 8, Other(ref attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::Nla::*; match *self { Unspec(ref bytes) | Parms(ref bytes) | Config(ref bytes) | Stats(ref bytes) => buffer.copy_from_slice(bytes.as_slice()), Name(ref string) => { buffer[..string.len()].copy_from_slice(string.as_bytes()); buffer[string.len()] = 0; } GcInterval(ref value) => NativeEndian::write_u64(buffer, *value), Threshold1(ref value) | Threshold2(ref value) | Threshold3(ref value) => NativeEndian::write_u32(buffer, *value), Other(ref attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { use self::Nla::*; match *self { Unspec(_) => NDTA_UNSPEC, Name(_) => NDTA_NAME, Config(_) => NDTA_CONFIG, Stats(_) => NDTA_STATS, Parms(_) => NDTA_PARMS, GcInterval(_) => NDTA_GC_INTERVAL, Threshold1(_) => NDTA_THRESH1, Threshold2(_) => NDTA_THRESH2, Threshold3(_) => NDTA_THRESH3, Other(ref attr) => attr.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Nla { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::Nla::*; let payload = buf.value(); Ok(match buf.kind() { NDTA_UNSPEC => Unspec(payload.to_vec()), NDTA_NAME => { Name(parse_string(payload).context("invalid NDTA_NAME value")?) } NDTA_CONFIG => Config(payload.to_vec()), NDTA_STATS => Stats(payload.to_vec()), NDTA_PARMS => Parms(payload.to_vec()), NDTA_GC_INTERVAL => GcInterval( parse_u64(payload).context("invalid NDTA_GC_INTERVAL value")?, ), NDTA_THRESH1 => Threshold1( parse_u32(payload).context("invalid NDTA_THRESH1 value")?, ), NDTA_THRESH2 => Threshold2( parse_u32(payload).context("invalid NDTA_THRESH2 value")?, ), NDTA_THRESH3 => Threshold3( parse_u32(payload).context("invalid NDTA_THRESH3 value")?, ), kind => Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } netlink-packet-route-0.17.0/src/rtnl/neighbour_table/nlas/stats.rs000064400000000000000000000042511046102023000233200ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] pub struct Stats { pub allocs: u64, pub destroys: u64, pub hash_grows: u64, pub res_failed: u64, pub lookups: u64, pub hits: u64, pub multicast_probes_received: u64, pub unicast_probes_received: u64, pub periodic_gc_runs: u64, pub forced_gc_runs: u64, } pub const STATS_LEN: usize = 80; buffer!(StatsBuffer(STATS_LEN) { allocs: (u64, 0..8), destroys: (u64, 8..16), hash_grows: (u64, 16..24), res_failed: (u64, 24..32), lookups: (u64, 32..40), hits: (u64, 40..48), multicast_probes_received: (u64, 48..56), unicast_probes_received: (u64, 56..64), periodic_gc_runs: (u64, 64..72), forced_gc_runs: (u64, 72..80), }); impl> Parseable> for Stats { fn parse(buf: &StatsBuffer) -> Result { Ok(Self { allocs: buf.allocs(), destroys: buf.destroys(), hash_grows: buf.hash_grows(), res_failed: buf.res_failed(), lookups: buf.lookups(), hits: buf.hits(), multicast_probes_received: buf.multicast_probes_received(), unicast_probes_received: buf.unicast_probes_received(), periodic_gc_runs: buf.periodic_gc_runs(), forced_gc_runs: buf.forced_gc_runs(), }) } } impl Emitable for Stats { fn buffer_len(&self) -> usize { STATS_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = StatsBuffer::new(buffer); buffer.set_allocs(self.allocs); buffer.set_destroys(self.destroys); buffer.set_hash_grows(self.hash_grows); buffer.set_res_failed(self.res_failed); buffer.set_lookups(self.lookups); buffer.set_hits(self.hits); buffer.set_multicast_probes_received(self.multicast_probes_received); buffer.set_unicast_probes_received(self.unicast_probes_received); buffer.set_periodic_gc_runs(self.periodic_gc_runs); buffer.set_forced_gc_runs(self.forced_gc_runs); } } netlink-packet-route-0.17.0/src/rtnl/nsid/buffer.rs000064400000000000000000000007541046102023000203060ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::{NlaBuffer, NlasIterator}, DecodeError, }; pub const NSID_HEADER_LEN: usize = 4; buffer!(NsidMessageBuffer(NSID_HEADER_LEN) { rtgen_family: (u8, 0), payload: (slice, NSID_HEADER_LEN..), }); impl<'a, T: AsRef<[u8]> + ?Sized> NsidMessageBuffer<&'a T> { pub fn nlas( &self, ) -> impl Iterator, DecodeError>> { NlasIterator::new(self.payload()) } } netlink-packet-route-0.17.0/src/rtnl/nsid/header.rs000064400000000000000000000014051046102023000202570ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; use super::{NsidMessageBuffer, NSID_HEADER_LEN}; #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct NsidHeader { pub rtgen_family: u8, } impl Emitable for NsidHeader { fn buffer_len(&self) -> usize { NSID_HEADER_LEN } fn emit(&self, buffer: &mut [u8]) { let mut packet = NsidMessageBuffer::new(buffer); packet.set_rtgen_family(self.rtgen_family); } } impl> Parseable> for NsidHeader { fn parse(buf: &NsidMessageBuffer) -> Result { Ok(NsidHeader { rtgen_family: buf.rtgen_family(), }) } } netlink-packet-route-0.17.0/src/rtnl/nsid/message.rs000064400000000000000000000070541046102023000204610ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; use crate::{nlas::nsid::Nla, NsidHeader, NsidMessageBuffer}; #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct NsidMessage { pub header: NsidHeader, pub nlas: Vec, } impl<'a, T: AsRef<[u8]> + 'a> Parseable> for NsidMessage { fn parse(buf: &NsidMessageBuffer<&'a T>) -> Result { Ok(Self { header: NsidHeader::parse(buf) .context("failed to parse nsid message header")?, nlas: Vec::::parse(buf) .context("failed to parse nsid message NLAs")?, }) } } impl<'a, T: AsRef<[u8]> + 'a> Parseable> for Vec { fn parse(buf: &NsidMessageBuffer<&'a T>) -> Result { let mut nlas = vec![]; for nla_buf in buf.nlas() { nlas.push(Nla::parse(&nla_buf?)?); } Ok(nlas) } } impl Emitable for NsidMessage { fn buffer_len(&self) -> usize { self.header.buffer_len() + self.nlas.as_slice().buffer_len() } fn emit(&self, buffer: &mut [u8]) { self.header.emit(buffer); self.nlas .as_slice() .emit(&mut buffer[self.header.buffer_len()..]); } } #[cfg(test)] mod test { use crate::{ nlas::nsid::Nla, NsidHeader, NsidMessage, RtnlMessage, RtnlMessageBuffer, NETNSA_NSID_NOT_ASSIGNED, RTM_GETNSID, RTM_NEWNSID, }; use netlink_packet_core::NetlinkBuffer; use netlink_packet_utils::traits::ParseableParametrized; #[rustfmt::skip] #[test] fn get_ns_id_request() { let data = vec![ 0x1c, 0x00, 0x00, 0x00, // length = 28 0x5a, 0x00, // message type = 90 = RTM_GETNSID 0x01, 0x00, // flags 0x00, 0x00, 0x00, 0x00, // seq number 0x00, 0x00, 0x00, 0x00, // pid // GETNSID message 0x00, // rtgen family 0x00, 0x00, 0x00, // padding // NLA 0x08, 0x00, // length = 8 0x03, 0x00, // type = 3 (Fd) 0x04, 0x00, 0x00, 0x00 // 4 ]; let expected = RtnlMessage::GetNsId(NsidMessage { header: NsidHeader { rtgen_family: 0 }, nlas: vec![Nla::Fd(4)], }); let actual = RtnlMessage::parse_with_param(&RtnlMessageBuffer::new(&NetlinkBuffer::new(&data).payload()), RTM_GETNSID).unwrap(); assert_eq!(expected, actual); } #[rustfmt::skip] #[test] fn get_ns_id_response() { let data = vec![ 0x1c, 0x00, 0x00, 0x00, // length = 28 0x58, 0x00, // message type = RTM_NEWNSID 0x00, 0x00, // flags 0x00, 0x00, 0x00, 0x00, // seq number 0x76, 0x12, 0x00, 0x00, // pid // NETNSID message 0x00, // rtgen family 0x00, 0x00, 0x00, // padding // NLA 0x08, 0x00, // length 0x01, 0x00, // type = NETNSA_NSID 0xff, 0xff, 0xff, 0xff // -1 ]; let expected = RtnlMessage::NewNsId(NsidMessage { header: NsidHeader { rtgen_family: 0 }, nlas: vec![Nla::Id(NETNSA_NSID_NOT_ASSIGNED)], }); let nl_buffer = NetlinkBuffer::new(&data).payload(); let rtnl_buffer = RtnlMessageBuffer::new(&nl_buffer); let actual = RtnlMessage::parse_with_param(&rtnl_buffer, RTM_NEWNSID).unwrap(); assert_eq!(expected, actual); } } netlink-packet-route-0.17.0/src/rtnl/nsid/mod.rs000064400000000000000000000002201046102023000176000ustar 00000000000000// SPDX-License-Identifier: MIT mod buffer; mod header; mod message; pub mod nlas; pub use self::{buffer::*, header::*, message::*, nlas::*}; netlink-packet-route-0.17.0/src/rtnl/nsid/nlas.rs000064400000000000000000000041531046102023000177670ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use crate::constants::*; use netlink_packet_utils::{ nla::{self, DefaultNla, NlaBuffer}, parsers::{parse_i32, parse_u32}, traits::Parseable, DecodeError, }; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum Nla { Unspec(Vec), Id(i32), Pid(u32), Fd(u32), Other(DefaultNla), } impl nla::Nla for Nla { fn value_len(&self) -> usize { use self::Nla::*; match *self { Unspec(ref bytes) => bytes.len(), Id(_) | Pid(_) | Fd(_) => 4, Other(ref attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::Nla::*; match *self { Unspec(ref bytes) => buffer.copy_from_slice(bytes.as_slice()), Fd(ref value) | Pid(ref value) => { NativeEndian::write_u32(buffer, *value) } Id(ref value) => NativeEndian::write_i32(buffer, *value), Other(ref attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { use self::Nla::*; match *self { Unspec(_) => NETNSA_NONE, Id(_) => NETNSA_NSID, Pid(_) => NETNSA_PID, Fd(_) => NETNSA_FD, Other(ref attr) => attr.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Nla { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::Nla::*; let payload = buf.value(); Ok(match buf.kind() { NETNSA_NONE => Unspec(payload.to_vec()), NETNSA_NSID => { Id(parse_i32(payload).context("invalid NETNSA_NSID")?) } NETNSA_PID => { Pid(parse_u32(payload).context("invalid NETNSA_PID")?) } NETNSA_FD => Fd(parse_u32(payload).context("invalid NETNSA_FD")?), kind => Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } netlink-packet-route-0.17.0/src/rtnl/route/buffer.rs000064400000000000000000000013121046102023000204760ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::{NlaBuffer, NlasIterator}, DecodeError, }; pub const ROUTE_HEADER_LEN: usize = 12; buffer!(RouteMessageBuffer(ROUTE_HEADER_LEN) { address_family: (u8, 0), destination_prefix_length: (u8, 1), source_prefix_length: (u8, 2), tos: (u8, 3), table: (u8, 4), protocol: (u8, 5), scope: (u8, 6), kind: (u8, 7), flags: (u32, 8..ROUTE_HEADER_LEN), payload: (slice, ROUTE_HEADER_LEN..), }); impl<'a, T: AsRef<[u8]> + ?Sized> RouteMessageBuffer<&'a T> { pub fn nlas( &self, ) -> impl Iterator, DecodeError>> { NlasIterator::new(self.payload()) } } netlink-packet-route-0.17.0/src/rtnl/route/header.rs000064400000000000000000000153041046102023000204630ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; use crate::{constants::*, RouteMessageBuffer, ROUTE_HEADER_LEN}; bitflags! { /// Flags that can be set in a `RTM_GETROUTE` ([`RtnlMessage::GetRoute`]) message. #[non_exhaustive] pub struct RouteFlags: u32 { /// If the route changes, notify the user via rtnetlink const RTM_F_NOTIFY = RTM_F_NOTIFY; /// This route is cloned. Cloned routes are routes coming from the cache instead of the /// FIB. For IPv4, the cache was removed in Linux 3.6 (see [IPv4 route lookup on Linux] for /// more information about IPv4 routing) /// /// [IPv4 route lookup on Linux]: https://vincent.bernat.ch/en/blog/2017-ipv4-route-lookup-linux const RTM_F_CLONED = RTM_F_CLONED; /// Multipath equalizer (not yet implemented) const RTM_F_EQUALIZE = RTM_F_EQUALIZE; /// Prefix addresses const RTM_F_PREFIX = RTM_F_PREFIX; /// Show the table from which the lookup result comes. Note that before commit /// `c36ba6603a11`, Linux would always hardcode [`RouteMessageHeader.table`] (known as /// `rtmsg.rtm_table` in the kernel) to `RT_TABLE_MAIN`. /// /// [`RouteMessageHeader.table`]: ../struct.RouteMessageHeader.html#structfield.table const RTM_F_LOOKUP_TABLE = RTM_F_LOOKUP_TABLE; /// Return the full FIB lookup match (see commit `b61798130f1be5bff08712308126c2d7ebe390ef`) const RTM_F_FIB_MATCH = RTM_F_FIB_MATCH; } } impl Default for RouteFlags { fn default() -> Self { Self::empty() } } /// High level representation of `RTM_GETROUTE`, `RTM_ADDROUTE`, `RTM_DELROUTE` /// messages headers. /// /// These headers have the following structure: /// /// ```no_rust /// 0 8 16 24 32 /// +----------------+----------------+----------------+----------------+ /// | address family | dest. length | source length | tos | /// +----------------+----------------+----------------+----------------+ /// | table | protocol | scope | type (kind) | /// +----------------+----------------+----------------+----------------+ /// | flags | /// +----------------+----------------+----------------+----------------+ /// ``` /// /// # Example /// /// ```rust /// extern crate netlink_packet_route; /// use netlink_packet_route::{constants::*, RouteFlags, RouteHeader}; /// /// fn main() { /// let mut hdr = RouteHeader::default(); /// assert_eq!(hdr.address_family, 0u8); /// assert_eq!(hdr.destination_prefix_length, 0u8); /// assert_eq!(hdr.source_prefix_length, 0u8); /// assert_eq!(hdr.tos, 0u8); /// assert_eq!(hdr.table, RT_TABLE_UNSPEC); /// assert_eq!(hdr.protocol, RTPROT_UNSPEC); /// assert_eq!(hdr.scope, RT_SCOPE_UNIVERSE); /// assert_eq!(hdr.kind, RTN_UNSPEC); /// assert_eq!(hdr.flags.bits(), 0u32); /// /// // set some values /// hdr.destination_prefix_length = 8; /// hdr.table = RT_TABLE_MAIN; /// hdr.protocol = RTPROT_KERNEL; /// hdr.scope = RT_SCOPE_NOWHERE; /// /// // ... /// } /// ``` #[derive(Debug, PartialEq, Eq, Hash, Clone, Default)] #[non_exhaustive] pub struct RouteHeader { /// Address family of the route: either [`AF_INET`] for IPv4 prefixes, or /// [`AF_INET6`] for IPv6 prefixes. pub address_family: u8, /// Prefix length of the destination subnet. /// /// Note that setting pub destination_prefix_length: u8, /// Prefix length of the source address. /// /// There could be multiple addresses from which a certain network is /// reachable. To decide which source address to use to reach and /// address in that network, the kernel rely on the route's /// source address for this destination. /// /// For instance, interface `if1` could have two addresses `10.0.0.1/24` /// and `10.0.0.128/24`, and we could have the following routes: /// /// ```no_rust /// 10.0.0.10/32 dev if1 scope link src 10.0.0.1 /// 10.0.0.11/32 dev if1 scope link src 10.0.0.1 /// 10.0.0.12/32 dev if1 scope link src 10.0.0.1 /// 10.0.0.0/24 dev if1 scope link src 10.0.0.128 /// ``` /// /// It means that for `10.0.0.10`, `10.0.0.11` and `10.0.0.12` the packets /// will be sent with `10.0.0.1` as source address, while for the rest /// of the `10.0.0.0/24` subnet, the source address will be /// `10.0.0.128` pub source_prefix_length: u8, /// TOS filter pub tos: u8, /// Routing table ID. It can be one of the `RT_TABLE_*` constants or a /// custom table number between 1 and 251 (included). Note that Linux /// supports routing table with an ID greater than 255, in which case /// this attribute will be set to [`RT_TABLE_COMPAT`] and an [`Nla::Table`] /// netlink attribute will be present in the message. pub table: u8, /// Protocol from which the route was learnt. It should be set to one of /// the `RTPROT_*` constants. pub protocol: u8, /// The scope of the area where the addresses in the destination subnet are /// valid. Predefined scope values are the `RT_SCOPE_*` constants. pub scope: u8, /// Route type. It should be set to one of the `RTN_*` constants. pub kind: u8, /// Flags when querying the kernel with a `RTM_GETROUTE` message. See /// [`RouteFlags`]. pub flags: RouteFlags, } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for RouteHeader { fn parse(buf: &RouteMessageBuffer<&'a T>) -> Result { Ok(RouteHeader { address_family: buf.address_family(), destination_prefix_length: buf.destination_prefix_length(), source_prefix_length: buf.source_prefix_length(), tos: buf.tos(), table: buf.table(), protocol: buf.protocol(), scope: buf.scope(), kind: buf.kind(), flags: RouteFlags::from_bits_truncate(buf.flags()), }) } } impl Emitable for RouteHeader { fn buffer_len(&self) -> usize { ROUTE_HEADER_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = RouteMessageBuffer::new(buffer); buffer.set_address_family(self.address_family); buffer.set_destination_prefix_length(self.destination_prefix_length); buffer.set_source_prefix_length(self.source_prefix_length); buffer.set_tos(self.tos); buffer.set_table(self.table); buffer.set_protocol(self.protocol); buffer.set_scope(self.scope); buffer.set_kind(self.kind); buffer.set_flags(self.flags.bits()); } } netlink-packet-route-0.17.0/src/rtnl/route/message.rs000064400000000000000000000070321046102023000206560ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::IpAddr; use anyhow::Context; use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; use crate::{nlas::route::Nla, RouteHeader, RouteMessageBuffer}; #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct RouteMessage { pub header: RouteHeader, pub nlas: Vec, } impl Emitable for RouteMessage { fn buffer_len(&self) -> usize { self.header.buffer_len() + self.nlas.as_slice().buffer_len() } fn emit(&self, buffer: &mut [u8]) { self.header.emit(buffer); self.nlas .as_slice() .emit(&mut buffer[self.header.buffer_len()..]); } } impl<'a, T: AsRef<[u8]> + 'a> Parseable> for RouteMessage { fn parse(buf: &RouteMessageBuffer<&'a T>) -> Result { Ok(RouteMessage { header: RouteHeader::parse(buf) .context("failed to parse route message header")?, nlas: Vec::::parse(buf) .context("failed to parse route message NLAs")?, }) } } impl<'a, T: AsRef<[u8]> + 'a> Parseable> for Vec { fn parse(buf: &RouteMessageBuffer<&'a T>) -> Result { let mut nlas = vec![]; for nla_buf in buf.nlas() { nlas.push(Nla::parse(&nla_buf?)?); } Ok(nlas) } } fn octets_to_addr(octets: &[u8]) -> Result { if octets.len() == 4 { let mut ary: [u8; 4] = Default::default(); ary.copy_from_slice(octets); Ok(IpAddr::from(ary)) } else if octets.len() == 16 { let mut ary: [u8; 16] = Default::default(); ary.copy_from_slice(octets); Ok(IpAddr::from(ary)) } else { Err(DecodeError::from("Cannot decode IP address")) } } impl RouteMessage { /// Returns the input interface index, if present. pub fn input_interface(&self) -> Option { self.nlas.iter().find_map(|nla| { if let Nla::Iif(v) = nla { Some(*v) } else { None } }) } /// Returns the output interface index, if present. pub fn output_interface(&self) -> Option { self.nlas.iter().find_map(|nla| { if let Nla::Oif(v) = nla { Some(*v) } else { None } }) } /// Returns the source address prefix, if present. pub fn source_prefix(&self) -> Option<(IpAddr, u8)> { self.nlas.iter().find_map(|nla| { if let Nla::Source(v) = nla { octets_to_addr(v) .ok() .map(|addr| (addr, self.header.source_prefix_length)) } else { None } }) } /// Returns the destination subnet prefix, if present. pub fn destination_prefix(&self) -> Option<(IpAddr, u8)> { self.nlas.iter().find_map(|nla| { if let Nla::Destination(v) = nla { octets_to_addr(v) .ok() .map(|addr| (addr, self.header.destination_prefix_length)) } else { None } }) } /// Returns the gateway address, if present. pub fn gateway(&self) -> Option { self.nlas.iter().find_map(|nla| { if let Nla::Gateway(v) = nla { octets_to_addr(v).ok() } else { None } }) } } netlink-packet-route-0.17.0/src/rtnl/route/mod.rs000064400000000000000000000002501046102023000200040ustar 00000000000000// SPDX-License-Identifier: MIT mod buffer; mod header; mod message; pub mod nlas; pub use self::{buffer::*, header::*, message::*, nlas::*}; #[cfg(test)] mod test; netlink-packet-route-0.17.0/src/rtnl/route/nlas/cache_info.rs000064400000000000000000000030731046102023000222460ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] pub struct CacheInfo { pub clntref: u32, pub last_use: u32, pub expires: u32, pub error: u32, pub used: u32, pub id: u32, pub ts: u32, pub ts_age: u32, } pub const CACHE_INFO_LEN: usize = 32; buffer!(CacheInfoBuffer(CACHE_INFO_LEN) { clntref: (u32, 0..4), last_use: (u32, 4..8), expires: (u32, 8..12), error: (u32, 12..16), used: (u32, 16..20), id: (u32, 20..24), ts: (u32, 24..28), ts_age: (u32, 28..32), }); impl> Parseable> for CacheInfo { fn parse(buf: &CacheInfoBuffer) -> Result { Ok(Self { clntref: buf.clntref(), last_use: buf.last_use(), expires: buf.expires(), error: buf.error(), used: buf.used(), id: buf.id(), ts: buf.ts(), ts_age: buf.ts_age(), }) } } impl Emitable for CacheInfo { fn buffer_len(&self) -> usize { CACHE_INFO_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = CacheInfoBuffer::new(buffer); buffer.set_clntref(self.clntref); buffer.set_last_use(self.last_use); buffer.set_expires(self.expires); buffer.set_error(self.error); buffer.set_used(self.used); buffer.set_id(self.id); buffer.set_ts(self.ts); buffer.set_ts_age(self.ts_age); } } netlink-packet-route-0.17.0/src/rtnl/route/nlas/metrics.rs000064400000000000000000000133531046102023000216400ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use std::mem::size_of; use crate::constants::*; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::parse_u32, traits::Parseable, DecodeError, }; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum Metrics { Unspec(Vec), Lock(u32), Mtu(u32), Window(u32), Rtt(u32), RttVar(u32), SsThresh(u32), Cwnd(u32), Advmss(u32), Reordering(u32), Hoplimit(u32), InitCwnd(u32), Features(u32), RtoMin(u32), InitRwnd(u32), QuickAck(u32), CcAlgo(u32), FastopenNoCookie(u32), Other(DefaultNla), } impl Nla for Metrics { #[rustfmt::skip] fn value_len(&self) -> usize { use self::Metrics::*; match *self { Unspec(ref bytes) => bytes.len(), Lock(_) | Mtu(_) | Window(_) | Rtt(_) | RttVar(_) | SsThresh(_) | Cwnd(_) | Advmss(_) | Reordering(_) | Hoplimit(_) | InitCwnd(_) | Features(_) | RtoMin(_) | InitRwnd(_) | QuickAck(_) | CcAlgo(_) | FastopenNoCookie(_) => size_of::(), Other(ref attr) => attr.value_len(), } } #[rustfmt::skip] fn emit_value(&self, buffer: &mut [u8]) { use self::Metrics::*; match *self { Unspec(ref bytes) => buffer.copy_from_slice(bytes.as_slice()), Lock(value) | Mtu(value) | Window(value) | Rtt(value) | RttVar(value) | SsThresh(value) | Cwnd(value) | Advmss(value) | Reordering(value) | Hoplimit(value) | InitCwnd(value) | Features(value) | RtoMin(value) | InitRwnd(value) | QuickAck(value) | CcAlgo(value) | FastopenNoCookie(value) => NativeEndian::write_u32(buffer, value), Other(ref attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { use self::Metrics::*; match *self { Unspec(_) => RTAX_UNSPEC, Lock(_) => RTAX_LOCK, Mtu(_) => RTAX_MTU, Window(_) => RTAX_WINDOW, Rtt(_) => RTAX_RTT, RttVar(_) => RTAX_RTTVAR, SsThresh(_) => RTAX_SSTHRESH, Cwnd(_) => RTAX_CWND, Advmss(_) => RTAX_ADVMSS, Reordering(_) => RTAX_REORDERING, Hoplimit(_) => RTAX_HOPLIMIT, InitCwnd(_) => RTAX_INITCWND, Features(_) => RTAX_FEATURES, RtoMin(_) => RTAX_RTO_MIN, InitRwnd(_) => RTAX_INITRWND, QuickAck(_) => RTAX_QUICKACK, CcAlgo(_) => RTAX_CC_ALGO, FastopenNoCookie(_) => RTAX_FASTOPEN_NO_COOKIE, Other(ref attr) => attr.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Metrics { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::Metrics::*; let payload = buf.value(); Ok(match buf.kind() { RTAX_UNSPEC => Unspec(payload.to_vec()), RTAX_LOCK => { Lock(parse_u32(payload).context("invalid RTAX_LOCK value")?) } RTAX_MTU => { Mtu(parse_u32(payload).context("invalid RTAX_MTU value")?) } RTAX_WINDOW => { Window(parse_u32(payload).context("invalid RTAX_WINDOW value")?) } RTAX_RTT => { Rtt(parse_u32(payload).context("invalid RTAX_RTT value")?) } RTAX_RTTVAR => { RttVar(parse_u32(payload).context("invalid RTAX_RTTVAR value")?) } RTAX_SSTHRESH => SsThresh( parse_u32(payload).context("invalid RTAX_SSTHRESH value")?, ), RTAX_CWND => { Cwnd(parse_u32(payload).context("invalid RTAX_CWND value")?) } RTAX_ADVMSS => { Advmss(parse_u32(payload).context("invalid RTAX_ADVMSS value")?) } RTAX_REORDERING => Reordering( parse_u32(payload).context("invalid RTAX_REORDERING value")?, ), RTAX_HOPLIMIT => Hoplimit( parse_u32(payload).context("invalid RTAX_HOPLIMIT value")?, ), RTAX_INITCWND => InitCwnd( parse_u32(payload).context("invalid RTAX_INITCWND value")?, ), RTAX_FEATURES => Features( parse_u32(payload).context("invalid RTAX_FEATURES value")?, ), RTAX_RTO_MIN => RtoMin( parse_u32(payload).context("invalid RTAX_RTO_MIN value")?, ), RTAX_INITRWND => InitRwnd( parse_u32(payload).context("invalid RTAX_INITRWND value")?, ), RTAX_QUICKACK => QuickAck( parse_u32(payload).context("invalid RTAX_QUICKACK value")?, ), RTAX_CC_ALGO => CcAlgo( parse_u32(payload).context("invalid RTAX_CC_ALGO value")?, ), RTAX_FASTOPEN_NO_COOKIE => FastopenNoCookie( parse_u32(payload) .context("invalid RTAX_FASTOPEN_NO_COOKIE value")?, ), _ => Other( DefaultNla::parse(buf) .context("invalid NLA value (unknown type) value")?, ), }) } } netlink-packet-route-0.17.0/src/rtnl/route/nlas/mfc_stats.rs000064400000000000000000000020171046102023000221500ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] pub struct MfcStats { pub packets: u64, pub bytes: u64, pub wrong_if: u64, } pub const MFC_STATS_LEN: usize = 24; buffer!(MfcStatsBuffer(MFC_STATS_LEN) { packets: (u64, 0..8), bytes: (u64, 8..16), wrong_if: (u64, 16..24), }); impl> Parseable> for MfcStats { fn parse(buf: &MfcStatsBuffer) -> Result { Ok(MfcStats { packets: buf.packets(), bytes: buf.bytes(), wrong_if: buf.wrong_if(), }) } } impl Emitable for MfcStats { fn buffer_len(&self) -> usize { MFC_STATS_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = MfcStatsBuffer::new(buffer); buffer.set_packets(self.packets); buffer.set_bytes(self.bytes); buffer.set_wrong_if(self.wrong_if); } } netlink-packet-route-0.17.0/src/rtnl/route/nlas/mod.rs000064400000000000000000000253111046102023000207460ustar 00000000000000// SPDX-License-Identifier: MIT mod cache_info; pub use self::cache_info::*; mod metrics; pub use self::metrics::*; mod mfc_stats; pub use self::mfc_stats::*; mod mpls_ip_tunnel; pub use self::mpls_ip_tunnel::*; mod next_hops; pub use self::next_hops::*; use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use crate::constants::*; use netlink_packet_utils::{ nla::{self, DefaultNla, NlaBuffer}, parsers::{parse_u16, parse_u32}, traits::Parseable, DecodeError, }; #[cfg(feature = "rich_nlas")] use netlink_packet_utils::traits::Emitable; /// Netlink attributes for `RTM_NEWROUTE`, `RTM_DELROUTE`, /// `RTM_GETROUTE` messages. #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum Nla { #[cfg(not(feature = "rich_nlas"))] Metrics(Vec), #[cfg(feature = "rich_nlas")] Metrics(Metrics), #[cfg(not(feature = "rich_nlas"))] MfcStats(Vec), #[cfg(feature = "rich_nlas")] MfcStats(MfcStats), #[cfg(not(feature = "rich_nlas"))] MultiPath(Vec), #[cfg(feature = "rich_nlas")] // See: https://codecave.cc/multipath-routing-in-linux-part-1.html MultiPath(Vec), #[cfg(not(feature = "rich_nlas"))] CacheInfo(Vec), #[cfg(feature = "rich_nlas")] CacheInfo(CacheInfo), Unspec(Vec), Destination(Vec), Source(Vec), Gateway(Vec), PrefSource(Vec), Session(Vec), MpAlgo(Vec), Via(Vec), NewDestination(Vec), Pref(Vec), Encap(Vec), Expires(Vec), Pad(Vec), Uid(Vec), TtlPropagate(Vec), EncapType(u16), Iif(u32), Oif(u32), Priority(u32), ProtocolInfo(u32), Flow(u32), Table(u32), Mark(u32), Other(DefaultNla), } impl nla::Nla for Nla { #[rustfmt::skip] fn value_len(&self) -> usize { use self::Nla::*; match *self { Unspec(ref bytes) | Destination(ref bytes) | Source(ref bytes) | Gateway(ref bytes) | PrefSource(ref bytes) | Session(ref bytes) | MpAlgo(ref bytes) | Via(ref bytes) | NewDestination(ref bytes) | Pref(ref bytes) | Encap(ref bytes) | Expires(ref bytes) | Pad(ref bytes) | Uid(ref bytes) | TtlPropagate(ref bytes) => bytes.len(), #[cfg(not(feature = "rich_nlas"))] CacheInfo(ref bytes) | MfcStats(ref bytes) | Metrics(ref bytes) | MultiPath(ref bytes) => bytes.len(), #[cfg(feature = "rich_nlas")] CacheInfo(ref cache_info) => cache_info.buffer_len(), #[cfg(feature = "rich_nlas")] MfcStats(ref stats) => stats.buffer_len(), #[cfg(feature = "rich_nlas")] Metrics(ref metrics) => metrics.buffer_len(), #[cfg(feature = "rich_nlas")] MultiPath(ref next_hops) => next_hops.iter().map(|nh| nh.buffer_len()).sum(), EncapType(_) => 2, Iif(_) | Oif(_) | Priority(_) | ProtocolInfo(_) | Flow(_) | Table(_) | Mark(_) => 4, Other(ref attr) => attr.value_len(), } } #[rustfmt::skip] fn emit_value(&self, buffer: &mut [u8]) { use self::Nla::*; match *self { Unspec(ref bytes) | Destination(ref bytes) | Source(ref bytes) | Gateway(ref bytes) | PrefSource(ref bytes) | Session(ref bytes) | MpAlgo(ref bytes) | Via(ref bytes) | NewDestination(ref bytes) | Pref(ref bytes) | Encap(ref bytes) | Expires(ref bytes) | Pad(ref bytes) | Uid(ref bytes) | TtlPropagate(ref bytes) => buffer.copy_from_slice(bytes.as_slice()), #[cfg(not(feature = "rich_nlas"))] MultiPath(ref bytes) | CacheInfo(ref bytes) | MfcStats(ref bytes) | Metrics(ref bytes) => buffer.copy_from_slice(bytes.as_slice()), #[cfg(feature = "rich_nlas")] CacheInfo(ref cache_info) => cache_info.emit(buffer), #[cfg(feature = "rich_nlas")] MfcStats(ref stats) => stats.emit(buffer), #[cfg(feature = "rich_nlas")] Metrics(ref metrics) => metrics.emit(buffer), #[cfg(feature = "rich_nlas")] MultiPath(ref next_hops) => { let mut offset = 0; for nh in next_hops { let len = nh.buffer_len(); nh.emit(&mut buffer[offset..offset+len]); offset += len } } EncapType(value) => NativeEndian::write_u16(buffer, value), Iif(value) | Oif(value) | Priority(value) | ProtocolInfo(value) | Flow(value) | Table(value) | Mark(value) => NativeEndian::write_u32(buffer, value), Other(ref attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { use self::Nla::*; match *self { Unspec(_) => RTA_UNSPEC, Destination(_) => RTA_DST, Source(_) => RTA_SRC, Iif(_) => RTA_IIF, Oif(_) => RTA_OIF, Gateway(_) => RTA_GATEWAY, Priority(_) => RTA_PRIORITY, PrefSource(_) => RTA_PREFSRC, Metrics(_) => RTA_METRICS, MultiPath(_) => RTA_MULTIPATH, ProtocolInfo(_) => RTA_PROTOINFO, Flow(_) => RTA_FLOW, CacheInfo(_) => RTA_CACHEINFO, Session(_) => RTA_SESSION, MpAlgo(_) => RTA_MP_ALGO, Table(_) => RTA_TABLE, Mark(_) => RTA_MARK, MfcStats(_) => RTA_MFC_STATS, Via(_) => RTA_VIA, NewDestination(_) => RTA_NEWDST, Pref(_) => RTA_PREF, EncapType(_) => RTA_ENCAP_TYPE, Encap(_) => RTA_ENCAP, Expires(_) => RTA_EXPIRES, Pad(_) => RTA_PAD, Uid(_) => RTA_UID, TtlPropagate(_) => RTA_TTL_PROPAGATE, Other(ref attr) => attr.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Nla { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::Nla::*; let payload = buf.value(); Ok(match buf.kind() { RTA_UNSPEC => Unspec(payload.to_vec()), RTA_DST => Destination(payload.to_vec()), RTA_SRC => Source(payload.to_vec()), RTA_GATEWAY => Gateway(payload.to_vec()), RTA_PREFSRC => PrefSource(payload.to_vec()), RTA_SESSION => Session(payload.to_vec()), RTA_MP_ALGO => MpAlgo(payload.to_vec()), RTA_VIA => Via(payload.to_vec()), RTA_NEWDST => NewDestination(payload.to_vec()), RTA_PREF => Pref(payload.to_vec()), RTA_ENCAP => Encap(payload.to_vec()), RTA_EXPIRES => Expires(payload.to_vec()), RTA_PAD => Pad(payload.to_vec()), RTA_UID => Uid(payload.to_vec()), RTA_TTL_PROPAGATE => TtlPropagate(payload.to_vec()), RTA_ENCAP_TYPE => EncapType( parse_u16(payload).context("invalid RTA_ENCAP_TYPE value")?, ), RTA_IIF => { Iif(parse_u32(payload).context("invalid RTA_IIF value")?) } RTA_OIF => { Oif(parse_u32(payload).context("invalid RTA_OIF value")?) } RTA_PRIORITY => Priority( parse_u32(payload).context("invalid RTA_PRIORITY value")?, ), RTA_PROTOINFO => ProtocolInfo( parse_u32(payload).context("invalid RTA_PROTOINFO value")?, ), RTA_FLOW => { Flow(parse_u32(payload).context("invalid RTA_FLOW value")?) } RTA_TABLE => { Table(parse_u32(payload).context("invalid RTA_TABLE value")?) } RTA_MARK => { Mark(parse_u32(payload).context("invalid RTA_MARK value")?) } #[cfg(not(feature = "rich_nlas"))] RTA_CACHEINFO => CacheInfo(payload.to_vec()), #[cfg(feature = "rich_nlas")] RTA_CACHEINFO => CacheInfo( cache_info::CacheInfo::parse( &CacheInfoBuffer::new_checked(payload) .context("invalid RTA_CACHEINFO value")?, ) .context("invalid RTA_CACHEINFO value")?, ), #[cfg(not(feature = "rich_nlas"))] RTA_MFC_STATS => MfcStats(payload.to_vec()), #[cfg(feature = "rich_nlas")] RTA_MFC_STATS => MfcStats( mfc_stats::MfcStats::parse( &MfcStatsBuffer::new_checked(payload) .context("invalid RTA_MFC_STATS value")?, ) .context("invalid RTA_MFC_STATS value")?, ), #[cfg(not(feature = "rich_nlas"))] RTA_METRICS => Metrics(payload.to_vec()), #[cfg(feature = "rich_nlas")] RTA_METRICS => Metrics( metrics::Metrics::parse( &NlaBuffer::new_checked(payload) .context("invalid RTA_METRICS value")?, ) .context("invalid RTA_METRICS value")?, ), #[cfg(not(feature = "rich_nlas"))] RTA_MULTIPATH => MultiPath(payload.to_vec()), #[cfg(feature = "rich_nlas")] RTA_MULTIPATH => { let mut next_hops = vec![]; let mut buf = payload; loop { let nh_buf = NextHopBuffer::new_checked(&buf) .context("invalid RTA_MULTIPATH value")?; let len = nh_buf.length() as usize; let nh = NextHop::parse(&nh_buf) .context("invalid RTA_MULTIPATH value")?; next_hops.push(nh); if buf.len() == len { break; } buf = &buf[len..]; } MultiPath(next_hops) } _ => Other( DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, ), }) } } netlink-packet-route-0.17.0/src/rtnl/route/nlas/mpls_ip_tunnel.rs000064400000000000000000000034431046102023000232210ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::parse_u8, traits::Parseable, DecodeError, }; use crate::constants::*; /// Netlink attributes for `RTA_ENCAP` with `RTA_ENCAP_TYPE` set to /// `LWTUNNEL_ENCAP_MPLS`. #[non_exhaustive] pub enum MplsIpTunnel { Destination(Vec), Ttl(u8), Other(DefaultNla), } impl Nla for MplsIpTunnel { fn value_len(&self) -> usize { use self::MplsIpTunnel::*; match self { Destination(bytes) => bytes.len(), Ttl(_) => 1, Other(attr) => attr.value_len(), } } fn kind(&self) -> u16 { use self::MplsIpTunnel::*; match self { Destination(_) => MPLS_IPTUNNEL_DST, Ttl(_) => MPLS_IPTUNNEL_TTL, Other(attr) => attr.kind(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::MplsIpTunnel::*; match self { Destination(bytes) => buffer.copy_from_slice(bytes.as_slice()), Ttl(ttl) => buffer[0] = *ttl, Other(attr) => attr.emit_value(buffer), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for MplsIpTunnel { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::MplsIpTunnel::*; let payload = buf.value(); Ok(match buf.kind() { MPLS_IPTUNNEL_DST => Destination(payload.to_vec()), MPLS_IPTUNNEL_TTL => { Ttl(parse_u8(payload) .context("invalid MPLS_IPTUNNEL_TTL value")?) } _ => Other( DefaultNla::parse(buf) .context("invalid NLA value (unknown type) value")?, ), }) } } netlink-packet-route-0.17.0/src/rtnl/route/nlas/next_hops.rs000064400000000000000000000076301046102023000222020ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use std::net::IpAddr; use crate::{constants, route::nlas::Nla}; use netlink_packet_utils::{ nla::{NlaBuffer, NlasIterator}, parsers::parse_ip, traits::{Emitable, Parseable}, DecodeError, }; bitflags! { #[non_exhaustive] pub struct NextHopFlags: u8 { const RTNH_F_DEAD = constants::RTNH_F_DEAD; const RTNH_F_PERVASIVE = constants::RTNH_F_PERVASIVE; const RTNH_F_ONLINK = constants::RTNH_F_ONLINK; const RTNH_F_OFFLOAD = constants::RTNH_F_OFFLOAD; const RTNH_F_LINKDOWN = constants::RTNH_F_LINKDOWN; const RTNH_F_UNRESOLVED = constants::RTNH_F_UNRESOLVED; } } const PAYLOAD_OFFSET: usize = 8; buffer!(NextHopBuffer { length: (u16, 0..2), flags: (u8, 2), hops: (u8, 3), interface_id: (u32, 4..8), payload: (slice, PAYLOAD_OFFSET..), }); impl> NextHopBuffer { pub fn new_checked(buffer: T) -> Result { let packet = Self::new(buffer); packet.check_buffer_length()?; Ok(packet) } fn check_buffer_length(&self) -> Result<(), DecodeError> { let len = self.buffer.as_ref().len(); if len < PAYLOAD_OFFSET { return Err(format!( "invalid NextHopBuffer: length {len} < {PAYLOAD_OFFSET}" ) .into()); } if len < self.length() as usize { return Err(format!( "invalid NextHopBuffer: length {} < {}", len, 8 + self.length() ) .into()); } Ok(()) } } impl<'a, T: AsRef<[u8]> + ?Sized> NextHopBuffer<&'a T> { pub fn nlas( &self, ) -> impl Iterator, DecodeError>> { NlasIterator::new( &self.payload()[..(self.length() as usize - PAYLOAD_OFFSET)], ) } } #[derive(Debug, Clone, Eq, PartialEq)] #[non_exhaustive] pub struct NextHop { /// Next-hop flags (see [`NextHopFlags`]) pub flags: NextHopFlags, /// Next-hop priority pub hops: u8, /// Interface index for the next-hop pub interface_id: u32, /// Attributes pub nlas: Vec, } impl<'a, T: AsRef<[u8]>> Parseable> for NextHop { fn parse(buf: &NextHopBuffer<&T>) -> Result { let nlas = Vec::::parse( &NextHopBuffer::new_checked(buf.buffer) .context("cannot parse route attributes in next-hop")?, ) .context("cannot parse route attributes in next-hop")?; Ok(NextHop { flags: NextHopFlags::from_bits_truncate(buf.flags()), hops: buf.hops(), interface_id: buf.interface_id(), nlas, }) } } impl<'a, T: AsRef<[u8]> + 'a> Parseable> for Vec { fn parse(buf: &NextHopBuffer<&'a T>) -> Result { let mut nlas = vec![]; for nla_buf in buf.nlas() { nlas.push(Nla::parse(&nla_buf?)?); } Ok(nlas) } } impl Emitable for NextHop { fn buffer_len(&self) -> usize { // len, flags, hops and interface id fields PAYLOAD_OFFSET + self.nlas.as_slice().buffer_len() } fn emit(&self, buffer: &mut [u8]) { let mut nh_buffer = NextHopBuffer::new(buffer); nh_buffer.set_length(self.buffer_len() as u16); nh_buffer.set_flags(self.flags.bits()); nh_buffer.set_hops(self.hops); nh_buffer.set_interface_id(self.interface_id); self.nlas.as_slice().emit(nh_buffer.payload_mut()) } } impl NextHop { /// Gateway address (it is actually encoded as an `RTA_GATEWAY` nla) pub fn gateway(&self) -> Option { self.nlas.iter().find_map(|nla| { if let Nla::Gateway(ip) = nla { parse_ip(ip).ok() } else { None } }) } } netlink-packet-route-0.17.0/src/rtnl/route/test.rs000064400000000000000000000104651046102023000202150ustar 00000000000000// SPDX-License-Identifier: MIT #[cfg(feature = "rich_nlas")] mod test_rich_nlas { use crate::rtnl::route::{ nlas::{NextHop, NextHopFlags, Nla}, RouteFlags, RouteMessage, RouteMessageBuffer, }; use netlink_packet_utils::traits::{Emitable, Parseable}; use std::net::Ipv6Addr; #[rustfmt::skip] static ROUTE_MSG: [u8; 100] = [ 0x0a, // address family 0x40, // length of destination 0x00, // length of source 0x00, // TOS 0xfe, // routing table id 0x03, // routing protocol (boot) 0x00, // route origin (global) 0x01, // gateway or direct route 0x00, 0x00, 0x00, 0x00, // Route destination address NLA 0x14, 0x00, // Length (20) 0x01, 0x00, // Type // Value 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // RTA_MULTIPATH attribute 0x44, 0x00, // Length (68) 0x09, 0x00, // Type // next-hop 1 0x1c, 0x00, // length (28) 0x00, // flags 0x00, // hops 0x00, 0x00, 0x00, 0x00, // interface ID // nested RTA_GATEWAY 0x14, 0x00, // Length (14) 0x05, 0x00, // Type // Value 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // next-hop 2 0x1c, 0x00, // length (28) 0x00, // flags 0x00, // hops 0x00, 0x00, 0x00, 0x00, // interface ID // nested RTA_GATEWAY 0x14, 0x00, // Length (14) 0x05, 0x00, // Type // Value 0xfc, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // next-hop 3 0x08, 0x00, // length (8) 0x00, // flags 0x00, // hops 0x02, 0x00, 0x00, 0x00, // interface ID ]; fn route_message() -> RouteMessage { let mut msg = RouteMessage::default(); msg.header.address_family = 0x0a; msg.header.destination_prefix_length = 0x40; msg.header.source_prefix_length = 0; msg.header.tos = 0; msg.header.table = 0xfe; msg.header.protocol = 0x03; msg.header.scope = 0x00; msg.header.kind = 0x01; msg.header.flags = RouteFlags::empty(); msg.nlas = vec![ Nla::Destination( "1001::".parse::().unwrap().octets().to_vec(), ), Nla::MultiPath(vec![ NextHop { flags: NextHopFlags::empty(), hops: 0, interface_id: 0, nlas: vec![Nla::Gateway( "fc00::1" .parse::() .unwrap() .octets() .to_vec(), )], }, NextHop { flags: NextHopFlags::empty(), hops: 0, interface_id: 0, nlas: vec![Nla::Gateway( "fc01::1" .parse::() .unwrap() .octets() .to_vec(), )], }, NextHop { flags: NextHopFlags::empty(), hops: 0, interface_id: 2, nlas: vec![], }, ]), ]; msg } #[test] fn parse_message_with_multipath_nla() { let expected = route_message(); let actual = RouteMessage::parse( &RouteMessageBuffer::new_checked(&&ROUTE_MSG[..]).unwrap(), ) .unwrap(); assert_eq!(actual, expected); } #[test] fn emit_message_with_multipath_nla() { let msg = route_message(); let mut buf = vec![0; 100]; assert_eq!(msg.buffer_len(), 100); msg.emit(&mut buf[..]); assert_eq!(buf, ROUTE_MSG); } } netlink-packet-route-0.17.0/src/rtnl/rule/buffer.rs000064400000000000000000000012441046102023000203130ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::{NlaBuffer, NlasIterator}, DecodeError, }; pub const RULE_HEADER_LEN: usize = 12; buffer!(RuleMessageBuffer(RULE_HEADER_LEN) { family: (u8, 0), dst_len: (u8, 1), src_len: (u8, 2), tos: (u8, 3), table: (u8, 4), reserve_1: (u8, 5), reserve_2: (u8, 6), action: (u8, 7), flags: (u32, 8..RULE_HEADER_LEN), payload: (slice, RULE_HEADER_LEN..), }); impl<'a, T: AsRef<[u8]> + ?Sized> RuleMessageBuffer<&'a T> { pub fn nlas( &self, ) -> impl Iterator, DecodeError>> { NlasIterator::new(self.payload()) } } netlink-packet-route-0.17.0/src/rtnl/rule/header.rs000064400000000000000000000042071046102023000202740ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; use super::{buffer::RuleMessageBuffer, RULE_HEADER_LEN}; use crate::constants::*; bitflags! { #[non_exhaustive] pub struct RuleFlags: u32 { const FIB_RULE_PERMANENT = FIB_RULE_PERMANENT; const FIB_RULE_INVERT = FIB_RULE_INVERT; const FIB_RULE_UNRESOLVED = FIB_RULE_UNRESOLVED; const FIB_RULE_IIF_DETACHED = FIB_RULE_IIF_DETACHED; const FIB_RULE_DEV_DETACHED = FIB_RULE_DEV_DETACHED; const FIB_RULE_OIF_DETACHED = FIB_RULE_OIF_DETACHED; const FIB_RULE_FIND_SADDR = FIB_RULE_FIND_SADDR; } } impl Default for RuleFlags { fn default() -> Self { Self::empty() } } // see https://github.com/torvalds/linux/blob/master/include/uapi/linux/fib_rules.h // see https://github.com/torvalds/linux/blob/master/include/net/fib_rules.h #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct RuleHeader { /// Address family: one of the `AF_*` constants. pub family: u8, pub dst_len: u8, pub src_len: u8, pub tos: u8, /// RT_TABLE_* pub table: u8, /// FR_ACT_* pub action: u8, /// fib rule flags pub flags: u32, } impl Emitable for RuleHeader { fn buffer_len(&self) -> usize { RULE_HEADER_LEN } fn emit(&self, buffer: &mut [u8]) { let mut packet = RuleMessageBuffer::new(buffer); packet.set_family(self.family); packet.set_dst_len(self.dst_len); packet.set_src_len(self.src_len); packet.set_flags(self.flags); packet.set_table(self.table); packet.set_tos(self.tos); packet.set_action(self.action); } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for RuleHeader { fn parse(buf: &RuleMessageBuffer<&'a T>) -> Result { Ok(RuleHeader { family: buf.family(), dst_len: buf.dst_len(), src_len: buf.src_len(), tos: buf.tos(), table: buf.table(), action: buf.action(), flags: buf.flags(), }) } } netlink-packet-route-0.17.0/src/rtnl/rule/message.rs000064400000000000000000000026051046102023000204700ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; use super::{buffer::RuleMessageBuffer, header::RuleHeader, nlas::Nla}; #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct RuleMessage { pub header: RuleHeader, pub nlas: Vec, } impl Emitable for RuleMessage { fn buffer_len(&self) -> usize { self.header.buffer_len() + self.nlas.as_slice().buffer_len() } fn emit(&self, buffer: &mut [u8]) { self.header.emit(buffer); self.nlas .as_slice() .emit(&mut buffer[self.header.buffer_len()..]); } } impl<'a, T: AsRef<[u8]> + 'a> Parseable> for RuleMessage { fn parse(buf: &RuleMessageBuffer<&'a T>) -> Result { let header = RuleHeader::parse(buf) .context("failed to parse link message header")?; let nlas = Vec::::parse(buf) .context("failed to parse link message NLAs")?; Ok(RuleMessage { header, nlas }) } } impl<'a, T: AsRef<[u8]> + 'a> Parseable> for Vec { fn parse(buf: &RuleMessageBuffer<&'a T>) -> Result { let mut nlas = vec![]; for nla_buf in buf.nlas() { nlas.push(Nla::parse(&nla_buf?)?); } Ok(nlas) } } netlink-packet-route-0.17.0/src/rtnl/rule/mod.rs000064400000000000000000000002541046102023000176210ustar 00000000000000// SPDX-License-Identifier: MIT pub mod buffer; pub mod header; pub mod message; pub mod nlas; pub use buffer::*; pub use header::*; pub use message::*; pub use nlas::*; netlink-packet-route-0.17.0/src/rtnl/rule/nlas/mod.rs000064400000000000000000000150561046102023000205640ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ byteorder::{ByteOrder, NativeEndian}, nla::{self, DefaultNla, NlaBuffer}, parsers::{parse_string, parse_u32, parse_u8}, DecodeError, Parseable, }; use crate::{ FRA_DPORT_RANGE, FRA_DST, FRA_FLOW, FRA_FWMARK, FRA_FWMASK, FRA_GOTO, FRA_IIFNAME, FRA_IP_PROTO, FRA_L3MDEV, FRA_OIFNAME, FRA_PAD, FRA_PRIORITY, FRA_PROTOCOL, FRA_SPORT_RANGE, FRA_SRC, FRA_SUPPRESS_IFGROUP, FRA_SUPPRESS_PREFIXLEN, FRA_TABLE, FRA_TUN_ID, FRA_UID_RANGE, FRA_UNSPEC, }; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum Nla { Unspec(Vec), /// destination address Destination(Vec), /// source address Source(Vec), /// input interface name Iifname(String), /// target to jump to when used with rule action `FR_ACT_GOTO` Goto(u32), Priority(u32), FwMark(u32), FwMask(u32), /// flow class id, Flow(u32), TunId(u32), SuppressIfGroup(u32), SuppressPrefixLen(u32), Table(u32), /// output interface name OifName(String), Pad(Vec), /// iif or oif is l3mdev goto its table L3MDev(u8), UidRange(Vec), /// RTPROT_* Protocol(u8), /// AF_* IpProto(u8), SourcePortRange(Vec), DestinationPortRange(Vec), Other(DefaultNla), } impl nla::Nla for Nla { fn value_len(&self) -> usize { use self::Nla::*; match self { Unspec(ref bytes) | Destination(ref bytes) | Source(ref bytes) | Pad(ref bytes) | UidRange(ref bytes) | SourcePortRange(ref bytes) | DestinationPortRange(ref bytes) => bytes.len(), Iifname(ref s) | OifName(ref s) => s.as_bytes().len() + 1, Priority(_) | FwMark(_) | FwMask(_) | Flow(_) | TunId(_) | Goto(_) | SuppressIfGroup(_) | SuppressPrefixLen(_) | Table(_) => 4, L3MDev(_) | Protocol(_) | IpProto(_) => 1, Other(attr) => attr.value_len(), } } fn kind(&self) -> u16 { use self::Nla::*; match self { Unspec(_) => FRA_UNSPEC, Destination(_) => FRA_DST, Source(_) => FRA_SRC, Iifname(_) => FRA_IIFNAME, Goto(_) => FRA_GOTO, Priority(_) => FRA_PRIORITY, FwMark(_) => FRA_FWMARK, FwMask(_) => FRA_FWMASK, Flow(_) => FRA_FLOW, TunId(_) => FRA_TUN_ID, SuppressIfGroup(_) => FRA_SUPPRESS_IFGROUP, SuppressPrefixLen(_) => FRA_SUPPRESS_PREFIXLEN, Table(_) => FRA_TABLE, OifName(_) => FRA_OIFNAME, Pad(_) => FRA_PAD, L3MDev(_) => FRA_L3MDEV, UidRange(_) => FRA_UID_RANGE, Protocol(_) => FRA_PROTOCOL, IpProto(_) => FRA_IP_PROTO, SourcePortRange(_) => FRA_SPORT_RANGE, DestinationPortRange(_) => FRA_DPORT_RANGE, Other(attr) => attr.kind(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::Nla::*; match self { Unspec(ref bytes) | Destination(ref bytes) | Source(ref bytes) | Pad(ref bytes) | UidRange(ref bytes) | SourcePortRange(ref bytes) | DestinationPortRange(ref bytes) => { buffer.copy_from_slice(bytes.as_slice()) } Iifname(ref s) | OifName(ref s) => { buffer[..s.len()].copy_from_slice(s.as_bytes()) } Priority(value) | FwMark(value) | FwMask(value) | Flow(value) | TunId(value) | Goto(value) | SuppressIfGroup(value) | SuppressPrefixLen(value) | Table(value) => NativeEndian::write_u32(buffer, *value), L3MDev(value) | Protocol(value) | IpProto(value) => { buffer[0] = *value } Other(attr) => attr.emit_value(buffer), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Nla { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use Nla::*; let payload = buf.value(); Ok(match buf.kind() { FRA_UNSPEC => Unspec(payload.to_vec()), FRA_DST => Destination(payload.to_vec()), FRA_SRC => Source(payload.to_vec()), FRA_IIFNAME => Iifname( parse_string(payload).context("invalid FRA_IIFNAME value")?, ), FRA_GOTO => { Goto(parse_u32(payload).context("invalid FRA_GOTO value")?) } FRA_PRIORITY => Priority( parse_u32(payload).context("invalid FRA_PRIORITY value")?, ), FRA_FWMARK => { FwMark(parse_u32(payload).context("invalid FRA_FWMARK value")?) } FRA_FLOW => { Flow(parse_u32(payload).context("invalid FRA_FLOW value")?) } FRA_TUN_ID => { TunId(parse_u32(payload).context("invalid FRA_TUN_ID value")?) } FRA_SUPPRESS_IFGROUP => SuppressIfGroup( parse_u32(payload) .context("invalid FRA_SUPPRESS_IFGROUP value")?, ), FRA_SUPPRESS_PREFIXLEN => SuppressPrefixLen( parse_u32(payload) .context("invalid FRA_SUPPRESS_PREFIXLEN value")?, ), FRA_TABLE => { Table(parse_u32(payload).context("invalid FRA_TABLE value")?) } FRA_FWMASK => { FwMask(parse_u32(payload).context("invalid FRA_FWMASK value")?) } FRA_OIFNAME => OifName( parse_string(payload).context("invalid FRA_OIFNAME value")?, ), FRA_PAD => Pad(payload.to_vec()), FRA_L3MDEV => { L3MDev(parse_u8(payload).context("invalid FRA_L3MDEV value")?) } FRA_UID_RANGE => UidRange(payload.to_vec()), FRA_PROTOCOL => Protocol( parse_u8(payload).context("invalid FRA_PROTOCOL value")?, ), FRA_IP_PROTO => IpProto( parse_u8(payload).context("invalid FRA_IP_PROTO value")?, ), FRA_SPORT_RANGE => SourcePortRange(payload.to_vec()), FRA_DPORT_RANGE => DestinationPortRange(payload.to_vec()), _ => Other( DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, ), }) } } netlink-packet-route-0.17.0/src/rtnl/tc/buffer.rs000064400000000000000000000011701046102023000177500ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::{NlaBuffer, NlasIterator}, DecodeError, }; pub const TC_HEADER_LEN: usize = 20; buffer!(TcMessageBuffer(TC_HEADER_LEN) { family: (u8, 0), pad1: (u8, 1), pad2: (u16, 2..4), index: (i32, 4..8), handle: (u32, 8..12), parent: (u32, 12..16), info: (u32, 16..TC_HEADER_LEN), payload: (slice, TC_HEADER_LEN..), }); impl<'a, T: AsRef<[u8]> + ?Sized> TcMessageBuffer<&'a T> { pub fn nlas( &self, ) -> impl Iterator, DecodeError>> { NlasIterator::new(self.payload()) } } netlink-packet-route-0.17.0/src/rtnl/tc/constants.rs000064400000000000000000000063461046102023000205250ustar 00000000000000// SPDX-License-Identifier: MIT /// Handles pub const TC_H_MAJ_MASK: u32 = 0xFFFF0000; pub const TC_H_MIN_MASK: u32 = 0x0000FFFF; #[macro_export] macro_rules! TC_H_MAKE { ($maj: expr, $min: expr) => { ($maj & TC_H_MAJ_MASK) | ($min & TC_H_MIN_MASK) }; } pub const TC_H_UNSPEC: u32 = 0; pub const TC_H_ROOT: u32 = 0xFFFFFFFF; pub const TC_H_INGRESS: u32 = 0xFFFFFFF1; pub const TC_H_CLSACT: u32 = TC_H_INGRESS; pub const TC_H_MIN_PRIORITY: u32 = 0xFFE0; pub const TC_H_MIN_INGRESS: u32 = 0xFFF2; pub const TC_H_MIN_EGRESS: u32 = 0xFFF3; // matchall filters pub const TCA_MATCHALL_UNSPEC: u16 = 0; pub const TCA_MATCHALL_CLASSID: u16 = 1; pub const TCA_MATCHALL_ACT: u16 = 2; pub const TCA_MATCHALL_FLAGS: u16 = 3; pub const TCA_MATCHALL_PCNT: u16 = 4; /// U32 filters pub const TCA_U32_UNSPEC: u16 = 0; pub const TCA_U32_CLASSID: u16 = 1; pub const TCA_U32_HASH: u16 = 2; pub const TCA_U32_LINK: u16 = 3; pub const TCA_U32_DIVISOR: u16 = 4; pub const TCA_U32_SEL: u16 = 5; pub const TCA_U32_POLICE: u16 = 6; pub const TCA_U32_ACT: u16 = 7; pub const TCA_U32_INDEV: u16 = 8; pub const TCA_U32_PCNT: u16 = 9; pub const TCA_U32_MARK: u16 = 10; pub const TCA_U32_FLAGS: u16 = 11; pub const TCA_U32_PAD: u16 = 12; pub const TCA_U32_MAX: u16 = TCA_U32_PAD; /// U32 Flags pub const TC_U32_TERMINAL: u8 = 1; pub const TC_U32_OFFSET: u8 = 2; pub const TC_U32_VAROFFSET: u8 = 4; pub const TC_U32_EAT: u8 = 8; pub const TC_U32_MAXDEPTH: u8 = 8; /// Action attributes pub const TCA_ACT_UNSPEC: u16 = 0; pub const TCA_ACT_KIND: u16 = 1; pub const TCA_ACT_OPTIONS: u16 = 2; pub const TCA_ACT_INDEX: u16 = 3; pub const TCA_ACT_STATS: u16 = 4; pub const TCA_ACT_PAD: u16 = 5; pub const TCA_ACT_COOKIE: u16 = 6; //TODO(wllenyj): Why not subtract 1? See `linux/pkt_cls.h` for original // definition. pub const TCA_ACT_MAX: u16 = 7; pub const TCA_OLD_COMPAT: u16 = TCA_ACT_MAX + 1; pub const TCA_ACT_MAX_PRIO: u16 = 32; pub const TCA_ACT_BIND: u16 = 1; pub const TCA_ACT_NOBIND: u16 = 0; pub const TCA_ACT_UNBIND: u16 = 1; pub const TCA_ACT_NOUNBIND: u16 = 0; pub const TCA_ACT_REPLACE: u16 = 1; pub const TCA_ACT_NOREPLACE: u16 = 0; pub const TC_ACT_UNSPEC: i32 = -1; pub const TC_ACT_OK: i32 = 0; pub const TC_ACT_RECLASSIFY: i32 = 1; pub const TC_ACT_SHOT: i32 = 2; pub const TC_ACT_PIPE: i32 = 3; pub const TC_ACT_STOLEN: i32 = 4; pub const TC_ACT_QUEUED: i32 = 5; pub const TC_ACT_REPEAT: i32 = 6; pub const TC_ACT_REDIRECT: i32 = 7; pub const TC_ACT_TRAP: i32 = 8; pub const TC_ACT_VALUE_MAX: i32 = TC_ACT_TRAP; pub const TC_ACT_JUMP: i32 = 0x10000000; pub const TCA_ACT_TAB: u16 = 1; // TCA_ROOT_TAB pub const TCAA_MAX: u16 = 1; /// Mirred action attr pub const TCA_MIRRED_UNSPEC: u16 = 0; pub const TCA_MIRRED_TM: u16 = 1; pub const TCA_MIRRED_PARMS: u16 = 2; pub const TCA_MIRRED_PAD: u16 = 3; pub const TCA_MIRRED_MAX: u16 = TCA_MIRRED_PAD; pub const TCA_EGRESS_REDIR: i32 = 1; /* packet redirect to EGRESS */ pub const TCA_EGRESS_MIRROR: i32 = 2; /* mirror packet to EGRESS */ pub const TCA_INGRESS_REDIR: i32 = 3; /* packet redirect to INGRESS */ pub const TCA_INGRESS_MIRROR: i32 = 4; /* mirror packet to INGRESS */ /// NAT action attr pub const TCA_NAT_UNSPEC: u16 = 0; pub const TCA_NAT_PARMS: u16 = 1; pub const TCA_NAT_TM: u16 = 2; pub const TCA_NAT_FLAG_EGRESS: u32 = 1; netlink-packet-route-0.17.0/src/rtnl/tc/message.rs000064400000000000000000000122441046102023000201270ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, NlasIterator}, parsers::{parse_string, parse_u8}, traits::{Emitable, Parseable, ParseableParametrized}, DecodeError, }; use crate::{ constants::*, nlas::tc::{Nla, Stats, Stats2, StatsBuffer, TcOpt}, TcMessageBuffer, TC_HEADER_LEN, }; #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct TcMessage { pub header: TcHeader, pub nlas: Vec, } impl TcMessage { pub fn into_parts(self) -> (TcHeader, Vec) { (self.header, self.nlas) } pub fn from_parts(header: TcHeader, nlas: Vec) -> Self { TcMessage { header, nlas } } /// Create a new `TcMessage` with the given index pub fn with_index(index: i32) -> Self { Self { header: TcHeader { index, ..Default::default() }, nlas: Vec::new(), } } } #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct TcHeader { pub family: u8, // Interface index pub index: i32, // Qdisc handle pub handle: u32, // Parent Qdisc pub parent: u32, pub info: u32, } impl Emitable for TcHeader { fn buffer_len(&self) -> usize { TC_HEADER_LEN } fn emit(&self, buffer: &mut [u8]) { let mut packet = TcMessageBuffer::new(buffer); packet.set_family(self.family); packet.set_index(self.index); packet.set_handle(self.handle); packet.set_parent(self.parent); packet.set_info(self.info); } } impl Emitable for TcMessage { fn buffer_len(&self) -> usize { self.header.buffer_len() + self.nlas.as_slice().buffer_len() } fn emit(&self, buffer: &mut [u8]) { self.header.emit(buffer); self.nlas .as_slice() .emit(&mut buffer[self.header.buffer_len()..]); } } impl> Parseable> for TcHeader { fn parse(buf: &TcMessageBuffer) -> Result { Ok(Self { family: buf.family(), index: buf.index(), handle: buf.handle(), parent: buf.parent(), info: buf.info(), }) } } impl<'a, T: AsRef<[u8]> + 'a> Parseable> for TcMessage { fn parse(buf: &TcMessageBuffer<&'a T>) -> Result { Ok(Self { header: TcHeader::parse(buf) .context("failed to parse tc message header")?, nlas: Vec::::parse(buf) .context("failed to parse tc message NLAs")?, }) } } impl<'a, T: AsRef<[u8]> + 'a> Parseable> for Vec { fn parse(buf: &TcMessageBuffer<&'a T>) -> Result { let mut nlas = vec![]; let mut kind = String::new(); for nla_buf in buf.nlas() { let buf = nla_buf.context("invalid tc nla")?; let payload = buf.value(); let nla = match buf.kind() { TCA_UNSPEC => Nla::Unspec(payload.to_vec()), TCA_KIND => { kind = parse_string(payload).context("invalid TCA_KIND")?; Nla::Kind(kind.clone()) } TCA_OPTIONS => { let mut nlas = vec![]; for nla in NlasIterator::new(payload) { let nla = nla.context("invalid TCA_OPTIONS")?; nlas.push( TcOpt::parse_with_param(&nla, &kind) .context("failed to parse TCA_OPTIONS")?, ) } Nla::Options(nlas) } TCA_STATS => Nla::Stats( Stats::parse( &StatsBuffer::new_checked(payload) .context("invalid TCA_STATS")?, ) .context("failed to parse TCA_STATS")?, ), TCA_XSTATS => Nla::XStats(payload.to_vec()), TCA_RATE => Nla::Rate(payload.to_vec()), TCA_FCNT => Nla::Fcnt(payload.to_vec()), TCA_STATS2 => { let mut nlas = vec![]; for nla in NlasIterator::new(payload) { let nla = nla.context("invalid TCA_STATS2")?; nlas.push( Stats2::parse(&nla) .context("failed to parse TCA_STATS2")?, ); } Nla::Stats2(nlas) } TCA_STAB => Nla::Stab(payload.to_vec()), TCA_CHAIN => Nla::Chain(payload.to_vec()), TCA_HW_OFFLOAD => Nla::HwOffload( parse_u8(payload) .context("failed to parse TCA_HW_OFFLOAD")?, ), _ => Nla::Other( DefaultNla::parse(&buf) .context("failed to parse tc nla")?, ), }; nlas.push(nla); } Ok(nlas) } } netlink-packet-route-0.17.0/src/rtnl/tc/mod.rs000064400000000000000000000002441046102023000172570ustar 00000000000000// SPDX-License-Identifier: MIT mod buffer; pub mod constants; mod message; pub mod nlas; pub use self::{buffer::*, message::*, nlas::*}; #[cfg(test)] mod test; netlink-packet-route-0.17.0/src/rtnl/tc/nlas/action/mirred.rs000064400000000000000000000067031046102023000222020ustar 00000000000000// SPDX-License-Identifier: MIT /// Mirred action /// /// The mirred action allows packet mirroring (copying) or /// redirecting (stealing) the packet it receives. Mirroring is what /// is sometimes referred to as Switch Port Analyzer (SPAN) and is /// commonly used to analyze and/or debug flows. use netlink_packet_utils::{ nla::{self, DefaultNla, NlaBuffer}, traits::{Emitable, Parseable}, DecodeError, }; use crate::tc::{constants::*, TC_GEN_BUF_LEN}; pub const KIND: &str = "mirred"; pub const TC_MIRRED_BUF_LEN: usize = TC_GEN_BUF_LEN + 8; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum Nla { Unspec(Vec), Tm(Vec), Parms(TcMirred), Other(DefaultNla), } impl nla::Nla for Nla { fn value_len(&self) -> usize { use self::Nla::*; match self { Unspec(bytes) | Tm(bytes) => bytes.len(), Parms(_) => TC_MIRRED_BUF_LEN, Other(attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::Nla::*; match self { Unspec(bytes) | Tm(bytes) => { buffer.copy_from_slice(bytes.as_slice()) } Parms(p) => p.emit(buffer), Other(attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { use self::Nla::*; match self { Unspec(_) => TCA_MIRRED_UNSPEC, Tm(_) => TCA_MIRRED_TM, Parms(_) => TCA_MIRRED_PARMS, Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Nla { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::Nla::*; let payload = buf.value(); Ok(match buf.kind() { TCA_MIRRED_UNSPEC => Unspec(payload.to_vec()), TCA_MIRRED_TM => Tm(payload.to_vec()), TCA_MIRRED_PARMS => { Parms(TcMirred::parse(&TcMirredBuffer::new_checked(payload)?)?) } _ => Other(DefaultNla::parse(buf)?), }) } } #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct TcMirred { pub index: u32, pub capab: u32, pub action: i32, pub refcnt: i32, pub bindcnt: i32, pub eaction: i32, pub ifindex: u32, } buffer!(TcMirredBuffer(TC_MIRRED_BUF_LEN) { index: (u32, 0..4), capab: (u32, 4..8), action: (i32, 8..12), refcnt: (i32, 12..16), bindcnt: (i32, 16..20), eaction: (i32, TC_GEN_BUF_LEN..(TC_GEN_BUF_LEN + 4)), ifindex: (u32, (TC_GEN_BUF_LEN + 4)..TC_MIRRED_BUF_LEN), }); impl Emitable for TcMirred { fn buffer_len(&self) -> usize { TC_MIRRED_BUF_LEN } fn emit(&self, buffer: &mut [u8]) { let mut packet = TcMirredBuffer::new(buffer); packet.set_index(self.index); packet.set_capab(self.capab); packet.set_action(self.action); packet.set_refcnt(self.refcnt); packet.set_bindcnt(self.bindcnt); packet.set_eaction(self.eaction); packet.set_ifindex(self.ifindex); } } impl> Parseable> for TcMirred { fn parse(buf: &TcMirredBuffer) -> Result { Ok(Self { index: buf.index(), capab: buf.capab(), action: buf.action(), refcnt: buf.refcnt(), bindcnt: buf.bindcnt(), eaction: buf.eaction(), ifindex: buf.ifindex(), }) } } netlink-packet-route-0.17.0/src/rtnl/tc/nlas/action/mod.rs000064400000000000000000000165451046102023000215040ustar 00000000000000// SPDX-License-Identifier: MIT pub mod mirred; pub mod nat; use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{self, DefaultNla, NlaBuffer, NlasIterator, NLA_F_NESTED}, parsers::{parse_string, parse_u32}, traits::{Emitable, Parseable, ParseableParametrized}, DecodeError, }; use crate::tc::{constants::*, Stats2}; pub const TC_GEN_BUF_LEN: usize = 20; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct Action { pub tab: u16, pub nlas: Vec, } impl Default for Action { fn default() -> Self { Self { tab: TCA_ACT_TAB, nlas: Vec::new(), } } } impl nla::Nla for Action { fn value_len(&self) -> usize { self.nlas.as_slice().buffer_len() } fn emit_value(&self, buffer: &mut [u8]) { self.nlas.as_slice().emit(buffer) } fn kind(&self) -> u16 { self.tab } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Action { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; let mut kind = String::new(); for iter in NlasIterator::new(buf.value()) { let buf = iter.context("invalid action nla")?; let payload = buf.value(); nlas.push(match buf.kind() { TCA_ACT_UNSPEC => ActNla::Unspec(payload.to_vec()), TCA_ACT_KIND => { kind = parse_string(payload) .context("failed to parse TCA_ACT_KIND")?; ActNla::Kind(kind.clone()) } TCA_ACT_OPTIONS => { let mut nlas = vec![]; for nla in NlasIterator::new(payload) { let nla = nla.context("invalid TCA_ACT_OPTIONS")?; nlas.push( ActOpt::parse_with_param(&nla, &kind) .context("failed to parse TCA_ACT_OPTIONS")?, ) } ActNla::Options(nlas) } TCA_ACT_INDEX => ActNla::Index( parse_u32(payload) .context("failed to parse TCA_ACT_INDEX")?, ), TCA_ACT_STATS => { let mut nlas = vec![]; for nla in NlasIterator::new(payload) { let nla = nla.context("invalid TCA_ACT_STATS")?; nlas.push( Stats2::parse(&nla) .context("failed to parse TCA_ACT_STATS")?, ); } ActNla::Stats(nlas) } TCA_ACT_COOKIE => ActNla::Cookie(payload.to_vec()), _ => ActNla::Other( DefaultNla::parse(&buf) .context("failed to parse action nla")?, ), }); } Ok(Self { tab: buf.kind(), nlas, }) } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum ActNla { Unspec(Vec), Kind(String), Options(Vec), Index(u32), Stats(Vec), Cookie(Vec), Other(DefaultNla), } impl nla::Nla for ActNla { fn value_len(&self) -> usize { use self::ActNla::*; match self { Unspec(bytes) | Cookie(bytes) => bytes.len(), Kind(k) => k.len() + 1, Options(opt) => opt.as_slice().buffer_len(), Index(_) => 4, Stats(s) => s.as_slice().buffer_len(), Other(attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::ActNla::*; match self { Unspec(bytes) | Cookie(bytes) => { buffer.copy_from_slice(bytes.as_slice()) } Kind(string) => { buffer[..string.as_bytes().len()] .copy_from_slice(string.as_bytes()); buffer[string.as_bytes().len()] = 0; } Options(opt) => opt.as_slice().emit(buffer), Index(value) => NativeEndian::write_u32(buffer, *value), Stats(s) => s.as_slice().emit(buffer), Other(attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { use self::ActNla::*; match self { Unspec(_) => TCA_ACT_UNSPEC, Kind(_) => TCA_ACT_KIND, Options(_) => TCA_ACT_OPTIONS | NLA_F_NESTED, Index(_) => TCA_ACT_INDEX, Stats(_) => TCA_ACT_STATS, Cookie(_) => TCA_ACT_COOKIE, Other(nla) => nla.kind(), } } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum ActOpt { Mirred(mirred::Nla), Nat(nat::Nla), // Other options Other(DefaultNla), } impl nla::Nla for ActOpt { fn value_len(&self) -> usize { use self::ActOpt::*; match self { Mirred(nla) => nla.value_len(), Nat(nla) => nla.value_len(), Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::ActOpt::*; match self { Mirred(nla) => nla.emit_value(buffer), Nat(nla) => nla.emit_value(buffer), Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::ActOpt::*; match self { Mirred(nla) => nla.kind(), Nat(nla) => nla.kind(), Other(nla) => nla.kind(), } } } impl<'a, T, S> ParseableParametrized, S> for ActOpt where T: AsRef<[u8]> + ?Sized, S: AsRef, { fn parse_with_param( buf: &NlaBuffer<&'a T>, kind: S, ) -> Result { Ok(match kind.as_ref() { mirred::KIND => Self::Mirred( mirred::Nla::parse(buf) .context("failed to parse mirred action")?, ), nat::KIND => Self::Nat( nat::Nla::parse(buf).context("failed to parse nat action")?, ), _ => Self::Other( DefaultNla::parse(buf) .context("failed to parse action options")?, ), }) } } #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct TcGen { pub index: u32, pub capab: u32, pub action: i32, pub refcnt: i32, pub bindcnt: i32, } buffer!(TcGenBuffer(TC_GEN_BUF_LEN) { index: (u32, 0..4), capab: (u32, 4..8), action: (i32, 8..12), refcnt: (i32, 12..16), bindcnt: (i32, 16..20), }); impl Emitable for TcGen { fn buffer_len(&self) -> usize { TC_GEN_BUF_LEN } fn emit(&self, buffer: &mut [u8]) { let mut packet = TcGenBuffer::new(buffer); packet.set_index(self.index); packet.set_capab(self.capab); packet.set_action(self.action); packet.set_refcnt(self.refcnt); packet.set_bindcnt(self.bindcnt); } } impl> Parseable> for TcGen { fn parse(buf: &TcGenBuffer) -> Result { Ok(Self { index: buf.index(), capab: buf.capab(), action: buf.action(), refcnt: buf.refcnt(), bindcnt: buf.bindcnt(), }) } } netlink-packet-route-0.17.0/src/rtnl/tc/nlas/action/nat.rs000064400000000000000000000105011046102023000214710ustar 00000000000000// SPDX-License-Identifier: MIT /// Nat action /// /// The nat action maps one IP prefix to another use std::net::Ipv4Addr; use netlink_packet_utils::{ nla::{self, DefaultNla, NlaBuffer}, traits::{Emitable, Parseable}, DecodeError, }; use crate::tc::{constants::*, TC_GEN_BUF_LEN}; pub const KIND: &str = "nat"; pub const TC_NAT_BUF_LEN: usize = TC_GEN_BUF_LEN + 16; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum Nla { Unspec(Vec), Tm(Vec), Parms(TcNat), Other(DefaultNla), } impl nla::Nla for Nla { fn value_len(&self) -> usize { use self::Nla::*; match self { Unspec(bytes) | Tm(bytes) => bytes.len(), Parms(_) => TC_NAT_BUF_LEN, Other(attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::Nla::*; match self { Unspec(bytes) | Tm(bytes) => { buffer.copy_from_slice(bytes.as_slice()) } Parms(p) => p.emit(buffer), Other(attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { use self::Nla::*; match self { Unspec(_) => TCA_NAT_UNSPEC, Tm(_) => TCA_NAT_TM, Parms(_) => TCA_NAT_PARMS, Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Nla { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::Nla::*; let payload = buf.value(); Ok(match buf.kind() { TCA_NAT_UNSPEC => Unspec(payload.to_vec()), TCA_NAT_TM => Tm(payload.to_vec()), TCA_NAT_PARMS => { Parms(TcNat::parse(&TcNatBuffer::new_checked(payload)?)?) } _ => Other(DefaultNla::parse(buf)?), }) } } #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct TcNat { pub index: u32, pub capab: u32, pub action: i32, pub refcnt: i32, pub bindcnt: i32, pub old_addr: Vec, pub new_addr: Vec, pub mask: Vec, pub flags: u32, } buffer!(TcNatBuffer(TC_NAT_BUF_LEN) { index: (u32, 0..4), capab: (u32, 4..8), action: (i32, 8..12), refcnt: (i32, 12..16), bindcnt: (i32, 16..20), old_addr: (slice, TC_GEN_BUF_LEN..(TC_GEN_BUF_LEN+4)), new_addr: (slice, (TC_GEN_BUF_LEN +4)..(TC_GEN_BUF_LEN+8)), mask: (slice, (TC_GEN_BUF_LEN +8)..(TC_GEN_BUF_LEN+12)), flags: (u32, (TC_GEN_BUF_LEN+12)..TC_NAT_BUF_LEN), }); impl TcNat { pub fn set_new_addr(mut self, target: Ipv4Addr) -> Self { self.new_addr = target.octets().to_vec(); self } pub fn set_old_addr(mut self, target: Ipv4Addr) -> Self { self.old_addr = target.octets().to_vec(); self } pub fn set_prefix(mut self, prefix_len: usize) -> Self { assert!(prefix_len <= 32); let prefix: u32 = if prefix_len == 0 { 0x0 } else { !((1 << (32 - prefix_len)) - 1) }; self.mask = prefix.to_be_bytes().to_vec(); self } pub fn egress(mut self) -> Self { self.flags = TCA_NAT_FLAG_EGRESS; self } } impl Emitable for TcNat { fn buffer_len(&self) -> usize { TC_NAT_BUF_LEN } fn emit(&self, buffer: &mut [u8]) { let mut packet = TcNatBuffer::new(buffer); packet.set_index(self.index); packet.set_capab(self.capab); packet.set_action(self.action); packet.set_refcnt(self.refcnt); packet.set_bindcnt(self.bindcnt); packet.old_addr_mut().copy_from_slice(&self.old_addr[0..4]); packet.new_addr_mut().copy_from_slice(&self.new_addr[0..4]); packet.mask_mut().copy_from_slice(&self.mask[0..4]); packet.set_flags(self.flags); } } impl<'buf, T: AsRef<[u8]> + ?Sized> Parseable> for TcNat { fn parse(buf: &TcNatBuffer<&'buf T>) -> Result { Ok(Self { index: buf.index(), capab: buf.capab(), action: buf.action(), refcnt: buf.refcnt(), bindcnt: buf.bindcnt(), old_addr: buf.old_addr().to_vec(), new_addr: buf.new_addr().to_vec(), mask: buf.mask().to_vec(), flags: buf.flags(), }) } } netlink-packet-route-0.17.0/src/rtnl/tc/nlas/filter/matchall.rs000064400000000000000000000054701046102023000225150ustar 00000000000000// SPDX-License-Identifier: MIT /// Matchall filter /// /// Matches all packets and performs an action on them. use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{self, DefaultNla, NlaBuffer, NlasIterator}, parsers::parse_u32, traits::{Emitable, Parseable}, DecodeError, }; use crate::tc::{constants::*, Action}; pub const KIND: &str = "matchall"; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum Nla { Unspec(Vec), ClassId(u32), Act(Vec), Pcnt(Vec), Flags(u32), Other(DefaultNla), } impl nla::Nla for Nla { fn value_len(&self) -> usize { use self::Nla::*; match self { Unspec(b) | Pcnt(b) => b.len(), ClassId(_) | Flags(_) => 4, Act(acts) => acts.as_slice().buffer_len(), Other(attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::Nla::*; match self { Unspec(b) | Pcnt(b) => buffer.copy_from_slice(b.as_slice()), ClassId(i) | Flags(i) => NativeEndian::write_u32(buffer, *i), Act(acts) => acts.as_slice().emit(buffer), Other(attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { use self::Nla::*; match self { Unspec(_) => TCA_MATCHALL_UNSPEC, ClassId(_) => TCA_MATCHALL_CLASSID, Act(_) => TCA_MATCHALL_ACT, Pcnt(_) => TCA_MATCHALL_PCNT, Flags(_) => TCA_MATCHALL_FLAGS, Other(attr) => attr.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Nla { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::Nla::*; let payload = buf.value(); Ok(match buf.kind() { TCA_MATCHALL_UNSPEC => Unspec(payload.to_vec()), TCA_MATCHALL_CLASSID => ClassId( parse_u32(payload) .context("failed to parse TCA_MATCHALL_UNSPEC")?, ), TCA_MATCHALL_ACT => { let mut acts = vec![]; for act in NlasIterator::new(payload) { let act = act.context("invalid TCA_MATCHALL_ACT")?; acts.push( Action::parse(&act) .context("failed to parse TCA_MATCHALL_ACT")?, ); } Act(acts) } TCA_MATCHALL_PCNT => Pcnt(payload.to_vec()), TCA_MATCHALL_FLAGS => Flags( parse_u32(payload) .context("failed to parse TCA_MATCHALL_FLAGS")?, ), _ => Other( DefaultNla::parse(buf).context("failed to parse u32 nla")?, ), }) } } netlink-packet-route-0.17.0/src/rtnl/tc/nlas/filter/mod.rs000064400000000000000000000001001046102023000214700ustar 00000000000000// SPDX-License-Identifier: MIT pub mod matchall; pub mod u32; netlink-packet-route-0.17.0/src/rtnl/tc/nlas/filter/u32.rs000064400000000000000000000171151046102023000213400ustar 00000000000000// SPDX-License-Identifier: MIT /// U32 filter /// /// In its simplest form the U32 filter is a list of records, each /// consisting of two fields: a selector and an action. The selectors, /// described below, are compared with the currently processed IP packet /// until the first match occurs, and then the associated action is /// performed. use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{self, DefaultNla, NlaBuffer, NlasIterator}, parsers::parse_u32, traits::{Emitable, Parseable}, DecodeError, }; use crate::tc::{constants::*, Action}; pub const KIND: &str = "u32"; const U32_SEL_BUF_LEN: usize = 16; const U32_KEY_BUF_LEN: usize = 16; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum Nla { Unspec(Vec), ClassId(u32), Hash(u32), Link(u32), Divisor(u32), Sel(Sel), Police(Vec), Act(Vec), Indev(Vec), Pcnt(Vec), Mark(Vec), Flags(u32), Other(DefaultNla), } impl nla::Nla for Nla { fn value_len(&self) -> usize { use self::Nla::*; match self { Unspec(b) | Police(b) | Indev(b) | Pcnt(b) | Mark(b) => b.len(), ClassId(_) | Hash(_) | Link(_) | Divisor(_) | Flags(_) => 4, Sel(s) => s.buffer_len(), Act(acts) => acts.as_slice().buffer_len(), Other(attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::Nla::*; match self { Unspec(b) | Police(b) | Indev(b) | Pcnt(b) | Mark(b) => { buffer.copy_from_slice(b.as_slice()) } ClassId(i) | Hash(i) | Link(i) | Divisor(i) | Flags(i) => { NativeEndian::write_u32(buffer, *i) } Sel(s) => s.emit(buffer), Act(acts) => acts.as_slice().emit(buffer), Other(attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { use self::Nla::*; match self { Unspec(_) => TCA_U32_UNSPEC, ClassId(_) => TCA_U32_CLASSID, Hash(_) => TCA_U32_HASH, Link(_) => TCA_U32_LINK, Divisor(_) => TCA_U32_DIVISOR, Sel(_) => TCA_U32_SEL, Police(_) => TCA_U32_POLICE, Act(_) => TCA_U32_ACT, Indev(_) => TCA_U32_INDEV, Pcnt(_) => TCA_U32_PCNT, Mark(_) => TCA_U32_MARK, Flags(_) => TCA_U32_FLAGS, Other(attr) => attr.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Nla { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::Nla::*; let payload = buf.value(); Ok(match buf.kind() { TCA_U32_UNSPEC => Unspec(payload.to_vec()), TCA_U32_CLASSID => ClassId( parse_u32(payload).context("failed to parse TCA_U32_UNSPEC")?, ), TCA_U32_HASH => Hash( parse_u32(payload).context("failed to parse TCA_U32_HASH")?, ), TCA_U32_LINK => Link( parse_u32(payload).context("failed to parse TCA_U32_LINK")?, ), TCA_U32_DIVISOR => Divisor( parse_u32(payload) .context("failed to parse TCA_U32_DIVISOR")?, ), TCA_U32_SEL => Sel(self::Sel::parse( &SelBuffer::new_checked(payload) .context("invalid TCA_U32_SEL")?, ) .context("failed to parse TCA_U32_SEL")?), TCA_U32_POLICE => Police(payload.to_vec()), TCA_U32_ACT => { let mut acts = vec![]; for act in NlasIterator::new(payload) { let act = act.context("invalid TCA_U32_ACT")?; acts.push( Action::parse(&act) .context("failed to parse TCA_U32_ACT")?, ); } Act(acts) } TCA_U32_INDEV => Indev(payload.to_vec()), TCA_U32_PCNT => Pcnt(payload.to_vec()), TCA_U32_MARK => Mark(payload.to_vec()), TCA_U32_FLAGS => Flags( parse_u32(payload).context("failed to parse TCA_U32_FLAGS")?, ), _ => Other( DefaultNla::parse(buf).context("failed to parse u32 nla")?, ), }) } } #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct Sel { pub flags: u8, pub offshift: u8, pub nkeys: u8, pub offmask: u16, pub off: u16, pub offoff: u16, pub hoff: u16, pub hmask: u32, pub keys: Vec, } buffer!(SelBuffer(U32_SEL_BUF_LEN) { flags: (u8, 0), offshift: (u8, 1), nkeys: (u8, 2), //pad: (u8, 3), offmask: (u16, 4..6), off: (u16, 6..8), offoff: (u16, 8..10), hoff: (u16, 10..12), hmask: (u32, 12..U32_SEL_BUF_LEN), keys: (slice, U32_SEL_BUF_LEN..), }); impl Emitable for Sel { fn buffer_len(&self) -> usize { U32_SEL_BUF_LEN + (self.nkeys as usize * U32_KEY_BUF_LEN) } fn emit(&self, buffer: &mut [u8]) { let mut packet = SelBuffer::new(buffer); packet.set_flags(self.flags); packet.set_offshift(self.offshift); packet.set_offmask(self.offmask); packet.set_off(self.off); packet.set_offoff(self.offoff); packet.set_hoff(self.hoff); packet.set_hmask(self.hmask); packet.set_nkeys(self.nkeys); assert_eq!(self.nkeys as usize, self.keys.len()); let key_buf = packet.keys_mut(); for (i, k) in self.keys.iter().enumerate() { k.emit( &mut key_buf [(i * U32_KEY_BUF_LEN)..((i + 1) * U32_KEY_BUF_LEN)], ); } } } impl + ?Sized> Parseable> for Sel { fn parse(buf: &SelBuffer<&T>) -> Result { let nkeys = buf.nkeys(); let mut keys = Vec::::with_capacity(nkeys.into()); let key_payload = buf.keys(); for i in 0..nkeys { let i = i as usize; let keybuf = KeyBuffer::new_checked( &key_payload[(i * U32_KEY_BUF_LEN)..(i + 1) * U32_KEY_BUF_LEN], ) .context("invalid u32 key")?; keys.push(Key::parse(&keybuf).context("failed to parse u32 key")?); } Ok(Self { flags: buf.flags(), offshift: buf.offshift(), nkeys, offmask: buf.offmask(), off: buf.off(), offoff: buf.offoff(), hoff: buf.hoff(), hmask: buf.hmask(), keys, }) } } #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct Key { pub mask: u32, pub val: u32, pub off: i32, pub offmask: i32, } buffer!(KeyBuffer(U32_KEY_BUF_LEN) { mask: (u32, 0..4), val: (u32, 4..8), off: (i32, 8..12), offmask: (i32, 12..U32_KEY_BUF_LEN), }); impl Emitable for Key { fn buffer_len(&self) -> usize { U32_KEY_BUF_LEN } fn emit(&self, buffer: &mut [u8]) { let mut packet = KeyBuffer::new(buffer); packet.set_mask(self.mask); packet.set_val(self.val); packet.set_off(self.off); packet.set_offmask(self.offmask); } } impl> Parseable> for Key { fn parse(buf: &KeyBuffer) -> Result { Ok(Self { mask: buf.mask(), val: buf.val(), off: buf.off(), offmask: buf.offmask(), }) } } netlink-packet-route-0.17.0/src/rtnl/tc/nlas/mod.rs000064400000000000000000000107731046102023000202240ustar 00000000000000// SPDX-License-Identifier: MIT mod stats; pub use self::stats::*; mod stats_queue; pub use self::stats_queue::*; mod stats_basic; pub use self::stats_basic::*; mod options; pub use self::options::*; mod qdisc; pub use self::qdisc::*; mod filter; pub use self::filter::*; mod action; pub use self::action::*; #[cfg(test)] mod test; use crate::constants::*; use netlink_packet_utils::{ nla::{self, DefaultNla, NlaBuffer}, traits::{Emitable, Parseable}, DecodeError, }; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum Nla { /// Unspecified Unspec(Vec), /// Name of queueing discipline Kind(String), /// Options follow Options(Vec), /// Statistics Stats(Stats), /// Module-specific statistics XStats(Vec), /// Rate limit Rate(Vec), Fcnt(Vec), Stats2(Vec), Stab(Vec), Chain(Vec), HwOffload(u8), Other(DefaultNla), } impl nla::Nla for Nla { #[rustfmt::skip] fn value_len(&self) -> usize { use self::Nla::*; match *self { // Vec Unspec(ref bytes) | XStats(ref bytes) | Rate(ref bytes) | Fcnt(ref bytes) | Stab(ref bytes) | Chain(ref bytes) => bytes.len(), HwOffload(_) => 1, Stats2(ref thing) => thing.as_slice().buffer_len(), Stats(_) => STATS_LEN, Kind(ref string) => string.as_bytes().len() + 1, Options(ref opt) => opt.as_slice().buffer_len(), // Defaults Other(ref attr) => attr.value_len(), } } #[cfg_attr(nightly, rustfmt::skip)] fn emit_value(&self, buffer: &mut [u8]) { use self::Nla::*; match *self { // Vec Unspec(ref bytes) | XStats(ref bytes) | Rate(ref bytes) | Fcnt(ref bytes) | Stab(ref bytes) | Chain(ref bytes) => buffer.copy_from_slice(bytes.as_slice()), HwOffload(ref val) => buffer[0] = *val, Stats2(ref stats) => stats.as_slice().emit(buffer), Stats(ref stats) => stats.emit(buffer), Kind(ref string) => { buffer[..string.as_bytes().len()].copy_from_slice(string.as_bytes()); buffer[string.as_bytes().len()] = 0; } Options(ref opt) => opt.as_slice().emit(buffer), // Default Other(ref attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { use self::Nla::*; match *self { Unspec(_) => TCA_UNSPEC, Kind(_) => TCA_KIND, Options(_) => TCA_OPTIONS, Stats(_) => TCA_STATS, XStats(_) => TCA_XSTATS, Rate(_) => TCA_RATE, Fcnt(_) => TCA_FCNT, Stats2(_) => TCA_STATS2, Stab(_) => TCA_STAB, Chain(_) => TCA_CHAIN, HwOffload(_) => TCA_HW_OFFLOAD, Other(ref nla) => nla.kind(), } } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum Stats2 { StatsApp(Vec), StatsBasic(Vec), StatsQueue(Vec), Other(DefaultNla), } impl nla::Nla for Stats2 { fn value_len(&self) -> usize { use self::Stats2::*; match *self { StatsBasic(ref bytes) | StatsQueue(ref bytes) | StatsApp(ref bytes) => bytes.len(), Other(ref nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::Stats2::*; match *self { StatsBasic(ref bytes) | StatsQueue(ref bytes) | StatsApp(ref bytes) => buffer.copy_from_slice(bytes.as_slice()), Other(ref nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::Stats2::*; match *self { StatsApp(_) => TCA_STATS_APP, StatsBasic(_) => TCA_STATS_BASIC, StatsQueue(_) => TCA_STATS_QUEUE, Other(ref nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Stats2 { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { TCA_STATS_APP => Self::StatsApp(payload.to_vec()), TCA_STATS_BASIC => Self::StatsBasic(payload.to_vec()), TCA_STATS_QUEUE => Self::StatsQueue(payload.to_vec()), _ => Self::Other(DefaultNla::parse(buf)?), }) } } netlink-packet-route-0.17.0/src/rtnl/tc/nlas/options.rs000064400000000000000000000036571046102023000211430ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{self, DefaultNla, NlaBuffer}, traits::{Parseable, ParseableParametrized}, DecodeError, }; use crate::tc::{ingress, matchall, u32}; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum TcOpt { // Qdisc specific options Ingress, // Filter specific options U32(u32::Nla), // matchall options Matchall(matchall::Nla), // Other options Other(DefaultNla), } impl nla::Nla for TcOpt { fn value_len(&self) -> usize { match self { Self::Ingress => 0, Self::U32(u) => u.value_len(), Self::Matchall(m) => m.value_len(), Self::Other(o) => o.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Ingress => unreachable!(), Self::U32(u) => u.emit_value(buffer), Self::Matchall(m) => m.emit_value(buffer), Self::Other(o) => o.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Ingress => unreachable!(), Self::U32(u) => u.kind(), Self::Matchall(m) => m.kind(), Self::Other(o) => o.kind(), } } } impl<'a, T, S> ParseableParametrized, S> for TcOpt where T: AsRef<[u8]> + ?Sized, S: AsRef, { fn parse_with_param( buf: &NlaBuffer<&'a T>, kind: S, ) -> Result { Ok(match kind.as_ref() { ingress::KIND => TcOpt::Ingress, u32::KIND => Self::U32( u32::Nla::parse(buf).context("failed to parse u32 nlas")?, ), matchall::KIND => Self::Matchall( matchall::Nla::parse(buf) .context("failed to parse matchall nlas")?, ), _ => Self::Other(DefaultNla::parse(buf)?), }) } } netlink-packet-route-0.17.0/src/rtnl/tc/nlas/qdisc/mod.rs000064400000000000000000000001331046102023000213140ustar 00000000000000// SPDX-License-Identifier: MIT pub mod ingress { pub const KIND: &str = "ingress"; } netlink-packet-route-0.17.0/src/rtnl/tc/nlas/stats.rs000064400000000000000000000035231046102023000205760ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; /// Generic queue statistics #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub struct Stats { /// Number of enqueued bytes pub bytes: u64, /// Number of enqueued packets pub packets: u32, /// Packets dropped because of lack of resources pub drops: u32, /// Number of throttle events when this flow goes out of allocated /// bandwidth pub overlimits: u32, /// Current flow byte rate pub bps: u32, /// Current flow packet rate pub pps: u32, pub qlen: u32, pub backlog: u32, } pub const STATS_LEN: usize = 36; buffer!(StatsBuffer(STATS_LEN) { bytes: (u64, 0..8), packets: (u32, 8..12), drops: (u32, 12..16), overlimits: (u32, 16..20), bps: (u32, 20..24), pps: (u32, 24..28), qlen: (u32, 28..32), backlog: (u32, 32..36), }); impl> Parseable> for Stats { fn parse(buf: &StatsBuffer) -> Result { Ok(Self { bytes: buf.bytes(), packets: buf.packets(), drops: buf.drops(), overlimits: buf.overlimits(), bps: buf.bps(), pps: buf.pps(), qlen: buf.qlen(), backlog: buf.backlog(), }) } } impl Emitable for Stats { fn buffer_len(&self) -> usize { STATS_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = StatsBuffer::new(buffer); buffer.set_bytes(self.bytes); buffer.set_packets(self.packets); buffer.set_drops(self.drops); buffer.set_overlimits(self.overlimits); buffer.set_bps(self.bps); buffer.set_pps(self.pps); buffer.set_qlen(self.qlen); buffer.set_backlog(self.backlog); } } netlink-packet-route-0.17.0/src/rtnl/tc/nlas/stats_basic.rs000064400000000000000000000017751046102023000217460ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; /// Byte/Packet throughput statistics #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub struct StatsBasic { /// number of seen bytes pub bytes: u64, /// number of seen packets pub packets: u32, } pub const STATS_BASIC_LEN: usize = 12; buffer!(StatsBasicBuffer(STATS_BASIC_LEN) { bytes: (u64, 0..8), packets: (u32, 8..12), }); impl> Parseable> for StatsBasic { fn parse(buf: &StatsBasicBuffer) -> Result { Ok(StatsBasic { bytes: buf.bytes(), packets: buf.packets(), }) } } impl Emitable for StatsBasic { fn buffer_len(&self) -> usize { STATS_BASIC_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = StatsBasicBuffer::new(buffer); buffer.set_bytes(self.bytes); buffer.set_packets(self.packets); } } netlink-packet-route-0.17.0/src/rtnl/tc/nlas/stats_queue.rs000064400000000000000000000027131046102023000220020ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; /// Queuing statistics #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub struct StatsQueue { /// queue length pub qlen: u32, /// backlog size of queue pub backlog: u32, /// number of dropped packets pub drops: u32, /// number of requeues pub requeues: u32, /// number of enqueues over the limit pub overlimits: u32, } pub const STATS_QUEUE_LEN: usize = 20; buffer!(StatsQueueBuffer( STATS_QUEUE_LEN) { qlen: (u32, 0..4), backlog: (u32, 4..8), drops: (u32, 8..12), requeues: (u32, 12..16), overlimits: (u32, 16..20), }); impl> Parseable> for StatsQueue { fn parse(buf: &StatsQueueBuffer) -> Result { Ok(Self { qlen: buf.qlen(), backlog: buf.backlog(), drops: buf.drops(), requeues: buf.requeues(), overlimits: buf.overlimits(), }) } } impl Emitable for StatsQueue { fn buffer_len(&self) -> usize { STATS_QUEUE_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = StatsQueueBuffer::new(buffer); buffer.set_qlen(self.qlen); buffer.set_backlog(self.backlog); buffer.set_drops(self.drops); buffer.set_requeues(self.requeues); buffer.set_overlimits(self.overlimits); } } netlink-packet-route-0.17.0/src/rtnl/tc/nlas/test.rs000064400000000000000000000217261046102023000204240ustar 00000000000000// SPDX-License-Identifier: MIT #![cfg(test)] use crate::{ constants::*, tc::{ self, constants::*, mirred, u32, ActNla, ActOpt, Action, Stats2, TcOpt, }, TcHeader, TcMessage, TcMessageBuffer, }; use netlink_packet_utils::{ nla::Nla, parsers::parse_u32, traits::{Emitable, Parseable}, }; #[rustfmt::skip] static FILTER_U32_ACTION_PACKET: [u8; 260] = [ 0, 0, 0, 0, // family, pad1, pad2 3, 0, 0, 0, // Interface index 0, 8, 0, 128, // handle: 0x800_00_800 => htid | hash | nodeid 255, 255, 255, 255, // parent: TC_H_ROOT 0, 3, 0, 192, // info: 0xc000_0300 => pref | protocol // nlas 8, 0, // length 1, 0, // type: TCA_KIND 117, 51, 50, 0, // u32\0 8, 0, 11, 0, // type: TCA_CHAIN 0, 0, 0, 0, 224, 0, 2, 0, // type: TCA_OPTIONS 36, 0, 5, 0, // type: TCA_U32_SEL 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 2, 0, // TCA_U32_HASH 0, 0, 0, 128, 8, 0, 11, 0, // TCA_U32_FLAGS 8, 0, 0, 0, 140, 0, 7, 0, // TCA_U32_ACT 136, 0, 1, 0, // TCA_ACT_TAB 11, 0, 1, 0, // TCA_ACT_KIND 109, 105, 114, 114, 101, 100, 0, 0, // "mirred\0" 48, 0, 4, 0, // TCA_ACT_STATS 20, 0, 1, 0, // TCA_STATS_BASIC 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 3, 0, // TCA_STATS_QUEUE 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 0, 2, 128, // TCA_ACT_OPTIONS, NLM_F_NESTED 32, 0, 2, 0, // TCA_MIRRED_PARMS 1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 36, 0, 1, 0, // TCA_MIRRED_TM 189, 117, 195, 9, 0, 0, 0, 0, 189, 117, 195, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 226, 238, 72, 0, 0, 0, 0, 28, 0, 9, 0, // TCA_U32_PCNT 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; #[test] #[allow(clippy::unusual_byte_groupings)] fn tc_filter_u32_read() { let packet = TcMessageBuffer::new(&FILTER_U32_ACTION_PACKET); assert_eq!(packet.family(), 0); assert_eq!(packet.index(), 3); assert_eq!(packet.handle(), 0x800_00_800); assert_eq!(packet.parent(), 0xffffffff); assert_eq!(packet.info(), 0xc000_0300); assert_eq!(packet.nlas().count(), 3); let mut nlas = packet.nlas(); let nla = nlas.next().unwrap().unwrap(); nla.check_buffer_length().unwrap(); assert_eq!(nla.length(), 8); assert_eq!(nla.kind(), TCA_KIND); assert_eq!(nla.value(), "u32\0".as_bytes()); let nla = nlas.next().unwrap().unwrap(); nla.check_buffer_length().unwrap(); assert_eq!(nla.length(), 8); assert_eq!(nla.kind(), TCA_CHAIN); assert_eq!(parse_u32(nla.value()).unwrap(), 0); let nla = nlas.next().unwrap().unwrap(); nla.check_buffer_length().unwrap(); assert_eq!(nla.length(), 224); assert_eq!(nla.kind(), TCA_OPTIONS); } #[test] fn tc_filter_u32_parse() { let packet = TcMessageBuffer::new_checked(&FILTER_U32_ACTION_PACKET).unwrap(); // TcMessage let msg = TcMessage::parse(&packet).unwrap(); assert_eq!(msg.header.index, 3); assert_eq!(msg.header.info, 0xc000_0300); assert_eq!(msg.nlas.len(), 3); // Nlas let mut iter = msg.nlas.iter(); // TCA_KIND assert_eq!( iter.next().unwrap(), &tc::Nla::Kind(String::from(u32::KIND)) ); // TCA_CHAIN assert!(matches!(iter.next().unwrap(), &tc::Nla::Chain(_))); // TCA_OPTIONS let nla = iter.next().unwrap(); let filter = if let tc::Nla::Options(f) = nla { assert_eq!(f.len(), 5); f } else { panic!("expect options nla"); }; // u32 option let mut fi = filter.iter(); let fa = fi.next().unwrap(); let ua = if let TcOpt::U32(u) = fa { u } else { panic!("expect u32 nla"); }; // TCA_U32_SEL let sel = if let u32::Nla::Sel(s) = ua { s } else { panic!("expect sel nla"); }; assert_eq!(sel.flags, TC_U32_TERMINAL); assert_eq!(sel.nkeys, 1); assert_eq!(sel.keys.len(), 1); assert_eq!(sel.keys[0], u32::Key::default()); // TCA_U32_HASH assert_eq!(fi.next().unwrap(), &TcOpt::U32(u32::Nla::Hash(0x80000000))); // TCA_U32_FLAGS assert_eq!(fi.next().unwrap(), &TcOpt::U32(u32::Nla::Flags(0x00000008))); // TCA_U32_ACT let fa = fi.next().unwrap(); let acts = if let TcOpt::U32(u) = fa { if let u32::Nla::Act(a) = u { a } else { panic!("expect u32 action"); } } else { panic!("expect u32 nla"); }; // TCA_ACT_TAB let mut act_iter = acts.iter(); let act = act_iter.next().unwrap(); assert_eq!(act.kind(), 1); // TCA_ACT_TAB assert_eq!(act.buffer_len(), 136); // TCA_ACT_TAB assert_eq!(act.tab, 1); let mut act_nlas_iter = act.nlas.iter(); // TCA_ACT_KIND assert_eq!( act_nlas_iter.next().unwrap(), &ActNla::Kind("mirred".to_string()) ); // TCA_ACT_STATS assert!(matches!(act_nlas_iter.next().unwrap(), ActNla::Stats(_))); // TCA_ACT_OPTIONS let act_nla = act_nlas_iter.next().unwrap(); let act_opts = if let ActNla::Options(opts) = act_nla { opts } else { panic!("expect action options"); }; let mut act_opts_iter = act_opts.iter(); // TCA_MIRRED_PARMS let act_opt = act_opts_iter.next().unwrap(); if let ActOpt::Mirred(mirred::Nla::Parms(p)) = act_opt { assert_eq!(p.index, 1); assert_eq!(p.capab, 0); assert_eq!(p.action, 4); assert_eq!(p.refcnt, 1); assert_eq!(p.bindcnt, 1); assert_eq!(p.eaction, 1); assert_eq!(p.ifindex, 3); } else { panic!("expect action mirred"); } // TCA_MIRRED_TM let act_opt = act_opts_iter.next().unwrap(); assert_eq!(act_opt.kind(), TCA_MIRRED_TM); assert_eq!(act_opt.buffer_len(), 36); // TCA_U32_PCNT let fa = fi.next().unwrap(); assert_eq!(fa.kind(), TCA_U32_PCNT); assert_eq!(fa.buffer_len(), 28); } #[test] #[allow(clippy::unusual_byte_groupings)] fn tc_filter_u32_emit() { // TcHeader let header = TcHeader { index: 3, handle: 0x800_00_800, parent: 0xffffffff, info: 0xc000_0300, ..Default::default() }; // Tc Nlas let nlas = vec![ tc::Nla::Kind(u32::KIND.to_string()), tc::Nla::Chain(vec![0, 0, 0, 0]), tc::Nla::Options(vec![ TcOpt::U32(u32::Nla::Sel(u32::Sel { flags: TC_U32_TERMINAL, offshift: 0, nkeys: 1, offmask: 0, off: 0, offoff: 0, hoff: 0, hmask: 0, keys: vec![u32::Key::default()], })), TcOpt::U32(u32::Nla::Hash(0x80000000)), TcOpt::U32(u32::Nla::Flags(0x00000008)), TcOpt::U32(u32::Nla::Act(vec![Action { tab: TCA_ACT_TAB, nlas: vec![ ActNla::Kind(mirred::KIND.to_string()), ActNla::Stats(vec![ Stats2::StatsBasic(vec![0u8; 16]), Stats2::StatsQueue(vec![0u8; 20]), ]), ActNla::Options(vec![ ActOpt::Mirred(mirred::Nla::Parms(mirred::TcMirred { index: 1, capab: 0, action: 4, refcnt: 1, bindcnt: 1, eaction: 1, ifindex: 3, })), ActOpt::Mirred(mirred::Nla::Tm( FILTER_U32_ACTION_PACKET[200..232].to_vec(), )), ]), ], }])), TcOpt::U32(u32::Nla::Pcnt(vec![0u8; 24])), ]), ]; let msg = TcMessage::from_parts(header, nlas); let mut buf = vec![0; 260]; assert_eq!(msg.buffer_len(), 260); msg.emit(&mut buf[..]); assert_eq!(&buf, &FILTER_U32_ACTION_PACKET); } netlink-packet-route-0.17.0/src/rtnl/tc/test.rs000064400000000000000000000121251046102023000174600ustar 00000000000000// SPDX-License-Identifier: MIT #![cfg(test)] use crate::{ constants::*, tc::{ingress, Nla, Stats, Stats2, StatsBuffer, TC_HEADER_LEN}, TcHeader, TcMessage, TcMessageBuffer, }; use netlink_packet_utils::{ nla::NlasIterator, traits::{Emitable, Parseable}, }; #[rustfmt::skip] static QDISC_INGRESS_PACKET: [u8; 136] = [ 0, // family 0, 0, 0, // pad1 + pad2 84, 0, 0, 0, // Interface index = 84 0, 0, 255, 255, // handle: 0xffff0000 241, 255, 255, 255, // parent: 0xfffffff1 1, 0, 0, 0, // info: refcnt: 1 // nlas 12, 0, // length 1, 0, // type: TCA_KIND 105, 110, 103, 114, 101, 115, 115, 0, // ingress\0 4, 0, // length 2, 0, // type: TCA_OPTIONS 5, 0, // length 12, 0,// type: TCA_HW_OFFLOAD 0, // data: 0 0, 0, 0,// padding 48, 0, // length 7, 0, // type: TCA_STATS2 20, 0, // length 1, 0, // type: TCA_STATS_BASIC 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 3, 0, // type: TCA_STATS_QUEUE 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, // length 3, 0, // type: TCA_STATS 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; #[test] fn tc_packet_header_read() { let packet = TcMessageBuffer::new(QDISC_INGRESS_PACKET); assert_eq!(packet.family(), 0); assert_eq!(packet.index(), 84); assert_eq!(packet.handle(), 0xffff0000); assert_eq!(packet.parent(), 0xfffffff1); assert_eq!(packet.info(), 1); } #[test] fn tc_packet_header_build() { let mut buf = vec![0xff; TC_HEADER_LEN]; { let mut packet = TcMessageBuffer::new(&mut buf); packet.set_family(0); packet.set_pad1(0); packet.set_pad2(0); packet.set_index(84); packet.set_handle(0xffff0000); packet.set_parent(0xfffffff1); packet.set_info(1); } assert_eq!(&buf[..], &QDISC_INGRESS_PACKET[0..TC_HEADER_LEN]); } #[test] fn tc_packet_nlas_read() { let packet = TcMessageBuffer::new(&QDISC_INGRESS_PACKET[..]); assert_eq!(packet.nlas().count(), 5); let mut nlas = packet.nlas(); let nla = nlas.next().unwrap().unwrap(); nla.check_buffer_length().unwrap(); assert_eq!(nla.length(), 12); assert_eq!(nla.kind(), TCA_KIND); assert_eq!(nla.value(), "ingress\0".as_bytes()); let nla = nlas.next().unwrap().unwrap(); nla.check_buffer_length().unwrap(); assert_eq!(nla.length(), 4); assert_eq!(nla.kind(), TCA_OPTIONS); assert_eq!(nla.value(), []); let nla = nlas.next().unwrap().unwrap(); nla.check_buffer_length().unwrap(); assert_eq!(nla.length(), 5); assert_eq!(nla.kind(), TCA_HW_OFFLOAD); assert_eq!(nla.value(), [0]); let nla = nlas.next().unwrap().unwrap(); nla.check_buffer_length().unwrap(); assert_eq!(nla.length(), 48); assert_eq!(nla.kind(), TCA_STATS2); let mut stats2_iter = NlasIterator::new(nla.value()); let stats2_nla = stats2_iter.next().unwrap().unwrap(); stats2_nla.check_buffer_length().unwrap(); assert_eq!(stats2_nla.length(), 20); assert_eq!(stats2_nla.kind(), TCA_STATS_BASIC); assert_eq!(stats2_nla.value(), [0; 16]); let s2 = Stats2::parse(&stats2_nla).unwrap(); assert!(matches!(s2, Stats2::StatsBasic(_))); let stats2_nla = stats2_iter.next().unwrap().unwrap(); stats2_nla.check_buffer_length().unwrap(); assert_eq!(stats2_nla.length(), 24); assert_eq!(stats2_nla.kind(), TCA_STATS_QUEUE); assert_eq!(stats2_nla.value(), [0; 20]); let s2 = Stats2::parse(&stats2_nla).unwrap(); assert!(matches!(s2, Stats2::StatsQueue(_))); let nla = nlas.next().unwrap().unwrap(); nla.check_buffer_length().unwrap(); assert_eq!(nla.length(), 44); assert_eq!(nla.kind(), TCA_STATS); assert_eq!(nla.value(), [0; 40]); let s = Stats::parse(&StatsBuffer::new(nla.value())).unwrap(); assert_eq!(s.packets, 0); assert_eq!(s.backlog, 0); } #[test] fn tc_qdisc_ingress_emit() { let header = TcHeader { index: 84, handle: 0xffff0000, parent: 0xfffffff1, info: 1, ..Default::default() }; let nlas = vec![Nla::Kind(ingress::KIND.into()), Nla::Options(vec![])]; let msg = TcMessage::from_parts(header, nlas); let mut buf = vec![0; 36]; assert_eq!(msg.buffer_len(), 36); msg.emit(&mut buf[..]); assert_eq!(&buf, &QDISC_INGRESS_PACKET[..36]); } #[test] fn tc_qdisc_ingress_read() { let packet = TcMessageBuffer::new_checked(&QDISC_INGRESS_PACKET).unwrap(); let msg = TcMessage::parse(&packet).unwrap(); assert_eq!(msg.header.index, 84); assert_eq!(msg.nlas.len(), 5); let mut iter = msg.nlas.iter(); let nla = iter.next().unwrap(); assert_eq!(nla, &Nla::Kind(String::from(ingress::KIND))); let nla = iter.next().unwrap(); assert_eq!(nla, &Nla::Options(vec![])); let nla = iter.next().unwrap(); assert_eq!(nla, &Nla::HwOffload(0)); } netlink-packet-route-0.17.0/src/rtnl/test.rs000064400000000000000000000065351046102023000170620ustar 00000000000000// SPDX-License-Identifier: MIT #![cfg(test)] use netlink_packet_core::NetlinkBuffer; use netlink_packet_utils::traits::ParseableParametrized; use crate::{ nlas::link::{Info, InfoKind, Nla}, LinkHeader, LinkMessage, RtnlMessage, RtnlMessageBuffer, RTM_NEWLINK, }; // This test was added because one of the NLA's payload is a string that is not // null terminated. I'm not sure if we missed something in the IFLA_LINK_INFO // spec, or if linux/iproute2 is being a bit inconsistent here. // // This message was created using `ip link add qemu-br1 type bridge`. #[rustfmt::skip] #[test] fn test_non_null_terminated_string() { let data = vec![ 0x40, 0x00, 0x00, 0x00, // length = 64 0x10, 0x00, // message type = 16 = (create network interface) 0x05, 0x06, // flags 0x81, 0x74, 0x57, 0x5c, // seq id 0x00, 0x00, 0x00, 0x00, // pid 0x00, // interface family 0x00, // padding 0x00, 0x00, // device type (NET/ROM pseudo) 0x00, 0x00, 0x00, 0x00, // interface index 0x00, 0x00, 0x00, 0x00, // device flags 0x00, 0x00, 0x00, 0x00, // device change flags // NLA: device name 0x0d, 0x00, // length = 13 0x03, 0x00, // type = 3 // value=qemu-br1 NOTE THAT THIS IS NULL-TERMINATED 0x71, 0x65, 0x6d, 0x75, 0x2d, 0x62, 0x72, 0x31, 0x00, 0x00, 0x00, 0x00, // padding // NLA: Link info 0x10, 0x00, // length = 16 0x12, 0x00, // type = link info // nested NLA: 0x0a, 0x00, // length = 10 0x01, 0x00, // type = 1 = IFLA_INFO_KIND // "bridge" NOTE THAT THIS IS NOT NULL-TERMINATED! 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x00, 0x00, // padding ]; let expected = RtnlMessage::NewLink(LinkMessage { header: LinkHeader::default(), nlas: vec![ Nla::IfName(String::from("qemu-br1")), Nla::Info(vec![Info::Kind(InfoKind::Bridge)]), ], }); let nl_buffer = NetlinkBuffer::new(&data).payload(); let rtnl_buffer = RtnlMessageBuffer::new(&nl_buffer); let actual = RtnlMessage::parse_with_param(&rtnl_buffer, RTM_NEWLINK).unwrap(); assert_eq!(expected, actual); } #[rustfmt::skip] #[test] fn test_attach_to_bridge() { use crate::*; let data = vec![ 0x28, 0x00, 0x00, 0x00, // length 0x10, 0x00, // type 0x05, 0x00, // flags 0x9c, 0x9d, 0x57, 0x5c, // seq id 0x00, 0x00, 0x00, 0x00, // pid 0x00, // interface family 0x00, // padding 0x00, 0x00, // device type 0x06, 0x00, 0x00, 0x00, // interface index 0x00, 0x00, 0x00, 0x00, // device flags 0x00, 0x00, 0x00, 0x00, // device change flags // NLA (set master) 0x08, 0x00, // length 0x0a, 0x00, // type 0x05, 0x00, 0x00, 0x00 // index of the master interface ]; let nl_buffer = NetlinkBuffer::new(&data).payload(); let rtnl_buffer = RtnlMessageBuffer::new(&nl_buffer); let actual = RtnlMessage::parse_with_param(&rtnl_buffer, RTM_NEWLINK).unwrap(); let expected = RtnlMessage::NewLink(LinkMessage { header: LinkHeader { interface_family: 0, index: 6, link_layer_type: 0, flags: 0, change_mask: 0, }, nlas: vec![Nla::Master(5)], }); assert_eq!(expected, actual); }