netlink-packet-route-0.21.0/.cargo_vcs_info.json0000644000000001360000000000100151610ustar { "git": { "sha1": "1bab12344b74809e90501c555dd32d6689f6dcf6" }, "path_in_vcs": "" }netlink-packet-route-0.21.0/.github/workflows/build.yml000064400000000000000000000014621046102023000211730ustar 00000000000000name: Build on: pull_request: types: [opened, synchronize, reopened] push: branches: - main jobs: build: name: Build runs-on: ubuntu-latest strategy: fail-fast: true matrix: include: - rust_target: "x86_64-unknown-linux-gnu" - rust_target: "x86_64-unknown-freebsd" - rust_target: "x86_64-unknown-fuchsia" - rust_target: "x86_64-apple-darwin" - rust_target: "x86_64-linux-android" steps: - uses: actions/checkout@v3 - name: Install Rust Stable run: | rustup override set stable rustup update stable rustup target add ${{ matrix.rust_target }} - name: Build test for ${{ matrix.rust_target }} run: cargo build --target ${{ matrix.rust_target }} netlink-packet-route-0.21.0/.github/workflows/clippy-rustfmt.yml000064400000000000000000000011211046102023000230660ustar 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.21.0/.github/workflows/license.yml000064400000000000000000000005201046102023000215100ustar 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.21.0/.github/workflows/main.yml000064400000000000000000000015101046102023000210120ustar 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 continue-on-error: true with: token: ${{ secrets.CODECOV_TOKEN }} files: lcov.info fail_ci_if_error: true netlink-packet-route-0.21.0/.gitignore000064400000000000000000000000461046102023000157410ustar 00000000000000Cargo.lock target vendor/ tags *.swp netlink-packet-route-0.21.0/.licenserc.yaml000064400000000000000000000003711046102023000166630ustar 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.21.0/.rustfmt.toml000064400000000000000000000001141046102023000164240ustar 00000000000000max_width = 80 wrap_comments = true reorder_imports = true edition = "2021" netlink-packet-route-0.21.0/CHANGELOG000064400000000000000000000126531046102023000151720ustar 00000000000000# Changelog ## [0.21.0] - 2024-09-12 ### Breaking changes - `InfoIpVlan::Flags` changed from u16 to `IpVlanFlags`. (321e4d5) - `AfSpecBridge::Flags` changed from u16 to `BridgeFlag`. (40d090b) - `InfoBond::ArpValidate` changed from u32 to `BondArpValidate`.(5246712) - `InfoVxlan::Group` changed from `Vec` to `Ipv4Addr`. (538e13b) - `InfoVxlan::Group6` changed from `Vec` to `Ipv6Addr`. (538e13b) - `InfoVxlan::Local` changed from `Vec` to `Ipv4Addr`. (538e13b) - `InfoVxlan::Local6` changed from `Vec` to `Ipv6Addr`. (538e13b) ### New features - bridge port: Introduce BridgeFlag, BridgeMode and BridgeVlanTunnelInfo. (40d090b) - Add support for geneve links. (7391b19) ### Bug fixes - Avoid panic in TcU32Selector parsing. (c49e3ac) - Avoid panic in RouteNextHopBuffer length checks. (666edbc) - Check NLA buffers on creation. (fceb9c2) - Check Map buffers on creation. (d53bbad) ## [0.20.1] - 2024-06-29 ### Breaking changes - N/A ### New features - Support for tc-actions. (65f699a, 0227234) - Support IFLA_VRF_PORT_TABLE attribute. (abfadc1) ### Bug fixes - N/A ## [0.20.0] - 2024-01-31 ### Breaking changes - Use bitflags. (3c08f7f, 5072f3a, 9271b33, 8564ed9, e26b489, b337e00, 386470f, 99bd9d2, 590411d, 6f63c6b) - bond: Convert `InfoBond::Mode` into an enum. (3e16f9c) - ipvlan: Change mode type from u16 to enum. (396d4b0) - link: Renamed `LinkAttribute::NetnsId` to `LinkNetNsId`. (528905c) - mac vlan/vtap: Changed MAC VLAN/VTAP mode from u32 to enum. (b23b165) - bridge: Change InfoBridge::VlanFiltering from u8 to bool. (de0f47a) ### New features - Added `InfoData::IpVtap`. (a8d125c) - impl `From` for `RouteAddress`. (984b358) ### Bug fixes - link: Check buffer length when parsing NLAs. (a543bb7) - link: Expand the buffer before parsing stats data from old kernel. (4d5f819) ## [0.19.0] - 2024-01-31 ### Breaking changes - `InfoBridge::RootId` and `InfoBridge::BridgeId` changed. (fb497b3) ### New features - Support bridge bond port information. (faffa52) - Support RTM_NEWPREFIX. (2a43e1c) - Add `Default` derive to `TcFqCodelQdStats` and etc. (e21122e) ### Bug fixes - N/A ## [0.18.1] - 2023-12-05 ### Breaking changes - N/A ### New features - N/A ### Bug fixes - Fix crash on RuleFlag of `attempt to subtract with overflow`. (ece8735) ## [0.18.0] - 2023-12-05 ### Breaking changes - MASSIVE changes to API in order to 1.0 preparation. Please check document or code for detail. Sorry for the inconvenience. ### New features - Support HSR interface. (37f9c5c) ### Bug fixes - vxlan: Do not fail on unknown option. (2457bdf) - bond: Do not fail on unknown option. (acac109) - vlan: Do not fail on unknown option. (1617948) ## [0.17.1] - 2023-08-30 ### Breaking changes - N/A ### New features - Add support of MACsec interface. (050fd64) ### Bug fixes - vxlan: fix port-range attribute marshalling. (55de269) - vxlan: fix port-range attribute endianness. (ce406b2) - vxlan: fix port attribute endianness. (927bdd7) ## [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.21.0/Cargo.lock0000644000000134530000000000100131420ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[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.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8" [[package]] name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "ctor" version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", "syn 1.0.109", ] [[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "libc" version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[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.21.0" dependencies = [ "anyhow", "bitflags", "byteorder", "libc", "log", "netlink-packet-core", "netlink-packet-utils", "netlink-sys", "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.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "416060d346fbaf1f23f9512963e3e878f1a78e707cb699ba9215761754244307" dependencies = [ "bytes", "libc", "log", ] [[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.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[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.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "thiserror" version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", "syn 2.0.77", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[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-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.21.0/Cargo.toml0000644000000036110000000000100131600ustar # 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 = "2021" name = "netlink-packet-route" version = "0.21.0" authors = ["Corentin Henry "] build = false autobins = false autoexamples = false autotests = false autobenches = false 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" [lib] name = "netlink_packet_route" path = "src/lib.rs" [[example]] name = "dump_neighbour_tables" path = "examples/dump_neighbour_tables.rs" [[example]] name = "dump_neighbours" path = "examples/dump_neighbours.rs" [[example]] name = "dump_packet_link_bridge_vlan" path = "examples/dump_packet_link_bridge_vlan.rs" [[example]] name = "dump_packet_links" path = "examples/dump_packet_links.rs" [[example]] name = "dump_rules" path = "examples/dump_rules.rs" [[example]] name = "new_rule" path = "examples/new_rule.rs" [dependencies.anyhow] version = "1.0.31" [dependencies.bitflags] version = "2" [dependencies.byteorder] version = "1.3.2" [dependencies.libc] version = "0.2.66" [dependencies.log] version = "0.4.20" features = ["std"] [dependencies.netlink-packet-core] version = "0.7.0" [dependencies.netlink-packet-utils] version = "0.5.2" [dev-dependencies.netlink-sys] version = "0.8.5" [dev-dependencies.pretty_assertions] version = "0.7.2" [features] rich_nlas = [] netlink-packet-route-0.21.0/Cargo.toml.orig0000644000000013520000000000100141170ustar [package] authors = ["Corentin Henry "] name = "netlink-packet-route" version = "0.21.0" edition = "2021" 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" bitflags = "2" byteorder = "1.3.2" libc = "0.2.66" log = { version = "0.4.20", features = ["std"] } netlink-packet-core = { version = "0.7.0" } netlink-packet-utils = { version = "0.5.2" } [[example]] name = "dump_packet_links" [dev-dependencies] netlink-sys = { version = "0.8.5" } pretty_assertions = "0.7.2" netlink-packet-route-0.21.0/Cargo.toml.orig000064400000000000000000000013521046102023000166410ustar 00000000000000[package] authors = ["Corentin Henry "] name = "netlink-packet-route" version = "0.21.0" edition = "2021" 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" bitflags = "2" byteorder = "1.3.2" libc = "0.2.66" log = { version = "0.4.20", features = ["std"] } netlink-packet-core = { version = "0.7.0" } netlink-packet-utils = { version = "0.5.2" } [[example]] name = "dump_packet_links" [dev-dependencies] netlink-sys = { version = "0.8.5" } pretty_assertions = "0.7.2" netlink-packet-route-0.21.0/LICENSE-MIT000064400000000000000000000027731046102023000154160ustar 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.21.0/README.md000064400000000000000000000053661046102023000152420ustar 00000000000000# Rust crate for Netlink Route Protocol The `netlink-packet-route` crate is designed to abstract Netlink route protocol([`rtnetlink`][rtnetlink_man]) packet into Rust data types. The goal of this crate is saving netlink user from reading Kernel Netlink codes. This crate grouped Netlink route protocol into these modules: * `link`: NIC interface, similar to to `ip link` command. * `address`: IP address, similar to `ip address` command. * `route`: Route, similar to `ip route` command. * `rule`: Route rule, similar to `ip rule` command. * `tc`: Traffic control, similar to `tc` command. * `neighbour`: Neighbour, similar to `ip neighbour` command. * `neighbour_table`: Neighbour table, similar to `ip ntable` command. * `nsid`: Namespace, similar to `ip netns` command. Normally, you should use [`rtnetlink`][rtnetlink_url] instead of using this crate directly. ## Development * Please use `git commit --signoff` to append Signed-off-by trailer to commit message. * For new source file, please append `// SPDX-License-Identifier: MIT` at the beginning with content of license new code to MIT license. * No panic is allowed, please use `Result<>` instead of `unwrap()` or `expect()`. * Please run `cargo fmt` and `cargo clippy` before creating pull request. * All struct/enum should be decorated by [`#[non_exhaustive]`][non_exhaustive_doc] to preserve API backwards compatibility unless explicit approval from maintainer. * Unknown netlink attribute should be stored into `Other(DefaultNla)` instead of breaking decoding. * Please use unit test case to cover the serialize and deserialize of the changed netlink attribute. To capture the netlink raw bytes, you may use tcpdump/wireshark again the `nlmon` interface. For example: ```bash modprobe nlmon ip link add nl0 type nlmon ip link set nl0 up tcpdump -i nl0 -w netlink_capture_file.cap # Then use wireshark to open this `netlink_capture_file.cap` # Find out the packet you are interested, # right click -> "Copy" -> "...as Hex Dump". # You may use https://github.com/cathay4t/hex_to_rust to convert this # hexdump to rust u8 array ``` * The integration test(play with linux kernel netlink interface) should be placed into `rtnetlink` crate. Current(netlink-packet-route) crate should only unit test case only. * For certain netlink message which cannot captured by nlmon, please use Rust Debug and explain every bits in comment. * Optionally, please run `tools/test_cross_build.sh` to make sure your code is compilable on other platforms. [rtnetlink_man]: https://www.man7.org/linux/man-pages/man7/rtnetlink.7.html [rtnetlink_url]: https://docs.rs/rtnetlink [non_exhaustive_doc]: https://doc.rust-lang.org/stable/reference/attributes/type_system.html#the-non_exhaustive-attribute netlink-packet-route-0.21.0/TODO.md000064400000000000000000000002471046102023000150430ustar 00000000000000 * Only tc has unparsed `Vec` left. * Still has many flags not converted into `Vec`. * Many place holders in `link::InfoData`. * Missing unit test cases. netlink-packet-route-0.21.0/src/address/addr_flags.rs000064400000000000000000000036351046102023000206300ustar 00000000000000// SPDX-License-Identifier: MIT const IFA_F_SECONDARY: u32 = 0x01; const IFA_F_NODAD: u32 = 0x02; const IFA_F_OPTIMISTIC: u32 = 0x04; const IFA_F_DADFAILED: u32 = 0x08; const IFA_F_HOMEADDRESS: u32 = 0x10; const IFA_F_DEPRECATED: u32 = 0x20; const IFA_F_TENTATIVE: u32 = 0x40; const IFA_F_PERMANENT: u32 = 0x80; const IFA_F_MANAGETEMPADDR: u32 = 0x100; const IFA_F_NOPREFIXROUTE: u32 = 0x200; const IFA_F_MCAUTOJOIN: u32 = 0x400; const IFA_F_STABLE_PRIVACY: u32 = 0x800; bitflags! { #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct AddressFlags : u32 { const Secondary = IFA_F_SECONDARY; const Nodad = IFA_F_NODAD; const Optimistic = IFA_F_OPTIMISTIC; const Dadfailed = IFA_F_DADFAILED; const Homeaddress = IFA_F_HOMEADDRESS; const Deprecated = IFA_F_DEPRECATED; const Tentative = IFA_F_TENTATIVE; const Permanent = IFA_F_PERMANENT; const Managetempaddr = IFA_F_MANAGETEMPADDR; const Noprefixroute = IFA_F_NOPREFIXROUTE; const Mcautojoin = IFA_F_MCAUTOJOIN; const StablePrivacy = IFA_F_STABLE_PRIVACY; const _ = !0; } } bitflags! { #[non_exhaustive] #[derive(Debug, Clone, Copy, Eq, PartialEq)] /// [`AddressHeaderFlags`] is only used for [`super::AddressHeader`] and holding /// subset(first byte) of [`AddressFlags`]. pub struct AddressHeaderFlags : u8 { const Secondary = IFA_F_SECONDARY as u8; const Nodad = IFA_F_NODAD as u8; const Optimistic = IFA_F_OPTIMISTIC as u8; const Dadfailed = IFA_F_DADFAILED as u8; const Homeaddress = IFA_F_HOMEADDRESS as u8; const Deprecated = IFA_F_DEPRECATED as u8; const Tentative = IFA_F_TENTATIVE as u8; const Permanent = IFA_F_PERMANENT as u8; const _ = !0; } } impl Default for AddressHeaderFlags { fn default() -> Self { Self::empty() } } netlink-packet-route-0.21.0/src/address/addr_scope.rs000064400000000000000000000022411046102023000206350ustar 00000000000000// SPDX-License-Identifier: MIT const RT_SCOPE_UNIVERSE: u8 = 0; // 1 and 199 is user defined values const RT_SCOPE_SITE: u8 = 200; const RT_SCOPE_LINK: u8 = 253; const RT_SCOPE_HOST: u8 = 254; const RT_SCOPE_NOWHERE: u8 = 255; #[derive(Clone, Eq, PartialEq, Debug, Copy, Default)] #[non_exhaustive] #[repr(u8)] pub enum AddressScope { #[default] Universe, Site, Link, Host, Nowhere, Other(u8), } impl From for AddressScope { fn from(d: u8) -> Self { match d { RT_SCOPE_UNIVERSE => Self::Universe, RT_SCOPE_SITE => Self::Site, RT_SCOPE_LINK => Self::Link, RT_SCOPE_HOST => Self::Host, RT_SCOPE_NOWHERE => Self::Nowhere, _ => Self::Other(d), } } } impl From for u8 { fn from(v: AddressScope) -> u8 { match v { AddressScope::Universe => RT_SCOPE_UNIVERSE, AddressScope::Site => RT_SCOPE_SITE, AddressScope::Link => RT_SCOPE_LINK, AddressScope::Host => RT_SCOPE_HOST, AddressScope::Nowhere => RT_SCOPE_NOWHERE, AddressScope::Other(d) => d, } } } netlink-packet-route-0.21.0/src/address/attribute.rs000064400000000000000000000164061046102023000205450ustar 00000000000000// SPDX-License-Identifier: MIT use std::mem::size_of; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::{parse_string, parse_u32}, DecodeError, Emitable, Parseable, }; use crate::address::{AddressFlags, CacheInfo, CacheInfoBuffer}; const IFA_ADDRESS: u16 = 1; const IFA_LOCAL: u16 = 2; const IFA_LABEL: u16 = 3; const IFA_BROADCAST: u16 = 4; const IFA_ANYCAST: u16 = 5; const IFA_CACHEINFO: u16 = 6; const IFA_MULTICAST: u16 = 7; const IFA_FLAGS: u16 = 8; // TODO(Gris Ge) // const IFA_RT_PRIORITY: u16 = 9; // const IFA_TARGET_NETNSID: u16 = 10, // const IFA_PROTO: u16 = 11; // 32 bites const IPV4_ADDR_LEN: usize = 4; // 128 bites const IPV6_ADDR_LEN: usize = 16; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum AddressAttribute { Address(IpAddr), Local(IpAddr), Label(String), /// IPv4 only Broadcast(Ipv4Addr), /// IPv6 only Anycast(Ipv6Addr), CacheInfo(CacheInfo), /// IPv6 only Multicast(Ipv6Addr), Flags(AddressFlags), Other(DefaultNla), } impl Nla for AddressAttribute { fn value_len(&self) -> usize { match *self { Self::Broadcast(_) => IPV4_ADDR_LEN, Self::Anycast(_) | Self::Multicast(_) => IPV6_ADDR_LEN, Self::Address(ref addr) | Self::Local(ref addr) => { if addr.is_ipv6() { IPV6_ADDR_LEN } else { IPV4_ADDR_LEN } } Self::Label(ref string) => string.as_bytes().len() + 1, Self::Flags(_) => size_of::(), Self::CacheInfo(ref attr) => attr.buffer_len(), Self::Other(ref attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match *self { Self::Broadcast(ref addr) => buffer.copy_from_slice(&addr.octets()), Self::Anycast(ref addr) | Self::Multicast(ref addr) => { buffer.copy_from_slice(&addr.octets()) } Self::Address(ref addr) | Self::Local(ref addr) => match addr { IpAddr::V4(addr4) => buffer.copy_from_slice(&addr4.octets()), IpAddr::V6(addr6) => buffer.copy_from_slice(&addr6.octets()), }, Self::Label(ref string) => { buffer[..string.len()].copy_from_slice(string.as_bytes()); buffer[string.len()] = 0; } Self::Flags(ref value) => { NativeEndian::write_u32(buffer, value.bits()) } Self::CacheInfo(ref attr) => attr.emit(buffer), Self::Other(ref attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { match *self { Self::Address(_) => IFA_ADDRESS, Self::Local(_) => IFA_LOCAL, Self::Label(_) => IFA_LABEL, Self::Broadcast(_) => IFA_BROADCAST, Self::Anycast(_) => IFA_ANYCAST, Self::CacheInfo(_) => IFA_CACHEINFO, Self::Multicast(_) => IFA_MULTICAST, Self::Flags(_) => IFA_FLAGS, Self::Other(ref nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for AddressAttribute { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { IFA_ADDRESS => { if payload.len() == IPV4_ADDR_LEN { let mut data = [0u8; IPV4_ADDR_LEN]; data.copy_from_slice(&payload[0..IPV4_ADDR_LEN]); Self::Address(IpAddr::from(data)) } else if payload.len() == IPV6_ADDR_LEN { let mut data = [0u8; IPV6_ADDR_LEN]; data.copy_from_slice(&payload[0..IPV6_ADDR_LEN]); Self::Address(IpAddr::from(data)) } else { return Err(DecodeError::from(format!( "Invalid IFA_LOCAL, got unexpected length \ of payload {:?}", payload ))); } } IFA_LOCAL => { if payload.len() == IPV4_ADDR_LEN { let mut data = [0u8; IPV4_ADDR_LEN]; data.copy_from_slice(&payload[0..IPV4_ADDR_LEN]); Self::Local(IpAddr::from(data)) } else if payload.len() == IPV6_ADDR_LEN { let mut data = [0u8; IPV6_ADDR_LEN]; data.copy_from_slice(&payload[0..IPV6_ADDR_LEN]); Self::Local(IpAddr::from(data)) } else { return Err(DecodeError::from(format!( "Invalid IFA_LOCAL, got unexpected length \ of payload {:?}", payload ))); } } IFA_LABEL => Self::Label( parse_string(payload).context("invalid IFA_LABEL value")?, ), IFA_BROADCAST => { if payload.len() == IPV4_ADDR_LEN { let mut data = [0u8; IPV4_ADDR_LEN]; data.copy_from_slice(&payload[0..IPV4_ADDR_LEN]); Self::Broadcast(Ipv4Addr::from(data)) } else { return Err(DecodeError::from(format!( "Invalid IFA_BROADCAST, got unexpected length \ of IPv4 address payload {:?}", payload ))); } } IFA_ANYCAST => { if payload.len() == IPV6_ADDR_LEN { let mut data = [0u8; IPV6_ADDR_LEN]; data.copy_from_slice(&payload[0..IPV6_ADDR_LEN]); Self::Anycast(Ipv6Addr::from(data)) } else { return Err(DecodeError::from(format!( "Invalid IFA_ANYCAST, got unexpected length \ of IPv6 address payload {:?}", payload ))); } } IFA_CACHEINFO => Self::CacheInfo( CacheInfo::parse(&CacheInfoBuffer::new(payload)) .context(format!("Invalid IFA_CACHEINFO {:?}", payload))?, ), IFA_MULTICAST => { if payload.len() == IPV6_ADDR_LEN { let mut data = [0u8; IPV6_ADDR_LEN]; data.copy_from_slice(&payload[0..IPV6_ADDR_LEN]); Self::Multicast(Ipv6Addr::from(data)) } else { return Err(DecodeError::from(format!( "Invalid IFA_MULTICAST, got unexpected length \ of IPv6 address payload {:?}", payload ))); } } IFA_FLAGS => Self::Flags(AddressFlags::from_bits_retain( parse_u32(payload).context("invalid IFA_FLAGS value")?, )), kind => Self::Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } netlink-packet-route-0.21.0/src/address/cache_info.rs000064400000000000000000000023361046102023000206150ustar 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: u32, pub ifa_valid: u32, pub cstamp: u32, pub tstamp: u32, } const ADDRESSS_CACHE_INFO_LEN: usize = 16; buffer!(CacheInfoBuffer(ADDRESSS_CACHE_INFO_LEN) { ifa_preferred: (u32, 0..4), ifa_valid: (u32, 4..8), cstamp: (u32, 8..12), tstamp: (u32, 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.21.0/src/address/message.rs000064400000000000000000000061531046102023000201640ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{NlaBuffer, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; use crate::{ address::{AddressAttribute, AddressHeaderFlags, AddressScope}, AddressFamily, }; 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 attributes( &self, ) -> impl Iterator, DecodeError>> { NlasIterator::new(self.payload()) } } #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct AddressMessage { pub header: AddressHeader, pub attributes: Vec, } #[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct AddressHeader { pub family: AddressFamily, pub prefix_len: u8, pub flags: AddressHeaderFlags, pub scope: AddressScope, 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.into()); packet.set_prefix_len(self.prefix_len); packet.set_flags(self.flags.bits()); packet.set_scope(self.scope.into()); packet.set_index(self.index); } } impl Emitable for AddressMessage { fn buffer_len(&self) -> usize { self.header.buffer_len() + self.attributes.as_slice().buffer_len() } fn emit(&self, buffer: &mut [u8]) { self.header.emit(buffer); self.attributes .as_slice() .emit(&mut buffer[self.header.buffer_len()..]); } } impl> Parseable> for AddressHeader { fn parse(buf: &AddressMessageBuffer) -> Result { Ok(Self { family: buf.family().into(), prefix_len: buf.prefix_len(), flags: AddressHeaderFlags::from_bits_retain(buf.flags()), scope: buf.scope().into(), 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")?, attributes: 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 attributes = vec![]; for nla_buf in buf.attributes() { attributes.push(AddressAttribute::parse(&nla_buf?)?); } Ok(attributes) } } netlink-packet-route-0.21.0/src/address/mod.rs000064400000000000000000000006361046102023000173170ustar 00000000000000// SPDX-License-Identifier: MIT mod addr_flags; mod addr_scope; mod attribute; mod cache_info; mod message; #[cfg(test)] mod tests; pub use self::addr_flags::{AddressFlags, AddressHeaderFlags}; pub use self::addr_scope::AddressScope; pub use self::attribute::AddressAttribute; pub use self::cache_info::{CacheInfo, CacheInfoBuffer}; pub use self::message::{AddressHeader, AddressMessage, AddressMessageBuffer}; netlink-packet-route-0.21.0/src/address/tests/ipv4.rs000064400000000000000000000034531046102023000205640ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::{IpAddr, Ipv4Addr}; use netlink_packet_utils::{Emitable, Parseable}; use crate::address::{ AddressAttribute, AddressFlags, AddressHeader, AddressHeaderFlags, AddressMessage, AddressMessageBuffer, AddressScope, CacheInfo, }; use crate::AddressFamily; // TODO(Gris Ge): Need test for `AddressAttribute::Broadcast` #[test] fn test_ipv4_get_loopback_address() { let raw = vec![ 0x02, 0x08, 0x80, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x7f, 0x00, 0x00, 0x01, 0x08, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x00, 0x01, 0x07, 0x00, 0x03, 0x00, 0x6c, 0x6f, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x80, 0x00, 0x00, 0x00, 0x14, 0x00, 0x06, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9c, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, ]; let expected = AddressMessage { header: AddressHeader { family: AddressFamily::Inet, prefix_len: 8, flags: AddressHeaderFlags::Permanent, scope: AddressScope::Host, index: 1, }, attributes: vec![ AddressAttribute::Address(IpAddr::V4(Ipv4Addr::LOCALHOST)), AddressAttribute::Local(IpAddr::V4(Ipv4Addr::LOCALHOST)), AddressAttribute::Label("lo".to_string()), AddressAttribute::Flags(AddressFlags::Permanent), AddressAttribute::CacheInfo(CacheInfo { ifa_preferred: u32::MAX, ifa_valid: u32::MAX, cstamp: 156, tstamp: 156, }), ], }; assert_eq!( expected, AddressMessage::parse(&AddressMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/address/tests/ipv6.rs000064400000000000000000000045321046102023000205650ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::{IpAddr, Ipv6Addr}; use netlink_packet_utils::{nla::NlaBuffer, Emitable, Parseable}; use crate::address::{ AddressAttribute, AddressFlags, AddressHeader, AddressHeaderFlags, AddressMessage, AddressMessageBuffer, AddressScope, CacheInfo, }; use crate::AddressFamily; // TODO(Gris Ge): Need test for `AddressAttribute::Anycast` and `Multicast`. #[test] fn test_addr_flag_stable_privacy() { let nla = AddressAttribute::Flags( AddressFlags::Permanent | AddressFlags::StablePrivacy, ); let raw: [u8; 8] = [ 0x08, 0x00, // length 8 0x08, 0x00, // IFA_FLAGS 0x80, 0x08, 0x00, 0x00, // IFA_F_PERMANENT | IFA_F_STABLE_PRIVACY ]; let nla_buffer = NlaBuffer::new_checked(&raw).unwrap(); let parsed = AddressAttribute::parse(&nla_buffer).unwrap(); assert_eq!(parsed, nla); assert_eq!(nla.buffer_len(), 8); let mut buffer: [u8; 8] = [0; 8]; nla.emit(&mut buffer); assert_eq!(buffer, raw); } #[test] fn test_get_loopback_ipv6_addr() { let raw = vec![ 0x0a, 0x80, 0x80, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x06, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8e, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x80, 0x02, 0x00, 0x00, ]; let expected = AddressMessage { header: AddressHeader { family: AddressFamily::Inet6, prefix_len: 128, flags: AddressHeaderFlags::Permanent, scope: AddressScope::Host, index: 1, }, attributes: vec![ AddressAttribute::Address(IpAddr::V6(Ipv6Addr::LOCALHOST)), AddressAttribute::CacheInfo(CacheInfo { ifa_preferred: u32::MAX, ifa_valid: u32::MAX, cstamp: 142, tstamp: 142, }), AddressAttribute::Flags( AddressFlags::Permanent | AddressFlags::Noprefixroute, ), ], }; assert_eq!( expected, AddressMessage::parse(&AddressMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/address/tests/mod.rs000064400000000000000000000001171046102023000204530ustar 00000000000000// SPDX-License-Identifier: MIT #[cfg(test)] mod ipv4; #[cfg(test)] mod ipv6; netlink-packet-route-0.21.0/src/address_family_fallback.rs000064400000000000000000000022361046102023000217160ustar 00000000000000// SPDX-License-Identifier: MIT #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] #[non_exhaustive] // We are using not using #[repr(u8)] here as we have duplicate(e.g. AF_ROUTE vs // AF_NETLINK) here pub enum AddressFamily { #[default] Unspec, Local, Unix, Inet, Inet6, Other(u8), } impl From for AddressFamily { fn from(d: u8) -> Self { match d { d if d == libc::AF_UNSPEC as u8 => Self::Unspec, d if d == libc::AF_LOCAL as u8 => Self::Local, d if d == libc::AF_UNIX as u8 => Self::Unix, d if d == libc::AF_INET as u8 => Self::Inet, d if d == libc::AF_INET6 as u8 => Self::Inet6, _ => Self::Other(d), } } } impl From for u8 { fn from(v: AddressFamily) -> u8 { match v { AddressFamily::Unspec => libc::AF_UNSPEC as u8, AddressFamily::Local => libc::AF_LOCAL as u8, AddressFamily::Unix => libc::AF_UNIX as u8, AddressFamily::Inet => libc::AF_INET as u8, AddressFamily::Inet6 => libc::AF_INET6 as u8, AddressFamily::Other(d) => d, } } } netlink-packet-route-0.21.0/src/address_family_freebsd.rs000064400000000000000000000104021046102023000215630ustar 00000000000000// SPDX-License-Identifier: MIT #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] #[non_exhaustive] pub enum AddressFamily { #[default] Unspec, Local, Unix, Inet, Implink, Pup, Chaos, Netbios, Iso, Osi, Ecma, Datakit, Ccitt, Sna, Decnet, Dli, Lat, Hylink, Appletalk, Route, Link, Coip, Cnt, Ipx, Sip, Isdn, E164, Inet6, Natm, Atm, Netgraph, Other(u8), } impl From for AddressFamily { fn from(d: u8) -> Self { match d { d if d == libc::AF_UNSPEC as u8 => Self::Unspec, d if d == libc::AF_LOCAL as u8 => Self::Local, d if d == libc::AF_UNIX as u8 => Self::Unix, d if d == libc::AF_INET as u8 => Self::Inet, d if d == libc::AF_IMPLINK as u8 => Self::Implink, d if d == libc::AF_PUP as u8 => Self::Pup, d if d == libc::AF_CHAOS as u8 => Self::Chaos, d if d == libc::AF_NETBIOS as u8 => Self::Netbios, d if d == libc::AF_ISO as u8 => Self::Iso, d if d == libc::AF_OSI as u8 => Self::Osi, d if d == libc::AF_ECMA as u8 => Self::Ecma, d if d == libc::AF_DATAKIT as u8 => Self::Datakit, d if d == libc::AF_CCITT as u8 => Self::Ccitt, d if d == libc::AF_SNA as u8 => Self::Sna, d if d == libc::AF_DECnet as u8 => Self::Decnet, d if d == libc::AF_DLI as u8 => Self::Dli, d if d == libc::AF_LAT as u8 => Self::Lat, d if d == libc::AF_HYLINK as u8 => Self::Hylink, d if d == libc::AF_APPLETALK as u8 => Self::Appletalk, d if d == libc::AF_ROUTE as u8 => Self::Route, d if d == libc::AF_LINK as u8 => Self::Link, d if d == libc::AF_COIP as u8 => Self::Coip, d if d == libc::AF_CNT as u8 => Self::Cnt, d if d == libc::AF_IPX as u8 => Self::Ipx, d if d == libc::AF_SIP as u8 => Self::Sip, d if d == libc::AF_ISDN as u8 => Self::Isdn, d if d == libc::AF_E164 as u8 => Self::E164, d if d == libc::AF_INET6 as u8 => Self::Inet6, d if d == libc::AF_NATM as u8 => Self::Natm, d if d == libc::AF_ATM as u8 => Self::Atm, d if d == libc::AF_NETGRAPH as u8 => Self::Netgraph, _ => Self::Other(d), } } } impl From for u8 { fn from(v: AddressFamily) -> u8 { match v { AddressFamily::Unspec => libc::AF_UNSPEC as u8, AddressFamily::Local => libc::AF_LOCAL as u8, AddressFamily::Unix => libc::AF_UNIX as u8, AddressFamily::Inet => libc::AF_INET as u8, AddressFamily::Implink => libc::AF_IMPLINK as u8, AddressFamily::Pup => libc::AF_PUP as u8, AddressFamily::Chaos => libc::AF_CHAOS as u8, AddressFamily::Netbios => libc::AF_NETBIOS as u8, AddressFamily::Iso => libc::AF_ISO as u8, AddressFamily::Osi => libc::AF_OSI as u8, AddressFamily::Ecma => libc::AF_ECMA as u8, AddressFamily::Datakit => libc::AF_DATAKIT as u8, AddressFamily::Ccitt => libc::AF_CCITT as u8, AddressFamily::Sna => libc::AF_SNA as u8, AddressFamily::Decnet => libc::AF_DECnet as u8, AddressFamily::Dli => libc::AF_DLI as u8, AddressFamily::Lat => libc::AF_LAT as u8, AddressFamily::Hylink => libc::AF_HYLINK as u8, AddressFamily::Appletalk => libc::AF_APPLETALK as u8, AddressFamily::Route => libc::AF_ROUTE as u8, AddressFamily::Link => libc::AF_LINK as u8, AddressFamily::Coip => libc::AF_COIP as u8, AddressFamily::Cnt => libc::AF_CNT as u8, AddressFamily::Ipx => libc::AF_IPX as u8, AddressFamily::Sip => libc::AF_SIP as u8, AddressFamily::Isdn => libc::AF_ISDN as u8, AddressFamily::E164 => libc::AF_E164 as u8, AddressFamily::Inet6 => libc::AF_INET6 as u8, AddressFamily::Natm => libc::AF_NATM as u8, AddressFamily::Atm => libc::AF_ATM as u8, AddressFamily::Netgraph => libc::AF_NETGRAPH as u8, AddressFamily::Other(d) => d, } } } netlink-packet-route-0.21.0/src/address_family_linux.rs000064400000000000000000000146341046102023000213230ustar 00000000000000// SPDX-License-Identifier: MIT const AF_KCM: u8 = 41; const AF_QIPCRTR: u8 = 42; const AF_SMC: u8 = 43; const AF_XDP: u8 = 44; const AF_MCTP: u8 = 45; #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] #[non_exhaustive] pub enum AddressFamily { #[default] Unspec, Local, Unix, Inet, Ax25, Ipx, Appletalk, Netrom, Bridge, Atmpvc, X25, Inet6, Rose, Decnet, Netbeui, Security, Key, Route, Netlink, Packet, Ash, Econet, Atmsvc, Rds, Sna, Irda, Pppox, Wanpipe, Llc, Ib, Mpls, Can, Tipc, Bluetooth, Iucv, Rxrpc, Isdn, Phonet, Ieee802154, Caif, Alg, Nfc, Vsock, Kcm, Qipcrtr, Smc, Xdp, Mctp, Other(u8), } impl From for AddressFamily { fn from(d: u8) -> Self { match d { d if d == libc::AF_UNSPEC as u8 => Self::Unspec, d if d == libc::AF_UNIX as u8 => Self::Unix, d if d == libc::AF_LOCAL as u8 => Self::Local, d if d == libc::AF_INET as u8 => Self::Inet, d if d == libc::AF_AX25 as u8 => Self::Ax25, d if d == libc::AF_IPX as u8 => Self::Ipx, d if d == libc::AF_APPLETALK as u8 => Self::Appletalk, d if d == libc::AF_NETROM as u8 => Self::Netrom, d if d == libc::AF_BRIDGE as u8 => Self::Bridge, d if d == libc::AF_ATMPVC as u8 => Self::Atmpvc, d if d == libc::AF_X25 as u8 => Self::X25, d if d == libc::AF_INET6 as u8 => Self::Inet6, d if d == libc::AF_ROSE as u8 => Self::Rose, d if d == libc::AF_DECnet as u8 => Self::Decnet, d if d == libc::AF_NETBEUI as u8 => Self::Netbeui, d if d == libc::AF_SECURITY as u8 => Self::Security, d if d == libc::AF_KEY as u8 => Self::Key, d if d == libc::AF_NETLINK as u8 => Self::Netlink, d if d == libc::AF_ROUTE as u8 => Self::Route, d if d == libc::AF_PACKET as u8 => Self::Packet, d if d == libc::AF_ASH as u8 => Self::Ash, d if d == libc::AF_ECONET as u8 => Self::Econet, d if d == libc::AF_ATMSVC as u8 => Self::Atmsvc, d if d == libc::AF_RDS as u8 => Self::Rds, d if d == libc::AF_SNA as u8 => Self::Sna, d if d == libc::AF_IRDA as u8 => Self::Irda, d if d == libc::AF_PPPOX as u8 => Self::Pppox, d if d == libc::AF_WANPIPE as u8 => Self::Wanpipe, d if d == libc::AF_LLC as u8 => Self::Llc, d if d == libc::AF_IB as u8 => Self::Ib, d if d == libc::AF_MPLS as u8 => Self::Mpls, d if d == libc::AF_CAN as u8 => Self::Can, d if d == libc::AF_TIPC as u8 => Self::Tipc, d if d == libc::AF_BLUETOOTH as u8 => Self::Bluetooth, d if d == libc::AF_IUCV as u8 => Self::Iucv, d if d == libc::AF_RXRPC as u8 => Self::Rxrpc, d if d == libc::AF_ISDN as u8 => Self::Isdn, d if d == libc::AF_PHONET as u8 => Self::Phonet, d if d == libc::AF_IEEE802154 as u8 => Self::Ieee802154, d if d == libc::AF_CAIF as u8 => Self::Caif, d if d == libc::AF_ALG as u8 => Self::Alg, d if d == libc::AF_NFC as u8 => Self::Nfc, d if d == libc::AF_VSOCK as u8 => Self::Vsock, d if d == AF_KCM => Self::Kcm, d if d == AF_QIPCRTR => Self::Qipcrtr, d if d == AF_SMC => Self::Smc, d if d == AF_XDP => Self::Xdp, d if d == AF_MCTP => Self::Mctp, _ => Self::Other(d), } } } impl From for u8 { fn from(v: AddressFamily) -> u8 { match v { AddressFamily::Unspec => libc::AF_UNSPEC as u8, AddressFamily::Local => libc::AF_LOCAL as u8, AddressFamily::Unix => libc::AF_UNIX as u8, AddressFamily::Inet => libc::AF_INET as u8, AddressFamily::Ax25 => libc::AF_AX25 as u8, AddressFamily::Ipx => libc::AF_IPX as u8, AddressFamily::Appletalk => libc::AF_APPLETALK as u8, AddressFamily::Netrom => libc::AF_NETROM as u8, AddressFamily::Bridge => libc::AF_BRIDGE as u8, AddressFamily::Atmpvc => libc::AF_ATMPVC as u8, AddressFamily::X25 => libc::AF_X25 as u8, AddressFamily::Inet6 => libc::AF_INET6 as u8, AddressFamily::Rose => libc::AF_ROSE as u8, AddressFamily::Decnet => libc::AF_DECnet as u8, AddressFamily::Netbeui => libc::AF_NETBEUI as u8, AddressFamily::Security => libc::AF_SECURITY as u8, AddressFamily::Key => libc::AF_KEY as u8, AddressFamily::Route => libc::AF_ROUTE as u8, AddressFamily::Netlink => libc::AF_NETLINK as u8, AddressFamily::Packet => libc::AF_PACKET as u8, AddressFamily::Ash => libc::AF_ASH as u8, AddressFamily::Econet => libc::AF_ECONET as u8, AddressFamily::Atmsvc => libc::AF_ATMSVC as u8, AddressFamily::Rds => libc::AF_RDS as u8, AddressFamily::Sna => libc::AF_SNA as u8, AddressFamily::Irda => libc::AF_IRDA as u8, AddressFamily::Pppox => libc::AF_PPPOX as u8, AddressFamily::Wanpipe => libc::AF_WANPIPE as u8, AddressFamily::Llc => libc::AF_LLC as u8, AddressFamily::Ib => libc::AF_IB as u8, AddressFamily::Mpls => libc::AF_MPLS as u8, AddressFamily::Can => libc::AF_CAN as u8, AddressFamily::Tipc => libc::AF_TIPC as u8, AddressFamily::Bluetooth => libc::AF_BLUETOOTH as u8, AddressFamily::Iucv => libc::AF_IUCV as u8, AddressFamily::Rxrpc => libc::AF_RXRPC as u8, AddressFamily::Isdn => libc::AF_ISDN as u8, AddressFamily::Phonet => libc::AF_PHONET as u8, AddressFamily::Ieee802154 => libc::AF_IEEE802154 as u8, AddressFamily::Caif => libc::AF_CAIF as u8, AddressFamily::Alg => libc::AF_ALG as u8, AddressFamily::Nfc => libc::AF_NFC as u8, AddressFamily::Vsock => libc::AF_VSOCK as u8, AddressFamily::Kcm => AF_KCM, AddressFamily::Qipcrtr => AF_QIPCRTR, AddressFamily::Smc => AF_SMC, AddressFamily::Xdp => AF_XDP, AddressFamily::Mctp => AF_MCTP, AddressFamily::Other(d) => d, } } } netlink-packet-route-0.21.0/src/ip.rs000064400000000000000000000141041046102023000155160ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use netlink_packet_utils::DecodeError; pub(crate) const IPV4_ADDR_LEN: usize = 4; pub(crate) const IPV6_ADDR_LEN: usize = 16; pub(crate) fn parse_ipv4_addr(raw: &[u8]) -> Result { if raw.len() == IPV4_ADDR_LEN { Ok(Ipv4Addr::new(raw[0], raw[1], raw[2], raw[3])) } else { Err(DecodeError::from(format!( "Invalid u8 array length {}, expecting \ {IPV4_ADDR_LEN} for IPv4 address, got {:?}", raw.len(), raw, ))) } } pub(crate) fn parse_ipv6_addr(raw: &[u8]) -> Result { if raw.len() == IPV6_ADDR_LEN { let mut data = [0u8; IPV6_ADDR_LEN]; data.copy_from_slice(raw); Ok(Ipv6Addr::from(data)) } else { Err(DecodeError::from(format!( "Invalid u8 array length {}, expecting {IPV6_ADDR_LEN} \ for IPv6 address, got {:?}", raw.len(), raw, ))) } } pub(crate) fn emit_ip_to_buffer(ip: &IpAddr, buffer: &mut [u8]) { match ip { IpAddr::V4(ip) => buffer.copy_from_slice(&ip.octets()), IpAddr::V6(ip) => buffer.copy_from_slice(&ip.octets()), } } pub(crate) fn parse_ip_addr(raw: &[u8]) -> Result { if raw.len() == IPV6_ADDR_LEN { parse_ipv6_addr(raw).map(IpAddr::from) } else if raw.len() == IPV4_ADDR_LEN { parse_ipv4_addr(raw).map(IpAddr::from) } else { Err(DecodeError::from(format!( "Invalid u8 array length {}, expecting {IPV6_ADDR_LEN} \ for IPv6 address or {IPV4_ADDR_LEN} for IPv4 address, got {:?}", raw.len(), raw, ))) } } pub(crate) fn ip_addr_len(addr: &IpAddr) -> usize { if addr.is_ipv4() { IPV4_ADDR_LEN } else { IPV6_ADDR_LEN } } pub(crate) fn emit_ip_addr(addr: &IpAddr, buffer: &mut [u8]) { match addr { IpAddr::V4(ip) => buffer.copy_from_slice(&ip.octets()), IpAddr::V6(ip) => buffer.copy_from_slice(&ip.octets()), } } // These is defined by Assigned Internet Protocol Numbers, no need to use libc // as they are supposed to identical between all operating system. const IPPROTO_HOPOPTS: i32 = 0; const IPPROTO_ICMP: i32 = 1; const IPPROTO_IGMP: i32 = 2; const IPPROTO_IPIP: i32 = 4; const IPPROTO_TCP: i32 = 6; const IPPROTO_EGP: i32 = 8; const IPPROTO_PUP: i32 = 12; const IPPROTO_UDP: i32 = 17; const IPPROTO_IDP: i32 = 22; const IPPROTO_TP: i32 = 29; const IPPROTO_DCCP: i32 = 33; const IPPROTO_IPV6: i32 = 41; const IPPROTO_RSVP: i32 = 46; const IPPROTO_GRE: i32 = 47; const IPPROTO_ESP: i32 = 50; const IPPROTO_AH: i32 = 51; const IPPROTO_MTP: i32 = 92; const IPPROTO_BEETPH: i32 = 94; const IPPROTO_ENCAP: i32 = 98; const IPPROTO_PIM: i32 = 103; const IPPROTO_COMP: i32 = 108; const IPPROTO_L2TP: i32 = 115; const IPPROTO_SCTP: i32 = 132; const IPPROTO_UDPLITE: i32 = 136; const IPPROTO_MPLS: i32 = 137; const IPPROTO_ETHERNET: i32 = 143; const IPPROTO_RAW: i32 = 255; const IPPROTO_MPTCP: i32 = 262; #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] #[non_exhaustive] pub enum IpProtocol { Hopopts, Icmp, Igmp, Ipip, Tcp, Egp, Pup, Udp, Idp, Tp, Dccp, Ipv6, Rsvp, Gre, Esp, Ah, Mtp, Beetph, Encap, Pim, Comp, L2tp, Sctp, Udplite, Mpls, Ethernet, #[default] Raw, Mptcp, Other(i32), } impl From for IpProtocol { fn from(d: i32) -> Self { match d { IPPROTO_HOPOPTS => Self::Hopopts, IPPROTO_ICMP => Self::Icmp, IPPROTO_IGMP => Self::Igmp, IPPROTO_IPIP => Self::Ipip, IPPROTO_TCP => Self::Tcp, IPPROTO_EGP => Self::Egp, IPPROTO_PUP => Self::Pup, IPPROTO_UDP => Self::Udp, IPPROTO_IDP => Self::Idp, IPPROTO_TP => Self::Tp, IPPROTO_DCCP => Self::Dccp, IPPROTO_IPV6 => Self::Ipv6, IPPROTO_RSVP => Self::Rsvp, IPPROTO_GRE => Self::Gre, IPPROTO_ESP => Self::Esp, IPPROTO_AH => Self::Ah, IPPROTO_MTP => Self::Mtp, IPPROTO_BEETPH => Self::Beetph, IPPROTO_ENCAP => Self::Encap, IPPROTO_PIM => Self::Pim, IPPROTO_COMP => Self::Comp, IPPROTO_L2TP => Self::L2tp, IPPROTO_SCTP => Self::Sctp, IPPROTO_UDPLITE => Self::Udplite, IPPROTO_MPLS => Self::Mpls, IPPROTO_ETHERNET => Self::Ethernet, IPPROTO_RAW => Self::Raw, IPPROTO_MPTCP => Self::Mptcp, _ => Self::Other(d), } } } impl From for i32 { fn from(v: IpProtocol) -> i32 { match v { IpProtocol::Hopopts => IPPROTO_HOPOPTS, IpProtocol::Icmp => IPPROTO_ICMP, IpProtocol::Igmp => IPPROTO_IGMP, IpProtocol::Ipip => IPPROTO_IPIP, IpProtocol::Tcp => IPPROTO_TCP, IpProtocol::Egp => IPPROTO_EGP, IpProtocol::Pup => IPPROTO_PUP, IpProtocol::Udp => IPPROTO_UDP, IpProtocol::Idp => IPPROTO_IDP, IpProtocol::Tp => IPPROTO_TP, IpProtocol::Dccp => IPPROTO_DCCP, IpProtocol::Ipv6 => IPPROTO_IPV6, IpProtocol::Rsvp => IPPROTO_RSVP, IpProtocol::Gre => IPPROTO_GRE, IpProtocol::Esp => IPPROTO_ESP, IpProtocol::Ah => IPPROTO_AH, IpProtocol::Mtp => IPPROTO_MTP, IpProtocol::Beetph => IPPROTO_BEETPH, IpProtocol::Encap => IPPROTO_ENCAP, IpProtocol::Pim => IPPROTO_PIM, IpProtocol::Comp => IPPROTO_COMP, IpProtocol::L2tp => IPPROTO_L2TP, IpProtocol::Sctp => IPPROTO_SCTP, IpProtocol::Udplite => IPPROTO_UDPLITE, IpProtocol::Mpls => IPPROTO_MPLS, IpProtocol::Ethernet => IPPROTO_ETHERNET, IpProtocol::Raw => IPPROTO_RAW, IpProtocol::Mptcp => IPPROTO_MPTCP, IpProtocol::Other(d) => d, } } } netlink-packet-route-0.21.0/src/lib.rs000064400000000000000000000040771046102023000156640ustar 00000000000000// SPDX-License-Identifier: MIT pub mod address; pub mod link; pub mod neighbour; pub mod neighbour_table; pub mod nsid; pub mod prefix; pub mod route; pub mod rule; pub mod tc; mod message; #[cfg(test)] mod tests; pub(crate) mod ip; #[cfg(any(target_os = "linux", target_os = "fuchsia"))] mod address_family_linux; #[cfg(any(target_os = "linux", target_os = "fuchsia"))] pub use self::address_family_linux::AddressFamily; #[cfg(target_os = "freebsd")] mod address_family_freebsd; #[cfg(target_os = "freebsd")] pub use self::address_family_freebsd::AddressFamily; #[cfg(not(any( target_os = "linux", target_os = "fuchsia", target_os = "freebsd", )))] mod address_family_fallback; #[cfg(not(any( target_os = "linux", target_os = "fuchsia", target_os = "freebsd", )))] pub use self::address_family_fallback::AddressFamily; pub use self::ip::IpProtocol; pub use self::message::{RouteNetlinkMessage, RouteNetlinkMessageBuffer}; /// The `netlink-packet-route` crate is designed to abstract Netlink route /// protocol(`rtnetlink`) packet into Rust data types. The goal of this crate is /// saving netlink user from reading Kernel Netlink codes. /// /// This crate grouped Netlink route protocol into these modules: /// * `link`: NIC interface, similar to to `ip link` command. /// * `address`: IP address, similar to `ip address` command. /// * `route`: Route, similar to `ip route` command. /// * `rule`: Route rule, similar to `ip rule` command. /// * `tc`: Traffic control, similar to `tc` command. /// * `neighbour`: Neighbour, similar to `ip neighbour` command. /// * `neighbour_table`: Neighbour table, similar to `ip ntable` command. /// * `nsid`: Namespace, similar to `ip netns` command. /// /// At the top level of this crate, we also provide: /// * [AddressFamily] /// /// Normally, you should use [`rtnetlink`][rtnetlink_url] instead of using this /// crate directly. /// /// [rtnetlink_url]: https://docs.rs/rtnetlink #[macro_use] extern crate netlink_packet_utils; #[cfg(test)] #[macro_use] extern crate pretty_assertions; #[macro_use] extern crate bitflags; netlink-packet-route-0.21.0/src/link/af_spec/bridge.rs000064400000000000000000000243651046102023000207110ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, parsers::{parse_u16, parse_u32}, traits::Parseable, DecodeError, Emitable, }; const IFLA_BRIDGE_FLAGS: u16 = 0; const IFLA_BRIDGE_MODE: u16 = 1; const IFLA_BRIDGE_VLAN_INFO: u16 = 2; const IFLA_BRIDGE_VLAN_TUNNEL_INFO: u16 = 3; // const IFLA_BRIDGE_MRP: u16 = 4; // const IFLA_BRIDGE_CFM: u16 = 5; // const IFLA_BRIDGE_MST: u16 = 6; #[derive(Clone, Eq, PartialEq, Debug)] #[non_exhaustive] pub enum AfSpecBridge { Flags(BridgeFlag), Mode(BridgeMode), VlanInfo(BridgeVlanInfo), VlanTunnelInfo(Vec), Other(DefaultNla), } impl Nla for AfSpecBridge { fn value_len(&self) -> usize { match self { Self::Flags(_) => BridgeFlag::LENGTH, Self::Mode(_) => BridgeMode::LENGTH, Self::VlanInfo(_) => BridgeVlanInfo::LENGTH, Self::VlanTunnelInfo(s) => s.as_slice().buffer_len(), Self::Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Flags(value) => { NativeEndian::write_u16(buffer, u16::from(*value)) } Self::Mode(value) => { NativeEndian::write_u16(buffer, u16::from(*value)) } Self::VlanInfo(info) => { buffer[..4].copy_from_slice(<[u8; 4]>::from(info).as_slice()) } Self::VlanTunnelInfo(s) => s.as_slice().emit(buffer), Self::Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Flags(_) => IFLA_BRIDGE_FLAGS, Self::Mode(_) => IFLA_BRIDGE_MODE, Self::VlanInfo(_) => IFLA_BRIDGE_VLAN_INFO, Self::VlanTunnelInfo(_) => IFLA_BRIDGE_VLAN_TUNNEL_INFO, Self::Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for AfSpecBridge { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { IFLA_BRIDGE_FLAGS => Self::Flags( parse_u16(payload) .context("Invalid IFLA_BRIDGE_FLAGS value")? .into(), ), IFLA_BRIDGE_MODE => Self::Mode( parse_u16(payload) .context("Invalid IFLA_BRIDGE_MODE value")? .into(), ), IFLA_BRIDGE_VLAN_INFO => Self::VlanInfo( BridgeVlanInfo::try_from(payload) .context("Invalid IFLA_BRIDGE_VLAN_INFO value")?, ), IFLA_BRIDGE_VLAN_TUNNEL_INFO => { let mut nlas = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "Invalid IFLA_BRIDGE_VLAN_TUNNEL_INFO for {payload:?}" ))?; let parsed = BridgeVlanTunnelInfo::parse(nla)?; nlas.push(parsed); } Self::VlanTunnelInfo(nlas) } kind => Self::Other( DefaultNla::parse(buf) .context(format!("Unknown NLA type {kind}"))?, ), }) } } #[cfg(any(target_os = "linux", target_os = "fuchsia"))] pub(crate) struct VecAfSpecBridge(pub(crate) Vec); #[cfg(any(target_os = "linux", target_os = "fuchsia"))] impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecAfSpecBridge { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; let err = "Invalid AF_INET NLA for IFLA_AF_SPEC(AF_BRIDGE)"; for nla in NlasIterator::new(buf.into_inner()) { let nla = nla.context(err)?; nlas.push(AfSpecBridge::parse(&nla).context(err)?); } Ok(Self(nlas)) } } const BRIDGE_VLAN_INFO_CONTROLLER: u16 = 1 << 0; const BRIDGE_VLAN_INFO_PVID: u16 = 1 << 1; const BRIDGE_VLAN_INFO_UNTAGGED: u16 = 1 << 2; const BRIDGE_VLAN_INFO_RANGE_BEGIN: u16 = 1 << 3; const BRIDGE_VLAN_INFO_RANGE_END: u16 = 1 << 4; const BRIDGE_VLAN_INFO_BRENTRY: u16 = 1 << 5; const BRIDGE_VLAN_INFO_ONLY_OPTS: u16 = 1 << 6; bitflags! { #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct BridgeVlanInfoFlags: u16 { /// Operate on Bridge device as well const Controller = BRIDGE_VLAN_INFO_CONTROLLER; /// VLAN is PVID, ingress untagged const Pvid = BRIDGE_VLAN_INFO_PVID; /// VLAN egresses untagged const Untagged= BRIDGE_VLAN_INFO_UNTAGGED; /// VLAN is start of vlan range const RangeBegin = BRIDGE_VLAN_INFO_RANGE_BEGIN; /// VLAN is end of vlan range const RangeEnd = BRIDGE_VLAN_INFO_RANGE_END; /// Global bridge VLAN entry const Brentry = BRIDGE_VLAN_INFO_BRENTRY; /// Skip create/delete/flags const OnlyOpts= BRIDGE_VLAN_INFO_ONLY_OPTS; const _ = !0; } } impl BridgeVlanInfoFlags { pub const LENGTH: usize = 2; } #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct BridgeVlanInfo { pub flags: BridgeVlanInfoFlags, pub vid: u16, } impl BridgeVlanInfo { pub const LENGTH: usize = 4; } 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.bits()); 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: BridgeVlanInfoFlags::from_bits_retain( 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:?}" ))) } } } // kernel constant name is BRIDGE_FLAGS_MASTER const BRIDGE_FLAGS_CONTROLLER: u16 = 1; const BRIDGE_FLAGS_SELF: u16 = 2; #[derive(Clone, Copy, Eq, PartialEq, Debug)] #[non_exhaustive] pub enum BridgeFlag { /// Bridge command to/from controller Controller, /// Bridge command to/from lowerdev LowerDev, Other(u16), } impl From for BridgeFlag { fn from(d: u16) -> Self { match d { BRIDGE_FLAGS_CONTROLLER => Self::Controller, BRIDGE_FLAGS_SELF => Self::LowerDev, _ => Self::Other(d), } } } impl From for u16 { fn from(v: BridgeFlag) -> u16 { match v { BridgeFlag::Controller => BRIDGE_FLAGS_CONTROLLER, BridgeFlag::LowerDev => BRIDGE_FLAGS_SELF, BridgeFlag::Other(d) => d, } } } impl BridgeFlag { pub const LENGTH: usize = 2; } const BRIDGE_MODE_VEB: u16 = 0; const BRIDGE_MODE_VEPA: u16 = 1; #[derive(Clone, Copy, Eq, PartialEq, Debug)] #[non_exhaustive] pub enum BridgeMode { /// Default loopback mode Veb, /// 802.1Qbg defined VEPA mode Vepa, Other(u16), } impl From for BridgeMode { fn from(d: u16) -> Self { match d { BRIDGE_MODE_VEB => Self::Veb, BRIDGE_MODE_VEPA => Self::Vepa, _ => Self::Other(d), } } } impl From for u16 { fn from(v: BridgeMode) -> u16 { match v { BridgeMode::Veb => BRIDGE_MODE_VEB, BridgeMode::Vepa => BRIDGE_MODE_VEPA, BridgeMode::Other(d) => d, } } } impl BridgeMode { pub const LENGTH: usize = 2; } const IFLA_BRIDGE_VLAN_TUNNEL_ID: u16 = 1; const IFLA_BRIDGE_VLAN_TUNNEL_VID: u16 = 2; const IFLA_BRIDGE_VLAN_TUNNEL_FLAGS: u16 = 3; #[derive(Clone, Eq, PartialEq, Debug)] #[non_exhaustive] pub enum BridgeVlanTunnelInfo { Id(u32), Vid(u16), Flags(BridgeVlanInfoFlags), Other(DefaultNla), } impl Nla for BridgeVlanTunnelInfo { fn value_len(&self) -> usize { match self { Self::Id(_) => 4, Self::Vid(_) => 2, Self::Flags(_) => BridgeVlanInfoFlags::LENGTH, Self::Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Id(v) => NativeEndian::write_u32(buffer, *v), Self::Vid(v) => NativeEndian::write_u16(buffer, *v), Self::Flags(value) => NativeEndian::write_u16(buffer, value.bits()), Self::Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Id(_) => IFLA_BRIDGE_VLAN_TUNNEL_ID, Self::Vid(_) => IFLA_BRIDGE_VLAN_TUNNEL_VID, Self::Flags(_) => IFLA_BRIDGE_VLAN_TUNNEL_FLAGS, Self::Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for BridgeVlanTunnelInfo { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { IFLA_BRIDGE_VLAN_TUNNEL_ID => { Self::Id(parse_u32(payload).context(format!( "Invalid IFLA_BRIDGE_VLAN_TUNNEL_ID {payload:?}" ))?) } IFLA_BRIDGE_VLAN_TUNNEL_VID => { Self::Vid(parse_u16(payload).context(format!( "Invalid IFLA_BRIDGE_VLAN_TUNNEL_VID {payload:?}" ))?) } IFLA_BRIDGE_VLAN_TUNNEL_FLAGS => { Self::Flags(BridgeVlanInfoFlags::from_bits_retain( parse_u16(payload).context(format!( "Invalid IFLA_BRIDGE_VLAN_TUNNEL_VID {payload:?}" ))?, )) } _ => { Self::Other(DefaultNla::parse(buf).context("Unknown NLA type")?) } }) } } netlink-packet-route-0.21.0/src/link/af_spec/inet.rs000064400000000000000000000212711046102023000204050ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{self, DefaultNla, NlaBuffer, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; use super::super::buffer_tool::expand_buffer_if_small; const IFLA_INET_CONF: u16 = 1; // This number might change when kernel add more IPV4_DEV_CONF const __IPV4_DEVCONF_MAX: usize = 34; const IPV4_DEVCONF_MAX: usize = __IPV4_DEVCONF_MAX - 1; const DEV_CONF_LEN: usize = IPV4_DEVCONF_MAX * 4; #[derive(Clone, Eq, PartialEq, Debug)] #[non_exhaustive] pub enum AfSpecInet { DevConf(InetDevConf), Other(DefaultNla), } pub(crate) struct VecAfSpecInet(pub(crate) Vec); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecAfSpecInet { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; let err = "Invalid AF_INET NLA for IFLA_AF_SPEC(AF_UNSPEC)"; for nla in NlasIterator::new(buf.into_inner()) { let nla = nla.context(err)?; nlas.push(AfSpecInet::parse(&nla)?); } Ok(Self(nlas)) } } impl nla::Nla for AfSpecInet { fn value_len(&self) -> usize { use self::AfSpecInet::*; match *self { DevConf(ref c) => c.buffer_len(), Other(ref nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::AfSpecInet::*; match *self { DevConf(ref c) => c.emit(buffer), Other(ref nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::AfSpecInet::*; match *self { DevConf(_) => IFLA_INET_CONF, 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() { IFLA_INET_CONF => { DevConf(InetDevConf::parse(&InetDevConfBuffer::new( expand_buffer_if_small( payload, DEV_CONF_LEN, "IFLA_INET_CONF", ) .as_slice(), ))?) } kind => Other(DefaultNla::parse(buf).context(format!( "Unknown NLA type {kind} for IFLA_AF_SPEC(inet)" ))?), }) } } 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), bc_forwarding: (i32, 124..128), arp_evict_nocarrier: (i32, 128..132), }); #[derive(Clone, Copy, Eq, PartialEq, Debug, Default)] #[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, pub bc_forwarding: i32, pub arp_evict_nocarrier: 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(), bc_forwarding: buf.bc_forwarding(), arp_evict_nocarrier: buf.arp_evict_nocarrier(), }) } } 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); buffer.set_bc_forwarding(self.bc_forwarding); buffer.set_arp_evict_nocarrier(self.arp_evict_nocarrier); } } netlink-packet-route-0.21.0/src/link/af_spec/inet6.rs000064400000000000000000000145171046102023000205000ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::Ipv6Addr; use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, parsers::{parse_u32, parse_u8}, traits::{Emitable, Parseable}, DecodeError, }; use super::super::{ buffer_tool::expand_buffer_if_small, Icmp6Stats, Icmp6StatsBuffer, Inet6CacheInfo, Inet6CacheInfoBuffer, Inet6DevConf, Inet6DevConfBuffer, Inet6IfaceFlags, Inet6Stats, Inet6StatsBuffer, }; use super::{ inet6_devconf::LINK_INET6_DEV_CONF_LEN, inet6_icmp::ICMP6_STATS_LEN, inet6_stats::INET6_STATS_LEN, }; use crate::ip::parse_ipv6_addr; const IFLA_INET6_FLAGS: u16 = 1; const IFLA_INET6_CONF: u16 = 2; const IFLA_INET6_STATS: u16 = 3; // No kernel code used IFLA_INET6_MCAST // const IFLA_INET6_MCAST: u16 = 4; const IFLA_INET6_CACHEINFO: u16 = 5; const IFLA_INET6_ICMP6STATS: u16 = 6; const IFLA_INET6_TOKEN: u16 = 7; const IFLA_INET6_ADDR_GEN_MODE: u16 = 8; const IFLA_INET6_RA_MTU: u16 = 9; #[derive(Clone, Eq, PartialEq, Debug)] #[non_exhaustive] pub enum AfSpecInet6 { //TODO(Gris Ge): Use Vec for `IFF_UP` and etc Flags(Inet6IfaceFlags), CacheInfo(Inet6CacheInfo), DevConf(Inet6DevConf), Stats(Inet6Stats), Icmp6Stats(Icmp6Stats), Token(Ipv6Addr), AddrGenMode(u8), RaMtu(u32), Other(DefaultNla), } pub(crate) struct VecAfSpecInet6(pub(crate) Vec); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecAfSpecInet6 { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; let err = "Invalid AF_INET6 NLA for IFLA_AF_SPEC(AF_UNSPEC)"; for nla in NlasIterator::new(buf.into_inner()) { let nla = nla.context(err)?; nlas.push(AfSpecInet6::parse(&nla).context(err)?); } Ok(Self(nlas)) } } impl Nla for AfSpecInet6 { fn value_len(&self) -> usize { use self::AfSpecInet6::*; match *self { CacheInfo(ref cache_info) => cache_info.buffer_len(), DevConf(ref dev_conf) => dev_conf.buffer_len(), Stats(ref stats) => stats.buffer_len(), Icmp6Stats(ref icmp_stats) => icmp_stats.buffer_len(), Flags(_) | RaMtu(_) => 4, Token(_) => 16, AddrGenMode(_) => 1, Other(ref nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::AfSpecInet6::*; match *self { Flags(ref value) => NativeEndian::write_u32(buffer, value.bits()), RaMtu(ref value) => NativeEndian::write_u32(buffer, *value), CacheInfo(ref v) => v.emit(buffer), DevConf(ref v) => v.emit(buffer), Stats(ref v) => v.emit(buffer), Icmp6Stats(ref v) => v.emit(buffer), Token(v) => buffer.copy_from_slice(&v.octets()), AddrGenMode(value) => buffer[0] = value, Other(ref nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::AfSpecInet6::*; match *self { Flags(_) => IFLA_INET6_FLAGS, CacheInfo(_) => IFLA_INET6_CACHEINFO, DevConf(_) => IFLA_INET6_CONF, Stats(_) => IFLA_INET6_STATS, Icmp6Stats(_) => IFLA_INET6_ICMP6STATS, Token(_) => IFLA_INET6_TOKEN, AddrGenMode(_) => IFLA_INET6_ADDR_GEN_MODE, RaMtu(_) => IFLA_INET6_RA_MTU, Other(ref nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for AfSpecInet6 { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::AfSpecInet6::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_INET6_FLAGS => Flags(Inet6IfaceFlags::from_bits_retain( parse_u32(payload).context("invalid IFLA_INET6_FLAGS value")?, )), IFLA_INET6_CACHEINFO => CacheInfo( Inet6CacheInfo::parse(&Inet6CacheInfoBuffer::new(payload)) .context(format!( "invalid IFLA_INET6_CACHEINFO value {:?}", payload ))?, ), IFLA_INET6_CONF => DevConf( Inet6DevConf::parse(&Inet6DevConfBuffer::new( expand_buffer_if_small( payload, LINK_INET6_DEV_CONF_LEN, "IFLA_INET6_CONF", ) .as_slice(), )) .context(format!( "invalid IFLA_INET6_CONF value {:?}", payload ))?, ), IFLA_INET6_STATS => Stats( Inet6Stats::parse(&Inet6StatsBuffer::new( expand_buffer_if_small( payload, INET6_STATS_LEN, "IFLA_INET6_STATS", ) .as_slice(), )) .context(format!( "invalid IFLA_INET6_STATS value {:?}", payload ))?, ), IFLA_INET6_ICMP6STATS => Icmp6Stats( super::super::Icmp6Stats::parse(&Icmp6StatsBuffer::new( expand_buffer_if_small( payload, ICMP6_STATS_LEN, "IFLA_INET6_ICMP6STATS", ) .as_slice(), )) .context(format!( "invalid IFLA_INET6_ICMP6STATS value {:?}", payload ))?, ), IFLA_INET6_TOKEN => Token( parse_ipv6_addr(payload) .context("invalid IFLA_INET6_TOKEN value")?, ), IFLA_INET6_ADDR_GEN_MODE => AddrGenMode( parse_u8(payload) .context("invalid IFLA_INET6_ADDR_GEN_MODE value")?, ), IFLA_INET6_RA_MTU => RaMtu( parse_u32(payload) .context("invalid IFLA_INET6_RA_MTU value")?, ), kind => Other(DefaultNla::parse(buf).context(format!( "unknown AF_INET6 NLA type {kind} for IFLA_AF_SPEC(AF_UNSPEC)" ))?), }) } } netlink-packet-route-0.21.0/src/link/af_spec/inet6_cache.rs000064400000000000000000000024731046102023000216210ustar 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, } 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.21.0/src/link/af_spec/inet6_devconf.rs000064400000000000000000000270361046102023000222040ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; // The DEVCONF_MAX will increase when kernel add more DEVCONF const DEVCONF_MAX: usize = 59; pub(crate) const LINK_INET6_DEV_CONF_LEN: usize = DEVCONF_MAX * 4; 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), rpl_seg_enabled: (i32, 204..208), ra_defrtr_metric: (i32, 208..212), ioam6_enabled: (i32, 212..216), ioam6_id: (i32, 216..220), ioam6_id_wide: (i32, 220..224), ndisc_evict_nocarrier: (i32, 224..228), accept_untracked_na: (i32, 228..232), accept_ra_min_lft: (i32, 232..236), }); 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(), rpl_seg_enabled: buf.rpl_seg_enabled(), ra_defrtr_metric: buf.ra_defrtr_metric(), ioam6_enabled: buf.ioam6_enabled(), ioam6_id: buf.ioam6_id(), ioam6_id_wide: buf.ioam6_id_wide(), ndisc_evict_nocarrier: buf.ndisc_evict_nocarrier(), accept_untracked_na: buf.accept_untracked_na(), accept_ra_min_lft: buf.accept_ra_min_lft(), }) } } 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); buffer.set_ndisc_tclass(self.ndisc_tclass); buffer.set_rpl_seg_enabled(self.rpl_seg_enabled); buffer.set_ra_defrtr_metric(self.ra_defrtr_metric); buffer.set_ioam6_enabled(self.ioam6_enabled); buffer.set_ioam6_id(self.ioam6_id); buffer.set_ioam6_id_wide(self.ioam6_id_wide); buffer.set_ndisc_evict_nocarrier(self.ndisc_evict_nocarrier); buffer.set_accept_untracked_na(self.accept_untracked_na); buffer.set_accept_ra_min_lft(self.accept_ra_min_lft); } } #[derive(Clone, Copy, Eq, PartialEq, Debug, Default)] #[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, pub rpl_seg_enabled: i32, pub ra_defrtr_metric: i32, pub ioam6_enabled: i32, pub ioam6_id: i32, pub ioam6_id_wide: i32, pub ndisc_evict_nocarrier: i32, pub accept_untracked_na: i32, pub accept_ra_min_lft: i32, } netlink-packet-route-0.21.0/src/link/af_spec/inet6_icmp.rs000064400000000000000000000027251046102023000215060ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; pub(crate) const ICMP6_STATS_LEN: usize = 48; #[derive(Clone, Copy, Eq, PartialEq, Debug, Default)] #[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, } 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.21.0/src/link/af_spec/inet6_iface_flag.rs000064400000000000000000000010261046102023000226070ustar 00000000000000// SPDX-License-Identifier: MIT const IF_RA_OTHERCONF: u32 = 0x80; const IF_RA_MANAGED: u32 = 0x40; const IF_RA_RCVD: u32 = 0x20; const IF_RS_SENT: u32 = 0x10; const IF_READY: u32 = 0x80000000; bitflags! { #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub struct Inet6IfaceFlags : u32 { const Otherconf = IF_RA_OTHERCONF; const RaManaged = IF_RA_MANAGED; const RaRcvd = IF_RA_RCVD; const RsSent = IF_RS_SENT; const Ready = IF_READY; const _ = !0; } } netlink-packet-route-0.21.0/src/link/af_spec/inet6_stats.rs000064400000000000000000000145411046102023000217130ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; pub(crate) 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, Default)] #[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.21.0/src/link/af_spec/mod.rs000064400000000000000000000016701046102023000202260ustar 00000000000000// SPDX-License-Identifier: MIT mod bridge; mod inet; mod inet6; mod inet6_cache; mod inet6_devconf; mod inet6_icmp; mod inet6_iface_flag; mod inet6_stats; mod unspec; pub use self::bridge::{ AfSpecBridge, BridgeFlag, BridgeMode, BridgeVlanInfo, BridgeVlanInfoFlags, BridgeVlanTunnelInfo, }; pub use self::inet::{AfSpecInet, InetDevConf}; pub use self::inet6::AfSpecInet6; pub use self::inet6_cache::{Inet6CacheInfo, Inet6CacheInfoBuffer}; pub use self::inet6_devconf::{Inet6DevConf, Inet6DevConfBuffer}; pub use self::inet6_icmp::{Icmp6Stats, Icmp6StatsBuffer}; pub use self::inet6_iface_flag::Inet6IfaceFlags; pub use self::inet6_stats::{Inet6Stats, Inet6StatsBuffer}; pub use self::unspec::AfSpecUnspec; #[cfg(any(target_os = "linux", target_os = "fuchsia"))] pub(crate) use self::bridge::VecAfSpecBridge; pub(crate) use self::inet::VecAfSpecInet; pub(crate) use self::inet6::VecAfSpecInet6; pub(crate) use self::unspec::VecAfSpecUnspec; netlink-packet-route-0.21.0/src/link/af_spec/unspec.rs000064400000000000000000000072641046102023000207510ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; use crate::link::{ af_spec::{VecAfSpecInet, VecAfSpecInet6}, AfSpecInet, AfSpecInet6, }; use crate::AddressFamily; // For `AF_UNSPEC`, the `IFLA_AF_SPEC` is two layer array: // // [{nla_len=408, nla_type=IFLA_AF_SPEC}, // [ // [{nla_len=140, nla_type=AF_INET}, [ // {nla_len=136, nla_type=IFLA_INET_CONF}, [ // [IPV4_DEVCONF_FORWARDING-1] = 0, // // [IPV4_DEVCONF_ARP_EVICT_NOCARRIER-1] = 1]]], // [{nla_len=264, nla_type=AF_INET6}, [ // [{nla_len=8, nla_type=IFLA_INET6_FLAGS}, IF_READY], // [{nla_len=20, nla_type=IFLA_INET6_CACHEINFO}, // { // max_reasm_len=65535, // tstamp=3794, // reachable_time=37584, // retrans_time=1000}], // [{nla_len=232, nla_type=IFLA_INET6_CONF}, // [[DEVCONF_FORWARDING] = 0, // // [DEVCONF_NDISC_EVICT_NOCARRIER] = 1]]]]]] #[derive(Clone, Eq, PartialEq, Debug)] #[non_exhaustive] pub enum AfSpecUnspec { Inet(Vec), Inet6(Vec), Other(DefaultNla), } pub(crate) struct VecAfSpecUnspec(pub(crate) Vec); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecAfSpecUnspec { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; let err = "Invalid NLA for IFLA_AF_SPEC(AF_UNSPEC)"; for nla in NlasIterator::new(buf.into_inner()) { let nla = nla.context(err)?; nlas.push(match nla.kind() { k if k == u8::from(AddressFamily::Inet) as u16 => { AfSpecUnspec::Inet( VecAfSpecInet::parse( &NlaBuffer::new_checked(&nla.value()) .context(err)?, ) .context(err)? .0, ) } k if k == u8::from(AddressFamily::Inet6) as u16 => { AfSpecUnspec::Inet6( VecAfSpecInet6::parse( &NlaBuffer::new_checked(&nla.value()) .context(err)?, ) .context(err)? .0, ) } kind => AfSpecUnspec::Other(DefaultNla::parse(&nla).context( format!( "Unknown AF_XXX type {kind} for IFLA_AF_SPEC(AF_UNSPEC)" ), )?), }) } Ok(Self(nlas)) } } impl Nla for AfSpecUnspec { fn value_len(&self) -> usize { match *self { Self::Inet(ref nlas) => nlas.as_slice().buffer_len(), Self::Inet6(ref nlas) => nlas.as_slice().buffer_len(), Self::Other(ref nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match *self { Self::Inet(ref nlas) => nlas.as_slice().emit(buffer), Self::Inet6(ref nlas) => nlas.as_slice().emit(buffer), Self::Other(ref nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { match *self { Self::Inet(_) => u8::from(AddressFamily::Inet) as u16, Self::Inet6(_) => u8::from(AddressFamily::Inet6) as u16, Self::Other(ref nla) => nla.kind(), } } } netlink-packet-route-0.21.0/src/link/attribute.rs000064400000000000000000000631071046102023000200550ustar 00000000000000// SPDX-License-Identifier: MIT use std::os::unix::io::RawFd; use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator, NLA_F_NESTED}, parsers::{parse_i32, parse_string, parse_u32, parse_u8}, traits::{Emitable, Parseable, ParseableParametrized}, DecodeError, }; #[cfg(any(target_os = "linux", target_os = "fuchsia",))] use super::af_spec::VecAfSpecBridge; #[cfg(any(target_os = "linux", target_os = "fuchsia",))] use super::proto_info::VecLinkProtoInfoBridge; use super::{ af_spec::VecAfSpecUnspec, buffer_tool::expand_buffer_if_small, ext_mask::VecLinkExtentMask, link_info::VecLinkInfo, proto_info::VecLinkProtoInfoInet6, sriov::{VecLinkVfInfo, VecLinkVfPort}, stats::LINK_STATS_LEN, stats64::LINK_STATS64_LEN, xdp::VecLinkXdp, AfSpecBridge, AfSpecUnspec, LinkEvent, LinkExtentMask, LinkInfo, LinkPhysId, LinkProtoInfoBridge, LinkProtoInfoInet6, LinkProtocolDownReason, LinkVfInfo, LinkVfPort, LinkWirelessEvent, LinkXdp, Map, MapBuffer, Prop, State, Stats, Stats64, Stats64Buffer, StatsBuffer, }; use crate::AddressFamily; const IFLA_ADDRESS: u16 = 1; const IFLA_BROADCAST: u16 = 2; const IFLA_IFNAME: u16 = 3; const IFLA_MTU: u16 = 4; const IFLA_LINK: u16 = 5; const IFLA_QDISC: u16 = 6; const IFLA_STATS: u16 = 7; // No kernel code is using IFLA_COST // const IFLA_COST: u16 = 8; // No kernel code is using IFLA_PRIORITY // const IFLA_PRIORITY: u16 = 9; const IFLA_MASTER: u16 = 10; const IFLA_WIRELESS: u16 = 11; const IFLA_PROTINFO: u16 = 12; const IFLA_TXQLEN: u16 = 13; const IFLA_MAP: u16 = 14; // No kernel code is using IFLA_WEIGHT // const IFLA_WEIGHT: u16 = 15; const IFLA_OPERSTATE: u16 = 16; const IFLA_LINKMODE: u16 = 17; const IFLA_LINKINFO: u16 = 18; const IFLA_NET_NS_PID: u16 = 19; const IFLA_IFALIAS: u16 = 20; const IFLA_NUM_VF: u16 = 21; const IFLA_VFINFO_LIST: u16 = 22; const IFLA_STATS64: u16 = 23; const IFLA_VF_PORTS: u16 = 24; const IFLA_PORT_SELF: u16 = 25; const IFLA_AF_SPEC: u16 = 26; const IFLA_GROUP: u16 = 27; const IFLA_NET_NS_FD: u16 = 28; const IFLA_EXT_MASK: u16 = 29; const IFLA_PROMISCUITY: u16 = 30; const IFLA_NUM_TX_QUEUES: u16 = 31; const IFLA_NUM_RX_QUEUES: u16 = 32; const IFLA_CARRIER: u16 = 33; const IFLA_PHYS_PORT_ID: u16 = 34; const IFLA_CARRIER_CHANGES: u16 = 35; const IFLA_PHYS_SWITCH_ID: u16 = 36; const IFLA_LINK_NETNSID: u16 = 37; const IFLA_PHYS_PORT_NAME: u16 = 38; const IFLA_PROTO_DOWN: u16 = 39; const IFLA_GSO_MAX_SEGS: u16 = 40; const IFLA_GSO_MAX_SIZE: u16 = 41; // const IFLA_PAD: u16 = 42; const IFLA_XDP: u16 = 43; const IFLA_EVENT: u16 = 44; const IFLA_NEW_NETNSID: u16 = 45; const IFLA_IF_NETNSID: u16 = 46; const IFLA_CARRIER_UP_COUNT: u16 = 47; const IFLA_CARRIER_DOWN_COUNT: u16 = 48; const IFLA_NEW_IFINDEX: u16 = 49; const IFLA_MIN_MTU: u16 = 50; const IFLA_MAX_MTU: u16 = 51; const IFLA_PROP_LIST: u16 = 52; const IFLA_PERM_ADDRESS: u16 = 54; const IFLA_PROTO_DOWN_REASON: u16 = 55; /* TODO:(Gris Ge) const IFLA_PARENT_DEV_NAME: u16 = 56; const IFLA_PARENT_DEV_BUS_NAME: u16 = 57; const IFLA_GRO_MAX_SIZE: u16 = 58; const IFLA_TSO_MAX_SIZE: u16 = 59; const IFLA_TSO_MAX_SEGS: u16 = 60; const IFLA_ALLMULTI: u16 = 61; const IFLA_DEVLINK_PORT: u16 = 62; */ #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum LinkAttribute { VfInfoList(Vec), VfPorts(Vec), PortSelf(LinkVfPort), PhysPortId(LinkPhysId), PhysSwitchId(LinkPhysId), Xdp(Vec), Event(LinkEvent), NewNetnsId(i32), IfNetnsId(i32), CarrierUpCount(u32), CarrierDownCount(u32), NewIfIndex(i32), LinkInfo(Vec), Wireless(LinkWirelessEvent), ProtoInfoBridge(Vec), ProtoInfoInet6(Vec), ProtoInfoUnknown(DefaultNla), PropList(Vec), ProtoDownReason(Vec), Address(Vec), Broadcast(Vec), /// Permanent hardware address of the device. The provides the same /// information as the ethtool ioctl interface. PermAddress(Vec), IfName(String), Qdisc(String), IfAlias(String), PhysPortName(String), Mode(u8), Carrier(u8), ProtoDown(u8), Mtu(u32), Link(u32), Controller(u32), TxQueueLen(u32), NetNsPid(u32), NumVf(u32), Group(u32), NetNsFd(RawFd), ExtMask(Vec), Promiscuity(u32), NumTxQueues(u32), NumRxQueues(u32), CarrierChanges(u32), GsoMaxSegs(u32), GsoMaxSize(u32), /// The minimum MTU for the device. MinMtu(u32), /// The maximum MTU for the device. MaxMtu(u32), LinkNetNsId(i32), OperState(State), Stats(Stats), Stats64(Stats64), Map(Map), // AF_SPEC (the type of af_spec depends on the interface family of the // message) AfSpecUnspec(Vec), AfSpecBridge(Vec), AfSpecUnknown(Vec), Other(DefaultNla), } impl Nla for LinkAttribute { fn value_len(&self) -> usize { match self { Self::VfInfoList(v) => v.as_slice().buffer_len(), Self::VfPorts(v) => v.as_slice().buffer_len(), Self::PortSelf(v) => v.buffer_len(), Self::PhysPortId(v) => v.buffer_len(), Self::PhysSwitchId(v) => v.buffer_len(), Self::Event(v) => v.buffer_len(), Self::Wireless(v) => v.buffer_len(), Self::ProtoInfoBridge(v) => v.as_slice().buffer_len(), Self::ProtoInfoInet6(v) => v.as_slice().buffer_len(), Self::ProtoDownReason(v) => v.as_slice().buffer_len(), Self::Address(bytes) | Self::Broadcast(bytes) | Self::PermAddress(bytes) | Self::AfSpecUnknown(bytes) => bytes.len(), Self::IfName(string) | Self::Qdisc(string) | Self::IfAlias(string) | Self::PhysPortName(string) => string.as_bytes().len() + 1, Self::Mode(_) | Self::Carrier(_) | Self::ProtoDown(_) => 1, Self::Mtu(_) | Self::NewNetnsId(_) | Self::IfNetnsId(_) | Self::Link(_) | Self::Controller(_) | Self::TxQueueLen(_) | Self::NetNsPid(_) | Self::NumVf(_) | Self::Group(_) | Self::NetNsFd(_) | Self::ExtMask(_) | Self::Promiscuity(_) | Self::NumTxQueues(_) | Self::NumRxQueues(_) | Self::CarrierChanges(_) | Self::GsoMaxSegs(_) | Self::GsoMaxSize(_) | Self::LinkNetNsId(_) | Self::MinMtu(_) | Self::CarrierUpCount(_) | Self::CarrierDownCount(_) | Self::NewIfIndex(_) | Self::MaxMtu(_) => 4, Self::OperState(_) => 1, Self::Stats(_) => LINK_STATS_LEN, Self::Stats64(_) => LINK_STATS64_LEN, Self::Map(nla) => nla.buffer_len(), Self::LinkInfo(nlas) => nlas.as_slice().buffer_len(), Self::Xdp(nlas) => nlas.as_slice().buffer_len(), Self::PropList(nlas) => nlas.as_slice().buffer_len(), Self::AfSpecUnspec(nlas) => nlas.as_slice().buffer_len(), Self::AfSpecBridge(nlas) => nlas.as_slice().buffer_len(), Self::ProtoInfoUnknown(attr) => attr.value_len(), Self::Other(attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::VfInfoList(v) => v.as_slice().emit(buffer), Self::VfPorts(v) => v.as_slice().emit(buffer), Self::PortSelf(v) => v.emit(buffer), Self::PhysPortId(v) => v.emit(buffer), Self::PhysSwitchId(v) => v.emit(buffer), Self::Event(v) => v.emit(buffer), Self::Wireless(v) => v.emit(buffer), Self::ProtoInfoBridge(v) => v.as_slice().emit(buffer), Self::ProtoInfoInet6(v) => v.as_slice().emit(buffer), Self::ProtoDownReason(v) => v.as_slice().emit(buffer), Self::Address(bytes) | Self::Broadcast(bytes) | Self::PermAddress(bytes) | Self::AfSpecUnknown(bytes) => { buffer.copy_from_slice(bytes.as_slice()) } Self::IfName(string) | Self::Qdisc(string) | Self::IfAlias(string) | Self::PhysPortName(string) => { buffer[..string.len()].copy_from_slice(string.as_bytes()); buffer[string.len()] = 0; } Self::Mode(val) | Self::Carrier(val) | Self::ProtoDown(val) => { buffer[0] = *val } Self::Mtu(value) | Self::Link(value) | Self::Controller(value) | Self::TxQueueLen(value) | Self::NetNsPid(value) | Self::NumVf(value) | Self::Group(value) | Self::Promiscuity(value) | Self::NumTxQueues(value) | Self::NumRxQueues(value) | Self::CarrierChanges(value) | Self::CarrierUpCount(value) | Self::CarrierDownCount(value) | Self::GsoMaxSegs(value) | Self::GsoMaxSize(value) | Self::MinMtu(value) | Self::MaxMtu(value) => NativeEndian::write_u32(buffer, *value), Self::ExtMask(value) => NativeEndian::write_u32( buffer, u32::from(&VecLinkExtentMask(value.to_vec())), ), Self::LinkNetNsId(v) | Self::NetNsFd(v) | Self::NewNetnsId(v) | Self::NewIfIndex(v) | Self::IfNetnsId(v) => NativeEndian::write_i32(buffer, *v), Self::Stats(nla) => nla.emit(buffer), Self::Map(nla) => nla.emit(buffer), Self::Stats64(nla) => nla.emit(buffer), Self::OperState(state) => buffer[0] = (*state).into(), Self::LinkInfo(nlas) => nlas.as_slice().emit(buffer), Self::Xdp(nlas) => nlas.as_slice().emit(buffer), Self::PropList(nlas) => nlas.as_slice().emit(buffer), Self::AfSpecUnspec(nlas) => nlas.as_slice().emit(buffer), Self::AfSpecBridge(nlas) => nlas.as_slice().emit(buffer), Self::ProtoInfoUnknown(attr) | Self::Other(attr) => { attr.emit_value(buffer) } } } fn kind(&self) -> u16 { match self { Self::VfInfoList(_) => IFLA_VFINFO_LIST, Self::VfPorts(_) => IFLA_VF_PORTS, Self::PortSelf(_) => IFLA_PORT_SELF, Self::PhysPortId(_) => IFLA_PHYS_PORT_ID, Self::PhysSwitchId(_) => IFLA_PHYS_SWITCH_ID, Self::LinkInfo(_) => IFLA_LINKINFO, Self::Wireless(_) => IFLA_WIRELESS, Self::ProtoInfoBridge(_) | Self::ProtoInfoInet6(_) => IFLA_PROTINFO, Self::ProtoInfoUnknown(attr) => attr.kind(), Self::Xdp(_) => IFLA_XDP, Self::Event(_) => IFLA_EVENT, Self::NewNetnsId(_) => IFLA_NEW_NETNSID, Self::IfNetnsId(_) => IFLA_IF_NETNSID, Self::CarrierUpCount(_) => IFLA_CARRIER_UP_COUNT, Self::CarrierDownCount(_) => IFLA_CARRIER_DOWN_COUNT, Self::NewIfIndex(_) => IFLA_NEW_IFINDEX, Self::PropList(_) => IFLA_PROP_LIST | NLA_F_NESTED, Self::ProtoDownReason(_) => IFLA_PROTO_DOWN_REASON, Self::Address(_) => IFLA_ADDRESS, Self::Broadcast(_) => IFLA_BROADCAST, Self::PermAddress(_) => IFLA_PERM_ADDRESS, Self::IfName(_) => IFLA_IFNAME, Self::Qdisc(_) => IFLA_QDISC, Self::IfAlias(_) => IFLA_IFALIAS, Self::PhysPortName(_) => IFLA_PHYS_PORT_NAME, Self::Mode(_) => IFLA_LINKMODE, Self::Carrier(_) => IFLA_CARRIER, Self::ProtoDown(_) => IFLA_PROTO_DOWN, Self::Mtu(_) => IFLA_MTU, Self::Link(_) => IFLA_LINK, Self::Controller(_) => IFLA_MASTER, Self::TxQueueLen(_) => IFLA_TXQLEN, Self::NetNsPid(_) => IFLA_NET_NS_PID, Self::NumVf(_) => IFLA_NUM_VF, Self::Group(_) => IFLA_GROUP, Self::NetNsFd(_) => IFLA_NET_NS_FD, Self::ExtMask(_) => IFLA_EXT_MASK, Self::Promiscuity(_) => IFLA_PROMISCUITY, Self::NumTxQueues(_) => IFLA_NUM_TX_QUEUES, Self::NumRxQueues(_) => IFLA_NUM_RX_QUEUES, Self::CarrierChanges(_) => IFLA_CARRIER_CHANGES, Self::GsoMaxSegs(_) => IFLA_GSO_MAX_SEGS, Self::GsoMaxSize(_) => IFLA_GSO_MAX_SIZE, Self::MinMtu(_) => IFLA_MIN_MTU, Self::MaxMtu(_) => IFLA_MAX_MTU, Self::LinkNetNsId(_) => IFLA_LINK_NETNSID, Self::OperState(_) => IFLA_OPERSTATE, Self::Map(_) => IFLA_MAP, Self::Stats(_) => IFLA_STATS, Self::Stats64(_) => IFLA_STATS64, Self::AfSpecUnspec(_) | Self::AfSpecBridge(_) | Self::AfSpecUnknown(_) => IFLA_AF_SPEC, Self::Other(attr) => attr.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> ParseableParametrized, AddressFamily> for LinkAttribute { fn parse_with_param( buf: &NlaBuffer<&'a T>, interface_family: AddressFamily, ) -> Result { let payload = buf.value(); Ok(match buf.kind() { IFLA_VFINFO_LIST => { let err = |payload| format!("invalid IFLA_VFINFO_LIST {payload:?}"); Self::VfInfoList( VecLinkVfInfo::parse( &NlaBuffer::new_checked(payload) .context(err(payload))?, ) .context(err(payload))? .0, ) } IFLA_VF_PORTS => { let err = |payload| format!("invalid IFLA_VF_PORTS {payload:?}"); Self::VfPorts( VecLinkVfPort::parse( &NlaBuffer::new_checked(payload) .context(err(payload))?, ) .context(err(payload))? .0, ) } IFLA_PORT_SELF => { let err = |payload| format!("invalid IFLA_PORT_SELF {payload:?}"); Self::PortSelf( LinkVfPort::parse( &NlaBuffer::new_checked(payload) .context(err(payload))?, ) .context(err(payload))?, ) } IFLA_PHYS_PORT_ID => { Self::PhysPortId(LinkPhysId::parse(payload).context( format!("invalid IFLA_PHYS_PORT_ID value {payload:?}"), )?) } IFLA_PHYS_SWITCH_ID => { Self::PhysSwitchId(LinkPhysId::parse(payload).context( format!("invalid IFLA_PHYS_SWITCH_ID value {payload:?}"), )?) } IFLA_WIRELESS => Self::Wireless( LinkWirelessEvent::parse(payload) .context(format!("invalid IFLA_WIRELESS {payload:?}"))?, ), IFLA_PROTINFO => { let err = |payload| { format!("invalid IFLA_PROTINFO for AF_INET6 {payload:?}") }; match interface_family { AddressFamily::Inet6 => Self::ProtoInfoInet6( VecLinkProtoInfoInet6::parse( &NlaBuffer::new_checked(payload) .context(err(payload))?, ) .context(err(payload))? .0, ), #[cfg(any(target_os = "linux", target_os = "fuchsia",))] AddressFamily::Bridge => Self::ProtoInfoBridge( VecLinkProtoInfoBridge::parse(&NlaBuffer::new_checked( payload, )?) .context(format!( "invalid IFLA_PROTINFO for AF_INET6 {payload:?}" ))? .0, ), _ => Self::ProtoInfoUnknown( DefaultNla::parse(buf).context(format!( "invalid IFLA_PROTINFO for \ {interface_family:?}: {payload:?}" ))?, ), } } IFLA_EVENT => Self::Event( LinkEvent::parse(payload) .context(format!("invalid IFLA_EVENT {payload:?}"))?, ), IFLA_NEW_NETNSID => Self::NewNetnsId( parse_i32(payload).context("invalid IFLA_NEW_NETNSID value")?, ), IFLA_IF_NETNSID => Self::IfNetnsId( parse_i32(payload).context("invalid IFLA_IF_NETNSID value")?, ), IFLA_CARRIER_UP_COUNT => Self::CarrierUpCount( parse_u32(payload) .context("invalid IFLA_CARRIER_UP_COUNT value")?, ), IFLA_CARRIER_DOWN_COUNT => Self::CarrierDownCount( parse_u32(payload) .context("invalid IFLA_CARRIER_DOWN_COUNT value")?, ), IFLA_NEW_IFINDEX => Self::NewIfIndex( parse_i32(payload).context("invalid IFLA_NEW_IFINDEX value")?, ), 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); } Self::PropList(nlas) } IFLA_PROTO_DOWN_REASON => { let mut nlas = vec![]; for nla in NlasIterator::new(payload) { let nla = &nla.context("invalid IFLA_PROTO_DOWN_REASON value")?; let parsed = LinkProtocolDownReason::parse(nla)?; nlas.push(parsed); } Self::ProtoDownReason(nlas) } // 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 => Self::Address(payload.to_vec()), IFLA_BROADCAST => Self::Broadcast(payload.to_vec()), IFLA_PERM_ADDRESS => Self::PermAddress(payload.to_vec()), // String IFLA_IFNAME => Self::IfName( parse_string(payload).context("invalid IFLA_IFNAME value")?, ), IFLA_QDISC => Self::Qdisc( parse_string(payload).context("invalid IFLA_QDISC value")?, ), IFLA_IFALIAS => Self::IfAlias( parse_string(payload).context("invalid IFLA_IFALIAS value")?, ), IFLA_PHYS_PORT_NAME => Self::PhysPortName( parse_string(payload) .context("invalid IFLA_PHYS_PORT_NAME value")?, ), IFLA_LINKMODE => Self::Mode( parse_u8(payload).context("invalid IFLA_LINKMODE value")?, ), IFLA_CARRIER => Self::Carrier( parse_u8(payload).context("invalid IFLA_CARRIER value")?, ), IFLA_PROTO_DOWN => Self::ProtoDown( parse_u8(payload).context("invalid IFLA_PROTO_DOWN value")?, ), IFLA_MTU => { Self::Mtu(parse_u32(payload).context("invalid IFLA_MTU value")?) } IFLA_LINK => Self::Link( parse_u32(payload).context("invalid IFLA_LINK value")?, ), IFLA_MASTER => Self::Controller( parse_u32(payload).context("invalid IFLA_MASTER value")?, ), IFLA_TXQLEN => Self::TxQueueLen( parse_u32(payload).context("invalid IFLA_TXQLEN value")?, ), IFLA_NET_NS_PID => Self::NetNsPid( parse_u32(payload).context("invalid IFLA_NET_NS_PID value")?, ), IFLA_NUM_VF => Self::NumVf( parse_u32(payload).context("invalid IFLA_NUM_VF value")?, ), IFLA_GROUP => Self::Group( parse_u32(payload).context("invalid IFLA_GROUP value")?, ), IFLA_NET_NS_FD => Self::NetNsFd( parse_i32(payload).context("invalid IFLA_NET_NS_FD value")?, ), IFLA_EXT_MASK => Self::ExtMask( VecLinkExtentMask::from( parse_u32(payload) .context("invalid IFLA_EXT_MASK value")?, ) .0, ), IFLA_PROMISCUITY => Self::Promiscuity( parse_u32(payload).context("invalid IFLA_PROMISCUITY value")?, ), IFLA_NUM_TX_QUEUES => Self::NumTxQueues( parse_u32(payload) .context("invalid IFLA_NUM_TX_QUEUES value")?, ), IFLA_NUM_RX_QUEUES => Self::NumRxQueues( parse_u32(payload) .context("invalid IFLA_NUM_RX_QUEUES value")?, ), IFLA_CARRIER_CHANGES => Self::CarrierChanges( parse_u32(payload) .context("invalid IFLA_CARRIER_CHANGES value")?, ), IFLA_GSO_MAX_SEGS => Self::GsoMaxSegs( parse_u32(payload) .context("invalid IFLA_GSO_MAX_SEGS value")?, ), IFLA_GSO_MAX_SIZE => Self::GsoMaxSize( parse_u32(payload) .context("invalid IFLA_GSO_MAX_SIZE value")?, ), IFLA_MIN_MTU => Self::MinMtu( parse_u32(payload).context("invalid IFLA_MIN_MTU value")?, ), IFLA_MAX_MTU => Self::MaxMtu( parse_u32(payload).context("invalid IFLA_MAX_MTU value")?, ), IFLA_LINK_NETNSID => Self::LinkNetNsId( parse_i32(payload) .context("invalid IFLA_LINK_NETNSID value")?, ), IFLA_OPERSTATE => Self::OperState( parse_u8(payload) .context("invalid IFLA_OPERSTATE value")? .into(), ), IFLA_MAP => { let err = |payload| format!("Invalid IFLA_MAP value {:?}", payload); Self::Map( super::Map::parse( &MapBuffer::new_checked(payload) .context(err(payload))?, ) .context(err(payload))?, ) } IFLA_STATS => Self::Stats( super::Stats::parse(&StatsBuffer::new( expand_buffer_if_small( payload, LINK_STATS_LEN, "IFLA_STATS", ) .as_slice(), )) .context(format!("Invalid IFLA_STATS value {:?}", payload))?, ), IFLA_STATS64 => { let payload = expand_buffer_if_small( payload, LINK_STATS64_LEN, "IFLA_STATS64", ); Self::Stats64( super::Stats64::parse(&Stats64Buffer::new( payload.as_slice(), )) .context(format!( "Invalid IFLA_STATS64 value {:?}", payload ))?, ) } IFLA_AF_SPEC => match interface_family { AddressFamily::Unspec => { let err = "invalid IFLA_AF_SPEC value for AF_UNSPEC"; Self::AfSpecUnspec( VecAfSpecUnspec::parse( &NlaBuffer::new_checked(&buf.value()) .context(err)?, ) .context(err)? .0, ) } #[cfg(any(target_os = "linux", target_os = "fuchsia",))] AddressFamily::Bridge => { let err = "invalid IFLA_AF_SPEC value for AF_BRIDGE"; Self::AfSpecBridge( VecAfSpecBridge::parse( &NlaBuffer::new_checked(&buf.value()) .context(err)?, ) .context(err)? .0, ) } _ => Self::AfSpecUnknown(payload.to_vec()), }, IFLA_LINKINFO => { let err = "invalid IFLA_LINKINFO value"; Self::LinkInfo( VecLinkInfo::parse( &NlaBuffer::new_checked(&buf.value()).context(err)?, ) .context(err)? .0, ) } IFLA_XDP => { let err = "invalid IFLA_XDP value"; let buf = NlaBuffer::new_checked(payload).context(err)?; Self::Xdp(VecLinkXdp::parse(&buf).context(err)?.0) } kind => Self::Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } netlink-packet-route-0.21.0/src/link/buffer_tool.rs000064400000000000000000000012721046102023000203530ustar 00000000000000// SPDX-License-Identifier: MIT pub(crate) fn expand_buffer_if_small( got: &[u8], expected_size: usize, nla_name: &str, ) -> Vec { let mut payload = got.to_vec(); match payload.len() { l if l > expected_size => { log::warn!( "Specified {nla_name} NLA attribute holds \ more(most likely new kernel) data which is unknown to \ netlink-packet-route crate, expecting \ {expected_size}, got {}", got.len() ); } l if l < expected_size => { payload.extend_from_slice(&vec![0; expected_size - got.len()]); } _ => (), } payload } netlink-packet-route-0.21.0/src/link/down_reason.rs000064400000000000000000000037231046102023000203660ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::parse_u32, DecodeError, Parseable, }; const IFLA_PROTO_DOWN_REASON_MASK: u16 = 1; const IFLA_PROTO_DOWN_REASON_VALUE: u16 = 2; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum LinkProtocolDownReason { Value(u32), Mask(u32), Other(DefaultNla), } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for LinkProtocolDownReason { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { IFLA_PROTO_DOWN_REASON_MASK => { Self::Mask(parse_u32(payload).context(format!( "invalid IFLA_PROTO_DOWN_REASON_MASK {payload:?}" ))?) } IFLA_PROTO_DOWN_REASON_VALUE => { Self::Value(parse_u32(payload).context(format!( "invalid IFLA_PROTO_DOWN_REASON_MASK {payload:?}" ))?) } kind => Self::Other(DefaultNla::parse(buf).context(format!( "unknown NLA type {kind} for IFLA_PROTO_DOWN_REASON: \ {payload:?}" ))?), }) } } impl Nla for LinkProtocolDownReason { fn kind(&self) -> u16 { match self { Self::Value(_) => IFLA_PROTO_DOWN_REASON_VALUE, Self::Mask(_) => IFLA_PROTO_DOWN_REASON_MASK, Self::Other(v) => v.kind(), } } fn value_len(&self) -> usize { match self { Self::Value(_) | Self::Mask(_) => 4, Self::Other(v) => v.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Value(v) | Self::Mask(v) => { NativeEndian::write_u32(buffer, *v) } Self::Other(v) => v.emit_value(buffer), } } } netlink-packet-route-0.21.0/src/link/event.rs000064400000000000000000000042211046102023000171630ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ parsers::parse_u32, DecodeError, Emitable, Parseable, }; const IFLA_EVENT_NONE: u32 = 0; const IFLA_EVENT_REBOOT: u32 = 1; const IFLA_EVENT_FEATURES: u32 = 2; const IFLA_EVENT_BONDING_FAILOVER: u32 = 3; const IFLA_EVENT_NOTIFY_PEERS: u32 = 4; const IFLA_EVENT_IGMP_RESEND: u32 = 5; const IFLA_EVENT_BONDING_OPTIONS: u32 = 6; #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] #[non_exhaustive] pub enum LinkEvent { #[default] None, Reboot, Features, BondingFailover, NotifyPeers, IgmpResend, BondingOptions, Other(u32), } impl From for LinkEvent { fn from(d: u32) -> Self { match d { IFLA_EVENT_NONE => Self::None, IFLA_EVENT_REBOOT => Self::Reboot, IFLA_EVENT_FEATURES => Self::Features, IFLA_EVENT_BONDING_FAILOVER => Self::BondingFailover, IFLA_EVENT_NOTIFY_PEERS => Self::NotifyPeers, IFLA_EVENT_IGMP_RESEND => Self::IgmpResend, IFLA_EVENT_BONDING_OPTIONS => Self::BondingOptions, _ => Self::Other(d), } } } impl From for u32 { fn from(v: LinkEvent) -> u32 { match v { LinkEvent::None => IFLA_EVENT_NONE, LinkEvent::Reboot => IFLA_EVENT_REBOOT, LinkEvent::Features => IFLA_EVENT_FEATURES, LinkEvent::BondingFailover => IFLA_EVENT_BONDING_FAILOVER, LinkEvent::NotifyPeers => IFLA_EVENT_NOTIFY_PEERS, LinkEvent::IgmpResend => IFLA_EVENT_IGMP_RESEND, LinkEvent::BondingOptions => IFLA_EVENT_BONDING_OPTIONS, LinkEvent::Other(d) => d, } } } impl + ?Sized> Parseable for LinkEvent { fn parse(buf: &T) -> Result { Ok(LinkEvent::from( parse_u32(buf.as_ref()).context("invalid IFLA_EVENT value")?, )) } } impl Emitable for LinkEvent { fn buffer_len(&self) -> usize { 4 } fn emit(&self, buffer: &mut [u8]) { NativeEndian::write_u32(buffer, u32::from(*self)); } } netlink-packet-route-0.21.0/src/link/ext_mask.rs000064400000000000000000000054121046102023000176600ustar 00000000000000// SPDX-License-Identifier: MIT const RTEXT_FILTER_VF: u32 = 1 << 0; const RTEXT_FILTER_BRVLAN: u32 = 1 << 1; const RTEXT_FILTER_BRVLAN_COMPRESSED: u32 = 1 << 2; const RTEXT_FILTER_SKIP_STATS: u32 = 1 << 3; const RTEXT_FILTER_MRP: u32 = 1 << 4; const RTEXT_FILTER_CFM_CONFIG: u32 = 1 << 5; const RTEXT_FILTER_CFM_STATUS: u32 = 1 << 6; const RTEXT_FILTER_MST: u32 = 1 << 7; #[derive(Debug, PartialEq, Eq, Clone, Default)] pub(crate) struct VecLinkExtentMask(pub(crate) Vec); #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] #[repr(u32)] pub enum LinkExtentMask { Vf, Brvlan, BrvlanCompressed, SkipStats, Mrp, CfmConfig, CfmStatus, Mst, Other(u32), } impl From for LinkExtentMask { fn from(d: u32) -> Self { match d { RTEXT_FILTER_VF => Self::Vf, RTEXT_FILTER_BRVLAN => Self::Brvlan, RTEXT_FILTER_BRVLAN_COMPRESSED => Self::BrvlanCompressed, RTEXT_FILTER_SKIP_STATS => Self::SkipStats, RTEXT_FILTER_MRP => Self::Mrp, RTEXT_FILTER_CFM_CONFIG => Self::CfmConfig, RTEXT_FILTER_CFM_STATUS => Self::CfmStatus, RTEXT_FILTER_MST => Self::Mst, _ => Self::Other(d), } } } impl From for u32 { fn from(v: LinkExtentMask) -> u32 { match v { LinkExtentMask::Vf => RTEXT_FILTER_VF, LinkExtentMask::Brvlan => RTEXT_FILTER_BRVLAN, LinkExtentMask::BrvlanCompressed => RTEXT_FILTER_BRVLAN_COMPRESSED, LinkExtentMask::SkipStats => RTEXT_FILTER_SKIP_STATS, LinkExtentMask::Mrp => RTEXT_FILTER_MRP, LinkExtentMask::CfmConfig => RTEXT_FILTER_CFM_CONFIG, LinkExtentMask::CfmStatus => RTEXT_FILTER_CFM_STATUS, LinkExtentMask::Mst => RTEXT_FILTER_MST, LinkExtentMask::Other(i) => i, } } } const ALL_LINK_FLAGS: [LinkExtentMask; 8] = [ LinkExtentMask::Vf, LinkExtentMask::Brvlan, LinkExtentMask::BrvlanCompressed, LinkExtentMask::SkipStats, LinkExtentMask::Mrp, LinkExtentMask::CfmConfig, LinkExtentMask::CfmStatus, LinkExtentMask::Mst, ]; impl From for VecLinkExtentMask { fn from(d: u32) -> Self { let mut got: u32 = 0; let mut ret = Vec::new(); for flag in ALL_LINK_FLAGS { if (d & u32::from(flag)) > 0 { ret.push(flag); got += u32::from(flag); } } if got != d { ret.push(LinkExtentMask::Other(d - got)); } Self(ret) } } impl From<&VecLinkExtentMask> for u32 { fn from(v: &VecLinkExtentMask) -> u32 { let mut d: u32 = 0; for flag in &v.0 { d += u32::from(*flag); } d } } netlink-packet-route-0.21.0/src/link/header.rs000064400000000000000000000067071046102023000173050ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::{NlaBuffer, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; use crate::{link::LinkLayerType, AddressFamily}; use super::link_flag::LinkFlags; 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 attributes( &self, ) -> impl Iterator, DecodeError>> { NlasIterator::new(self.payload()) } } /// 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)] pub struct LinkHeader { /// Address family: one of the `AF_*` constants. /// The [AddressFamily] has `From` and `From for u8` /// implemented. pub interface_family: AddressFamily, /// Link index. pub index: u32, /// Link type. It should be set to one of the `ARPHRD_*` /// constants. The most common value is [LinkLayerType::Ether] for /// Ethernet. /// The LinkLayerType has `From` and `From for u16` /// implemented. pub link_layer_type: LinkLayerType, /// State of the link, described by a combinations of `IFF_*` /// constants. pub flags: LinkFlags, /// Change mask for the `flags` field. pub change_mask: LinkFlags, } 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(u8::from(self.interface_family)); packet.set_link_index(self.index); packet.set_change_mask(self.change_mask.bits()); packet.set_link_layer_type(u16::from(self.link_layer_type)); packet.set_flags(self.flags.bits()); } } impl> Parseable> for LinkHeader { fn parse(buf: &LinkMessageBuffer) -> Result { Ok(Self { interface_family: buf.interface_family().into(), link_layer_type: buf.link_layer_type().into(), index: buf.link_index(), change_mask: LinkFlags::from_bits_retain(buf.change_mask()), flags: LinkFlags::from_bits_retain(buf.flags()), }) } } netlink-packet-route-0.21.0/src/link/link_flag.rs000064400000000000000000000033571046102023000200010ustar 00000000000000// SPDX-License-Identifier: MIT use std::fmt; const IFF_UP: u32 = 1 << 0; const IFF_BROADCAST: u32 = 1 << 1; const IFF_DEBUG: u32 = 1 << 2; const IFF_LOOPBACK: u32 = 1 << 3; const IFF_POINTOPOINT: u32 = 1 << 4; const IFF_NOTRAILERS: u32 = 1 << 5; const IFF_RUNNING: u32 = 1 << 6; const IFF_NOARP: u32 = 1 << 7; const IFF_PROMISC: u32 = 1 << 8; const IFF_ALLMULTI: u32 = 1 << 9; // Kernel constant name is IFF_MASTER const IFF_CONTROLLER: u32 = 1 << 10; // Kernel constant name is IFF_SLAVE const IFF_PORT: u32 = 1 << 11; const IFF_MULTICAST: u32 = 1 << 12; const IFF_PORTSEL: u32 = 1 << 13; const IFF_AUTOMEDIA: u32 = 1 << 14; const IFF_DYNAMIC: u32 = 1 << 15; const IFF_LOWER_UP: u32 = 1 << 16; const IFF_DORMANT: u32 = 1 << 17; const IFF_ECHO: u32 = 1 << 18; bitflags! { #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] #[non_exhaustive] pub struct LinkFlags: u32 { const Up = IFF_UP; const Broadcast = IFF_BROADCAST; const Debug = IFF_DEBUG; const Loopback = IFF_LOOPBACK; const Pointopoint = IFF_POINTOPOINT; const Notrailers = IFF_NOTRAILERS; const Running = IFF_RUNNING; const Noarp = IFF_NOARP; const Promisc = IFF_PROMISC; const Allmulti = IFF_ALLMULTI; const Controller = IFF_CONTROLLER; const Port = IFF_PORT; const Multicast = IFF_MULTICAST; const Portsel = IFF_PORTSEL; const Automedia = IFF_AUTOMEDIA; const Dynamic = IFF_DYNAMIC; const LowerUp = IFF_LOWER_UP; const Dormant = IFF_DORMANT; const Echo = IFF_ECHO; const _ = !0; } } impl fmt::Display for LinkFlags { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { bitflags::parser::to_writer(self, f) } } netlink-packet-route-0.21.0/src/link/link_info/bond.rs000064400000000000000000000563731046102023000207530ustar 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::{DefaultNla, Nla, NlaBuffer, NlasIterator}, parsers::{parse_ip, parse_mac, parse_u16, parse_u32, parse_u8}, traits::{Emitable, Parseable}, DecodeError, }; const IFLA_BOND_AD_INFO_AGGREGATOR: u16 = 1; const IFLA_BOND_AD_INFO_NUM_PORTS: u16 = 2; const IFLA_BOND_AD_INFO_ACTOR_KEY: u16 = 3; const IFLA_BOND_AD_INFO_PARTNER_KEY: u16 = 4; const IFLA_BOND_AD_INFO_PARTNER_MAC: u16 = 5; const IFLA_BOND_MODE: u16 = 1; const IFLA_BOND_ACTIVE_PORT: u16 = 2; const IFLA_BOND_MIIMON: u16 = 3; const IFLA_BOND_UPDELAY: u16 = 4; const IFLA_BOND_DOWNDELAY: u16 = 5; const IFLA_BOND_USE_CARRIER: u16 = 6; const IFLA_BOND_ARP_INTERVAL: u16 = 7; const IFLA_BOND_ARP_IP_TARGET: u16 = 8; const IFLA_BOND_ARP_VALIDATE: u16 = 9; const IFLA_BOND_ARP_ALL_TARGETS: u16 = 10; const IFLA_BOND_PRIMARY: u16 = 11; const IFLA_BOND_PRIMARY_RESELECT: u16 = 12; const IFLA_BOND_FAIL_OVER_MAC: u16 = 13; const IFLA_BOND_XMIT_HASH_POLICY: u16 = 14; const IFLA_BOND_RESEND_IGMP: u16 = 15; const IFLA_BOND_NUM_PEER_NOTIF: u16 = 16; const IFLA_BOND_ALL_PORTS_ACTIVE: u16 = 17; const IFLA_BOND_MIN_LINKS: u16 = 18; const IFLA_BOND_LP_INTERVAL: u16 = 19; const IFLA_BOND_PACKETS_PER_PORT: u16 = 20; const IFLA_BOND_AD_LACP_RATE: u16 = 21; const IFLA_BOND_AD_SELECT: u16 = 22; const IFLA_BOND_AD_INFO: u16 = 23; const IFLA_BOND_AD_ACTOR_SYS_PRIO: u16 = 24; const IFLA_BOND_AD_USER_PORT_KEY: u16 = 25; const IFLA_BOND_AD_ACTOR_SYSTEM: u16 = 26; const IFLA_BOND_TLB_DYNAMIC_LB: u16 = 27; const IFLA_BOND_PEER_NOTIF_DELAY: u16 = 28; const IFLA_BOND_AD_LACP_ACTIVE: u16 = 29; const IFLA_BOND_MISSED_MAX: u16 = 30; const IFLA_BOND_NS_IP6_TARGET: u16 = 31; const BOND_MODE_ROUNDROBIN: u8 = 0; const BOND_MODE_ACTIVEBACKUP: u8 = 1; const BOND_MODE_XOR: u8 = 2; const BOND_MODE_BROADCAST: u8 = 3; const BOND_MODE_8023AD: u8 = 4; const BOND_MODE_TLB: u8 = 5; const BOND_MODE_ALB: u8 = 6; const BOND_STATE_ACTIVE: u8 = 0; const BOND_STATE_BACKUP: u8 = 1; const BOND_ARP_VALIDATE_NONE: u32 = 0; const BOND_ARP_VALIDATE_ACTIVE: u32 = 1 << BOND_STATE_ACTIVE as u32; const BOND_ARP_VALIDATE_BACKUP: u32 = 1 << BOND_STATE_BACKUP as u32; const BOND_ARP_VALIDATE_ALL: u32 = BOND_ARP_VALIDATE_ACTIVE | BOND_ARP_VALIDATE_BACKUP; const BOND_ARP_FILTER: u32 = BOND_ARP_VALIDATE_ALL + 1; const BOND_ARP_FILTER_ACTIVE: u32 = BOND_ARP_FILTER | BOND_ARP_VALIDATE_ACTIVE; const BOND_ARP_FILTER_BACKUP: u32 = BOND_ARP_FILTER | BOND_ARP_VALIDATE_BACKUP; #[derive(Debug, Clone, Eq, PartialEq)] #[non_exhaustive] pub enum BondAdInfo { Aggregator(u16), NumPorts(u16), ActorKey(u16), PartnerKey(u16), PartnerMac([u8; 6]), Other(DefaultNla), } impl Nla for BondAdInfo { fn value_len(&self) -> usize { match self { Self::Aggregator(_) | Self::NumPorts(_) | Self::ActorKey(_) | Self::PartnerKey(_) => 2, Self::PartnerMac(_) => 6, Self::Other(v) => v.value_len(), } } fn kind(&self) -> u16 { match self { Self::Aggregator(_) => IFLA_BOND_AD_INFO_AGGREGATOR, Self::NumPorts(_) => IFLA_BOND_AD_INFO_NUM_PORTS, Self::ActorKey(_) => IFLA_BOND_AD_INFO_ACTOR_KEY, Self::PartnerKey(_) => IFLA_BOND_AD_INFO_PARTNER_KEY, Self::PartnerMac(_) => IFLA_BOND_AD_INFO_PARTNER_MAC, Self::Other(v) => v.kind(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Aggregator(d) | Self::NumPorts(d) | Self::ActorKey(d) | Self::PartnerKey(d) => NativeEndian::write_u16(buffer, *d), Self::PartnerMac(mac) => buffer.copy_from_slice(mac), Self::Other(v) => v.emit_value(buffer), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for BondAdInfo { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { IFLA_BOND_AD_INFO_AGGREGATOR => Self::Aggregator( parse_u16(payload) .context("invalid IFLA_BOND_AD_INFO_AGGREGATOR value")?, ), IFLA_BOND_AD_INFO_NUM_PORTS => Self::NumPorts( parse_u16(payload) .context("invalid IFLA_BOND_AD_INFO_NUM_PORTS value")?, ), IFLA_BOND_AD_INFO_ACTOR_KEY => Self::ActorKey( parse_u16(payload) .context("invalid IFLA_BOND_AD_INFO_ACTOR_KEY value")?, ), IFLA_BOND_AD_INFO_PARTNER_KEY => Self::PartnerKey( parse_u16(payload) .context("invalid IFLA_BOND_AD_INFO_PARTNER_KEY value")?, ), IFLA_BOND_AD_INFO_PARTNER_MAC => Self::PartnerMac( parse_mac(payload) .context("invalid IFLA_BOND_AD_INFO_PARTNER_MAC value")?, ), _ => Self::Other(DefaultNla::parse(buf).context(format!( "invalid NLA for {}: {payload:?}", buf.kind() ))?), }) } } #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub enum BondMode { #[default] BalanceRr, ActiveBackup, BalanceXor, Broadcast, Ieee8023Ad, BalanceTlb, BalanceAlb, Other(u8), } impl From for BondMode { fn from(d: u8) -> Self { match d { BOND_MODE_ROUNDROBIN => Self::BalanceRr, BOND_MODE_ACTIVEBACKUP => Self::ActiveBackup, BOND_MODE_XOR => Self::BalanceXor, BOND_MODE_BROADCAST => Self::Broadcast, BOND_MODE_8023AD => Self::Ieee8023Ad, BOND_MODE_TLB => Self::BalanceTlb, BOND_MODE_ALB => Self::BalanceAlb, _ => Self::Other(d), } } } impl From for u8 { fn from(d: BondMode) -> Self { match d { BondMode::BalanceRr => BOND_MODE_ROUNDROBIN, BondMode::ActiveBackup => BOND_MODE_ACTIVEBACKUP, BondMode::BalanceXor => BOND_MODE_XOR, BondMode::Broadcast => BOND_MODE_BROADCAST, BondMode::Ieee8023Ad => BOND_MODE_8023AD, BondMode::BalanceTlb => BOND_MODE_TLB, BondMode::BalanceAlb => BOND_MODE_ALB, BondMode::Other(d) => d, } } } impl std::fmt::Display for BondMode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let kernel_name = match self { BondMode::BalanceRr => "balance-rr", BondMode::ActiveBackup => "active-backup", BondMode::BalanceXor => "balance-xor", BondMode::Broadcast => "broadcast", BondMode::Ieee8023Ad => "802.3ad", BondMode::BalanceTlb => "balance-tlb", BondMode::BalanceAlb => "balance-alb", BondMode::Other(d) => return write!(f, "unknown-variant ({d})"), }; f.write_str(kernel_name) } } #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] pub enum BondArpValidate { #[default] None, Active, Backup, All, Filter, FilterActive, FilterBackup, Other(u32), } impl From for u32 { fn from(value: BondArpValidate) -> Self { match value { BondArpValidate::None => BOND_ARP_VALIDATE_NONE, BondArpValidate::Active => BOND_ARP_VALIDATE_ACTIVE, BondArpValidate::Backup => BOND_ARP_VALIDATE_BACKUP, BondArpValidate::All => BOND_ARP_VALIDATE_ALL, BondArpValidate::Filter => BOND_ARP_FILTER, BondArpValidate::FilterActive => BOND_ARP_FILTER_ACTIVE, BondArpValidate::FilterBackup => BOND_ARP_FILTER_BACKUP, BondArpValidate::Other(d) => d, } } } impl From for BondArpValidate { fn from(value: u32) -> Self { match value { BOND_ARP_VALIDATE_NONE => BondArpValidate::None, BOND_ARP_VALIDATE_ACTIVE => BondArpValidate::Active, BOND_ARP_VALIDATE_BACKUP => BondArpValidate::Backup, BOND_ARP_VALIDATE_ALL => BondArpValidate::All, BOND_ARP_FILTER => BondArpValidate::Filter, BOND_ARP_FILTER_ACTIVE => BondArpValidate::FilterActive, BOND_ARP_FILTER_BACKUP => BondArpValidate::FilterBackup, d => BondArpValidate::Other(d), } } } impl std::fmt::Display for BondArpValidate { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let kernel_name = match self { BondArpValidate::None => "none", BondArpValidate::Active => "active", BondArpValidate::Backup => "backup", BondArpValidate::All => "all", BondArpValidate::Filter => "filter", BondArpValidate::FilterActive => "filter_active", BondArpValidate::FilterBackup => "filter_backup", BondArpValidate::Other(d) => { return write!(f, "unknown-variant ({d})") } }; f.write_str(kernel_name) } } // 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(BondMode), ActivePort(u32), MiiMon(u32), UpDelay(u32), DownDelay(u32), UseCarrier(u8), ArpInterval(u32), ArpIpTarget(Vec), ArpValidate(BondArpValidate), 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), Other(DefaultNla), } impl Nla for InfoBond { fn value_len(&self) -> usize { match self { Self::Mode(_) | Self::UseCarrier(_) | Self::PrimaryReselect(_) | Self::FailOverMac(_) | Self::XmitHashPolicy(_) | Self::NumPeerNotif(_) | Self::AllPortsActive(_) | Self::AdLacpActive(_) | Self::AdLacpRate(_) | Self::AdSelect(_) | Self::TlbDynamicLb(_) | Self::MissedMax(_) => 1, Self::AdActorSysPrio(_) | Self::AdUserPortKey(_) => 2, Self::ActivePort(_) | Self::MiiMon(_) | Self::UpDelay(_) | Self::DownDelay(_) | Self::ArpInterval(_) | Self::ArpValidate(_) | Self::ArpAllTargets(_) | Self::Primary(_) | Self::ResendIgmp(_) | Self::MinLinks(_) | Self::LpInterval(_) | Self::PacketsPerPort(_) | Self::PeerNotifDelay(_) => 4, Self::ArpIpTarget(ref addrs) => { BondIpAddrNlaList::from(addrs).as_slice().buffer_len() } Self::NsIp6Target(ref addrs) => { BondIpAddrNlaList::from(addrs).as_slice().buffer_len() } Self::AdActorSystem(_) => 6, Self::AdInfo(infos) => infos.as_slice().buffer_len(), Self::Other(v) => v.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Mode(value) => buffer[0] = (*value).into(), Self::UseCarrier(value) | Self::PrimaryReselect(value) | Self::FailOverMac(value) | Self::XmitHashPolicy(value) | Self::NumPeerNotif(value) | Self::AllPortsActive(value) | Self::AdLacpActive(value) | Self::AdLacpRate(value) | Self::AdSelect(value) | Self::TlbDynamicLb(value) | Self::MissedMax(value) => buffer[0] = *value, Self::AdActorSysPrio(value) | Self::AdUserPortKey(value) => { NativeEndian::write_u16(buffer, *value) } Self::ArpValidate(value) => { NativeEndian::write_u32(buffer, (*value).into()) } Self::ActivePort(value) | Self::MiiMon(value) | Self::UpDelay(value) | Self::DownDelay(value) | Self::ArpInterval(value) | Self::ArpAllTargets(value) | Self::Primary(value) | Self::ResendIgmp(value) | Self::MinLinks(value) | Self::LpInterval(value) | Self::PacketsPerPort(value) | Self::PeerNotifDelay(value) => { NativeEndian::write_u32(buffer, *value) } Self::AdActorSystem(bytes) => buffer.copy_from_slice(bytes), Self::ArpIpTarget(addrs) => { BondIpAddrNlaList::from(addrs).as_slice().emit(buffer) } Self::NsIp6Target(addrs) => { BondIpAddrNlaList::from(addrs).as_slice().emit(buffer) } Self::AdInfo(infos) => infos.as_slice().emit(buffer), Self::Other(v) => v.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Mode(_) => IFLA_BOND_MODE, Self::ActivePort(_) => IFLA_BOND_ACTIVE_PORT, Self::MiiMon(_) => IFLA_BOND_MIIMON, Self::UpDelay(_) => IFLA_BOND_UPDELAY, Self::DownDelay(_) => IFLA_BOND_DOWNDELAY, Self::UseCarrier(_) => IFLA_BOND_USE_CARRIER, Self::ArpInterval(_) => IFLA_BOND_ARP_INTERVAL, Self::ArpIpTarget(_) => IFLA_BOND_ARP_IP_TARGET, Self::ArpValidate(_) => IFLA_BOND_ARP_VALIDATE, Self::ArpAllTargets(_) => IFLA_BOND_ARP_ALL_TARGETS, Self::Primary(_) => IFLA_BOND_PRIMARY, Self::PrimaryReselect(_) => IFLA_BOND_PRIMARY_RESELECT, Self::FailOverMac(_) => IFLA_BOND_FAIL_OVER_MAC, Self::XmitHashPolicy(_) => IFLA_BOND_XMIT_HASH_POLICY, Self::ResendIgmp(_) => IFLA_BOND_RESEND_IGMP, Self::NumPeerNotif(_) => IFLA_BOND_NUM_PEER_NOTIF, Self::AllPortsActive(_) => IFLA_BOND_ALL_PORTS_ACTIVE, Self::MinLinks(_) => IFLA_BOND_MIN_LINKS, Self::LpInterval(_) => IFLA_BOND_LP_INTERVAL, Self::PacketsPerPort(_) => IFLA_BOND_PACKETS_PER_PORT, Self::AdLacpRate(_) => IFLA_BOND_AD_LACP_RATE, Self::AdSelect(_) => IFLA_BOND_AD_SELECT, Self::AdInfo(_) => IFLA_BOND_AD_INFO, Self::AdActorSysPrio(_) => IFLA_BOND_AD_ACTOR_SYS_PRIO, Self::AdUserPortKey(_) => IFLA_BOND_AD_USER_PORT_KEY, Self::AdActorSystem(_) => IFLA_BOND_AD_ACTOR_SYSTEM, Self::TlbDynamicLb(_) => IFLA_BOND_TLB_DYNAMIC_LB, Self::PeerNotifDelay(_) => IFLA_BOND_PEER_NOTIF_DELAY, Self::AdLacpActive(_) => IFLA_BOND_AD_LACP_ACTIVE, Self::MissedMax(_) => IFLA_BOND_MISSED_MAX, Self::NsIp6Target(_) => IFLA_BOND_NS_IP6_TARGET, Self::Other(v) => v.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoBond { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { IFLA_BOND_MODE => Self::Mode( parse_u8(payload) .context("invalid IFLA_BOND_MODE value")? .into(), ), IFLA_BOND_ACTIVE_PORT => Self::ActivePort( parse_u32(payload) .context("invalid IFLA_BOND_ACTIVE_PORT value")?, ), IFLA_BOND_MIIMON => Self::MiiMon( parse_u32(payload).context("invalid IFLA_BOND_MIIMON value")?, ), IFLA_BOND_UPDELAY => Self::UpDelay( parse_u32(payload) .context("invalid IFLA_BOND_UPDELAY value")?, ), IFLA_BOND_DOWNDELAY => Self::DownDelay( parse_u32(payload) .context("invalid IFLA_BOND_DOWNDELAY value")?, ), IFLA_BOND_USE_CARRIER => Self::UseCarrier( parse_u8(payload) .context("invalid IFLA_BOND_USE_CARRIER value")?, ), IFLA_BOND_ARP_INTERVAL => Self::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); } } Self::ArpIpTarget(addrs) } IFLA_BOND_ARP_VALIDATE => Self::ArpValidate( parse_u32(payload) .context("invalid IFLA_BOND_ARP_VALIDATE value")? .into(), ), IFLA_BOND_ARP_ALL_TARGETS => Self::ArpAllTargets( parse_u32(payload) .context("invalid IFLA_BOND_ARP_ALL_TARGETS value")?, ), IFLA_BOND_PRIMARY => Self::Primary( parse_u32(payload) .context("invalid IFLA_BOND_PRIMARY value")?, ), IFLA_BOND_PRIMARY_RESELECT => Self::PrimaryReselect( parse_u8(payload) .context("invalid IFLA_BOND_PRIMARY_RESELECT value")?, ), IFLA_BOND_FAIL_OVER_MAC => Self::FailOverMac( parse_u8(payload) .context("invalid IFLA_BOND_FAIL_OVER_MAC value")?, ), IFLA_BOND_XMIT_HASH_POLICY => Self::XmitHashPolicy( parse_u8(payload) .context("invalid IFLA_BOND_XMIT_HASH_POLICY value")?, ), IFLA_BOND_RESEND_IGMP => Self::ResendIgmp( parse_u32(payload) .context("invalid IFLA_BOND_RESEND_IGMP value")?, ), IFLA_BOND_NUM_PEER_NOTIF => Self::NumPeerNotif( parse_u8(payload) .context("invalid IFLA_BOND_NUM_PEER_NOTIF value")?, ), IFLA_BOND_ALL_PORTS_ACTIVE => Self::AllPortsActive( parse_u8(payload) .context("invalid IFLA_BOND_ALL_PORTS_ACTIVE value")?, ), IFLA_BOND_MIN_LINKS => Self::MinLinks( parse_u32(payload) .context("invalid IFLA_BOND_MIN_LINKS value")?, ), IFLA_BOND_LP_INTERVAL => Self::LpInterval( parse_u32(payload) .context("invalid IFLA_BOND_LP_INTERVAL value")?, ), IFLA_BOND_PACKETS_PER_PORT => Self::PacketsPerPort( parse_u32(payload) .context("invalid IFLA_BOND_PACKETS_PER_PORT value")?, ), IFLA_BOND_AD_LACP_RATE => Self::AdLacpRate( parse_u8(payload) .context("invalid IFLA_BOND_AD_LACP_RATE value")?, ), IFLA_BOND_AD_SELECT => Self::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); } Self::AdInfo(infos) } IFLA_BOND_AD_ACTOR_SYS_PRIO => Self::AdActorSysPrio( parse_u16(payload) .context("invalid IFLA_BOND_AD_ACTOR_SYS_PRIO value")?, ), IFLA_BOND_AD_USER_PORT_KEY => Self::AdUserPortKey( parse_u16(payload) .context("invalid IFLA_BOND_AD_USER_PORT_KEY value")?, ), IFLA_BOND_AD_ACTOR_SYSTEM => Self::AdActorSystem( parse_mac(payload) .context("invalid IFLA_BOND_AD_ACTOR_SYSTEM value")?, ), IFLA_BOND_TLB_DYNAMIC_LB => Self::TlbDynamicLb( parse_u8(payload) .context("invalid IFLA_BOND_TLB_DYNAMIC_LB value")?, ), IFLA_BOND_PEER_NOTIF_DELAY => Self::PeerNotifDelay( parse_u32(payload) .context("invalid IFLA_BOND_PEER_NOTIF_DELAY value")?, ), IFLA_BOND_AD_LACP_ACTIVE => Self::AdLacpActive( parse_u8(payload) .context("invalid IFLA_BOND_AD_LACP_ACTIVE value")?, ), IFLA_BOND_MISSED_MAX => Self::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); } } Self::NsIp6Target(addrs) } _ => Self::Other(DefaultNla::parse(buf).context(format!( "invalid NLA for {}: {payload:?}", buf.kind() ))?), }) } } netlink-packet-route-0.21.0/src/link/link_info/bond_port.rs000064400000000000000000000134431046102023000220060ustar 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, }; const IFLA_BOND_PORT_STATE_ACTIVE: u8 = 0; const IFLA_BOND_PORT_STATE_BACKUP: u8 = 1; const IFLA_BOND_PORT_MII_STATUS_UP: u8 = 0; const IFLA_BOND_PORT_MII_STATUS_GOING_DOWN: u8 = 1; const IFLA_BOND_PORT_MII_STATUS_DOWN: u8 = 2; const IFLA_BOND_PORT_MII_STATUS_GOING_BACK: u8 = 3; const IFLA_BOND_PORT_STATE: u16 = 1; const IFLA_BOND_PORT_MII_STATUS: u16 = 2; const IFLA_BOND_PORT_LINK_FAILURE_COUNT: u16 = 3; const IFLA_BOND_PORT_PERM_HWADDR: u16 = 4; const IFLA_BOND_PORT_QUEUE_ID: u16 = 5; // const IFLA_BOND_PORT_AD_AGGREGATOR_ID: u16 = 6; // const IFLA_BOND_PORT_AD_ACTOR_OPER_PORT_STATE: u16 = 7; // const IFLA_BOND_PORT_AD_PARTNER_OPER_PORT_STATE: u16 = 8; const IFLA_BOND_PORT_PRIO: u16 = 9; #[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.21.0/src/link/link_info/bridge.rs000064400000000000000000000635621046102023000212630ustar 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, }; const IFLA_BR_FORWARD_DELAY: u16 = 1; const IFLA_BR_HELLO_TIME: u16 = 2; const IFLA_BR_MAX_AGE: u16 = 3; const IFLA_BR_AGEING_TIME: u16 = 4; const IFLA_BR_STP_STATE: u16 = 5; const IFLA_BR_PRIORITY: u16 = 6; const IFLA_BR_VLAN_FILTERING: u16 = 7; const IFLA_BR_VLAN_PROTOCOL: u16 = 8; const IFLA_BR_GROUP_FWD_MASK: u16 = 9; const IFLA_BR_ROOT_ID: u16 = 10; const IFLA_BR_BRIDGE_ID: u16 = 11; const IFLA_BR_ROOT_PORT: u16 = 12; const IFLA_BR_ROOT_PATH_COST: u16 = 13; const IFLA_BR_TOPOLOGY_CHANGE: u16 = 14; const IFLA_BR_TOPOLOGY_CHANGE_DETECTED: u16 = 15; const IFLA_BR_HELLO_TIMER: u16 = 16; const IFLA_BR_TCN_TIMER: u16 = 17; const IFLA_BR_TOPOLOGY_CHANGE_TIMER: u16 = 18; const IFLA_BR_GC_TIMER: u16 = 19; const IFLA_BR_GROUP_ADDR: u16 = 20; const IFLA_BR_FDB_FLUSH: u16 = 21; const IFLA_BR_MCAST_ROUTER: u16 = 22; const IFLA_BR_MCAST_SNOOPING: u16 = 23; const IFLA_BR_MCAST_QUERY_USE_IFADDR: u16 = 24; const IFLA_BR_MCAST_QUERIER: u16 = 25; const IFLA_BR_MCAST_HASH_ELASTICITY: u16 = 26; const IFLA_BR_MCAST_HASH_MAX: u16 = 27; const IFLA_BR_MCAST_LAST_MEMBER_CNT: u16 = 28; const IFLA_BR_MCAST_STARTUP_QUERY_CNT: u16 = 29; const IFLA_BR_MCAST_LAST_MEMBER_INTVL: u16 = 30; const IFLA_BR_MCAST_MEMBERSHIP_INTVL: u16 = 31; const IFLA_BR_MCAST_QUERIER_INTVL: u16 = 32; const IFLA_BR_MCAST_QUERY_INTVL: u16 = 33; const IFLA_BR_MCAST_QUERY_RESPONSE_INTVL: u16 = 34; const IFLA_BR_MCAST_STARTUP_QUERY_INTVL: u16 = 35; const IFLA_BR_NF_CALL_IPTABLES: u16 = 36; const IFLA_BR_NF_CALL_IP6TABLES: u16 = 37; const IFLA_BR_NF_CALL_ARPTABLES: u16 = 38; const IFLA_BR_VLAN_DEFAULT_PVID: u16 = 39; // const IFLA_BR_PAD: u16 = 40; const IFLA_BR_VLAN_STATS_ENABLED: u16 = 41; const IFLA_BR_MCAST_STATS_ENABLED: u16 = 42; const IFLA_BR_MCAST_IGMP_VERSION: u16 = 43; const IFLA_BR_MCAST_MLD_VERSION: u16 = 44; const IFLA_BR_VLAN_STATS_PER_PORT: u16 = 45; const IFLA_BR_MULTI_BOOLOPT: u16 = 46; const IFLA_BR_MCAST_QUERIER_STATE: u16 = 47; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoBridge { GroupAddr([u8; 6]), FdbFlush, 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(BridgeId), BridgeId(BridgeId), RootPort(u16), VlanDefaultPvid(u16), VlanFiltering(bool), 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 { fn value_len(&self) -> usize { match self { // The existance of FdbFlush means true Self::FdbFlush => 0, Self::HelloTimer(_) | Self::TcnTimer(_) | Self::TopologyChangeTimer(_) | Self::GcTimer(_) | Self::MulticastMembershipInterval(_) | Self::MulticastQuerierInterval(_) | Self::MulticastQueryInterval(_) | Self::MulticastQueryResponseInterval(_) | Self::MulticastLastMemberInterval(_) | Self::MulticastStartupQueryInterval(_) => 8, Self::ForwardDelay(_) | Self::HelloTime(_) | Self::MaxAge(_) | Self::AgeingTime(_) | Self::StpState(_) | Self::MulticastHashElasticity(_) | Self::MulticastHashMax(_) | Self::MulticastLastMemberCount(_) | Self::MulticastStartupQueryCount(_) | Self::RootPathCost(_) => 4, Self::Priority(_) | Self::VlanProtocol(_) | Self::GroupFwdMask(_) | Self::RootPort(_) | Self::VlanDefaultPvid(_) => 2, Self::RootId(_) | Self::BridgeId(_) | Self::MultiBoolOpt(_) => 8, Self::GroupAddr(_) => 6, Self::VlanFiltering(_) => 1, Self::TopologyChange(_) | Self::TopologyChangeDetected(_) | Self::MulticastRouter(_) | Self::MulticastSnooping(_) | Self::MulticastQueryUseIfaddr(_) | Self::MulticastQuerier(_) | Self::NfCallIpTables(_) | Self::NfCallIp6Tables(_) | Self::NfCallArpTables(_) | Self::VlanStatsEnabled(_) | Self::MulticastStatsEnabled(_) | Self::MulticastIgmpVersion(_) | Self::MulticastMldVersion(_) | Self::VlanStatsPerHost(_) => 1, Self::MulticastQuerierState(nlas) => nlas.as_slice().buffer_len(), Self::Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::FdbFlush => (), Self::HelloTimer(value) | Self::TcnTimer(value) | Self::TopologyChangeTimer(value) | Self::GcTimer(value) | Self::MulticastMembershipInterval(value) | Self::MulticastQuerierInterval(value) | Self::MulticastQueryInterval(value) | Self::MulticastQueryResponseInterval(value) | Self::MulticastLastMemberInterval(value) | Self::MulticastStartupQueryInterval(value) | Self::MultiBoolOpt(value) => { NativeEndian::write_u64(buffer, *value) } Self::ForwardDelay(value) | Self::HelloTime(value) | Self::MaxAge(value) | Self::AgeingTime(value) | Self::StpState(value) | Self::MulticastHashElasticity(value) | Self::MulticastHashMax(value) | Self::MulticastLastMemberCount(value) | Self::MulticastStartupQueryCount(value) | Self::RootPathCost(value) => { NativeEndian::write_u32(buffer, *value) } Self::Priority(value) | Self::GroupFwdMask(value) | Self::RootPort(value) | Self::VlanDefaultPvid(value) => { NativeEndian::write_u16(buffer, *value) } Self::VlanProtocol(value) => BigEndian::write_u16(buffer, *value), Self::RootId(bridge_id) | Self::BridgeId(bridge_id) => { bridge_id.emit(buffer) } Self::GroupAddr(value) => buffer.copy_from_slice(&value[..]), Self::VlanFiltering(value) => buffer[0] = (*value).into(), Self::TopologyChange(value) | Self::TopologyChangeDetected(value) | Self::MulticastRouter(value) | Self::MulticastSnooping(value) | Self::MulticastQueryUseIfaddr(value) | Self::MulticastQuerier(value) | Self::NfCallIpTables(value) | Self::NfCallIp6Tables(value) | Self::NfCallArpTables(value) | Self::VlanStatsEnabled(value) | Self::MulticastStatsEnabled(value) | Self::MulticastIgmpVersion(value) | Self::MulticastMldVersion(value) | Self::VlanStatsPerHost(value) => buffer[0] = *value, Self::MulticastQuerierState(nlas) => nlas.as_slice().emit(buffer), Self::Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::GroupAddr(_) => IFLA_BR_GROUP_ADDR, Self::FdbFlush => IFLA_BR_FDB_FLUSH, Self::HelloTimer(_) => IFLA_BR_HELLO_TIMER, Self::TcnTimer(_) => IFLA_BR_TCN_TIMER, Self::TopologyChangeTimer(_) => IFLA_BR_TOPOLOGY_CHANGE_TIMER, Self::GcTimer(_) => IFLA_BR_GC_TIMER, Self::MulticastMembershipInterval(_) => { IFLA_BR_MCAST_MEMBERSHIP_INTVL } Self::MulticastQuerierInterval(_) => IFLA_BR_MCAST_QUERIER_INTVL, Self::MulticastQueryInterval(_) => IFLA_BR_MCAST_QUERY_INTVL, Self::MulticastQueryResponseInterval(_) => { IFLA_BR_MCAST_QUERY_RESPONSE_INTVL } Self::ForwardDelay(_) => IFLA_BR_FORWARD_DELAY, Self::HelloTime(_) => IFLA_BR_HELLO_TIME, Self::MaxAge(_) => IFLA_BR_MAX_AGE, Self::AgeingTime(_) => IFLA_BR_AGEING_TIME, Self::StpState(_) => IFLA_BR_STP_STATE, Self::MulticastHashElasticity(_) => IFLA_BR_MCAST_HASH_ELASTICITY, Self::MulticastHashMax(_) => IFLA_BR_MCAST_HASH_MAX, Self::MulticastLastMemberCount(_) => IFLA_BR_MCAST_LAST_MEMBER_CNT, Self::MulticastStartupQueryCount(_) => { IFLA_BR_MCAST_STARTUP_QUERY_CNT } Self::MulticastLastMemberInterval(_) => { IFLA_BR_MCAST_LAST_MEMBER_INTVL } Self::MulticastStartupQueryInterval(_) => { IFLA_BR_MCAST_STARTUP_QUERY_INTVL } Self::RootPathCost(_) => IFLA_BR_ROOT_PATH_COST, Self::Priority(_) => IFLA_BR_PRIORITY, Self::VlanProtocol(_) => IFLA_BR_VLAN_PROTOCOL, Self::GroupFwdMask(_) => IFLA_BR_GROUP_FWD_MASK, Self::RootId(_) => IFLA_BR_ROOT_ID, Self::BridgeId(_) => IFLA_BR_BRIDGE_ID, Self::RootPort(_) => IFLA_BR_ROOT_PORT, Self::VlanDefaultPvid(_) => IFLA_BR_VLAN_DEFAULT_PVID, Self::VlanFiltering(_) => IFLA_BR_VLAN_FILTERING, Self::TopologyChange(_) => IFLA_BR_TOPOLOGY_CHANGE, Self::TopologyChangeDetected(_) => IFLA_BR_TOPOLOGY_CHANGE_DETECTED, Self::MulticastRouter(_) => IFLA_BR_MCAST_ROUTER, Self::MulticastSnooping(_) => IFLA_BR_MCAST_SNOOPING, Self::MulticastQueryUseIfaddr(_) => IFLA_BR_MCAST_QUERY_USE_IFADDR, Self::MulticastQuerier(_) => IFLA_BR_MCAST_QUERIER, Self::NfCallIpTables(_) => IFLA_BR_NF_CALL_IPTABLES, Self::NfCallIp6Tables(_) => IFLA_BR_NF_CALL_IP6TABLES, Self::NfCallArpTables(_) => IFLA_BR_NF_CALL_ARPTABLES, Self::VlanStatsEnabled(_) => IFLA_BR_VLAN_STATS_ENABLED, Self::MulticastStatsEnabled(_) => IFLA_BR_MCAST_STATS_ENABLED, Self::MulticastIgmpVersion(_) => IFLA_BR_MCAST_IGMP_VERSION, Self::MulticastMldVersion(_) => IFLA_BR_MCAST_MLD_VERSION, Self::VlanStatsPerHost(_) => IFLA_BR_VLAN_STATS_PER_PORT, Self::MultiBoolOpt(_) => IFLA_BR_MULTI_BOOLOPT, Self::MulticastQuerierState(_) => { IFLA_BR_MCAST_QUERIER_STATE | NLA_F_NESTED } Self::Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoBridge { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { IFLA_BR_FDB_FLUSH => Self::FdbFlush, IFLA_BR_HELLO_TIMER => Self::HelloTimer( parse_u64(payload) .context("invalid IFLA_BR_HELLO_TIMER value")?, ), IFLA_BR_TCN_TIMER => Self::TcnTimer( parse_u64(payload) .context("invalid IFLA_BR_TCN_TIMER value")?, ), IFLA_BR_TOPOLOGY_CHANGE_TIMER => Self::TopologyChangeTimer( parse_u64(payload) .context("invalid IFLA_BR_TOPOLOGY_CHANGE_TIMER value")?, ), IFLA_BR_GC_TIMER => Self::GcTimer( parse_u64(payload).context("invalid IFLA_BR_GC_TIMER value")?, ), IFLA_BR_MCAST_LAST_MEMBER_INTVL => { Self::MulticastLastMemberInterval( parse_u64(payload).context( "invalid IFLA_BR_MCAST_LAST_MEMBER_INTVL value", )?, ) } IFLA_BR_MCAST_MEMBERSHIP_INTVL => { Self::MulticastMembershipInterval( parse_u64(payload).context( "invalid IFLA_BR_MCAST_MEMBERSHIP_INTVL value", )?, ) } IFLA_BR_MCAST_QUERIER_INTVL => Self::MulticastQuerierInterval( parse_u64(payload) .context("invalid IFLA_BR_MCAST_QUERIER_INTVL value")?, ), IFLA_BR_MCAST_QUERY_INTVL => Self::MulticastQueryInterval( parse_u64(payload) .context("invalid IFLA_BR_MCAST_QUERY_INTVL value")?, ), IFLA_BR_MCAST_QUERY_RESPONSE_INTVL => { Self::MulticastQueryResponseInterval( parse_u64(payload).context( "invalid IFLA_BR_MCAST_QUERY_RESPONSE_INTVL value", )?, ) } IFLA_BR_MCAST_STARTUP_QUERY_INTVL => { Self::MulticastStartupQueryInterval( parse_u64(payload).context( "invalid IFLA_BR_MCAST_STARTUP_QUERY_INTVL value", )?, ) } IFLA_BR_FORWARD_DELAY => Self::ForwardDelay( parse_u32(payload) .context("invalid IFLA_BR_FORWARD_DELAY value")?, ), IFLA_BR_HELLO_TIME => Self::HelloTime( parse_u32(payload) .context("invalid IFLA_BR_HELLO_TIME value")?, ), IFLA_BR_MAX_AGE => Self::MaxAge( parse_u32(payload).context("invalid IFLA_BR_MAX_AGE value")?, ), IFLA_BR_AGEING_TIME => Self::AgeingTime( parse_u32(payload) .context("invalid IFLA_BR_AGEING_TIME value")?, ), IFLA_BR_STP_STATE => Self::StpState( parse_u32(payload) .context("invalid IFLA_BR_STP_STATE value")?, ), IFLA_BR_MCAST_HASH_ELASTICITY => Self::MulticastHashElasticity( parse_u32(payload) .context("invalid IFLA_BR_MCAST_HASH_ELASTICITY value")?, ), IFLA_BR_MCAST_HASH_MAX => Self::MulticastHashMax( parse_u32(payload) .context("invalid IFLA_BR_MCAST_HASH_MAX value")?, ), IFLA_BR_MCAST_LAST_MEMBER_CNT => Self::MulticastLastMemberCount( parse_u32(payload) .context("invalid IFLA_BR_MCAST_LAST_MEMBER_CNT value")?, ), IFLA_BR_MCAST_STARTUP_QUERY_CNT => { Self::MulticastStartupQueryCount( parse_u32(payload).context( "invalid IFLA_BR_MCAST_STARTUP_QUERY_CNT value", )?, ) } IFLA_BR_ROOT_PATH_COST => Self::RootPathCost( parse_u32(payload) .context("invalid IFLA_BR_ROOT_PATH_COST value")?, ), IFLA_BR_PRIORITY => Self::Priority( parse_u16(payload).context("invalid IFLA_BR_PRIORITY value")?, ), IFLA_BR_VLAN_PROTOCOL => Self::VlanProtocol( parse_u16_be(payload) .context("invalid IFLA_BR_VLAN_PROTOCOL value")?, ), IFLA_BR_GROUP_FWD_MASK => Self::GroupFwdMask( parse_u16(payload) .context("invalid IFLA_BR_GROUP_FWD_MASK value")?, ), IFLA_BR_ROOT_ID => Self::RootId( BridgeId::parse(&BridgeIdBuffer::new(payload)) .context("invalid IFLA_BR_ROOT_ID value")?, ), IFLA_BR_BRIDGE_ID => Self::BridgeId( BridgeId::parse(&BridgeIdBuffer::new(payload)) .context("invalid IFLA_BR_BRIDGE_ID value")?, ), IFLA_BR_GROUP_ADDR => Self::GroupAddr( parse_mac(payload) .context("invalid IFLA_BR_GROUP_ADDR value")?, ), IFLA_BR_ROOT_PORT => Self::RootPort( parse_u16(payload) .context("invalid IFLA_BR_ROOT_PORT value")?, ), IFLA_BR_VLAN_DEFAULT_PVID => Self::VlanDefaultPvid( parse_u16(payload) .context("invalid IFLA_BR_VLAN_DEFAULT_PVID value")?, ), IFLA_BR_VLAN_FILTERING => Self::VlanFiltering( parse_u8(payload) .context("invalid IFLA_BR_VLAN_FILTERING value")? > 0, ), IFLA_BR_TOPOLOGY_CHANGE => Self::TopologyChange( parse_u8(payload) .context("invalid IFLA_BR_TOPOLOGY_CHANGE value")?, ), IFLA_BR_TOPOLOGY_CHANGE_DETECTED => { Self::TopologyChangeDetected(parse_u8(payload).context( "invalid IFLA_BR_TOPOLOGY_CHANGE_DETECTED value", )?) } IFLA_BR_MCAST_ROUTER => Self::MulticastRouter( parse_u8(payload) .context("invalid IFLA_BR_MCAST_ROUTER value")?, ), IFLA_BR_MCAST_SNOOPING => Self::MulticastSnooping( parse_u8(payload) .context("invalid IFLA_BR_MCAST_SNOOPING value")?, ), IFLA_BR_MCAST_QUERY_USE_IFADDR => Self::MulticastQueryUseIfaddr( parse_u8(payload) .context("invalid IFLA_BR_MCAST_QUERY_USE_IFADDR value")?, ), IFLA_BR_MCAST_QUERIER => Self::MulticastQuerier( parse_u8(payload) .context("invalid IFLA_BR_MCAST_QUERIER value")?, ), IFLA_BR_NF_CALL_IPTABLES => Self::NfCallIpTables( parse_u8(payload) .context("invalid IFLA_BR_NF_CALL_IPTABLES value")?, ), IFLA_BR_NF_CALL_IP6TABLES => Self::NfCallIp6Tables( parse_u8(payload) .context("invalid IFLA_BR_NF_CALL_IP6TABLES value")?, ), IFLA_BR_NF_CALL_ARPTABLES => Self::NfCallArpTables( parse_u8(payload) .context("invalid IFLA_BR_NF_CALL_ARPTABLES value")?, ), IFLA_BR_VLAN_STATS_ENABLED => Self::VlanStatsEnabled( parse_u8(payload) .context("invalid IFLA_BR_VLAN_STATS_ENABLED value")?, ), IFLA_BR_MCAST_STATS_ENABLED => Self::MulticastStatsEnabled( parse_u8(payload) .context("invalid IFLA_BR_MCAST_STATS_ENABLED value")?, ), IFLA_BR_MCAST_IGMP_VERSION => Self::MulticastIgmpVersion( parse_u8(payload) .context("invalid IFLA_BR_MCAST_IGMP_VERSION value")?, ), IFLA_BR_MCAST_MLD_VERSION => Self::MulticastMldVersion( parse_u8(payload) .context("invalid IFLA_BR_MCAST_MLD_VERSION value")?, ), IFLA_BR_VLAN_STATS_PER_PORT => Self::VlanStatsPerHost( parse_u8(payload) .context("invalid IFLA_BR_VLAN_STATS_PER_PORT value")?, ), IFLA_BR_MULTI_BOOLOPT => Self::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); } Self::MulticastQuerierState(v) } _ => Self::Other(DefaultNla::parse(buf).context( "invalid link info bridge NLA value (unknown type)", )?), }) } } const BRIDGE_ID_LEN: usize = 8; #[derive(Debug, PartialEq, Eq, Clone)] pub struct BridgeId { pub priority: u16, pub address: [u8; 6], } buffer!(BridgeIdBuffer(BRIDGE_ID_LEN) { priority: (u16, 0..2), address: (slice, 2..BRIDGE_ID_LEN) }); impl + ?Sized> Parseable> for BridgeId { fn parse(buf: &BridgeIdBuffer<&T>) -> Result { // Priority is encoded in big endian. From kernel's // net/bridge/br_netlink.c br_fill_info(): // u16 priority = (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1]; Ok(Self { priority: u16::from_be(buf.priority()), address: parse_mac(buf.address()) .context("invalid MAC address in BridgeId buffer")?, }) } } impl Emitable for BridgeId { fn buffer_len(&self) -> usize { BRIDGE_ID_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = BridgeIdBuffer::new(buffer); buffer.set_priority(self.priority.to_be()); buffer.address_mut().copy_from_slice(&self.address[..]); } } 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, 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}"))?, ), }) } } netlink-packet-route-0.21.0/src/link/link_info/bridge_port.rs000064400000000000000000000602131046102023000223150ustar 00000000000000// SPDX-License-Identifier: MIT use crate::link::{BridgeId, BridgeIdBuffer}; use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::{parse_u16, parse_u32, parse_u64, parse_u8}, traits::{Emitable, Parseable}, DecodeError, }; const IFLA_BRPORT_STATE: u16 = 1; const IFLA_BRPORT_PRIORITY: u16 = 2; const IFLA_BRPORT_COST: u16 = 3; const IFLA_BRPORT_MODE: u16 = 4; const IFLA_BRPORT_GUARD: u16 = 5; const IFLA_BRPORT_PROTECT: u16 = 6; const IFLA_BRPORT_FAST_LEAVE: u16 = 7; const IFLA_BRPORT_LEARNING: u16 = 8; const IFLA_BRPORT_UNICAST_FLOOD: u16 = 9; const IFLA_BRPORT_PROXYARP: u16 = 10; // only used in one driver, we don't know if its type is stable: // const IFLA_BRPORT_LEARNING_SYNC: u16 = 11; const IFLA_BRPORT_PROXYARP_WIFI: u16 = 12; const IFLA_BRPORT_ROOT_ID: u16 = 13; const IFLA_BRPORT_BRIDGE_ID: u16 = 14; const IFLA_BRPORT_DESIGNATED_PORT: u16 = 15; const IFLA_BRPORT_DESIGNATED_COST: u16 = 16; const IFLA_BRPORT_ID: u16 = 17; const IFLA_BRPORT_NO: u16 = 18; const IFLA_BRPORT_TOPOLOGY_CHANGE_ACK: u16 = 19; const IFLA_BRPORT_CONFIG_PENDING: u16 = 20; const IFLA_BRPORT_MESSAGE_AGE_TIMER: u16 = 21; const IFLA_BRPORT_FORWARD_DELAY_TIMER: u16 = 22; const IFLA_BRPORT_HOLD_TIMER: u16 = 23; const IFLA_BRPORT_FLUSH: u16 = 24; const IFLA_BRPORT_MULTICAST_ROUTER: u16 = 25; // const IFLA_BRPORT_PAD: u16 = 26; const IFLA_BRPORT_MCAST_FLOOD: u16 = 27; const IFLA_BRPORT_MCAST_TO_UCAST: u16 = 28; const IFLA_BRPORT_VLAN_TUNNEL: u16 = 29; const IFLA_BRPORT_BCAST_FLOOD: u16 = 30; const IFLA_BRPORT_GROUP_FWD_MASK: u16 = 31; const IFLA_BRPORT_NEIGH_SUPPRESS: u16 = 32; const IFLA_BRPORT_ISOLATED: u16 = 33; const IFLA_BRPORT_BACKUP_PORT: u16 = 34; const IFLA_BRPORT_MRP_RING_OPEN: u16 = 35; const IFLA_BRPORT_MRP_IN_OPEN: u16 = 36; const IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT: u16 = 37; const IFLA_BRPORT_MCAST_EHT_HOSTS_CNT: u16 = 38; const IFLA_BRPORT_LOCKED: u16 = 39; const IFLA_BRPORT_MAB: u16 = 40; const IFLA_BRPORT_MCAST_N_GROUPS: u16 = 41; const IFLA_BRPORT_MCAST_MAX_GROUPS: u16 = 42; const IFLA_BRPORT_NEIGH_VLAN_SUPPRESS: u16 = 43; const IFLA_BRPORT_BACKUP_NHID: u16 = 44; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoBridgePort { State(BridgePortState), Priority(u16), Cost(u32), HairpinMode(bool), Guard(bool), Protect(bool), FastLeave(bool), Learning(bool), UnicastFlood(bool), ProxyARP(bool), ProxyARPWifi(bool), RootId(BridgeId), BridgeId(BridgeId), DesignatedPort(u16), DesignatedCost(u16), PortId(u16), PortNumber(u16), TopologyChangeAck(bool), ConfigPending(bool), MessageAgeTimer(u64), ForwardDelayTimer(u64), HoldTimer(u64), Flush, MulticastRouter(BridgePortMulticastRouter), MulticastFlood(bool), MulticastToUnicast(bool), VlanTunnel(bool), BroadcastFlood(bool), GroupFwdMask(u16), NeighSupress(bool), Isolated(bool), BackupPort(u32), MrpRingOpen(bool), MrpInOpen(bool), MulticastEhtHostsLimit(u32), MulticastEhtHostsCnt(u32), Locked(bool), Mab(bool), MulticastNGroups(u32), MulticastMaxGroups(u32), NeighVlanSupress(bool), BackupNextHopId(u32), Other(DefaultNla), } impl Nla for InfoBridgePort { fn value_len(&self) -> usize { match self { InfoBridgePort::Flush => 0, InfoBridgePort::State(_) | InfoBridgePort::HairpinMode(_) | InfoBridgePort::Guard(_) | InfoBridgePort::Protect(_) | InfoBridgePort::FastLeave(_) | InfoBridgePort::Learning(_) | InfoBridgePort::UnicastFlood(_) | InfoBridgePort::ProxyARP(_) | InfoBridgePort::ProxyARPWifi(_) | InfoBridgePort::TopologyChangeAck(_) | InfoBridgePort::ConfigPending(_) | InfoBridgePort::MulticastRouter(_) | InfoBridgePort::MulticastFlood(_) | InfoBridgePort::MulticastToUnicast(_) | InfoBridgePort::VlanTunnel(_) | InfoBridgePort::BroadcastFlood(_) | InfoBridgePort::NeighSupress(_) | InfoBridgePort::Isolated(_) | InfoBridgePort::MrpRingOpen(_) | InfoBridgePort::MrpInOpen(_) | InfoBridgePort::Locked(_) | InfoBridgePort::Mab(_) | InfoBridgePort::NeighVlanSupress(_) => 1, InfoBridgePort::Priority(_) | InfoBridgePort::DesignatedPort(_) | InfoBridgePort::DesignatedCost(_) | InfoBridgePort::PortId(_) | InfoBridgePort::PortNumber(_) | InfoBridgePort::GroupFwdMask(_) => 2, InfoBridgePort::Cost(_) | InfoBridgePort::BackupPort(_) | InfoBridgePort::MulticastEhtHostsLimit(_) | InfoBridgePort::MulticastEhtHostsCnt(_) | InfoBridgePort::MulticastNGroups(_) | InfoBridgePort::MulticastMaxGroups(_) | InfoBridgePort::BackupNextHopId(_) => 4, InfoBridgePort::RootId(_) | InfoBridgePort::BridgeId(_) | InfoBridgePort::MessageAgeTimer(_) | InfoBridgePort::ForwardDelayTimer(_) | InfoBridgePort::HoldTimer(_) => 8, InfoBridgePort::Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { InfoBridgePort::Flush => (), InfoBridgePort::HairpinMode(value) | InfoBridgePort::Guard(value) | InfoBridgePort::Protect(value) | InfoBridgePort::FastLeave(value) | InfoBridgePort::Learning(value) | InfoBridgePort::UnicastFlood(value) | InfoBridgePort::ProxyARP(value) | InfoBridgePort::TopologyChangeAck(value) | InfoBridgePort::ConfigPending(value) | InfoBridgePort::ProxyARPWifi(value) | InfoBridgePort::MulticastFlood(value) | InfoBridgePort::MulticastToUnicast(value) | InfoBridgePort::VlanTunnel(value) | InfoBridgePort::BroadcastFlood(value) | InfoBridgePort::NeighSupress(value) | InfoBridgePort::Isolated(value) | InfoBridgePort::MrpRingOpen(value) | InfoBridgePort::MrpInOpen(value) | InfoBridgePort::Locked(value) | InfoBridgePort::Mab(value) | InfoBridgePort::NeighVlanSupress(value) => { buffer[0] = if *value { 1 } else { 0 } } InfoBridgePort::Priority(value) | InfoBridgePort::DesignatedPort(value) | InfoBridgePort::DesignatedCost(value) | InfoBridgePort::PortId(value) | InfoBridgePort::PortNumber(value) | InfoBridgePort::GroupFwdMask(value) => { NativeEndian::write_u16(buffer, *value) } InfoBridgePort::Cost(value) | InfoBridgePort::BackupPort(value) | InfoBridgePort::MulticastEhtHostsLimit(value) | InfoBridgePort::MulticastEhtHostsCnt(value) | InfoBridgePort::MulticastNGroups(value) | InfoBridgePort::MulticastMaxGroups(value) | InfoBridgePort::BackupNextHopId(value) => { NativeEndian::write_u32(buffer, *value) } InfoBridgePort::MessageAgeTimer(value) | InfoBridgePort::ForwardDelayTimer(value) | InfoBridgePort::HoldTimer(value) => { NativeEndian::write_u64(buffer, *value) } InfoBridgePort::RootId(bridge_id) | InfoBridgePort::BridgeId(bridge_id) => bridge_id.emit(buffer), InfoBridgePort::State(state) => buffer[0] = (*state).into(), InfoBridgePort::MulticastRouter(mcast_router) => { buffer[0] = (*mcast_router).into() } InfoBridgePort::Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { match self { InfoBridgePort::State(_) => IFLA_BRPORT_STATE, InfoBridgePort::Priority(_) => IFLA_BRPORT_PRIORITY, InfoBridgePort::Cost(_) => IFLA_BRPORT_COST, InfoBridgePort::HairpinMode(_) => IFLA_BRPORT_MODE, InfoBridgePort::Guard(_) => IFLA_BRPORT_GUARD, InfoBridgePort::Protect(_) => IFLA_BRPORT_PROTECT, InfoBridgePort::FastLeave(_) => IFLA_BRPORT_FAST_LEAVE, InfoBridgePort::Learning(_) => IFLA_BRPORT_LEARNING, InfoBridgePort::UnicastFlood(_) => IFLA_BRPORT_UNICAST_FLOOD, InfoBridgePort::ProxyARP(_) => IFLA_BRPORT_PROXYARP, InfoBridgePort::ProxyARPWifi(_) => IFLA_BRPORT_PROXYARP_WIFI, InfoBridgePort::RootId(_) => IFLA_BRPORT_ROOT_ID, InfoBridgePort::BridgeId(_) => IFLA_BRPORT_BRIDGE_ID, InfoBridgePort::DesignatedPort(_) => IFLA_BRPORT_DESIGNATED_PORT, InfoBridgePort::DesignatedCost(_) => IFLA_BRPORT_DESIGNATED_COST, InfoBridgePort::PortId(_) => IFLA_BRPORT_ID, InfoBridgePort::PortNumber(_) => IFLA_BRPORT_NO, InfoBridgePort::TopologyChangeAck(_) => { IFLA_BRPORT_TOPOLOGY_CHANGE_ACK } InfoBridgePort::ConfigPending(_) => IFLA_BRPORT_CONFIG_PENDING, InfoBridgePort::MessageAgeTimer(_) => IFLA_BRPORT_MESSAGE_AGE_TIMER, InfoBridgePort::ForwardDelayTimer(_) => { IFLA_BRPORT_FORWARD_DELAY_TIMER } InfoBridgePort::HoldTimer(_) => IFLA_BRPORT_HOLD_TIMER, InfoBridgePort::Flush => IFLA_BRPORT_FLUSH, InfoBridgePort::MulticastRouter(_) => IFLA_BRPORT_MULTICAST_ROUTER, InfoBridgePort::MulticastFlood(_) => IFLA_BRPORT_MCAST_FLOOD, InfoBridgePort::MulticastToUnicast(_) => IFLA_BRPORT_MCAST_TO_UCAST, InfoBridgePort::VlanTunnel(_) => IFLA_BRPORT_VLAN_TUNNEL, InfoBridgePort::BroadcastFlood(_) => IFLA_BRPORT_BCAST_FLOOD, InfoBridgePort::GroupFwdMask(_) => IFLA_BRPORT_GROUP_FWD_MASK, InfoBridgePort::NeighSupress(_) => IFLA_BRPORT_NEIGH_SUPPRESS, InfoBridgePort::Isolated(_) => IFLA_BRPORT_ISOLATED, InfoBridgePort::BackupPort(_) => IFLA_BRPORT_BACKUP_PORT, InfoBridgePort::MrpRingOpen(_) => IFLA_BRPORT_MRP_RING_OPEN, InfoBridgePort::MrpInOpen(_) => IFLA_BRPORT_MRP_IN_OPEN, InfoBridgePort::MulticastEhtHostsLimit(_) => { IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT } InfoBridgePort::MulticastEhtHostsCnt(_) => { IFLA_BRPORT_MCAST_EHT_HOSTS_CNT } InfoBridgePort::Locked(_) => IFLA_BRPORT_LOCKED, InfoBridgePort::Mab(_) => IFLA_BRPORT_MAB, InfoBridgePort::MulticastNGroups(_) => IFLA_BRPORT_MCAST_N_GROUPS, InfoBridgePort::MulticastMaxGroups(_) => { IFLA_BRPORT_MCAST_MAX_GROUPS } InfoBridgePort::NeighVlanSupress(_) => { IFLA_BRPORT_NEIGH_VLAN_SUPPRESS } InfoBridgePort::BackupNextHopId(_) => IFLA_BRPORT_BACKUP_NHID, InfoBridgePort::Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoBridgePort { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { IFLA_BRPORT_STATE => InfoBridgePort::State( parse_u8(payload) .with_context(|| { format!("invalid IFLA_BRPORT_STATE {payload:?}") })? .into(), ), IFLA_BRPORT_PRIORITY => { InfoBridgePort::Priority(parse_u16(payload).with_context( || format!("invalid IFLA_BRPORT_PRIORITY {payload:?}"), )?) } IFLA_BRPORT_COST => { InfoBridgePort::Cost(parse_u32(payload).with_context(|| { format!("invalid IFLA_BRPORT_COST {payload:?}") })?) } IFLA_BRPORT_MODE => InfoBridgePort::HairpinMode( parse_u8(payload).with_context(|| { format!("invalid IFLA_BRPORT_MODE {payload:?}") })? > 0, ), IFLA_BRPORT_GUARD => InfoBridgePort::Guard( parse_u8(payload).with_context(|| { format!("invalid IFLA_BRPORT_GUARD {payload:?}") })? > 0, ), IFLA_BRPORT_PROTECT => InfoBridgePort::Protect( parse_u8(payload).with_context(|| { format!("invalid IFLA_BRPORT_PROTECT {payload:?}") })? > 0, ), IFLA_BRPORT_FAST_LEAVE => InfoBridgePort::FastLeave( parse_u8(payload).with_context(|| { format!( "invalid IFLA_BRPORT_FAST_LEAVE {payload:?}" ) })? > 0, ), IFLA_BRPORT_LEARNING => InfoBridgePort::Learning( parse_u8(payload).with_context(|| { format!("invalid IFLA_BRPORT_LEARNING {payload:?}") })? > 0, ), IFLA_BRPORT_UNICAST_FLOOD => InfoBridgePort::UnicastFlood( parse_u8(payload).with_context(|| { format!("invalid IFLA_BRPORT_UNICAST_FLOOD {payload:?}") })? > 0, ), IFLA_BRPORT_PROXYARP => InfoBridgePort::ProxyARP( parse_u8(payload).with_context(|| { format!("invalid IFLA_BRPORT_PROXYARP {payload:?}") })? > 0, ), IFLA_BRPORT_PROXYARP_WIFI => InfoBridgePort::ProxyARPWifi( parse_u8(payload).with_context(|| { format!("invalid IFLA_BRPORT_PROXYARP_WIFI {payload:?}") })? > 0, ), IFLA_BRPORT_ROOT_ID => Self::RootId( BridgeId::parse(&BridgeIdBuffer::new(payload)).with_context(|| { format!("invalid IFLA_BRPORT_ROOT_ID {payload:?}") })?, ), IFLA_BRPORT_BRIDGE_ID => Self::BridgeId( BridgeId::parse(&BridgeIdBuffer::new(payload)).with_context(|| { format!("invalid IFLA_BRPORT_BRIDGE_ID {payload:?}") })?, ), IFLA_BRPORT_DESIGNATED_PORT => InfoBridgePort::DesignatedPort( parse_u16(payload).with_context(|| { format!("invalid IFLA_BRPORT_DESIGNATED_PORT {payload:?}") })?, ), IFLA_BRPORT_DESIGNATED_COST => InfoBridgePort::DesignatedCost( parse_u16(payload).with_context(|| { format!("invalid IFLA_BRPORT_DESIGNATED_COST {payload:?}") })?, ), IFLA_BRPORT_ID => { InfoBridgePort::PortId(parse_u16(payload).with_context(|| { format!("invalid IFLA_BRPORT_ID {payload:?}") })?) } IFLA_BRPORT_NO => { InfoBridgePort::PortNumber(parse_u16(payload).with_context(|| { format!("invalid IFLA_BRPORT_NO {payload:?}") })?) } IFLA_BRPORT_TOPOLOGY_CHANGE_ACK => { InfoBridgePort::TopologyChangeAck( parse_u8(payload).with_context(|| { format!( "invalid IFLA_BRPORT_TOPOLOGY_CHANGE_ACK {payload:?}" ) })? > 0, ) } IFLA_BRPORT_CONFIG_PENDING => InfoBridgePort::ConfigPending( parse_u8(payload).with_context(|| { format!("invalid IFLA_BRPORT_CONFIG_PENDING {payload:?}") })? > 0, ), IFLA_BRPORT_MESSAGE_AGE_TIMER => InfoBridgePort::MessageAgeTimer( parse_u64(payload).with_context(|| { format!("invalid IFLA_BRPORT_MESSAGE_AGE_TIMER {payload:?}") })?, ), IFLA_BRPORT_FORWARD_DELAY_TIMER => { InfoBridgePort::ForwardDelayTimer( parse_u64(payload).with_context(|| { format!( "invalid IFLA_BRPORT_FORWARD_DELAY_TIMER {payload:?}" ) })?, ) } IFLA_BRPORT_HOLD_TIMER => { InfoBridgePort::HoldTimer(parse_u64(payload).with_context( || format!("invalid IFLA_BRPORT_HOLD_TIMER {payload:?}"), )?) } IFLA_BRPORT_FLUSH => InfoBridgePort::Flush, IFLA_BRPORT_MULTICAST_ROUTER => InfoBridgePort::MulticastRouter( parse_u8(payload) .with_context(|| { format!( "invalid IFLA_BRPORT_MULTICAST_ROUTER {payload:?}" ) })? .into(), ), IFLA_BRPORT_MCAST_FLOOD => InfoBridgePort::MulticastFlood( parse_u8(payload).with_context(|| { format!("invalid IFLA_BRPORT_MCAST_FLOOD {payload:?}") })? > 0, ), IFLA_BRPORT_MCAST_TO_UCAST => InfoBridgePort::MulticastToUnicast( parse_u8(payload).with_context(|| { format!("invalid IFLA_BRPORT_MCAST_TO_UCAST {payload:?}") })? > 0, ), IFLA_BRPORT_VLAN_TUNNEL => InfoBridgePort::VlanTunnel( parse_u8(payload).with_context(|| { format!("invalid IFLA_BRPORT_VLAN_TUNNEL {payload:?}") })? > 0, ), IFLA_BRPORT_BCAST_FLOOD => InfoBridgePort::BroadcastFlood( parse_u8(payload).with_context(|| { format!("invalid IFLA_BRPORT_BCAST_FLOOD {payload:?}") })? > 0, ), IFLA_BRPORT_GROUP_FWD_MASK => InfoBridgePort::GroupFwdMask( parse_u16(payload).with_context(|| { format!("invalid IFLA_BRPORT_GROUP_FWD_MASK {payload:?}") })?, ), IFLA_BRPORT_NEIGH_SUPPRESS => InfoBridgePort::NeighSupress( parse_u8(payload).with_context(|| { format!("invalid IFLA_BRPORT_NEIGH_SUPPRESS {payload:?}") })? > 0, ), IFLA_BRPORT_ISOLATED => InfoBridgePort::Isolated( parse_u8(payload).with_context(|| { format!("invalid IFLA_BRPORT_ISOLATED {payload:?}") })? > 0, ), IFLA_BRPORT_BACKUP_PORT => { InfoBridgePort::BackupPort(parse_u32(payload).with_context( || format!("invalid IFLA_BRPORT_BACKUP_PORT {payload:?}"), )?) } IFLA_BRPORT_MRP_RING_OPEN => InfoBridgePort::MrpRingOpen( parse_u8(payload).with_context(|| { format!("invalid IFLA_BRPORT_MRP_RING_OPEN {payload:?}") })? > 0, ), IFLA_BRPORT_MRP_IN_OPEN => InfoBridgePort::MrpInOpen( parse_u8(payload).with_context(|| { format!("invalid IFLA_BRPORT_MRP_IN_OPEN {payload:?}") })? > 0, ), IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT => { InfoBridgePort::MulticastEhtHostsLimit( parse_u32(payload).with_context(|| { format!( "invalid IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT {payload:?}" ) })?, ) } IFLA_BRPORT_MCAST_EHT_HOSTS_CNT => { InfoBridgePort::MulticastEhtHostsCnt( parse_u32(payload).with_context(|| { format!( "invalid IFLA_BRPORT_MCAST_EHT_HOSTS_CNT {payload:?}" ) })? ) } IFLA_BRPORT_LOCKED => InfoBridgePort::Locked( parse_u8(payload).with_context(|| { format!("invalid IFLA_BRPORT_LOCKED {payload:?}") })? > 0, ), IFLA_BRPORT_MAB => InfoBridgePort::Mab( parse_u8(payload).with_context(|| { format!("invalid IFLA_BRPORT_MAB {payload:?}") })? > 0, ), IFLA_BRPORT_MCAST_N_GROUPS => InfoBridgePort::MulticastNGroups( parse_u32(payload).with_context(|| { format!("invalid IFLA_BRPORT_MCAST_N_GROUPS {payload:?}") })?, ), IFLA_BRPORT_MCAST_MAX_GROUPS => InfoBridgePort::MulticastMaxGroups( parse_u32(payload).with_context(|| { format!("invalid IFLA_BRPORT_MCAST_MAX_GROUPS {payload:?}") })?, ), IFLA_BRPORT_NEIGH_VLAN_SUPPRESS => InfoBridgePort::NeighVlanSupress( parse_u8(payload).with_context(|| { format!( "invalid IFLA_BRPORT_NEIGH_VLAN_SUPPRESS {payload:?}" ) })? > 0, ), IFLA_BRPORT_BACKUP_NHID => InfoBridgePort::BackupNextHopId( parse_u32(payload).with_context(|| { format!("invalid IFLA_BRPORT_BACKUP_NHID {payload:?}") })?, ), kind => InfoBridgePort::Other( DefaultNla::parse(buf).with_context(|| { format!( "failed to parse bridge port NLA of type '{kind}' into DefaultNla" ) })?, ), }) } } const BR_STATE_DISABLED: u8 = 0; const BR_STATE_LISTENING: u8 = 1; const BR_STATE_LEARNING: u8 = 2; const BR_STATE_FORWARDING: u8 = 3; const BR_STATE_BLOCKING: u8 = 4; #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] pub enum BridgePortState { Disabled, Listening, Learning, Forwarding, Blocking, Other(u8), } impl From for BridgePortState { fn from(value: u8) -> Self { match value { BR_STATE_DISABLED => BridgePortState::Disabled, BR_STATE_LISTENING => BridgePortState::Listening, BR_STATE_LEARNING => BridgePortState::Learning, BR_STATE_FORWARDING => BridgePortState::Forwarding, BR_STATE_BLOCKING => BridgePortState::Blocking, _ => BridgePortState::Other(value), } } } impl From for u8 { fn from(value: BridgePortState) -> Self { match value { BridgePortState::Disabled => BR_STATE_DISABLED, BridgePortState::Listening => BR_STATE_LISTENING, BridgePortState::Learning => BR_STATE_LEARNING, BridgePortState::Forwarding => BR_STATE_FORWARDING, BridgePortState::Blocking => BR_STATE_BLOCKING, BridgePortState::Other(v) => v, } } } const MDB_RTR_TYPE_DISABLED: u8 = 0; const MDB_RTR_TYPE_TEMP_QUERY: u8 = 1; const MDB_RTR_TYPE_PERM: u8 = 2; const MDB_RTR_TYPE_TEMP: u8 = 3; #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] pub enum BridgePortMulticastRouter { Disabled, TempQuery, Perm, Temp, Other(u8), } impl From for BridgePortMulticastRouter { fn from(value: u8) -> Self { match value { MDB_RTR_TYPE_DISABLED => BridgePortMulticastRouter::Disabled, MDB_RTR_TYPE_TEMP_QUERY => BridgePortMulticastRouter::TempQuery, MDB_RTR_TYPE_PERM => BridgePortMulticastRouter::Perm, MDB_RTR_TYPE_TEMP => BridgePortMulticastRouter::Temp, _ => BridgePortMulticastRouter::Other(value), } } } impl From for u8 { fn from(value: BridgePortMulticastRouter) -> Self { match value { BridgePortMulticastRouter::Disabled => MDB_RTR_TYPE_DISABLED, BridgePortMulticastRouter::TempQuery => MDB_RTR_TYPE_TEMP_QUERY, BridgePortMulticastRouter::Perm => MDB_RTR_TYPE_PERM, BridgePortMulticastRouter::Temp => MDB_RTR_TYPE_TEMP, BridgePortMulticastRouter::Other(v) => v, } } } netlink-packet-route-0.21.0/src/link/link_info/geneve.rs000064400000000000000000000164031046102023000212700ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::{Ipv4Addr, Ipv6Addr}; use anyhow::Context; use byteorder::{BigEndian, ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::{parse_u16_be, parse_u32, parse_u32_be, parse_u8}, traits::Parseable, DecodeError, }; const IFLA_GENEVE_ID: u16 = 1; const IFLA_GENEVE_REMOTE: u16 = 2; const IFLA_GENEVE_TTL: u16 = 3; const IFLA_GENEVE_TOS: u16 = 4; const IFLA_GENEVE_PORT: u16 = 5; const IFLA_GENEVE_COLLECT_METADATA: u16 = 6; const IFLA_GENEVE_REMOTE6: u16 = 7; const IFLA_GENEVE_UDP_CSUM: u16 = 8; const IFLA_GENEVE_UDP_ZERO_CSUM6_TX: u16 = 9; const IFLA_GENEVE_UDP_ZERO_CSUM6_RX: u16 = 10; const IFLA_GENEVE_LABEL: u16 = 11; const IFLA_GENEVE_TTL_INHERIT: u16 = 12; const IFLA_GENEVE_DF: u16 = 13; const IFLA_GENEVE_INNER_PROTO_INHERIT: u16 = 14; const GENEVE_DF_UNSET: u8 = 0; const GENEVE_DF_SET: u8 = 1; const GENEVE_DF_INHERIT: u8 = 2; #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub enum GeneveDf { Unset, Set, Inherit, Other(u8), } impl From for GeneveDf { fn from(d: u8) -> Self { match d { GENEVE_DF_UNSET => Self::Unset, GENEVE_DF_SET => Self::Set, GENEVE_DF_INHERIT => Self::Inherit, _ => Self::Other(d), } } } impl From for u8 { fn from(d: GeneveDf) -> Self { match d { GeneveDf::Unset => GENEVE_DF_UNSET, GeneveDf::Set => GENEVE_DF_SET, GeneveDf::Inherit => GENEVE_DF_INHERIT, GeneveDf::Other(value) => value, } } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoGeneve { Id(u32), Remote(Ipv4Addr), Remote6(Ipv6Addr), Ttl(u8), Tos(u8), Port(u16), CollectMetadata, UdpCsum(bool), UdpZeroCsum6Tx(bool), UdpZeroCsum6Rx(bool), Label(u32), TtlInherit(bool), Df(GeneveDf), InnerProtoInherit, Other(DefaultNla), } impl Nla for InfoGeneve { fn value_len(&self) -> usize { use self::InfoGeneve::*; match self { Id(_) | Remote(_) | Label(_) => 4, Remote6(_) => 16, Ttl(_) | Tos(_) | UdpCsum(_) | UdpZeroCsum6Tx(_) | UdpZeroCsum6Rx(_) | TtlInherit(_) | Df(_) => 1, Port(_) => 2, CollectMetadata | InnerProtoInherit => 0, Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::InfoGeneve::*; match self { Id(value) => NativeEndian::write_u32(buffer, *value), Remote(value) => buffer.copy_from_slice(&value.octets()), Remote6(value) => buffer.copy_from_slice(&value.octets()), Ttl(value) | Tos(value) => buffer[0] = *value, Port(value) => BigEndian::write_u16(buffer, *value), CollectMetadata | InnerProtoInherit => (), UdpCsum(value) | UdpZeroCsum6Tx(value) | UdpZeroCsum6Rx(value) | TtlInherit(value) => buffer[0] = *value as u8, Label(value) => BigEndian::write_u32(buffer, *value), Df(value) => buffer[0] = (*value).into(), Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::InfoGeneve::*; match self { Id(_) => IFLA_GENEVE_ID, Remote(_) => IFLA_GENEVE_REMOTE, Remote6(_) => IFLA_GENEVE_REMOTE6, Ttl(_) => IFLA_GENEVE_TTL, Tos(_) => IFLA_GENEVE_TOS, Port(_) => IFLA_GENEVE_PORT, CollectMetadata => IFLA_GENEVE_COLLECT_METADATA, UdpCsum(_) => IFLA_GENEVE_UDP_CSUM, UdpZeroCsum6Tx(_) => IFLA_GENEVE_UDP_ZERO_CSUM6_TX, UdpZeroCsum6Rx(_) => IFLA_GENEVE_UDP_ZERO_CSUM6_RX, Label(_) => IFLA_GENEVE_LABEL, TtlInherit(_) => IFLA_GENEVE_TTL_INHERIT, Df(_) => IFLA_GENEVE_DF, InnerProtoInherit => IFLA_GENEVE_INNER_PROTO_INHERIT, Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoGeneve { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoGeneve::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_GENEVE_ID => { Id(parse_u32(payload) .context("invalid IFLA_GENEVE_ID value")?) } IFLA_GENEVE_REMOTE => { if payload.len() == 4 { let mut data = [0u8; 4]; data.copy_from_slice(&payload[0..4]); Remote(Ipv4Addr::from(data)) } else { return Err(DecodeError::from(format!( "Invalid IFLA_GENEVE_REMOTE, got unexpected length \ of IPv4 address payload {:?}", payload ))); } } IFLA_GENEVE_REMOTE6 => { if payload.len() == 16 { let mut data = [0u8; 16]; data.copy_from_slice(&payload[0..16]); Remote6(Ipv6Addr::from(data)) } else { return Err(DecodeError::from(format!( "Invalid IFLA_GENEVE_REMOTE6, got unexpected length \ of IPv6 address payload {:?}", payload ))); } } IFLA_GENEVE_TTL => { Ttl(parse_u8(payload) .context("invalid IFLA_GENEVE_TTL value")?) } IFLA_GENEVE_TOS => { Tos(parse_u8(payload) .context("invalid IFLA_GENEVE_TOS value")?) } IFLA_GENEVE_PORT => Port( parse_u16_be(payload) .context("invalid IFLA_GENEVE_PORT value")?, ), IFLA_GENEVE_COLLECT_METADATA => CollectMetadata, IFLA_GENEVE_UDP_CSUM => UdpCsum( parse_u8(payload) .context("invalid IFLA_GENEVE_UDP_CSUM value")? > 0, ), IFLA_GENEVE_UDP_ZERO_CSUM6_TX => UdpZeroCsum6Tx( parse_u8(payload) .context("invalid IFLA_GENEVE_UDP_ZERO_CSUM6_TX value")? > 0, ), IFLA_GENEVE_UDP_ZERO_CSUM6_RX => UdpZeroCsum6Rx( parse_u8(payload) .context("invalid IFLA_GENEVE_UDP_ZERO_CSUM6_RX value")? > 0, ), IFLA_GENEVE_LABEL => Label( parse_u32_be(payload) .context("invalid IFLA_GENEVE_LABEL value")?, ), IFLA_GENEVE_TTL_INHERIT => TtlInherit( parse_u8(payload) .context("invalid IFLA_GENEVE_TTL_INHERIT value")? > 0, ), IFLA_GENEVE_DF => Df(parse_u8(payload) .context("invalid IFLA_GENEVE_DF value")? .into()), IFLA_GENEVE_INNER_PROTO_INHERIT => InnerProtoInherit, kind => Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } netlink-packet-route-0.21.0/src/link/link_info/gre.rs000064400000000000000000000020471046102023000205730ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, DecodeError, Parseable, }; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoGreTun { Other(DefaultNla), } impl Nla for InfoGreTun { fn value_len(&self) -> usize { match self { Self::Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoGreTun { fn parse(buf: &NlaBuffer<&'a T>) -> Result { #[allow(clippy::match_single_binding)] Ok(match buf.kind() { kind => Self::Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind} for gre"))?, ), }) } } netlink-packet-route-0.21.0/src/link/link_info/gre6.rs000064400000000000000000000020551046102023000206600ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, DecodeError, Parseable, }; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoGreTun6 { Other(DefaultNla), } impl Nla for InfoGreTun6 { fn value_len(&self) -> usize { match self { Self::Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoGreTun6 { fn parse(buf: &NlaBuffer<&'a T>) -> Result { #[allow(clippy::match_single_binding)] Ok(match buf.kind() { kind => Self::Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind} for ip6gre"))?, ), }) } } netlink-packet-route-0.21.0/src/link/link_info/gre_tap.rs000064400000000000000000000020521046102023000214330ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, DecodeError, Parseable, }; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoGreTap { Other(DefaultNla), } impl Nla for InfoGreTap { fn value_len(&self) -> usize { match self { Self::Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoGreTap { fn parse(buf: &NlaBuffer<&'a T>) -> Result { #[allow(clippy::match_single_binding)] Ok(match buf.kind() { kind => Self::Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind} for gretap"))?, ), }) } } netlink-packet-route-0.21.0/src/link/link_info/gre_tap6.rs000064400000000000000000000020561046102023000215250ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, DecodeError, Parseable, }; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoGreTap6 { Other(DefaultNla), } impl Nla for InfoGreTap6 { fn value_len(&self) -> usize { match self { Self::Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoGreTap6 { fn parse(buf: &NlaBuffer<&'a T>) -> Result { #[allow(clippy::match_single_binding)] Ok(match buf.kind() { kind => Self::Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind} for gretap6"))?, ), }) } } netlink-packet-route-0.21.0/src/link/link_info/gtp.rs000064400000000000000000000020361046102023000206060ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, DecodeError, Parseable, }; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoGtp { Other(DefaultNla), } impl Nla for InfoGtp { fn value_len(&self) -> usize { match self { Self::Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoGtp { fn parse(buf: &NlaBuffer<&'a T>) -> Result { #[allow(clippy::match_single_binding)] Ok(match buf.kind() { kind => Self::Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind} for gtp"))?, ), }) } } netlink-packet-route-0.21.0/src/link/link_info/hsr.rs000064400000000000000000000110761046102023000206140ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::{parse_mac, parse_u16, parse_u32, parse_u8}, traits::Parseable, DecodeError, }; // Kernel constant name is IFLA_HSR_SLAVE1 const IFLA_HSR_PORT1: u16 = 1; // Kernel constant name is IFLA_HSR_SLAVE2 const IFLA_HSR_PORT2: u16 = 2; const IFLA_HSR_MULTICAST_SPEC: u16 = 3; const IFLA_HSR_SUPERVISION_ADDR: u16 = 4; const IFLA_HSR_SEQ_NR: u16 = 5; const IFLA_HSR_VERSION: u16 = 6; const IFLA_HSR_PROTOCOL: u16 = 7; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoHsr { Port1(u32), Port2(u32), MulticastSpec(u8), SupervisionAddr([u8; 6]), Version(u8), SeqNr(u16), Protocol(HsrProtocol), Other(DefaultNla), } impl Nla for InfoHsr { fn value_len(&self) -> usize { use self::InfoHsr::*; match self { SupervisionAddr(_) => 6, Port1(_) | Port2(_) => 4, SeqNr(_) => 2, MulticastSpec(_) | Version(_) | Protocol(_) => 1, Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::InfoHsr::*; match self { Port1(value) | Port2(value) => { NativeEndian::write_u32(buffer, *value) } MulticastSpec(value) | Version(value) => buffer[0] = *value, SeqNr(value) => NativeEndian::write_u16(buffer, *value), Protocol(value) => buffer[0] = (*value).into(), SupervisionAddr(ref value) => buffer.copy_from_slice(&value[..]), Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::InfoHsr::*; match self { Port1(_) => IFLA_HSR_PORT1, Port2(_) => IFLA_HSR_PORT2, MulticastSpec(_) => IFLA_HSR_MULTICAST_SPEC, SupervisionAddr(_) => IFLA_HSR_SUPERVISION_ADDR, SeqNr(_) => IFLA_HSR_SEQ_NR, Version(_) => IFLA_HSR_VERSION, Protocol(_) => IFLA_HSR_PROTOCOL, Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoHsr { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoHsr::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_HSR_PORT1 => Port1( parse_u32(payload).context("invalid IFLA_HSR_PORT1 value")?, ), IFLA_HSR_PORT2 => Port2( parse_u32(payload).context("invalid IFLA_HSR_PORT2 value")?, ), IFLA_HSR_MULTICAST_SPEC => MulticastSpec( parse_u8(payload) .context("invalid IFLA_HSR_MULTICAST_SPEC value")?, ), IFLA_HSR_SUPERVISION_ADDR => SupervisionAddr( parse_mac(payload) .context("invalid IFLA_HSR_SUPERVISION_ADDR value")?, ), IFLA_HSR_SEQ_NR => SeqNr( parse_u16(payload).context("invalid IFLA_HSR_SEQ_NR value")?, ), IFLA_HSR_VERSION => Version( parse_u8(payload).context("invalid IFLA_HSR_VERSION value")?, ), IFLA_HSR_PROTOCOL => Protocol( parse_u8(payload) .context("invalid IFLA_HSR_PROTOCOL value")? .into(), ), kind => Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } const HSR_PROTOCOL_HSR: u8 = 0; const HSR_PROTOCOL_PRP: u8 = 1; #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] #[repr(u8)] pub enum HsrProtocol { Hsr = HSR_PROTOCOL_HSR, Prp = HSR_PROTOCOL_PRP, Other(u8), } impl From for HsrProtocol { fn from(d: u8) -> Self { match d { HSR_PROTOCOL_HSR => Self::Hsr, HSR_PROTOCOL_PRP => Self::Prp, _ => Self::Other(d), } } } impl From for u8 { fn from(d: HsrProtocol) -> Self { match d { HsrProtocol::Hsr => HSR_PROTOCOL_HSR, HsrProtocol::Prp => HSR_PROTOCOL_PRP, HsrProtocol::Other(value) => value, } } } impl std::fmt::Display for HsrProtocol { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Hsr => write!(f, "hsr"), Self::Prp => write!(f, "prp"), Self::Other(d) => write!(f, "{}", d), } } } netlink-packet-route-0.21.0/src/link/link_info/info_data.rs000064400000000000000000000344721046102023000217510ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{Nla, NlaBuffer, NlasIterator}, DecodeError, Emitable, Parseable, }; use super::super::{ InfoBond, InfoBridge, InfoGeneve, InfoGreTap, InfoGreTap6, InfoGreTun, InfoGreTun6, InfoGtp, InfoHsr, InfoIpVlan, InfoIpVtap, InfoIpoib, InfoKind, InfoMacSec, InfoMacVlan, InfoMacVtap, InfoSitTun, InfoTun, InfoVeth, InfoVlan, InfoVrf, InfoVti, InfoVxlan, InfoXfrm, }; const IFLA_INFO_DATA: u16 = 2; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoData { Bridge(Vec), Tun(Vec), Vlan(Vec), Veth(InfoVeth), Vxlan(Vec), Bond(Vec), IpVlan(Vec), IpVtap(Vec), MacVlan(Vec), MacVtap(Vec), GreTap(Vec), GreTap6(Vec), SitTun(Vec), GreTun(Vec), GreTun6(Vec), Vti(Vec), Vrf(Vec), Gtp(Vec), Ipoib(Vec), Xfrm(Vec), MacSec(Vec), Hsr(Vec), Geneve(Vec), Other(Vec), } impl Nla for InfoData { fn value_len(&self) -> usize { match self { Self::Bond(nlas) => nlas.as_slice().buffer_len(), Self::Bridge(nlas) => nlas.as_slice().buffer_len(), Self::Vlan(nlas) => nlas.as_slice().buffer_len(), Self::Veth(msg) => msg.buffer_len(), Self::IpVlan(nlas) => nlas.as_slice().buffer_len(), Self::IpVtap(nlas) => nlas.as_slice().buffer_len(), Self::Ipoib(nlas) => nlas.as_slice().buffer_len(), Self::MacVlan(nlas) => nlas.as_slice().buffer_len(), Self::MacVtap(nlas) => nlas.as_slice().buffer_len(), Self::Vrf(nlas) => nlas.as_slice().buffer_len(), Self::Vxlan(nlas) => nlas.as_slice().buffer_len(), Self::Xfrm(nlas) => nlas.as_slice().buffer_len(), Self::MacSec(nlas) => nlas.as_slice().buffer_len(), Self::Hsr(nlas) => nlas.as_slice().buffer_len(), Self::Tun(nlas) => nlas.as_slice().buffer_len(), Self::GreTap(nlas) => nlas.as_slice().buffer_len(), Self::GreTap6(nlas) => nlas.as_slice().buffer_len(), Self::SitTun(nlas) => nlas.as_slice().buffer_len(), Self::GreTun(nlas) => nlas.as_slice().buffer_len(), Self::GreTun6(nlas) => nlas.as_slice().buffer_len(), Self::Vti(nlas) => nlas.as_slice().buffer_len(), Self::Gtp(nlas) => nlas.as_slice().buffer_len(), Self::Geneve(nlas) => nlas.as_slice().buffer_len(), Self::Other(v) => v.len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Bond(nlas) => nlas.as_slice().emit(buffer), Self::Bridge(nlas) => nlas.as_slice().emit(buffer), Self::Vlan(nlas) => nlas.as_slice().emit(buffer), Self::Veth(msg) => msg.emit(buffer), Self::IpVlan(nlas) => nlas.as_slice().emit(buffer), Self::IpVtap(nlas) => nlas.as_slice().emit(buffer), Self::Ipoib(nlas) => nlas.as_slice().emit(buffer), Self::MacVlan(nlas) => nlas.as_slice().emit(buffer), Self::MacVtap(nlas) => nlas.as_slice().emit(buffer), Self::Vrf(nlas) => nlas.as_slice().emit(buffer), Self::Vxlan(nlas) => nlas.as_slice().emit(buffer), Self::Xfrm(nlas) => nlas.as_slice().emit(buffer), Self::MacSec(nlas) => nlas.as_slice().emit(buffer), Self::Hsr(nlas) => nlas.as_slice().emit(buffer), Self::Tun(nlas) => nlas.as_slice().emit(buffer), Self::GreTap(nlas) => nlas.as_slice().emit(buffer), Self::GreTap6(nlas) => nlas.as_slice().emit(buffer), Self::SitTun(nlas) => nlas.as_slice().emit(buffer), Self::GreTun(nlas) => nlas.as_slice().emit(buffer), Self::GreTun6(nlas) => nlas.as_slice().emit(buffer), Self::Vti(nlas) => nlas.as_slice().emit(buffer), Self::Gtp(nlas) => nlas.as_slice().emit(buffer), Self::Geneve(nlas) => nlas.as_slice().emit(buffer), Self::Other(v) => buffer.copy_from_slice(v.as_slice()), } } fn kind(&self) -> u16 { IFLA_INFO_DATA } } impl InfoData { pub(crate) fn parse_with_param( payload: &[u8], kind: &InfoKind, ) -> Result { Ok(match kind { InfoKind::Bridge => { let mut v = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; let parsed = InfoBridge::parse(nla)?; v.push(parsed); } InfoData::Bridge(v) } InfoKind::Vlan => { let mut v = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; let parsed = InfoVlan::parse(nla)?; v.push(parsed); } InfoData::Vlan(v) } InfoKind::Tun => { let mut nlas = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; let parsed = InfoTun::parse(nla)?; nlas.push(parsed); } InfoData::Tun(nlas) } InfoKind::Veth => { let nla_buf = NlaBuffer::new_checked(&payload).context( format!("invalid IFLA_INFO_DATA for {kind} {payload:?}"), )?; let parsed = InfoVeth::parse(&nla_buf)?; InfoData::Veth(parsed) } InfoKind::Vxlan => { let mut v = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; let parsed = InfoVxlan::parse(nla)?; v.push(parsed); } InfoData::Vxlan(v) } InfoKind::Bond => { let mut v = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; let parsed = InfoBond::parse(nla)?; v.push(parsed); } InfoData::Bond(v) } InfoKind::IpVlan => { let mut v = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; let parsed = InfoIpVlan::parse(nla)?; v.push(parsed); } InfoData::IpVlan(v) } InfoKind::IpVtap => { let mut v = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; let parsed = InfoIpVtap::parse(nla)?; v.push(parsed); } InfoData::IpVtap(v) } InfoKind::MacVlan => { let mut v = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; let parsed = InfoMacVlan::parse(nla)?; v.push(parsed); } InfoData::MacVlan(v) } InfoKind::MacVtap => { let mut v = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; let parsed = InfoMacVtap::parse(nla)?; v.push(parsed); } InfoData::MacVtap(v) } InfoKind::GreTap => { let mut v = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; let parsed = InfoGreTap::parse(nla)?; v.push(parsed); } InfoData::GreTap(v) } InfoKind::GreTap6 => { let mut v = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; let parsed = InfoGreTap6::parse(nla)?; v.push(parsed); } InfoData::GreTap6(v) } InfoKind::SitTun => { let mut v = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; let parsed = InfoSitTun::parse(nla)?; v.push(parsed); } InfoData::SitTun(v) } InfoKind::GreTun => { let mut v = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; let parsed = InfoGreTun::parse(nla)?; v.push(parsed); } InfoData::GreTun(v) } InfoKind::GreTun6 => { let mut v = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; let parsed = InfoGreTun6::parse(nla)?; v.push(parsed); } InfoData::GreTun6(v) } InfoKind::Vti => { let mut v = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; let parsed = InfoVti::parse(nla)?; v.push(parsed); } InfoData::Vti(v) } InfoKind::Vrf => { let mut v = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; let parsed = InfoVrf::parse(nla)?; v.push(parsed); } InfoData::Vrf(v) } InfoKind::Gtp => { let mut v = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; let parsed = InfoGtp::parse(nla)?; v.push(parsed); } InfoData::Gtp(v) } InfoKind::Ipoib => { let mut v = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; let parsed = InfoIpoib::parse(nla)?; v.push(parsed); } InfoData::Ipoib(v) } InfoKind::Xfrm => { let mut v = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; let parsed = InfoXfrm::parse(nla)?; v.push(parsed); } InfoData::Xfrm(v) } InfoKind::MacSec => { let mut v = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; let parsed = InfoMacSec::parse(nla)?; v.push(parsed); } InfoData::MacSec(v) } InfoKind::Hsr => { let mut v = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; let parsed = InfoHsr::parse(nla)?; v.push(parsed); } InfoData::Hsr(v) } InfoKind::Geneve => { let mut v = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; let parsed = InfoGeneve::parse(nla)?; v.push(parsed); } InfoData::Geneve(v) } _ => InfoData::Other(payload.to_vec()), }) } } netlink-packet-route-0.21.0/src/link/link_info/info_port.rs000064400000000000000000000104321046102023000220120ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{Nla, NlaBuffer, NlasIterator}, parsers::parse_string, DecodeError, Emitable, Parseable, }; use super::{ super::{InfoBondPort, InfoBridgePort}, InfoVrf, }; const BOND: &str = "bond"; const BRIDGE: &str = "bridge"; const VRF: &str = "vrf"; const IFLA_INFO_PORT_KIND: u16 = 4; const IFLA_INFO_PORT_DATA: u16 = 5; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoPortKind { Bond, Bridge, Vrf, Other(String), } impl std::fmt::Display for InfoPortKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{}", match self { Self::Bond => BOND, Self::Bridge => BRIDGE, Self::Vrf => VRF, Self::Other(s) => s.as_str(), } ) } } impl Nla for InfoPortKind { fn value_len(&self) -> usize { let len = match self { Self::Bond => BOND.len(), Self::Bridge => BRIDGE.len(), Self::Vrf => VRF.len(), Self::Other(s) => s.len(), }; len + 1 } fn emit_value(&self, buffer: &mut [u8]) { let s = match self { Self::Bond => BOND, Self::Bridge => BRIDGE, Self::Vrf => VRF, Self::Other(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 { 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 => Self::Bond, BRIDGE => Self::Bridge, VRF => Self::Vrf, _ => Self::Other(s), }) } } pub type InfoVrfPort = InfoVrf; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoPortData { BondPort(Vec), BridgePort(Vec), VrfPort(Vec), Other(Vec), } impl Nla for InfoPortData { fn value_len(&self) -> usize { match self { Self::BondPort(nlas) => nlas.as_slice().buffer_len(), Self::BridgePort(nlas) => nlas.as_slice().buffer_len(), Self::VrfPort(nlas) => nlas.as_slice().buffer_len(), Self::Other(bytes) => bytes.len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::BondPort(nlas) => nlas.as_slice().emit(buffer), Self::BridgePort(nlas) => nlas.as_slice().emit(buffer), Self::VrfPort(nlas) => nlas.as_slice().emit(buffer), Self::Other(bytes) => buffer.copy_from_slice(bytes), } } fn kind(&self) -> u16 { IFLA_INFO_PORT_DATA } } impl InfoPortData { pub(crate) fn parse_with_param( payload: &[u8], kind: InfoPortKind, ) -> Result { let port_data = match kind { InfoPortKind::Bond => NlasIterator::new(payload) .map(|nla| nla.and_then(|nla| InfoBondPort::parse(&nla))) .collect::, _>>() .map(InfoPortData::BondPort), InfoPortKind::Bridge => NlasIterator::new(payload) .map(|nla| nla.and_then(|nla| InfoBridgePort::parse(&nla))) .collect::, _>>() .map(InfoPortData::BridgePort), InfoPortKind::Vrf => NlasIterator::new(payload) .map(|nla| nla.and_then(|nla| InfoVrfPort::parse(&nla))) .collect::, _>>() .map(InfoPortData::VrfPort), InfoPortKind::Other(_) => Ok(InfoPortData::Other(payload.to_vec())), }; Ok(port_data.context(format!( "failed to parse IFLA_INFO_PORT_DATA (IFLA_INFO_PORT_KIND is '{kind}')" ))?) } } netlink-packet-route-0.21.0/src/link/link_info/infos.rs000064400000000000000000000252721046102023000211410ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, parsers::parse_string, DecodeError, Emitable, Parseable, ParseableParametrized, }; use super::super::{InfoData, InfoPortData, InfoPortKind, LinkXstats}; const IFLA_INFO_KIND: u16 = 1; const IFLA_INFO_DATA: u16 = 2; const IFLA_INFO_XSTATS: u16 = 3; const IFLA_INFO_PORT_KIND: u16 = 4; const IFLA_INFO_PORT_DATA: u16 = 5; 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 IPVTAP: &str = "ipvtap"; 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"; const MACSEC: &str = "macsec"; const HSR: &str = "hsr"; const GENEVE: &str = "geneve"; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum LinkInfo { Xstats(LinkXstats), Kind(InfoKind), Data(InfoData), PortKind(InfoPortKind), PortData(InfoPortData), Other(DefaultNla), } impl Nla for LinkInfo { fn value_len(&self) -> usize { match self { Self::Xstats(v) => v.buffer_len(), Self::Kind(nla) => nla.value_len(), Self::Data(nla) => nla.value_len(), Self::PortKind(nla) => nla.value_len(), Self::PortData(nla) => nla.value_len(), Self::Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Xstats(v) => v.emit(buffer), Self::Kind(nla) => nla.emit_value(buffer), Self::Data(nla) => nla.emit_value(buffer), Self::PortKind(nla) => nla.emit_value(buffer), Self::PortData(nla) => nla.emit_value(buffer), Self::Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Xstats(_) => IFLA_INFO_XSTATS, Self::PortKind(_) => IFLA_INFO_PORT_KIND, Self::PortData(_) => IFLA_INFO_PORT_DATA, Self::Kind(_) => IFLA_INFO_KIND, Self::Data(_) => IFLA_INFO_DATA, Self::Other(nla) => nla.kind(), } } } pub(crate) struct VecLinkInfo(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 VecLinkInfo 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 VecLinkInfo { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = Vec::new(); let mut link_info_kind: Option = None; let mut link_info_port_kind: Option = None; for nla in NlasIterator::new(buf.into_inner()) { let nla = nla?; match nla.kind() { IFLA_INFO_XSTATS => { if let Some(link_info_kind) = &link_info_kind { nlas.push(LinkInfo::Xstats( LinkXstats::parse_with_param(&nla, link_info_kind)?, )); } else { return Err("IFLA_INFO_XSTATS is not \ preceded by an IFLA_INFO_KIND" .into()); } } IFLA_INFO_PORT_KIND => { let parsed = InfoPortKind::parse(&nla)?; nlas.push(LinkInfo::PortKind(parsed.clone())); link_info_port_kind = Some(parsed); } IFLA_INFO_PORT_DATA => { if let Some(link_info_port_kind) = link_info_port_kind { nlas.push(LinkInfo::PortData( InfoPortData::parse_with_param( nla.value(), link_info_port_kind, )?, )); } 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)?; nlas.push(LinkInfo::Kind(parsed.clone())); link_info_kind = Some(parsed); } IFLA_INFO_DATA => { if let Some(link_info_kind) = &link_info_kind { nlas.push(LinkInfo::Data(InfoData::parse_with_param( nla.value(), link_info_kind, )?)); } else { return Err("IFLA_INFO_DATA is not preceded by an \ IFLA_INFO_KIND" .into()); } } _kind => nlas.push(LinkInfo::Other( DefaultNla::parse(&nla).context(format!( "Unknown NLA type for IFLA_INFO_DATA {:?}", nla ))?, )), } } Ok(Self(nlas)) } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoKind { Dummy, Ifb, Bridge, Tun, Nlmon, Vlan, Veth, Vxlan, Bond, IpVlan, IpVtap, MacVlan, MacVtap, GreTap, GreTap6, IpTun, SitTun, GreTun, GreTun6, Vti, Vrf, Gtp, Ipoib, Wireguard, Xfrm, MacSec, Hsr, Geneve, Other(String), } impl std::fmt::Display for InfoKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{}", match self { Self::Dummy => DUMMY, Self::Ifb => IFB, Self::Bridge => BRIDGE, Self::Tun => TUN, Self::Nlmon => NLMON, Self::Vlan => VLAN, Self::Veth => VETH, Self::Vxlan => VXLAN, Self::Bond => BOND, Self::IpVlan => IPVLAN, Self::IpVtap => IPVTAP, Self::MacVlan => MACVLAN, Self::MacVtap => MACVTAP, Self::GreTap => GRETAP, Self::GreTap6 => IP6GRETAP, Self::IpTun => IPIP, Self::SitTun => SIT, Self::GreTun => GRE, Self::GreTun6 => IP6GRE, Self::Vti => VTI, Self::Vrf => VRF, Self::Gtp => GTP, Self::Ipoib => IPOIB, Self::Wireguard => WIREGUARD, Self::Xfrm => XFRM, Self::MacSec => MACSEC, Self::Hsr => HSR, Self::Geneve => GENEVE, Self::Other(s) => s.as_str(), } ) } } impl Nla for InfoKind { fn value_len(&self) -> usize { let len = match self { Self::Dummy => DUMMY.len(), Self::Ifb => IFB.len(), Self::Bridge => BRIDGE.len(), Self::Tun => TUN.len(), Self::Nlmon => NLMON.len(), Self::Vlan => VLAN.len(), Self::Veth => VETH.len(), Self::Vxlan => VXLAN.len(), Self::Bond => BOND.len(), Self::IpVlan => IPVLAN.len(), Self::IpVtap => IPVTAP.len(), Self::MacVlan => MACVLAN.len(), Self::MacVtap => MACVTAP.len(), Self::GreTap => GRETAP.len(), Self::GreTap6 => IP6GRETAP.len(), Self::IpTun => IPIP.len(), Self::SitTun => SIT.len(), Self::GreTun => GRE.len(), Self::GreTun6 => IP6GRE.len(), Self::Vti => VTI.len(), Self::Vrf => VRF.len(), Self::Gtp => GTP.len(), Self::Ipoib => IPOIB.len(), Self::Wireguard => WIREGUARD.len(), Self::Xfrm => XFRM.len(), Self::MacSec => MACSEC.len(), Self::Hsr => HSR.len(), Self::Geneve => GENEVE.len(), Self::Other(s) => s.len(), }; len + 1 } fn emit_value(&self, buffer: &mut [u8]) { let kind = self.to_string(); let s = kind.as_str(); buffer[..s.len()].copy_from_slice(s.to_string().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 { 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 => Self::Dummy, IFB => Self::Ifb, BRIDGE => Self::Bridge, TUN => Self::Tun, NLMON => Self::Nlmon, VLAN => Self::Vlan, VETH => Self::Veth, VXLAN => Self::Vxlan, BOND => Self::Bond, IPVLAN => Self::IpVlan, IPVTAP => Self::IpVtap, MACVLAN => Self::MacVlan, MACVTAP => Self::MacVtap, GRETAP => Self::GreTap, IP6GRETAP => Self::GreTap6, IPIP => Self::IpTun, SIT => Self::SitTun, GRE => Self::GreTun, IP6GRE => Self::GreTun6, VTI => Self::Vti, VRF => Self::Vrf, GTP => Self::Gtp, IPOIB => Self::Ipoib, WIREGUARD => Self::Wireguard, MACSEC => Self::MacSec, XFRM => Self::Xfrm, HSR => Self::Hsr, GENEVE => Self::Geneve, _ => Self::Other(s), }) } } netlink-packet-route-0.21.0/src/link/link_info/ipoib.rs000064400000000000000000000041671046102023000211250ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::parse_u16, traits::Parseable, DecodeError, }; const IFLA_IPOIB_PKEY: u16 = 1; const IFLA_IPOIB_MODE: u16 = 2; const IFLA_IPOIB_UMCAST: u16 = 3; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoIpoib { Pkey(u16), Mode(u16), UmCast(u16), Other(DefaultNla), } impl Nla for InfoIpoib { fn value_len(&self) -> usize { use self::InfoIpoib::*; match self { Pkey(_) | Mode(_) | UmCast(_) => 2, Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::InfoIpoib::*; match self { 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 { 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_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} for IFLA_INFO_DATA(ipoib)" ))?), }) } } netlink-packet-route-0.21.0/src/link/link_info/ipvlan.rs000064400000000000000000000115741046102023000213140ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::parse_u16, traits::Parseable, DecodeError, }; const IFLA_IPVLAN_MODE: u16 = 1; const IFLA_IPVLAN_FLAGS: u16 = 2; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoIpVlan { Mode(IpVlanMode), Flags(IpVlanFlags), Other(DefaultNla), } impl Nla for InfoIpVlan { fn value_len(&self) -> usize { use self::InfoIpVlan::*; match self { Mode(_) | Flags(_) => 2, Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::InfoIpVlan::*; match self { Mode(value) => NativeEndian::write_u16(buffer, (*value).into()), Flags(f) => NativeEndian::write_u16(buffer, f.bits()), Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::InfoIpVlan::*; match self { 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_MODE => Mode( parse_u16(payload) .context("invalid IFLA_IPVLAN_MODE value")? .into(), ), IFLA_IPVLAN_FLAGS => Self::Flags(IpVlanFlags::from_bits_retain( parse_u16(payload) .context("failed to parse IFLA_IPVLAN_FLAGS")?, )), kind => Other(DefaultNla::parse(buf).context(format!( "unknown NLA type {kind} for IFLA_INFO_DATA(ipvlan)" ))?), }) } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoIpVtap { Mode(IpVtapMode), Flags(IpVtapFlags), Other(DefaultNla), } impl Nla for InfoIpVtap { fn value_len(&self) -> usize { use self::InfoIpVtap::*; match self { Mode(_) | Flags(_) => 2, Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::InfoIpVtap::*; match self { Mode(value) => NativeEndian::write_u16(buffer, (*value).into()), Flags(f) => NativeEndian::write_u16(buffer, f.bits()), Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::InfoIpVtap::*; match self { Mode(_) => IFLA_IPVLAN_MODE, Flags(_) => IFLA_IPVLAN_FLAGS, Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoIpVtap { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoIpVtap::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_IPVLAN_MODE => Mode( parse_u16(payload) .context("invalid IFLA_IPVLAN_MODE value")? .into(), ), IFLA_IPVLAN_FLAGS => Self::Flags(IpVtapFlags::from_bits_retain( parse_u16(payload) .context("failed to parse IFLA_IPVLAN_FLAGS")?, )), kind => Other(DefaultNla::parse(buf).context(format!( "unknown NLA type {kind} for IFLA_INFO_DATA(ipvlan)" ))?), }) } } const IPVLAN_MODE_L2: u16 = 0; const IPVLAN_MODE_L3: u16 = 1; const IPVLAN_MODE_L3S: u16 = 2; #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub enum IpVlanMode { L2, L3, L3S, Other(u16), } pub type IpVtapMode = IpVlanMode; impl From for IpVlanMode { fn from(d: u16) -> Self { match d { IPVLAN_MODE_L2 => Self::L2, IPVLAN_MODE_L3 => Self::L3, IPVLAN_MODE_L3S => Self::L3S, _ => { log::warn!("Unknown IP VLAN mode {}", d); Self::Other(d) } } } } impl From for u16 { fn from(v: IpVlanMode) -> u16 { match v { IpVlanMode::L2 => IPVLAN_MODE_L2, IpVlanMode::L3 => IPVLAN_MODE_L3, IpVlanMode::L3S => IPVLAN_MODE_L3S, IpVlanMode::Other(d) => d, } } } const IPVLAN_F_PRIVATE: u16 = 0x01; const IPVLAN_F_VEPA: u16 = 0x02; bitflags! { #[non_exhaustive] #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct IpVlanFlags: u16 { const Private = IPVLAN_F_PRIVATE; const Vepa = IPVLAN_F_VEPA; const _ = !0; } } impl Default for IpVlanFlags { fn default() -> Self { Self::empty() } } pub type IpVtapFlags = IpVlanFlags; netlink-packet-route-0.21.0/src/link/link_info/mac_vlan.rs000064400000000000000000000256271046102023000216070ustar 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_u16, parse_u32}, traits::{Emitable, Parseable}, DecodeError, }; const IFLA_MACVLAN_MODE: u16 = 1; const IFLA_MACVLAN_FLAGS: u16 = 2; const IFLA_MACVLAN_MACADDR_MODE: u16 = 3; const IFLA_MACVLAN_MACADDR: u16 = 4; const IFLA_MACVLAN_MACADDR_DATA: u16 = 5; const IFLA_MACVLAN_MACADDR_COUNT: u16 = 6; const IFLA_MACVLAN_BC_QUEUE_LEN: u16 = 7; const IFLA_MACVLAN_BC_QUEUE_LEN_USED: u16 = 8; const IFLA_MACVLAN_BC_CUTOFF: u16 = 9; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoMacVlan { Mode(MacVlanMode), Flags(u16), MacAddrMode(u32), MacAddr([u8; 6]), /// A list of InfoMacVlan::MacAddr MacAddrData(Vec), MacAddrCount(u32), BcQueueLen(u32), BcQueueLenUsed(u32), BcCutoff(i32), Other(DefaultNla), } impl Nla for InfoMacVlan { fn value_len(&self) -> usize { match self { Self::Mode(_) => 4, Self::Flags(_) => 2, Self::MacAddrMode(_) => 4, Self::MacAddr(_) => 6, Self::MacAddrData(ref nlas) => nlas.as_slice().buffer_len(), Self::MacAddrCount(_) => 4, Self::BcQueueLen(_) => 4, Self::BcQueueLenUsed(_) => 4, Self::BcCutoff(_) => 4, Self::Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Mode(value) => { NativeEndian::write_u32(buffer, (*value).into()) } Self::Flags(value) => NativeEndian::write_u16(buffer, *value), Self::MacAddrMode(value) => NativeEndian::write_u32(buffer, *value), Self::MacAddr(bytes) => buffer.copy_from_slice(bytes), Self::MacAddrData(ref nlas) => nlas.as_slice().emit(buffer), Self::MacAddrCount(value) => { NativeEndian::write_u32(buffer, *value) } Self::BcQueueLen(value) => NativeEndian::write_u32(buffer, *value), Self::BcQueueLenUsed(value) => { NativeEndian::write_u32(buffer, *value) } Self::BcCutoff(value) => NativeEndian::write_i32(buffer, *value), Self::Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::InfoMacVlan::*; match self { 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_MODE => Mode( parse_u32(payload) .context("invalid IFLA_MACVLAN_MODE value")? .into(), ), 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} for IFLA_INFO_DATA(mac_vlan)" ))?), }) } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoMacVtap { Mode(MacVtapMode), 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 { 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 { Mode(value) => NativeEndian::write_u32(buffer, (*value).into()), 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 { 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_MODE => Mode( parse_u32(payload) .context("invalid IFLA_MACVLAN_MODE value")? .into(), ), 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} for IFLA_INFO_DATA(mac_vtap)" ))?), }) } } const MACVLAN_MODE_PRIVATE: u32 = 1; const MACVLAN_MODE_VEPA: u32 = 2; const MACVLAN_MODE_BRIDGE: u32 = 4; const MACVLAN_MODE_PASSTHRU: u32 = 8; const MACVLAN_MODE_SOURCE: u32 = 16; #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub enum MacVlanMode { Private, Vepa, Bridge, Passthrough, Source, Other(u32), } pub type MacVtapMode = MacVlanMode; impl From for MacVlanMode { fn from(d: u32) -> Self { match d { MACVLAN_MODE_PRIVATE => Self::Private, MACVLAN_MODE_VEPA => Self::Vepa, MACVLAN_MODE_BRIDGE => Self::Bridge, MACVLAN_MODE_PASSTHRU => Self::Passthrough, MACVLAN_MODE_SOURCE => Self::Source, _ => { log::warn!("Unknown MAC VLAN mode {}", d); Self::Other(d) } } } } impl From for u32 { fn from(v: MacVlanMode) -> u32 { match v { MacVlanMode::Private => MACVLAN_MODE_PRIVATE, MacVlanMode::Vepa => MACVLAN_MODE_VEPA, MacVlanMode::Bridge => MACVLAN_MODE_BRIDGE, MacVlanMode::Passthrough => MACVLAN_MODE_PASSTHRU, MacVlanMode::Source => MACVLAN_MODE_SOURCE, MacVlanMode::Other(d) => d, } } } netlink-packet-route-0.21.0/src/link/link_info/macsec.rs000064400000000000000000000220531046102023000212500ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::{parse_u16, parse_u32, parse_u64, parse_u8}, traits::Parseable, DecodeError, }; const IFLA_MACSEC_SCI: u16 = 1; const IFLA_MACSEC_PORT: u16 = 2; const IFLA_MACSEC_ICV_LEN: u16 = 3; const IFLA_MACSEC_CIPHER_SUITE: u16 = 4; const IFLA_MACSEC_WINDOW: u16 = 5; const IFLA_MACSEC_ENCODING_SA: u16 = 6; const IFLA_MACSEC_ENCRYPT: u16 = 7; const IFLA_MACSEC_PROTECT: u16 = 8; const IFLA_MACSEC_INC_SCI: u16 = 9; const IFLA_MACSEC_ES: u16 = 10; const IFLA_MACSEC_SCB: u16 = 11; const IFLA_MACSEC_REPLAY_PROTECT: u16 = 12; const IFLA_MACSEC_VALIDATION: u16 = 13; // const IFLA_MACSEC_PAD: u16 = 14; const IFLA_MACSEC_OFFLOAD: u16 = 15; const MACSEC_VALIDATE_DISABLED: u8 = 0; const MACSEC_VALIDATE_CHECK: u8 = 1; const MACSEC_VALIDATE_STRICT: u8 = 2; const MACSEC_OFFLOAD_OFF: u8 = 0; const MACSEC_OFFLOAD_PHY: u8 = 1; const MACSEC_OFFLOAD_MAC: u8 = 2; const MACSEC_CIPHER_ID_GCM_AES_128: u64 = 0x0080C20001000001; const MACSEC_CIPHER_ID_GCM_AES_256: u64 = 0x0080C20001000002; const MACSEC_CIPHER_ID_GCM_AES_XPN_128: u64 = 0x0080C20001000003; const MACSEC_CIPHER_ID_GCM_AES_XPN_256: u64 = 0x0080C20001000004; const MACSEC_DEFAULT_CIPHER_ID: u64 = 0x0080020001000001; #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub enum MacSecCipherId { #[deprecated] DefaultGcmAes128, GcmAes128, GcmAes256, GcmAesXpn128, GcmAesXpn256, Other(u64), } impl From for MacSecCipherId { fn from(d: u64) -> Self { match d { #[allow(deprecated)] MACSEC_DEFAULT_CIPHER_ID => Self::DefaultGcmAes128, MACSEC_CIPHER_ID_GCM_AES_128 => Self::GcmAes128, MACSEC_CIPHER_ID_GCM_AES_256 => Self::GcmAes256, MACSEC_CIPHER_ID_GCM_AES_XPN_128 => Self::GcmAesXpn128, MACSEC_CIPHER_ID_GCM_AES_XPN_256 => Self::GcmAesXpn256, _ => Self::Other(d), } } } impl From for u64 { fn from(d: MacSecCipherId) -> Self { match d { #[allow(deprecated)] MacSecCipherId::DefaultGcmAes128 => MACSEC_DEFAULT_CIPHER_ID, MacSecCipherId::GcmAes128 => MACSEC_CIPHER_ID_GCM_AES_128, MacSecCipherId::GcmAes256 => MACSEC_CIPHER_ID_GCM_AES_256, MacSecCipherId::GcmAesXpn128 => MACSEC_CIPHER_ID_GCM_AES_XPN_128, MacSecCipherId::GcmAesXpn256 => MACSEC_CIPHER_ID_GCM_AES_XPN_256, MacSecCipherId::Other(value) => value, } } } #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub enum MacSecValidate { Disabled, Check, Strict, Other(u8), } impl From for MacSecValidate { fn from(d: u8) -> Self { match d { MACSEC_VALIDATE_DISABLED => Self::Disabled, MACSEC_VALIDATE_CHECK => Self::Check, MACSEC_VALIDATE_STRICT => Self::Strict, _ => Self::Other(d), } } } impl From for u8 { fn from(d: MacSecValidate) -> Self { match d { MacSecValidate::Disabled => MACSEC_VALIDATE_DISABLED, MacSecValidate::Check => MACSEC_VALIDATE_CHECK, MacSecValidate::Strict => MACSEC_VALIDATE_STRICT, MacSecValidate::Other(value) => value, } } } #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub enum MacSecOffload { Off, Phy, Mac, Other(u8), } impl From for MacSecOffload { fn from(d: u8) -> Self { match d { MACSEC_OFFLOAD_OFF => Self::Off, MACSEC_OFFLOAD_PHY => Self::Phy, MACSEC_OFFLOAD_MAC => Self::Mac, _ => Self::Other(d), } } } impl From for u8 { fn from(d: MacSecOffload) -> Self { match d { MacSecOffload::Off => MACSEC_OFFLOAD_OFF, MacSecOffload::Phy => MACSEC_OFFLOAD_PHY, MacSecOffload::Mac => MACSEC_OFFLOAD_MAC, MacSecOffload::Other(value) => value, } } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoMacSec { Sci(u64), Port(u16), IcvLen(u8), CipherSuite(MacSecCipherId), Window(u32), EncodingSa(u8), Encrypt(u8), Protect(u8), IncSci(u8), Es(u8), Scb(u8), ReplayProtect(u8), Validation(MacSecValidate), Offload(MacSecOffload), Other(DefaultNla), } impl Nla for InfoMacSec { fn value_len(&self) -> usize { use self::InfoMacSec::*; match self { Sci(_) | CipherSuite(_) => 8, Window(_) => 4, Port(_) => 2, IcvLen(_) | EncodingSa(_) | Encrypt(_) | Protect(_) | IncSci(_) | Es(_) | Scb(_) | ReplayProtect(_) | Validation(_) | Offload(_) => 1, Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::InfoMacSec::*; match self { Sci(value) => NativeEndian::write_u64(buffer, *value), CipherSuite(value) => { NativeEndian::write_u64(buffer, (*value).into()) } Window(value) => NativeEndian::write_u32(buffer, *value), Port(value) => NativeEndian::write_u16(buffer, *value), IcvLen(value) | EncodingSa(value) | Encrypt(value) | Protect(value) | IncSci(value) | Es(value) | Scb(value) | ReplayProtect(value) => buffer[0] = *value, Offload(value) => buffer[0] = (*value).into(), Validation(value) => buffer[0] = (*value).into(), Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::InfoMacSec::*; match self { Sci(_) => IFLA_MACSEC_SCI, Port(_) => IFLA_MACSEC_PORT, IcvLen(_) => IFLA_MACSEC_ICV_LEN, CipherSuite(_) => IFLA_MACSEC_CIPHER_SUITE, Window(_) => IFLA_MACSEC_WINDOW, EncodingSa(_) => IFLA_MACSEC_ENCODING_SA, Encrypt(_) => IFLA_MACSEC_ENCRYPT, Protect(_) => IFLA_MACSEC_PROTECT, IncSci(_) => IFLA_MACSEC_INC_SCI, Es(_) => IFLA_MACSEC_ES, Scb(_) => IFLA_MACSEC_SCB, ReplayProtect(_) => IFLA_MACSEC_REPLAY_PROTECT, Validation(_) => IFLA_MACSEC_VALIDATION, Offload(_) => IFLA_MACSEC_OFFLOAD, Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoMacSec { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoMacSec::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_MACSEC_SCI => { Sci(parse_u64(payload) .context("invalid IFLA_MACSEC_SCI value")?) } IFLA_MACSEC_PORT => Port( parse_u16(payload).context("invalid IFLA_MACSEC_PORT value")?, ), IFLA_MACSEC_ICV_LEN => IcvLen( parse_u8(payload) .context("invalid IFLA_MACSEC_ICV_LEN value")?, ), IFLA_MACSEC_CIPHER_SUITE => CipherSuite( parse_u64(payload) .context("invalid IFLA_MACSEC_CIPHER_SUITE value")? .into(), ), IFLA_MACSEC_WINDOW => Window( parse_u32(payload) .context("invalid IFLA_MACSEC_WINDOW value")?, ), IFLA_MACSEC_ENCODING_SA => EncodingSa( parse_u8(payload) .context("invalid IFLA_MACSEC_ENCODING_SA value")?, ), IFLA_MACSEC_ENCRYPT => Encrypt( parse_u8(payload) .context("invalid IFLA_MACSEC_ENCRYPT value")?, ), IFLA_MACSEC_PROTECT => Protect( parse_u8(payload) .context("invalid IFLA_MACSEC_PROTECT value")?, ), IFLA_MACSEC_INC_SCI => IncSci( parse_u8(payload) .context("invalid IFLA_MACSEC_INC_SCI value")?, ), IFLA_MACSEC_ES => { Es(parse_u8(payload).context("invalid IFLA_MACSEC_ES value")?) } IFLA_MACSEC_SCB => { Scb(parse_u8(payload) .context("invalid IFLA_MACSEC_SCB value")?) } IFLA_MACSEC_REPLAY_PROTECT => ReplayProtect( parse_u8(payload) .context("invalid IFLA_MACSEC_REPLAY_PROTECT value")?, ), IFLA_MACSEC_VALIDATION => Validation( parse_u8(payload) .context("invalid IFLA_MACSEC_VALIDATION value")? .into(), ), IFLA_MACSEC_OFFLOAD => Offload( parse_u8(payload) .context("invalid IFLA_MACSEC_OFFLOAD value")? .into(), ), kind => Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } netlink-packet-route-0.21.0/src/link/link_info/mod.rs000064400000000000000000000032451046102023000205760ustar 00000000000000// SPDX-License-Identifier: MIT mod bond; mod bond_port; mod bridge; mod bridge_port; mod geneve; mod gre; mod gre6; mod gre_tap; mod gre_tap6; mod gtp; mod hsr; mod info_data; mod info_port; mod infos; mod ipoib; mod ipvlan; mod mac_vlan; mod macsec; mod sit; mod tun; mod veth; mod vlan; mod vrf; mod vti; mod vxlan; mod xfrm; mod xstats; pub use self::bond::{BondAdInfo, BondArpValidate, BondMode, InfoBond}; pub use self::bond_port::{BondPortState, InfoBondPort, MiiStatus}; pub use self::bridge::{ BridgeId, BridgeIdBuffer, BridgeQuerierState, InfoBridge, }; pub use self::bridge_port::{ BridgePortMulticastRouter, BridgePortState, InfoBridgePort, }; pub use self::geneve::{GeneveDf, InfoGeneve}; pub use self::gre::InfoGreTun; pub use self::gre6::InfoGreTun6; pub use self::gre_tap::InfoGreTap; pub use self::gre_tap6::InfoGreTap6; pub use self::gtp::InfoGtp; pub use self::hsr::{HsrProtocol, InfoHsr}; pub use self::info_data::InfoData; pub use self::info_port::{InfoPortData, InfoPortKind, InfoVrfPort}; pub use self::infos::{InfoKind, LinkInfo}; pub use self::ipoib::InfoIpoib; pub use self::ipvlan::{ InfoIpVlan, InfoIpVtap, IpVlanFlags, IpVlanMode, IpVtapFlags, IpVtapMode, }; pub use self::mac_vlan::{InfoMacVlan, InfoMacVtap, MacVlanMode, MacVtapMode}; pub use self::macsec::{ InfoMacSec, MacSecCipherId, MacSecOffload, MacSecValidate, }; pub use self::sit::InfoSitTun; pub use self::tun::InfoTun; pub use self::veth::InfoVeth; pub use self::vlan::{InfoVlan, VlanQosMapping}; pub use self::vrf::InfoVrf; pub use self::vti::InfoVti; pub use self::vxlan::InfoVxlan; pub use self::xfrm::InfoXfrm; pub use self::xstats::LinkXstats; pub(crate) use self::infos::VecLinkInfo; netlink-packet-route-0.21.0/src/link/link_info/sit.rs000064400000000000000000000020471046102023000206150ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, DecodeError, Parseable, }; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoSitTun { Other(DefaultNla), } impl Nla for InfoSitTun { fn value_len(&self) -> usize { match self { Self::Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoSitTun { fn parse(buf: &NlaBuffer<&'a T>) -> Result { #[allow(clippy::match_single_binding)] Ok(match buf.kind() { kind => Self::Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind} for sit"))?, ), }) } } netlink-packet-route-0.21.0/src/link/link_info/tun.rs000064400000000000000000000020301046102023000206140ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, DecodeError, Parseable, }; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoTun { Other(DefaultNla), } impl Nla for InfoTun { fn value_len(&self) -> usize { match self { Self::Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoTun { fn parse(buf: &NlaBuffer<&'a T>) -> Result { #[allow(clippy::match_single_binding)] Ok(match buf.kind() { kind => Self::Other(DefaultNla::parse(buf).context(format!( "unknown NLA type {kind} for IFLA_INFO_DATA(vrf)" ))?), }) } } netlink-packet-route-0.21.0/src/link/link_info/veth.rs000064400000000000000000000035261046102023000207670ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, traits::{Emitable, Parseable}, DecodeError, }; use super::super::{LinkMessage, LinkMessageBuffer}; const VETH_INFO_PEER: u16 = 1; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] // This data is not for querying/dumping as in kernel 6.5.8, // because the `struct rtnl_link_ops veth_link_ops` does not have `fill_info`. // Only for create veth pub enum InfoVeth { Peer(LinkMessage), Other(DefaultNla), } impl Nla for InfoVeth { fn value_len(&self) -> usize { use self::InfoVeth::*; match *self { Peer(ref message) => message.buffer_len(), Other(ref attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::InfoVeth::*; match *self { Peer(ref message) => message.emit(buffer), Other(ref attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { use self::InfoVeth::*; match *self { Peer(_) => VETH_INFO_PEER, Other(ref attr) => attr.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoVeth { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoVeth::*; let payload = buf.value(); Ok(match buf.kind() { 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}"))?, ), }) } } netlink-packet-route-0.21.0/src/link/link_info/vlan.rs000064400000000000000000000130451046102023000207560ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{BigEndian, ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, parsers::{parse_u16, parse_u16_be, parse_u32}, traits::{Emitable, Parseable}, DecodeError, }; use crate::link::VlanProtocol; const IFLA_VLAN_ID: u16 = 1; const IFLA_VLAN_FLAGS: u16 = 2; const IFLA_VLAN_EGRESS_QOS: u16 = 3; const IFLA_VLAN_INGRESS_QOS: u16 = 4; const IFLA_VLAN_PROTOCOL: u16 = 5; const IFLA_VLAN_QOS_MAPPING: u16 = 1; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoVlan { Id(u16), Flags((u32, u32)), EgressQos(Vec), IngressQos(Vec), Protocol(VlanProtocol), Other(DefaultNla), } impl Nla for InfoVlan { fn value_len(&self) -> usize { match self { Self::Id(_) | Self::Protocol(_) => 2, Self::Flags(_) => 8, Self::EgressQos(mappings) | Self::IngressQos(mappings) => { mappings.as_slice().buffer_len() } Self::Other(v) => v.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::EgressQos(ref mappings) | Self::IngressQos(ref mappings) => { mappings.as_slice().emit(buffer) } Self::Id(value) => NativeEndian::write_u16(buffer, *value), Self::Protocol(value) => { BigEndian::write_u16(buffer, (*value).into()) } Self::Flags(flags) => { NativeEndian::write_u32(&mut buffer[0..4], flags.0); NativeEndian::write_u32(&mut buffer[4..8], flags.1) } Self::Other(v) => v.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Id(_) => IFLA_VLAN_ID, Self::Flags(_) => IFLA_VLAN_FLAGS, Self::EgressQos(_) => IFLA_VLAN_EGRESS_QOS, Self::IngressQos(_) => IFLA_VLAN_INGRESS_QOS, Self::Protocol(_) => IFLA_VLAN_PROTOCOL, Self::Other(v) => v.kind(), } } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum VlanQosMapping { /// Tuple (from, to) Mapping(u32, u32), Other(DefaultNla), } impl Nla for VlanQosMapping { fn value_len(&self) -> usize { match self { VlanQosMapping::Mapping { .. } => 8, VlanQosMapping::Other(nla) => nla.value_len(), } } fn kind(&self) -> u16 { match self { VlanQosMapping::Mapping { .. } => IFLA_VLAN_QOS_MAPPING, VlanQosMapping::Other(nla) => nla.kind(), } } fn emit_value(&self, buffer: &mut [u8]) { use VlanQosMapping::*; match self { 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_MAPPING => { if payload.len() != 8 { return Err("invalid IFLA_VLAN_QOS_MAPPING value".into()); } Mapping( parse_u32(&payload[..4]) .context("expected u32 from value")?, parse_u32(&payload[4..]) .context("expected u32 to value")?, ) } kind => Other(DefaultNla::parse(buf).context(format!( "unknown NLA type {kind} for VLAN QoS mapping" ))?), }) } } 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_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")? .into(), ), _ => Self::Other(DefaultNla::parse(buf).context(format!( "invalid NLA for {}: {payload:?}", buf.kind() ))?), }) } } netlink-packet-route-0.21.0/src/link/link_info/vrf.rs000064400000000000000000000027751046102023000206230ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::parse_u32, traits::Parseable, DecodeError, }; const IFLA_VRF_TABLE: u16 = 1; #[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} for IFLA_INFO_DATA(vrf)" ))?), }) } } netlink-packet-route-0.21.0/src/link/link_info/vti.rs000064400000000000000000000020361046102023000206160ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, DecodeError, Parseable, }; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoVti { Other(DefaultNla), } impl Nla for InfoVti { fn value_len(&self) -> usize { match self { Self::Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoVti { fn parse(buf: &NlaBuffer<&'a T>) -> Result { #[allow(clippy::match_single_binding)] Ok(match buf.kind() { kind => Self::Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind} for vti"))?, ), }) } } netlink-packet-route-0.21.0/src/link/link_info/vxlan.rs000064400000000000000000000322231046102023000211450ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::{Ipv4Addr, Ipv6Addr}; use anyhow::Context; use byteorder::{BigEndian, ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::{parse_u16_be, parse_u32, parse_u8}, traits::Parseable, DecodeError, }; const IFLA_VXLAN_ID: u16 = 1; const IFLA_VXLAN_GROUP: u16 = 2; const IFLA_VXLAN_LINK: u16 = 3; const IFLA_VXLAN_LOCAL: u16 = 4; const IFLA_VXLAN_TTL: u16 = 5; const IFLA_VXLAN_TOS: u16 = 6; const IFLA_VXLAN_LEARNING: u16 = 7; const IFLA_VXLAN_AGEING: u16 = 8; const IFLA_VXLAN_LIMIT: u16 = 9; const IFLA_VXLAN_PORT_RANGE: u16 = 10; const IFLA_VXLAN_PROXY: u16 = 11; const IFLA_VXLAN_RSC: u16 = 12; const IFLA_VXLAN_L2MISS: u16 = 13; const IFLA_VXLAN_L3MISS: u16 = 14; const IFLA_VXLAN_PORT: u16 = 15; const IFLA_VXLAN_GROUP6: u16 = 16; const IFLA_VXLAN_LOCAL6: u16 = 17; const IFLA_VXLAN_UDP_CSUM: u16 = 18; const IFLA_VXLAN_UDP_ZERO_CSUM6_TX: u16 = 19; const IFLA_VXLAN_UDP_ZERO_CSUM6_RX: u16 = 20; const IFLA_VXLAN_REMCSUM_TX: u16 = 21; const IFLA_VXLAN_REMCSUM_RX: u16 = 22; const IFLA_VXLAN_GBP: u16 = 23; const IFLA_VXLAN_REMCSUM_NOPARTIAL: u16 = 24; const IFLA_VXLAN_COLLECT_METADATA: u16 = 25; const IFLA_VXLAN_LABEL: u16 = 26; const IFLA_VXLAN_GPE: u16 = 27; const IFLA_VXLAN_TTL_INHERIT: u16 = 28; const IFLA_VXLAN_DF: u16 = 29; const IFLA_VXLAN_VNIFILTER: u16 = 30; const IFLA_VXLAN_LOCALBYPASS: u16 = 31; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoVxlan { Id(u32), Group(Ipv4Addr), Group6(Ipv6Addr), Link(u32), Local(Ipv4Addr), Local6(Ipv6Addr), Tos(u8), Ttl(u8), Label(u32), Learning(bool), Ageing(u32), Limit(u32), PortRange((u16, u16)), Proxy(bool), Rsc(bool), L2Miss(bool), L3Miss(bool), CollectMetadata(bool), Port(u16), UDPCsum(bool), UDPZeroCsumTX(bool), UDPZeroCsumRX(bool), RemCsumTX(bool), RemCsumRX(bool), Gbp(bool), Gpe(bool), RemCsumNoPartial(bool), TtlInherit(bool), Df(u8), Vnifilter(bool), Localbypass(bool), Other(DefaultNla), } impl Nla for InfoVxlan { fn value_len(&self) -> usize { match self { Self::Tos(_) | Self::Ttl(_) | Self::Learning(_) | Self::Proxy(_) | Self::Rsc(_) | Self::L2Miss(_) | Self::L3Miss(_) | Self::CollectMetadata(_) | Self::UDPCsum(_) | Self::UDPZeroCsumTX(_) | Self::UDPZeroCsumRX(_) | Self::RemCsumTX(_) | Self::RemCsumRX(_) | Self::TtlInherit(_) | Self::Df(_) | Self::Vnifilter(_) | Self::Localbypass(_) => 1, Self::Gbp(_) | Self::Gpe(_) | Self::RemCsumNoPartial(_) => 0, Self::Port(_) => 2, Self::Id(_) | Self::Label(_) | Self::Link(_) | Self::Ageing(_) | Self::Limit(_) | Self::PortRange(_) | Self::Group(_) | Self::Local(_) => 4, Self::Group6(_) | Self::Local6(_) => 16, Self::Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Id(value) | Self::Label(value) | Self::Link(value) | Self::Ageing(value) | Self::Limit(value) => NativeEndian::write_u32(buffer, *value), Self::Gbp(_value) | Self::Gpe(_value) | Self::RemCsumNoPartial(_value) => (), Self::Tos(value) | Self::Ttl(value) | Self::Df(value) => { buffer[0] = *value } Self::Vnifilter(value) | Self::Localbypass(value) | Self::Learning(value) | Self::Proxy(value) | Self::Rsc(value) | Self::L2Miss(value) | Self::L3Miss(value) | Self::CollectMetadata(value) | Self::UDPCsum(value) | Self::UDPZeroCsumTX(value) | Self::UDPZeroCsumRX(value) | Self::RemCsumTX(value) | Self::RemCsumRX(value) | Self::TtlInherit(value) => buffer[0] = *value as u8, Self::Group(value) | Self::Local(value) => { buffer.copy_from_slice(&value.octets()) } Self::Group6(value) | Self::Local6(value) => { buffer.copy_from_slice(&value.octets()) } Self::Port(value) => BigEndian::write_u16(buffer, *value), Self::PortRange(range) => { BigEndian::write_u16(buffer, range.0); BigEndian::write_u16(&mut buffer[2..], range.1) } Self::Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Id(_) => IFLA_VXLAN_ID, Self::Group(_) => IFLA_VXLAN_GROUP, Self::Group6(_) => IFLA_VXLAN_GROUP6, Self::Link(_) => IFLA_VXLAN_LINK, Self::Local(_) => IFLA_VXLAN_LOCAL, Self::Local6(_) => IFLA_VXLAN_LOCAL6, Self::Tos(_) => IFLA_VXLAN_TOS, Self::Ttl(_) => IFLA_VXLAN_TTL, Self::Label(_) => IFLA_VXLAN_LABEL, Self::Learning(_) => IFLA_VXLAN_LEARNING, Self::Ageing(_) => IFLA_VXLAN_AGEING, Self::Limit(_) => IFLA_VXLAN_LIMIT, Self::PortRange(_) => IFLA_VXLAN_PORT_RANGE, Self::Proxy(_) => IFLA_VXLAN_PROXY, Self::Rsc(_) => IFLA_VXLAN_RSC, Self::L2Miss(_) => IFLA_VXLAN_L2MISS, Self::L3Miss(_) => IFLA_VXLAN_L3MISS, Self::CollectMetadata(_) => IFLA_VXLAN_COLLECT_METADATA, Self::Port(_) => IFLA_VXLAN_PORT, Self::UDPCsum(_) => IFLA_VXLAN_UDP_CSUM, Self::UDPZeroCsumTX(_) => IFLA_VXLAN_UDP_ZERO_CSUM6_TX, Self::UDPZeroCsumRX(_) => IFLA_VXLAN_UDP_ZERO_CSUM6_RX, Self::RemCsumTX(_) => IFLA_VXLAN_REMCSUM_TX, Self::RemCsumRX(_) => IFLA_VXLAN_REMCSUM_RX, Self::Gbp(_) => IFLA_VXLAN_GBP, Self::Gpe(_) => IFLA_VXLAN_GPE, Self::RemCsumNoPartial(_) => IFLA_VXLAN_REMCSUM_NOPARTIAL, Self::TtlInherit(_) => IFLA_VXLAN_TTL_INHERIT, Self::Df(_) => IFLA_VXLAN_DF, Self::Vnifilter(_) => IFLA_VXLAN_VNIFILTER, Self::Localbypass(_) => IFLA_VXLAN_LOCALBYPASS, Self::Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoVxlan { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { IFLA_VXLAN_ID => { Self::Id(parse_u32(payload).context("invalid IFLA_VXLAN_ID value")?) } IFLA_VXLAN_GROUP => { if payload.len() == 4 { let mut data = [0u8; 4]; data.copy_from_slice(&payload[0..4]); Self::Group(Ipv4Addr::from(data)) } else { return Err(DecodeError::from(format!( "Invalid IFLA_VXLAN_GROUP, got unexpected length \ of IPv4 address payload {:?}", payload ))); } } IFLA_VXLAN_LOCAL => { if payload.len() == 4 { let mut data = [0u8; 4]; data.copy_from_slice(&payload[0..4]); Self::Local(Ipv4Addr::from(data)) } else { return Err(DecodeError::from(format!( "Invalid IFLA_VXLAN_LOCAL, got unexpected length \ of IPv4 address payload {:?}", payload ))); } } IFLA_VXLAN_GROUP6 => { if payload.len() == 16 { let mut data = [0u8; 16]; data.copy_from_slice(&payload[0..16]); Self::Group6(Ipv6Addr::from(data)) } else { return Err(DecodeError::from(format!( "Invalid IFLA_VXLAN_GROUP6, got unexpected length \ of IPv6 address payload {:?}", payload ))); } }, IFLA_VXLAN_LOCAL6 => { if payload.len() == 16 { let mut data = [0u8; 16]; data.copy_from_slice(&payload[0..16]); Self::Local6(Ipv6Addr::from(data)) } else { return Err(DecodeError::from(format!( "Invalid IFLA_VXLAN_LOCAL6, got unexpected length \ of IPv6 address payload {:?}", payload ))); } }, IFLA_VXLAN_LINK => Self::Link( parse_u32(payload).context("invalid IFLA_VXLAN_LINK value")?, ), IFLA_VXLAN_TOS => { Self::Tos(parse_u8(payload) .context("invalid IFLA_VXLAN_TOS value")?) } IFLA_VXLAN_TTL => { Self::Ttl(parse_u8(payload) .context("invalid IFLA_VXLAN_TTL value")?) } IFLA_VXLAN_LABEL => Self::Label( parse_u32(payload).context("invalid IFLA_VXLAN_LABEL value")?, ), IFLA_VXLAN_LEARNING => Self::Learning( parse_u8(payload) .context("invalid IFLA_VXLAN_LEARNING value")? > 0, ), IFLA_VXLAN_AGEING => Self::Ageing( parse_u32(payload) .context("invalid IFLA_VXLAN_AGEING value")?, ), IFLA_VXLAN_LIMIT => Self::Limit( parse_u32(payload).context("invalid IFLA_VXLAN_LIMIT value")?, ), IFLA_VXLAN_PROXY => Self::Proxy( parse_u8(payload).context("invalid IFLA_VXLAN_PROXY value")? > 0, ), IFLA_VXLAN_RSC => { Self::Rsc(parse_u8(payload) .context("invalid IFLA_VXLAN_RSC value")?> 0) } IFLA_VXLAN_L2MISS => Self::L2Miss( parse_u8(payload).context("invalid IFLA_VXLAN_L2MISS value")? > 0, ), IFLA_VXLAN_L3MISS => Self::L3Miss( parse_u8(payload).context("invalid IFLA_VXLAN_L3MISS value")? > 0, ), IFLA_VXLAN_COLLECT_METADATA => Self::CollectMetadata( parse_u8(payload) .context("invalid IFLA_VXLAN_COLLECT_METADATA value")? >0, ), IFLA_VXLAN_PORT_RANGE => { let err = "invalid IFLA_VXLAN_PORT value"; if payload.len() != 4 { return Err(err.into()); } let low = parse_u16_be(&payload[0..2]).context(err)?; let high = parse_u16_be(&payload[2..]).context(err)?; Self::PortRange((low, high)) } IFLA_VXLAN_PORT => Self::Port( parse_u16_be(payload) .context("invalid IFLA_VXLAN_PORT value")?, ), IFLA_VXLAN_UDP_CSUM => Self::UDPCsum( parse_u8(payload) .context("invalid IFLA_VXLAN_UDP_CSUM value")? > 0, ), IFLA_VXLAN_UDP_ZERO_CSUM6_TX => Self::UDPZeroCsumTX( parse_u8(payload) .context("invalid IFLA_VXLAN_UDP_ZERO_CSUM6_TX value")? > 0, ), IFLA_VXLAN_UDP_ZERO_CSUM6_RX => Self::UDPZeroCsumRX( parse_u8(payload) .context("invalid IFLA_VXLAN_UDP_ZERO_CSUM6_RX value")? > 0, ), IFLA_VXLAN_REMCSUM_TX => Self::RemCsumTX( parse_u8(payload) .context("invalid IFLA_VXLAN_REMCSUM_TX value")? > 0, ), IFLA_VXLAN_REMCSUM_RX => Self::RemCsumRX( parse_u8(payload) .context("invalid IFLA_VXLAN_REMCSUM_RX value")? > 0, ), IFLA_VXLAN_DF => { Self::Df(parse_u8(payload).context("invalid IFLA_VXLAN_DF value")?) } IFLA_VXLAN_GBP => { Self::Gbp(true) } IFLA_VXLAN_GPE => { Self::Gpe(true) } IFLA_VXLAN_REMCSUM_NOPARTIAL => Self::RemCsumNoPartial(true), IFLA_VXLAN_TTL_INHERIT => Self::TtlInherit( parse_u8(payload) .context("invalid IFLA_VXLAN_TTL_INHERIT value")? > 0, ), IFLA_VXLAN_VNIFILTER => Self::Vnifilter( parse_u8(payload) .context("invalid IFLA_VXLAN_VNIFILTER value")? > 0, ), IFLA_VXLAN_LOCALBYPASS => Self::Localbypass( parse_u8(payload) .context("invalid IFLA_VXLAN_LOCALBYPASS value")? > 0, ), unknown_kind => Self::Other(DefaultNla::parse(buf).context(format!( "Failed to parse IFLA_INFO_DATA(vxlan) NLA type: {unknown_kind} as DefaultNla" ))?), }) } } netlink-packet-route-0.21.0/src/link/link_info/xfrm.rs000064400000000000000000000034551046102023000207760ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::parse_u32, traits::Parseable, DecodeError, }; const IFLA_XFRM_LINK: u16 = 1; const IFLA_XFRM_IF_ID: u16 = 2; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoXfrm { Link(u32), IfId(u32), Other(DefaultNla), } impl Nla for InfoXfrm { fn value_len(&self) -> usize { use self::InfoXfrm::*; match self { Link(_) => 4, IfId(_) => 4, Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::InfoXfrm::*; match self { 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::InfoXfrm::*; match self { Link(_) => IFLA_XFRM_LINK, IfId(_) => IFLA_XFRM_IF_ID, Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoXfrm { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoXfrm::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_XFRM_LINK => Link( parse_u32(payload).context("invalid IFLA_XFRM_LINK 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} for IFLA_INFO_DATA(xfrm)" ))?), }) } } netlink-packet-route-0.21.0/src/link/link_info/xstats.rs000064400000000000000000000017131046102023000213430ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::NlaBuffer, DecodeError, Emitable, ParseableParametrized, }; use crate::link::InfoKind; // This is filled by driver via `struct rtnl_link_ops.fill_xstats` // Currently(Linux kernel 6.5.8), only the `can` interface support so. #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum LinkXstats { Other(Vec), } impl Emitable for LinkXstats { fn buffer_len(&self) -> usize { match self { Self::Other(v) => v.len(), } } fn emit(&self, buffer: &mut [u8]) { match self { Self::Other(v) => buffer.copy_from_slice(v.as_slice()), } } } impl<'a, T: AsRef<[u8]> + ?Sized> ParseableParametrized, &InfoKind> for LinkXstats { fn parse_with_param( buf: &NlaBuffer<&'a T>, _kind: &InfoKind, ) -> Result { Ok(Self::Other(buf.value().to_vec())) } } netlink-packet-route-0.21.0/src/link/link_layer_type.rs000064400000000000000000000262311046102023000212410ustar 00000000000000// SPDX-License-Identifier: MIT const ARPHRD_NETROM: u16 = 0; const ARPHRD_ETHER: u16 = 1; const ARPHRD_EETHER: u16 = 2; const ARPHRD_AX25: u16 = 3; const ARPHRD_PRONET: u16 = 4; const ARPHRD_CHAOS: u16 = 5; const ARPHRD_IEEE802: u16 = 6; const ARPHRD_ARCNET: u16 = 7; const ARPHRD_APPLETLK: u16 = 8; const ARPHRD_DLCI: u16 = 15; const ARPHRD_ATM: u16 = 19; const ARPHRD_METRICOM: u16 = 23; const ARPHRD_IEEE1394: u16 = 24; const ARPHRD_EUI64: u16 = 27; const ARPHRD_INFINIBAND: u16 = 32; const ARPHRD_SLIP: u16 = 256; const ARPHRD_CSLIP: u16 = 257; const ARPHRD_SLIP6: u16 = 258; const ARPHRD_CSLIP6: u16 = 259; const ARPHRD_RSRVD: u16 = 260; const ARPHRD_ADAPT: u16 = 264; const ARPHRD_ROSE: u16 = 270; const ARPHRD_X25: u16 = 271; const ARPHRD_HWX25: u16 = 272; const ARPHRD_CAN: u16 = 280; const ARPHRD_PPP: u16 = 512; const ARPHRD_CISCO: u16 = 513; const ARPHRD_HDLC: u16 = ARPHRD_CISCO; const ARPHRD_LAPB: u16 = 516; const ARPHRD_DDCMP: u16 = 517; const ARPHRD_RAWHDLC: u16 = 518; const ARPHRD_RAWIP: u16 = 519; const ARPHRD_TUNNEL: u16 = 768; const ARPHRD_TUNNEL6: u16 = 769; const ARPHRD_FRAD: u16 = 770; const ARPHRD_SKIP: u16 = 771; const ARPHRD_LOOPBACK: u16 = 772; const ARPHRD_LOCALTLK: u16 = 773; const ARPHRD_FDDI: u16 = 774; const ARPHRD_BIF: u16 = 775; const ARPHRD_SIT: u16 = 776; const ARPHRD_IPDDP: u16 = 777; const ARPHRD_IPGRE: u16 = 778; const ARPHRD_PIMREG: u16 = 779; const ARPHRD_HIPPI: u16 = 780; const ARPHRD_ASH: u16 = 781; const ARPHRD_ECONET: u16 = 782; const ARPHRD_IRDA: u16 = 783; const ARPHRD_FCPP: u16 = 784; const ARPHRD_FCAL: u16 = 785; const ARPHRD_FCPL: u16 = 786; const ARPHRD_FCFABRIC: u16 = 787; const ARPHRD_IEEE802_TR: u16 = 800; const ARPHRD_IEEE80211: u16 = 801; const ARPHRD_IEEE80211_PRISM: u16 = 802; const ARPHRD_IEEE80211_RADIOTAP: u16 = 803; const ARPHRD_IEEE802154: u16 = 804; const ARPHRD_IEEE802154_MONITOR: u16 = 805; const ARPHRD_PHONET: u16 = 820; const ARPHRD_PHONET_PIPE: u16 = 821; const ARPHRD_CAIF: u16 = 822; const ARPHRD_IP6GRE: u16 = 823; const ARPHRD_NETLINK: u16 = 824; const ARPHRD_6LOWPAN: u16 = 825; const ARPHRD_VSOCKMON: u16 = 826; const ARPHRD_VOID: u16 = 0xffff; const ARPHRD_NONE: u16 = 0xfffe; #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] #[non_exhaustive] #[repr(u16)] // Since this list seldom changes, we do not add `Other(u16)` for unknown data. // For unknown value, we log a warning message pub enum LinkLayerType { #[default] Netrom = ARPHRD_NETROM, Ether = ARPHRD_ETHER, Eether = ARPHRD_EETHER, Ax25 = ARPHRD_AX25, Pronet = ARPHRD_PRONET, Chaos = ARPHRD_CHAOS, Ieee802 = ARPHRD_IEEE802, Arcnet = ARPHRD_ARCNET, Appletlk = ARPHRD_APPLETLK, Dlci = ARPHRD_DLCI, Atm = ARPHRD_ATM, Metricom = ARPHRD_METRICOM, Ieee1394 = ARPHRD_IEEE1394, Eui64 = ARPHRD_EUI64, Infiniband = ARPHRD_INFINIBAND, Slip = ARPHRD_SLIP, Cslip = ARPHRD_CSLIP, Slip6 = ARPHRD_SLIP6, Cslip6 = ARPHRD_CSLIP6, Rsrvd = ARPHRD_RSRVD, Adapt = ARPHRD_ADAPT, Rose = ARPHRD_ROSE, X25 = ARPHRD_X25, Hwx25 = ARPHRD_HWX25, Can = ARPHRD_CAN, Ppp = ARPHRD_PPP, Hdlc = ARPHRD_HDLC, Lapb = ARPHRD_LAPB, Ddcmp = ARPHRD_DDCMP, Rawhdlc = ARPHRD_RAWHDLC, Rawip = ARPHRD_RAWIP, Tunnel = ARPHRD_TUNNEL, Tunnel6 = ARPHRD_TUNNEL6, Frad = ARPHRD_FRAD, Skip = ARPHRD_SKIP, Loopback = ARPHRD_LOOPBACK, Localtlk = ARPHRD_LOCALTLK, Fddi = ARPHRD_FDDI, Bif = ARPHRD_BIF, Sit = ARPHRD_SIT, Ipddp = ARPHRD_IPDDP, Ipgre = ARPHRD_IPGRE, Pimreg = ARPHRD_PIMREG, Hippi = ARPHRD_HIPPI, Ash = ARPHRD_ASH, Econet = ARPHRD_ECONET, Irda = ARPHRD_IRDA, Fcpp = ARPHRD_FCPP, Fcal = ARPHRD_FCAL, Fcpl = ARPHRD_FCPL, Fcfabric = ARPHRD_FCFABRIC, Ieee802Tr = ARPHRD_IEEE802_TR, Ieee80211 = ARPHRD_IEEE80211, Ieee80211Prism = ARPHRD_IEEE80211_PRISM, Ieee80211Radiotap = ARPHRD_IEEE80211_RADIOTAP, Ieee802154 = ARPHRD_IEEE802154, Ieee802154Monitor = ARPHRD_IEEE802154_MONITOR, Phonet = ARPHRD_PHONET, PhonetPipe = ARPHRD_PHONET_PIPE, Caif = ARPHRD_CAIF, Ip6gre = ARPHRD_IP6GRE, Netlink = ARPHRD_NETLINK, Sixlowpan = ARPHRD_6LOWPAN, Vsockmon = ARPHRD_VSOCKMON, /// Void type, nothing is known Void = ARPHRD_VOID, /// zero header length None = ARPHRD_NONE, } impl From for LinkLayerType { fn from(d: u16) -> Self { match d { d if d == ARPHRD_NETROM => Self::Netrom, d if d == ARPHRD_ETHER => Self::Ether, d if d == ARPHRD_EETHER => Self::Eether, d if d == ARPHRD_AX25 => Self::Ax25, d if d == ARPHRD_PRONET => Self::Pronet, d if d == ARPHRD_CHAOS => Self::Chaos, d if d == ARPHRD_IEEE802 => Self::Ieee802, d if d == ARPHRD_ARCNET => Self::Arcnet, d if d == ARPHRD_APPLETLK => Self::Appletlk, d if d == ARPHRD_DLCI => Self::Dlci, d if d == ARPHRD_ATM => Self::Atm, d if d == ARPHRD_METRICOM => Self::Metricom, d if d == ARPHRD_IEEE1394 => Self::Ieee1394, d if d == ARPHRD_EUI64 => Self::Eui64, d if d == ARPHRD_INFINIBAND => Self::Infiniband, d if d == ARPHRD_SLIP => Self::Slip, d if d == ARPHRD_CSLIP => Self::Cslip, d if d == ARPHRD_SLIP6 => Self::Slip6, d if d == ARPHRD_CSLIP6 => Self::Cslip6, d if d == ARPHRD_RSRVD => Self::Rsrvd, d if d == ARPHRD_ADAPT => Self::Adapt, d if d == ARPHRD_ROSE => Self::Rose, d if d == ARPHRD_X25 => Self::X25, d if d == ARPHRD_HWX25 => Self::Hwx25, d if d == ARPHRD_CAN => Self::Can, d if d == ARPHRD_PPP => Self::Ppp, d if d == ARPHRD_HDLC => Self::Hdlc, d if d == ARPHRD_LAPB => Self::Lapb, d if d == ARPHRD_DDCMP => Self::Ddcmp, d if d == ARPHRD_RAWHDLC => Self::Rawhdlc, d if d == ARPHRD_RAWIP => Self::Rawip, d if d == ARPHRD_TUNNEL => Self::Tunnel, d if d == ARPHRD_TUNNEL6 => Self::Tunnel6, d if d == ARPHRD_FRAD => Self::Frad, d if d == ARPHRD_SKIP => Self::Skip, d if d == ARPHRD_LOOPBACK => Self::Loopback, d if d == ARPHRD_LOCALTLK => Self::Localtlk, d if d == ARPHRD_FDDI => Self::Fddi, d if d == ARPHRD_BIF => Self::Bif, d if d == ARPHRD_SIT => Self::Sit, d if d == ARPHRD_IPDDP => Self::Ipddp, d if d == ARPHRD_IPGRE => Self::Ipgre, d if d == ARPHRD_PIMREG => Self::Pimreg, d if d == ARPHRD_HIPPI => Self::Hippi, d if d == ARPHRD_ASH => Self::Ash, d if d == ARPHRD_ECONET => Self::Econet, d if d == ARPHRD_IRDA => Self::Irda, d if d == ARPHRD_FCPP => Self::Fcpp, d if d == ARPHRD_FCAL => Self::Fcal, d if d == ARPHRD_FCPL => Self::Fcpl, d if d == ARPHRD_FCFABRIC => Self::Fcfabric, d if d == ARPHRD_IEEE802_TR => Self::Ieee802Tr, d if d == ARPHRD_IEEE80211 => Self::Ieee80211, d if d == ARPHRD_IEEE80211_PRISM => Self::Ieee80211Prism, d if d == ARPHRD_IEEE80211_RADIOTAP => Self::Ieee80211Radiotap, d if d == ARPHRD_IEEE802154 => Self::Ieee802154, d if d == ARPHRD_IEEE802154_MONITOR => Self::Ieee802154Monitor, d if d == ARPHRD_PHONET => Self::Phonet, d if d == ARPHRD_PHONET_PIPE => Self::PhonetPipe, d if d == ARPHRD_CAIF => Self::Caif, d if d == ARPHRD_IP6GRE => Self::Ip6gre, d if d == ARPHRD_NETLINK => Self::Netlink, d if d == ARPHRD_6LOWPAN => Self::Sixlowpan, d if d == ARPHRD_VSOCKMON => Self::Vsockmon, d if d == ARPHRD_VOID => Self::Void, d if d == ARPHRD_NONE => Self::None, _ => { log::warn!( "BUG: Got unknown ARPHRD_XXX {d} for LinkLayerType, \ treating it as LinkLayerType::Void" ); Self::Void } } } } impl From for u16 { fn from(v: LinkLayerType) -> u16 { v as u16 } } impl std::fmt::Display for LinkLayerType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{}", match self { Self::Netrom => "NETROM", Self::Ether => "ETHER", Self::Eether => "EETHER", Self::Ax25 => "AX25", Self::Pronet => "PRONET", Self::Chaos => "CHAOS", Self::Ieee802 => "IEEE802", Self::Arcnet => "ARCNET", Self::Appletlk => "APPLETLK", Self::Dlci => "DLCI", Self::Atm => "ATM", Self::Metricom => "METRICOM", Self::Ieee1394 => "IEEE1394", Self::Eui64 => "EUI64", Self::Infiniband => "INFINIBAND", Self::Slip => "SLIP", Self::Cslip => "CSLIP", Self::Slip6 => "SLIP6", Self::Cslip6 => "CSLIP6", Self::Rsrvd => "RSRVD", Self::Adapt => "ADAPT", Self::Rose => "ROSE", Self::X25 => "X25", Self::Hwx25 => "HWX25", Self::Can => "CAN", Self::Ppp => "PPP", Self::Hdlc => "HDLC", Self::Lapb => "LAPB", Self::Ddcmp => "DDCMP", Self::Rawhdlc => "RAWHDLC", Self::Rawip => "RAWIP", Self::Tunnel => "TUNNEL", Self::Tunnel6 => "TUNNEL6", Self::Frad => "FRAD", Self::Skip => "SKIP", Self::Loopback => "LOOPBACK", Self::Localtlk => "LOCALTLK", Self::Fddi => "FDDI", Self::Bif => "BIF", Self::Sit => "SIT", Self::Ipddp => "IPDDP", Self::Ipgre => "IPGRE", Self::Pimreg => "PIMREG", Self::Hippi => "HIPPI", Self::Ash => "ASH", Self::Econet => "ECONET", Self::Irda => "IRDA", Self::Fcpp => "FCPP", Self::Fcal => "FCAL", Self::Fcpl => "FCPL", Self::Fcfabric => "FCFABRIC", Self::Ieee802Tr => "IEEE802_TR", Self::Ieee80211 => "IEEE80211", Self::Ieee80211Prism => "IEEE80211_PRISM", Self::Ieee80211Radiotap => "IEEE80211_RADIOTAP", Self::Ieee802154 => "IEEE802154", Self::Ieee802154Monitor => "IEEE802154_MONITOR", Self::Phonet => "PHONET", Self::PhonetPipe => "PHONET_PIPE", Self::Caif => "CAIF", Self::Ip6gre => "IP6GRE", Self::Netlink => "NETLINK", Self::Sixlowpan => "6LOWPAN", Self::Vsockmon => "VSOCKMON", Self::Void => "VOID", Self::None => "NONE", } ) } } impl LinkLayerType { #[allow(non_upper_case_globals)] pub const Cisco: LinkLayerType = LinkLayerType::Hdlc; } netlink-packet-route-0.21.0/src/link/link_state.rs000064400000000000000000000032041046102023000201770ustar 00000000000000// SPDX-License-Identifier: MIT const IF_OPER_UNKNOWN: u8 = 0; const IF_OPER_NOTPRESENT: u8 = 1; const IF_OPER_DOWN: u8 = 2; const IF_OPER_LOWERLAYERDOWN: u8 = 3; const IF_OPER_TESTING: u8 = 4; const IF_OPER_DORMANT: u8 = 5; const IF_OPER_UP: u8 = 6; #[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, /// Place holder for new state introduced by kernel when current crate does /// not support so. 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.21.0/src/link/map.rs000064400000000000000000000025541046102023000166260ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; const LINK_MAP_LEN: usize = 32; 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, Default)] #[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.21.0/src/link/message.rs000064400000000000000000000033631046102023000174740ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ traits::{Emitable, Parseable, ParseableParametrized}, DecodeError, }; use crate::link::{LinkAttribute, LinkHeader, LinkMessageBuffer}; use crate::AddressFamily; #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct LinkMessage { pub header: LinkHeader, pub attributes: Vec, } impl Emitable for LinkMessage { fn buffer_len(&self) -> usize { self.header.buffer_len() + self.attributes.as_slice().buffer_len() } fn emit(&self, buffer: &mut [u8]) { self.header.emit(buffer); self.attributes .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 attributes = Vec::::parse_with_param(buf, interface_family) .context("failed to parse link message NLAs")?; Ok(LinkMessage { header, attributes }) } } impl<'a, T: AsRef<[u8]> + 'a> ParseableParametrized, AddressFamily> for Vec { fn parse_with_param( buf: &LinkMessageBuffer<&'a T>, family: AddressFamily, ) -> Result { let mut attributes = vec![]; for nla_buf in buf.attributes() { attributes .push(LinkAttribute::parse_with_param(&nla_buf?, family)?); } Ok(attributes) } } netlink-packet-route-0.21.0/src/link/mod.rs000064400000000000000000000052501046102023000166240ustar 00000000000000// SPDX-License-Identifier: MIT mod af_spec; mod attribute; mod buffer_tool; mod down_reason; mod event; pub(crate) mod ext_mask; mod header; mod link_flag; mod link_info; mod link_layer_type; mod link_state; mod map; mod message; mod phys_id; mod prop_list; mod proto_info; pub(crate) mod sriov; mod stats; mod stats64; mod vlan_protocol; mod wireless; mod xdp; mod tests; pub use self::af_spec::{ AfSpecBridge, AfSpecInet, AfSpecInet6, AfSpecUnspec, BridgeFlag, BridgeMode, BridgeVlanInfo, BridgeVlanInfoFlags, BridgeVlanTunnelInfo, Icmp6Stats, Icmp6StatsBuffer, Inet6CacheInfo, Inet6CacheInfoBuffer, Inet6DevConf, Inet6DevConfBuffer, Inet6IfaceFlags, Inet6Stats, Inet6StatsBuffer, InetDevConf, }; pub use self::attribute::LinkAttribute; pub use self::down_reason::LinkProtocolDownReason; pub use self::event::LinkEvent; pub use self::ext_mask::LinkExtentMask; pub use self::header::{LinkHeader, LinkMessageBuffer}; pub use self::link_flag::LinkFlags; pub use self::link_info::{ BondAdInfo, BondArpValidate, BondMode, BondPortState, BridgeId, BridgeIdBuffer, BridgePortMulticastRouter, BridgePortState, BridgeQuerierState, GeneveDf, HsrProtocol, InfoBond, InfoBondPort, InfoBridge, InfoBridgePort, InfoData, InfoGeneve, InfoGreTap, InfoGreTap6, InfoGreTun, InfoGreTun6, InfoGtp, InfoHsr, InfoIpVlan, InfoIpVtap, InfoIpoib, InfoKind, InfoMacSec, InfoMacVlan, InfoMacVtap, InfoPortData, InfoPortKind, InfoSitTun, InfoTun, InfoVeth, InfoVlan, InfoVrf, InfoVrfPort, InfoVti, InfoVxlan, InfoXfrm, IpVlanFlags, IpVlanMode, IpVtapFlags, IpVtapMode, LinkInfo, LinkXstats, MacSecCipherId, MacSecOffload, MacSecValidate, MacVlanMode, MacVtapMode, MiiStatus, VlanQosMapping, }; pub use self::link_layer_type::LinkLayerType; pub use self::link_state::State; pub use self::map::{Map, MapBuffer}; pub use self::message::LinkMessage; pub use self::phys_id::LinkPhysId; pub use self::prop_list::Prop; pub use self::proto_info::{LinkProtoInfoBridge, LinkProtoInfoInet6}; pub use self::sriov::{ LinkVfInfo, LinkVfPort, VfInfo, VfInfoBroadcast, VfInfoBroadcastBuffer, VfInfoGuid, VfInfoGuidBuffer, VfInfoLinkState, VfInfoLinkStateBuffer, VfInfoMac, VfInfoMacBuffer, VfInfoRate, VfInfoRateBuffer, VfInfoRssQueryEn, VfInfoRssQueryEnBuffer, VfInfoSpoofCheck, VfInfoSpoofCheckBuffer, VfInfoTrust, VfInfoTrustBuffer, VfInfoTxRate, VfInfoTxRateBuffer, VfInfoVlan, VfInfoVlanBuffer, VfLinkState, VfPort, VfStats, VfVlan, VfVlanInfo, }; pub use self::stats::{Stats, StatsBuffer}; pub use self::stats64::{Stats64, Stats64Buffer}; pub use self::vlan_protocol::VlanProtocol; pub use self::wireless::LinkWirelessEvent; pub use self::xdp::{LinkXdp, XdpAttached}; netlink-packet-route-0.21.0/src/link/phys_id.rs000064400000000000000000000014301046102023000175000ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; const MAX_PHYS_ITEM_ID_LEN: usize = 32; #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] pub struct LinkPhysId { pub id: [u8; MAX_PHYS_ITEM_ID_LEN], pub len: usize, } impl Parseable<[u8]> for LinkPhysId { fn parse(buf: &[u8]) -> Result { let len = buf.len() % MAX_PHYS_ITEM_ID_LEN; let mut id = [0; MAX_PHYS_ITEM_ID_LEN]; id[..len].copy_from_slice(&buf[..len]); Ok(Self { id, len }) } } impl Emitable for LinkPhysId { fn buffer_len(&self) -> usize { self.len } fn emit(&self, buffer: &mut [u8]) { buffer[..self.len].copy_from_slice(&self.id[..self.len]) } } netlink-packet-route-0.21.0/src/link/prop_list.rs000064400000000000000000000032071046102023000200600ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::parse_string, traits::Parseable, DecodeError, }; const IFLA_ALT_IFNAME: u16 = 53; #[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.21.0/src/link/proto_info/bridge.rs000064400000000000000000000031071046102023000214560ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, traits::Parseable, DecodeError, }; #[derive(Clone, Eq, PartialEq, Debug)] #[non_exhaustive] pub enum LinkProtoInfoBridge { Other(DefaultNla), } pub(crate) struct VecLinkProtoInfoBridge(pub(crate) Vec); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecLinkProtoInfoBridge { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; for nla in NlasIterator::new(buf.into_inner()) { let nla = nla.context(format!( "invalid bridge IFLA_PROTINFO {:?}", buf.value() ))?; nlas.push(LinkProtoInfoBridge::parse(&nla)?); } Ok(Self(nlas)) } } impl Nla for LinkProtoInfoBridge { fn value_len(&self) -> usize { match *self { Self::Other(ref nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match *self { Self::Other(ref nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { match *self { Self::Other(ref nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for LinkProtoInfoBridge { fn parse(buf: &NlaBuffer<&'a T>) -> Result { Ok(Self::Other(DefaultNla::parse(buf).context(format!( "invalid bridge IFLA_PROTINFO {:?}", buf.value() ))?)) } } netlink-packet-route-0.21.0/src/link/proto_info/inet6.rs000064400000000000000000000030761046102023000212540ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, traits::Parseable, DecodeError, }; #[derive(Clone, Eq, PartialEq, Debug)] #[non_exhaustive] pub enum LinkProtoInfoInet6 { Other(DefaultNla), } pub(crate) struct VecLinkProtoInfoInet6(pub(crate) Vec); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecLinkProtoInfoInet6 { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; for nla in NlasIterator::new(buf.into_inner()) { let nla = nla.context(format!( "invalid inet6 IFLA_PROTINFO {:?}", buf.value() ))?; nlas.push(LinkProtoInfoInet6::parse(&nla)?); } Ok(Self(nlas)) } } impl Nla for LinkProtoInfoInet6 { fn value_len(&self) -> usize { match *self { Self::Other(ref nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match *self { Self::Other(ref nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { match *self { Self::Other(ref nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for LinkProtoInfoInet6 { fn parse(buf: &NlaBuffer<&'a T>) -> Result { Ok(Self::Other(DefaultNla::parse(buf).context(format!( "invalid inet6 IFLA_PROTINFO {:?}", buf.value() ))?)) } } netlink-packet-route-0.21.0/src/link/proto_info/mod.rs000064400000000000000000000003661046102023000210050ustar 00000000000000// SPDX-License-Identifier: MIT mod bridge; mod inet6; pub use self::bridge::LinkProtoInfoBridge; pub use self::inet6::LinkProtoInfoInet6; pub(crate) use self::bridge::VecLinkProtoInfoBridge; pub(crate) use self::inet6::VecLinkProtoInfoInet6; netlink-packet-route-0.21.0/src/link/sriov/broadcast.rs000064400000000000000000000022651046102023000211540ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{DecodeError, Emitable, Parseable}; const VF_INFO_BROADCAST_LEN: usize = 32; #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub struct VfInfoBroadcast { pub addr: [u8; VF_INFO_BROADCAST_LEN], } impl VfInfoBroadcast { pub fn new(addr: &[u8]) -> Self { let mut ret = Self::default(); if addr.len() > VF_INFO_BROADCAST_LEN { ret.addr.copy_from_slice(&addr[..VF_INFO_BROADCAST_LEN]) } else { ret.addr[..addr.len()].copy_from_slice(addr) } ret } } buffer!(VfInfoBroadcastBuffer(VF_INFO_BROADCAST_LEN) { addr: (slice, 0..VF_INFO_BROADCAST_LEN), }); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfInfoBroadcast { fn parse(buf: &VfInfoBroadcastBuffer<&T>) -> Result { Ok(Self::new(buf.addr())) } } impl Emitable for VfInfoBroadcast { fn buffer_len(&self) -> usize { VF_INFO_BROADCAST_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = VfInfoBroadcastBuffer::new(buffer); buffer.addr_mut().copy_from_slice(&self.addr); } } netlink-packet-route-0.21.0/src/link/sriov/guid.rs000064400000000000000000000017101046102023000201340ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{DecodeError, Emitable, Parseable}; const VF_INFO_GUID_LEN: usize = 12; #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub struct VfInfoGuid { pub vf_id: u32, pub guid: u64, } impl VfInfoGuid { pub fn new(vf_id: u32, guid: u64) -> Self { Self { vf_id, guid } } } buffer!(VfInfoGuidBuffer(VF_INFO_GUID_LEN) { vf_id: (u32, 0..4), guid: (u64, 4..12), }); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfInfoGuid { fn parse(buf: &VfInfoGuidBuffer<&T>) -> Result { Ok(Self::new(buf.vf_id(), buf.guid())) } } impl Emitable for VfInfoGuid { fn buffer_len(&self) -> usize { VF_INFO_GUID_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = VfInfoGuidBuffer::new(buffer); buffer.set_vf_id(self.vf_id); buffer.set_guid(self.guid); } } netlink-packet-route-0.21.0/src/link/sriov/link_state.rs000064400000000000000000000036721046102023000213520ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{DecodeError, Emitable, Parseable}; const VF_INFO_LINK_STATE_LEN: usize = 8; #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub struct VfInfoLinkState { pub vf_id: u32, pub state: VfLinkState, } impl VfInfoLinkState { pub fn new(vf_id: u32, state: VfLinkState) -> Self { Self { vf_id, state } } } buffer!(VfInfoLinkStateBuffer(VF_INFO_LINK_STATE_LEN) { vf_id: (u32, 0..4), state: (u32, 4..8), }); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfInfoLinkState { fn parse(buf: &VfInfoLinkStateBuffer<&T>) -> Result { Ok(Self::new(buf.vf_id(), buf.state().into())) } } impl Emitable for VfInfoLinkState { fn buffer_len(&self) -> usize { VF_INFO_LINK_STATE_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = VfInfoLinkStateBuffer::new(buffer); buffer.set_vf_id(self.vf_id); buffer.set_state(self.state.into()); } } const IFLA_VF_LINK_STATE_AUTO: u32 = 0; const IFLA_VF_LINK_STATE_ENABLE: u32 = 1; const IFLA_VF_LINK_STATE_DISABLE: u32 = 2; #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub enum VfLinkState { #[default] Auto, Enable, Disable, Other(u32), } impl From for VfLinkState { fn from(d: u32) -> Self { match d { IFLA_VF_LINK_STATE_AUTO => Self::Auto, IFLA_VF_LINK_STATE_ENABLE => Self::Enable, IFLA_VF_LINK_STATE_DISABLE => Self::Disable, _ => Self::Other(d), } } } impl From for u32 { fn from(v: VfLinkState) -> u32 { match v { VfLinkState::Auto => IFLA_VF_LINK_STATE_AUTO, VfLinkState::Enable => IFLA_VF_LINK_STATE_ENABLE, VfLinkState::Disable => IFLA_VF_LINK_STATE_DISABLE, VfLinkState::Other(d) => d, } } } netlink-packet-route-0.21.0/src/link/sriov/mac.rs000064400000000000000000000024301046102023000177440ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{DecodeError, Emitable, Parseable}; const MAX_ADDR_LEN: usize = 32; const VF_INFO_MAC_LEN: usize = MAX_ADDR_LEN + 4; #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub struct VfInfoMac { pub vf_id: u32, pub mac: [u8; MAX_ADDR_LEN], } impl VfInfoMac { pub fn new(vf_id: u32, mac: &[u8]) -> Self { let mut ret = Self { vf_id, ..Default::default() }; if mac.len() >= MAX_ADDR_LEN { ret.mac.copy_from_slice(&mac[..MAX_ADDR_LEN]); } else { ret.mac[..mac.len()].copy_from_slice(mac); } ret } } buffer!(VfInfoMacBuffer(VF_INFO_MAC_LEN) { vf_id: (u32, 0..4), mac: (slice, 4..VF_INFO_MAC_LEN), }); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfInfoMac { fn parse(buf: &VfInfoMacBuffer<&T>) -> Result { Ok(Self::new(buf.vf_id(), buf.mac())) } } impl Emitable for VfInfoMac { fn buffer_len(&self) -> usize { VF_INFO_MAC_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = VfInfoMacBuffer::new(buffer); buffer.set_vf_id(self.vf_id); buffer.mac_mut().copy_from_slice(&self.mac); } } netlink-packet-route-0.21.0/src/link/sriov/mod.rs000064400000000000000000000020621046102023000177640ustar 00000000000000// SPDX-License-Identifier: MIT mod broadcast; mod guid; mod link_state; mod mac; mod rate; mod rss_query; mod spoofchk; mod stats; mod trust; mod tx_rate; mod vf_list; mod vf_port; mod vf_vlan; mod vlan; pub(crate) use self::vf_list::VecLinkVfInfo; pub(crate) use self::vf_port::VecLinkVfPort; pub use self::broadcast::{VfInfoBroadcast, VfInfoBroadcastBuffer}; pub use self::guid::{VfInfoGuid, VfInfoGuidBuffer}; pub use self::link_state::{ VfInfoLinkState, VfInfoLinkStateBuffer, VfLinkState, }; pub use self::mac::{VfInfoMac, VfInfoMacBuffer}; pub use self::rate::{VfInfoRate, VfInfoRateBuffer}; pub use self::rss_query::{VfInfoRssQueryEn, VfInfoRssQueryEnBuffer}; pub use self::spoofchk::{VfInfoSpoofCheck, VfInfoSpoofCheckBuffer}; pub use self::stats::VfStats; pub use self::trust::{VfInfoTrust, VfInfoTrustBuffer}; pub use self::tx_rate::{VfInfoTxRate, VfInfoTxRateBuffer}; pub use self::vf_list::{LinkVfInfo, VfInfo}; pub use self::vf_port::{LinkVfPort, VfPort}; pub use self::vf_vlan::{VfVlan, VfVlanInfo}; pub use self::vlan::{VfInfoVlan, VfInfoVlanBuffer}; netlink-packet-route-0.21.0/src/link/sriov/rate.rs000064400000000000000000000024151046102023000201420ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{DecodeError, Emitable, Parseable}; const VF_INFO_RATE_LEN: usize = 12; #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub struct VfInfoRate { pub vf_id: u32, pub min_tx_rate: u32, pub max_tx_rate: u32, } impl VfInfoRate { pub fn new(vf_id: u32, min_tx_rate: u32, max_tx_rate: u32) -> Self { Self { vf_id, min_tx_rate, max_tx_rate, } } } buffer!(VfInfoRateBuffer(VF_INFO_RATE_LEN) { vf_id: (u32, 0..4), min_tx_rate: (u32, 4..8), max_tx_rate: (u32, 8..12) }); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfInfoRate { fn parse(buf: &VfInfoRateBuffer<&T>) -> Result { Ok(Self { vf_id: buf.vf_id(), min_tx_rate: buf.min_tx_rate(), max_tx_rate: buf.max_tx_rate(), }) } } impl Emitable for VfInfoRate { fn buffer_len(&self) -> usize { VF_INFO_RATE_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = VfInfoRateBuffer::new(buffer); buffer.set_vf_id(self.vf_id); buffer.set_min_tx_rate(self.min_tx_rate); buffer.set_max_tx_rate(self.max_tx_rate); } } netlink-packet-route-0.21.0/src/link/sriov/rss_query.rs000064400000000000000000000021601046102023000212400ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{DecodeError, Emitable, Parseable}; const VF_INFO_RSS_QUERY_EN_LEN: usize = 8; #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub struct VfInfoRssQueryEn { pub vf_id: u32, pub enabled: bool, } impl VfInfoRssQueryEn { pub fn new(vf_id: u32, enabled: bool) -> Self { Self { vf_id, enabled } } } buffer!(VfInfoRssQueryEnBuffer(VF_INFO_RSS_QUERY_EN_LEN) { vf_id: (u32, 0..4), setting: (u32, 4..8), }); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfInfoRssQueryEn { fn parse(buf: &VfInfoRssQueryEnBuffer<&T>) -> Result { Ok(Self::new( buf.vf_id(), buf.setting() > 0 && buf.setting() != u32::MAX, )) } } impl Emitable for VfInfoRssQueryEn { fn buffer_len(&self) -> usize { VF_INFO_RSS_QUERY_EN_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = VfInfoRssQueryEnBuffer::new(buffer); buffer.set_vf_id(self.vf_id); buffer.set_setting(self.enabled as u32); } } netlink-packet-route-0.21.0/src/link/sriov/spoofchk.rs000064400000000000000000000021441046102023000210220ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{DecodeError, Emitable, Parseable}; const VF_INFO_SPOOFCHK_LEN: usize = 8; #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub struct VfInfoSpoofCheck { pub vf_id: u32, pub enabled: bool, } impl VfInfoSpoofCheck { pub fn new(vf_id: u32, enabled: bool) -> Self { Self { vf_id, enabled } } } buffer!(VfInfoSpoofCheckBuffer(VF_INFO_SPOOFCHK_LEN) { vf_id: (u32, 0..4), setting: (u32, 4..8), }); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfInfoSpoofCheck { fn parse(buf: &VfInfoSpoofCheckBuffer<&T>) -> Result { Ok(Self::new( buf.vf_id(), buf.setting() > 0 && buf.setting() != u32::MAX, )) } } impl Emitable for VfInfoSpoofCheck { fn buffer_len(&self) -> usize { VF_INFO_SPOOFCHK_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = VfInfoSpoofCheckBuffer::new(buffer); buffer.set_vf_id(self.vf_id); buffer.set_setting(self.enabled as u32); } } netlink-packet-route-0.21.0/src/link/sriov/stats.rs000064400000000000000000000100421046102023000203400ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::parse_u64, DecodeError, Parseable, }; const IFLA_VF_STATS_RX_PACKETS: u16 = 0; const IFLA_VF_STATS_TX_PACKETS: u16 = 1; const IFLA_VF_STATS_RX_BYTES: u16 = 2; const IFLA_VF_STATS_TX_BYTES: u16 = 3; const IFLA_VF_STATS_BROADCAST: u16 = 4; const IFLA_VF_STATS_MULTICAST: u16 = 5; // const IFLA_VF_STATS_PAD: u16 = 6; const IFLA_VF_STATS_RX_DROPPED: u16 = 7; const IFLA_VF_STATS_TX_DROPPED: u16 = 8; #[derive(Debug, Clone, Eq, PartialEq)] #[non_exhaustive] pub enum VfStats { RxPackets(u64), TxPackets(u64), RxBytes(u64), TxBytes(u64), Broadcast(u64), Multicast(u64), RxDropped(u64), TxDropped(u64), Other(DefaultNla), } impl Nla for VfStats { fn value_len(&self) -> usize { match self { Self::Other(v) => v.value_len(), _ => 8, } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::RxPackets(v) | Self::TxPackets(v) | Self::RxBytes(v) | Self::TxBytes(v) | Self::Broadcast(v) | Self::Multicast(v) | Self::RxDropped(v) | Self::TxDropped(v) => NativeEndian::write_u64(buffer, *v), Self::Other(attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::RxPackets(_) => IFLA_VF_STATS_RX_PACKETS, Self::TxPackets(_) => IFLA_VF_STATS_TX_PACKETS, Self::RxBytes(_) => IFLA_VF_STATS_RX_BYTES, Self::TxBytes(_) => IFLA_VF_STATS_TX_BYTES, Self::Broadcast(_) => IFLA_VF_STATS_BROADCAST, Self::Multicast(_) => IFLA_VF_STATS_MULTICAST, Self::RxDropped(_) => IFLA_VF_STATS_RX_DROPPED, Self::TxDropped(_) => IFLA_VF_STATS_TX_DROPPED, Self::Other(v) => v.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfStats { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { IFLA_VF_STATS_RX_PACKETS => { Self::RxPackets(parse_u64(payload).context(format!( "invalid IFLA_VF_STATS_RX_PACKETS value {payload:?}" ))?) } IFLA_VF_STATS_TX_PACKETS => { Self::TxPackets(parse_u64(payload).context(format!( "invalid IFLA_VF_STATS_TX_PACKETS value {payload:?}" ))?) } IFLA_VF_STATS_RX_BYTES => { Self::RxBytes(parse_u64(payload).context(format!( "invalid IFLA_VF_STATS_RX_BYTES value {payload:?}" ))?) } IFLA_VF_STATS_TX_BYTES => { Self::TxBytes(parse_u64(payload).context(format!( "invalid IFLA_VF_STATS_TX_BYTES value {payload:?}" ))?) } IFLA_VF_STATS_BROADCAST => { Self::Broadcast(parse_u64(payload).context(format!( "invalid IFLA_VF_STATS_BROADCAST value {payload:?}" ))?) } IFLA_VF_STATS_MULTICAST => { Self::Multicast(parse_u64(payload).context(format!( "invalid IFLA_VF_STATS_MULTICAST value {payload:?}" ))?) } IFLA_VF_STATS_RX_DROPPED => { Self::RxDropped(parse_u64(payload).context(format!( "invalid IFLA_VF_STATS_RX_DROPPED value {payload:?}" ))?) } IFLA_VF_STATS_TX_DROPPED => { Self::TxDropped(parse_u64(payload).context(format!( "invalid IFLA_VF_STATS_TX_DROPPED value {payload:?}" ))?) } kind => Self::Other(DefaultNla::parse(buf).context(format!( "failed to parse {kind} as DefaultNla: {payload:?}" ))?), }) } } netlink-packet-route-0.21.0/src/link/sriov/trust.rs000064400000000000000000000020631046102023000203670ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{DecodeError, Emitable, Parseable}; const VF_INFO_TRUST_LEN: usize = 8; #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub struct VfInfoTrust { pub vf_id: u32, pub enabled: bool, } impl VfInfoTrust { pub fn new(vf_id: u32, enabled: bool) -> Self { Self { vf_id, enabled } } } buffer!(VfInfoTrustBuffer(VF_INFO_TRUST_LEN) { vf_id: (u32, 0..4), setting: (u32, 4..8), }); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfInfoTrust { fn parse(buf: &VfInfoTrustBuffer<&T>) -> Result { Ok(Self::new( buf.vf_id(), buf.setting() > 0 && buf.setting() != u32::MAX, )) } } impl Emitable for VfInfoTrust { fn buffer_len(&self) -> usize { VF_INFO_TRUST_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = VfInfoTrustBuffer::new(buffer); buffer.set_vf_id(self.vf_id); buffer.set_setting(self.enabled as u32); } } netlink-packet-route-0.21.0/src/link/sriov/tx_rate.rs000064400000000000000000000020131046102023000206470ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{DecodeError, Emitable, Parseable}; const VF_INFO_TX_RATE_LEN: usize = 8; #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub struct VfInfoTxRate { pub vf_id: u32, pub rate: u32, } impl VfInfoTxRate { pub fn new(vf_id: u32, rate: u32) -> Self { Self { vf_id, rate } } } buffer!(VfInfoTxRateBuffer(VF_INFO_TX_RATE_LEN) { vf_id: (u32, 0..4), rate: (u32, 4..8), }); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfInfoTxRate { fn parse(buf: &VfInfoTxRateBuffer<&T>) -> Result { Ok(Self { vf_id: buf.vf_id(), rate: buf.rate(), }) } } impl Emitable for VfInfoTxRate { fn buffer_len(&self) -> usize { VF_INFO_TX_RATE_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = VfInfoTxRateBuffer::new(buffer); buffer.set_vf_id(self.vf_id); buffer.set_rate(self.rate); } } netlink-packet-route-0.21.0/src/link/sriov/vf_list.rs000064400000000000000000000222101046102023000206500ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, DecodeError, Emitable, Parseable, }; use crate::link::{ VfInfoBroadcast, VfInfoBroadcastBuffer, VfInfoGuid, VfInfoGuidBuffer, VfInfoLinkState, VfInfoLinkStateBuffer, VfInfoMac, VfInfoMacBuffer, VfInfoRate, VfInfoRateBuffer, VfInfoRssQueryEn, VfInfoRssQueryEnBuffer, VfInfoSpoofCheck, VfInfoSpoofCheckBuffer, VfInfoTrust, VfInfoTrustBuffer, VfInfoTxRate, VfInfoTxRateBuffer, VfInfoVlan, VfInfoVlanBuffer, VfStats, VfVlan, }; const IFLA_VF_INFO: u16 = 1; #[derive(Debug, Clone, Eq, PartialEq)] pub(crate) struct VecLinkVfInfo(pub(crate) Vec); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecLinkVfInfo { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; for nla in NlasIterator::new(buf.into_inner()) { let nla = &nla.context(format!( "invalid IFLA_VFINFO_LIST value: {:?}", buf.value() ))?; if nla.kind() == IFLA_VF_INFO { nlas.push(LinkVfInfo::parse(&NlaBuffer::new_checked( nla.value(), )?)?); } else { log::warn!( "BUG: Expecting IFLA_VF_INFO in IFLA_VFINFO_LIST, \ but got {}", nla.kind() ); } } Ok(Self(nlas)) } } #[derive(Debug, Clone, Default, Eq, PartialEq)] pub struct LinkVfInfo(pub Vec); impl Nla for LinkVfInfo { fn value_len(&self) -> usize { self.0.as_slice().buffer_len() } fn emit_value(&self, buffer: &mut [u8]) { self.0.as_slice().emit(buffer) } fn kind(&self) -> u16 { IFLA_VF_INFO } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for LinkVfInfo { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; for nla in NlasIterator::new(buf.into_inner()) { let nla = &nla.context(format!( "invalid IFLA_VF_INFO value {:?}", buf.value() ))?; nlas.push(VfInfo::parse(nla)?); } Ok(Self(nlas)) } } const IFLA_VF_MAC: u16 = 1; const IFLA_VF_VLAN: u16 = 2; const IFLA_VF_TX_RATE: u16 = 3; const IFLA_VF_SPOOFCHK: u16 = 4; const IFLA_VF_LINK_STATE: u16 = 5; const IFLA_VF_RATE: u16 = 6; const IFLA_VF_RSS_QUERY_EN: u16 = 7; const IFLA_VF_STATS: u16 = 8; const IFLA_VF_TRUST: u16 = 9; const IFLA_VF_IB_NODE_GUID: u16 = 10; const IFLA_VF_IB_PORT_GUID: u16 = 11; const IFLA_VF_VLAN_LIST: u16 = 12; const IFLA_VF_BROADCAST: u16 = 13; #[derive(Debug, Clone, Eq, PartialEq)] #[non_exhaustive] pub enum VfInfo { Mac(VfInfoMac), Broadcast(VfInfoBroadcast), Vlan(VfInfoVlan), Rate(VfInfoRate), TxRate(VfInfoTxRate), SpoofCheck(VfInfoSpoofCheck), LinkState(VfInfoLinkState), RssQueryEn(VfInfoRssQueryEn), Trust(VfInfoTrust), IbNodeGuid(VfInfoGuid), IbPortGuid(VfInfoGuid), VlanList(Vec), Stats(Vec), Other(DefaultNla), } impl Nla for VfInfo { fn value_len(&self) -> usize { match self { Self::Mac(v) => v.buffer_len(), Self::Vlan(v) => v.buffer_len(), Self::Broadcast(v) => v.buffer_len(), Self::Rate(v) => v.buffer_len(), Self::TxRate(v) => v.buffer_len(), Self::SpoofCheck(v) => v.buffer_len(), Self::LinkState(v) => v.buffer_len(), Self::RssQueryEn(v) => v.buffer_len(), Self::Trust(v) => v.buffer_len(), Self::IbNodeGuid(v) => v.buffer_len(), Self::IbPortGuid(v) => v.buffer_len(), Self::VlanList(v) => v.as_slice().buffer_len(), Self::Stats(v) => v.as_slice().buffer_len(), Self::Other(v) => v.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Mac(v) => v.emit(buffer), Self::Vlan(v) => v.emit(buffer), Self::Broadcast(v) => v.emit(buffer), Self::Rate(v) => v.emit(buffer), Self::TxRate(v) => v.emit(buffer), Self::SpoofCheck(v) => v.emit(buffer), Self::LinkState(v) => v.emit(buffer), Self::RssQueryEn(v) => v.emit(buffer), Self::Trust(v) => v.emit(buffer), Self::IbNodeGuid(v) => v.emit(buffer), Self::IbPortGuid(v) => v.emit(buffer), Self::VlanList(v) => v.as_slice().emit(buffer), Self::Stats(v) => v.as_slice().emit(buffer), Self::Other(attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Mac(_) => IFLA_VF_MAC, Self::Vlan(_) => IFLA_VF_VLAN, Self::Broadcast(_) => IFLA_VF_BROADCAST, Self::Rate(_) => IFLA_VF_RATE, Self::TxRate(_) => IFLA_VF_TX_RATE, Self::SpoofCheck(_) => IFLA_VF_SPOOFCHK, Self::LinkState(_) => IFLA_VF_LINK_STATE, Self::RssQueryEn(_) => IFLA_VF_RSS_QUERY_EN, Self::Trust(_) => IFLA_VF_TRUST, Self::IbNodeGuid(_) => IFLA_VF_IB_NODE_GUID, Self::IbPortGuid(_) => IFLA_VF_IB_PORT_GUID, Self::VlanList(_) => IFLA_VF_VLAN_LIST, Self::Stats(_) => IFLA_VF_STATS, Self::Other(v) => v.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfInfo { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { IFLA_VF_MAC => Self::Mac( VfInfoMac::parse(&VfInfoMacBuffer::new(payload)) .context(format!("invalid IFLA_VF_MAC {payload:?}"))?, ), IFLA_VF_VLAN => Self::Vlan( VfInfoVlan::parse(&VfInfoVlanBuffer::new(payload)) .context(format!("invalid IFLA_VF_VLAN {payload:?}"))?, ), IFLA_VF_BROADCAST => Self::Broadcast( VfInfoBroadcast::parse(&VfInfoBroadcastBuffer::new(payload)) .context(format!( "invalid IFLA_VF_BROADCAST {payload:?}" ))?, ), IFLA_VF_RATE => Self::Rate( VfInfoRate::parse(&VfInfoRateBuffer::new(payload)) .context(format!("invalid IFLA_VF_RATE {payload:?}"))?, ), IFLA_VF_TX_RATE => Self::TxRate( VfInfoTxRate::parse(&VfInfoTxRateBuffer::new(payload)) .context(format!("invalid IFLA_VF_TX_RATE {payload:?}"))?, ), IFLA_VF_SPOOFCHK => Self::SpoofCheck( VfInfoSpoofCheck::parse(&VfInfoSpoofCheckBuffer::new(payload)) .context(format!("invalid IFLA_VF_SPOOFCHK {payload:?}"))?, ), IFLA_VF_LINK_STATE => Self::LinkState( VfInfoLinkState::parse(&VfInfoLinkStateBuffer::new(payload)) .context(format!( "invalid IFLA_VF_LINK_STATE {payload:?}" ))?, ), IFLA_VF_RSS_QUERY_EN => Self::RssQueryEn( VfInfoRssQueryEn::parse(&VfInfoRssQueryEnBuffer::new(payload)) .context(format!( "invalid IFLA_VF_RSS_QUERY_EN {payload:?}" ))?, ), IFLA_VF_TRUST => Self::Trust( VfInfoTrust::parse(&VfInfoTrustBuffer::new(payload)) .context(format!("invalid IFLA_VF_TRUST {payload:?}"))?, ), IFLA_VF_IB_NODE_GUID => Self::IbNodeGuid( VfInfoGuid::parse(&VfInfoGuidBuffer::new(payload)).context( format!("invalid IFLA_VF_IB_NODE_GUID {payload:?}"), )?, ), IFLA_VF_IB_PORT_GUID => Self::IbPortGuid( VfInfoGuid::parse(&VfInfoGuidBuffer::new(payload)).context( format!("invalid IFLA_VF_IB_PORT_GUID {payload:?}"), )?, ), IFLA_VF_VLAN_LIST => { let mut nlas: Vec = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_VF_VLAN_LIST value: {:?}", buf.value() ))?; nlas.push(VfVlan::parse(nla)?); } Self::VlanList(nlas) } IFLA_VF_STATS => { let mut nlas: Vec = Vec::new(); for nla in NlasIterator::new(payload) { let nla = &nla.context(format!( "invalid IFLA_VF_STATS value: {:?}", buf.value() ))?; nlas.push(VfStats::parse(nla)?); } Self::Stats(nlas) } kind => Self::Other(DefaultNla::parse(buf).context(format!( "failed to parse {kind} as DefaultNla: {payload:?}" ))?), }) } } netlink-packet-route-0.21.0/src/link/sriov/vf_port.rs000064400000000000000000000064141046102023000206710ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, DecodeError, Emitable, Parseable, }; #[derive(Debug, Clone, Eq, PartialEq)] pub(crate) struct VecLinkVfPort(pub(crate) Vec); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecLinkVfPort { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; for nla in NlasIterator::new(buf.into_inner()) { let nla = &nla.context(format!( "invalid IFLA_VF_PORTS value: {:?}", buf.value() ))?; if nla.kind() == IFLA_VF_PORT { nlas.push(LinkVfPort::parse(&NlaBuffer::new_checked( nla.value(), )?)?); } else { log::warn!( "BUG: Expecting IFLA_VF_PORT in IFLA_VF_PORTS, \ but got {}", nla.kind() ); } } Ok(Self(nlas)) } } const IFLA_VF_PORT: u16 = 1; #[derive(Debug, Clone, Default, Eq, PartialEq)] pub struct LinkVfPort(pub Vec); impl Nla for LinkVfPort { fn value_len(&self) -> usize { self.0.as_slice().buffer_len() } fn emit_value(&self, buffer: &mut [u8]) { self.0.as_slice().emit(buffer) } fn kind(&self) -> u16 { IFLA_VF_PORT } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for LinkVfPort { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; for nla in NlasIterator::new(buf.into_inner()) { let nla = &nla.context(format!( "invalid IFLA_VF_PORT value {:?}", buf.value() ))?; nlas.push(VfPort::parse(nla)?); } Ok(Self(nlas)) } } /* const IFLA_PORT_VF: u16 = 1; const IFLA_PORT_PROFILE: u16 = 2; // No kernel code is accepting or generating IFLA_PORT_VSI_TYPE. // const IFLA_PORT_VSI_TYPE: u16 = 3; const IFLA_PORT_INSTANCE_UUID: u16 = 4; const IFLA_PORT_HOST_UUID: u16 = 5; const IFLA_PORT_REQUEST: u16 = 6; const IFLA_PORT_RESPONSE: u16 = 7; const UUID_LEN: usize = 16; */ #[derive(Debug, Clone, Eq, PartialEq)] #[non_exhaustive] pub enum VfPort { // Vf(u32), // Profile(String), // InstanceUuid([u8; UUID_LEN]), // HostUuid([u8; UUID_LEN]), // Request(u8), Other(DefaultNla), } impl Nla for VfPort { fn value_len(&self) -> usize { match self { Self::Other(v) => v.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Other(attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Other(v) => v.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfPort { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); #[allow(clippy::match_single_binding)] Ok(match buf.kind() { kind => Self::Other(DefaultNla::parse(buf).context(format!( "failed to parse {kind} as DefaultNla: {payload:?}" ))?), }) } } netlink-packet-route-0.21.0/src/link/sriov/vf_vlan.rs000064400000000000000000000055071046102023000206470ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, DecodeError, Emitable, Parseable, }; use crate::link::VlanProtocol; const IFLA_VF_VLAN_INFO: u16 = 1; #[derive(Debug, Clone, Eq, PartialEq)] #[non_exhaustive] pub enum VfVlan { Info(VfVlanInfo), Other(DefaultNla), } impl Nla for VfVlan { fn value_len(&self) -> usize { match self { Self::Info(v) => v.buffer_len(), Self::Other(v) => v.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Info(v) => v.emit(buffer), Self::Other(attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Info(_) => IFLA_VF_VLAN_INFO, Self::Other(v) => v.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfVlan { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { IFLA_VF_VLAN_INFO => Self::Info( VfVlanInfo::parse(&VfVlanInfoBuffer::new(payload)).context( format!("invalid IFLA_VF_VLAN_INFO {payload:?}"), )?, ), kind => Self::Other(DefaultNla::parse(buf).context(format!( "failed to parse {kind} as DefaultNla: {payload:?}" ))?), }) } } const VF_VLAN_INFO_LEN: usize = 16; // with 2 bytes padding #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub struct VfVlanInfo { pub vf_id: u32, pub vlan_id: u32, pub qos: u32, pub protocol: VlanProtocol, } impl VfVlanInfo { pub fn new( vf_id: u32, vlan_id: u32, qos: u32, protocol: VlanProtocol, ) -> Self { Self { vf_id, vlan_id, qos, protocol, } } } buffer!(VfVlanInfoBuffer(VF_VLAN_INFO_LEN) { vf_id: (u32, 0..4), vlan_id: (u32, 4..8), qos: (u32, 8..12), protocol: (u16, 12..14), }); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfVlanInfo { fn parse(buf: &VfVlanInfoBuffer<&T>) -> Result { Ok(Self { vf_id: buf.vf_id(), vlan_id: buf.vlan_id(), qos: buf.qos(), protocol: u16::from_be(buf.protocol()).into(), }) } } impl Emitable for VfVlanInfo { fn buffer_len(&self) -> usize { VF_VLAN_INFO_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = VfVlanInfoBuffer::new(buffer); buffer.set_vf_id(self.vf_id); buffer.set_vlan_id(self.vlan_id); buffer.set_qos(self.qos); buffer.set_protocol(u16::from(self.protocol).to_be()); } } netlink-packet-route-0.21.0/src/link/sriov/vlan.rs000064400000000000000000000022551046102023000201510ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{DecodeError, Emitable, Parseable}; const VF_INFO_VLAN_LEN: usize = 12; #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub struct VfInfoVlan { pub vf_id: u32, pub vlan_id: u32, pub qos: u32, } impl VfInfoVlan { pub fn new(vf_id: u32, vlan_id: u32, qos: u32) -> Self { Self { vf_id, vlan_id, qos, } } } buffer!(VfInfoVlanBuffer(VF_INFO_VLAN_LEN) { vf_id: (u32, 0..4), vlan_id: (u32, 4..8), qos: (u32, 8..12) }); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfInfoVlan { fn parse(buf: &VfInfoVlanBuffer<&T>) -> Result { Ok(Self { vf_id: buf.vf_id(), vlan_id: buf.vlan_id(), qos: buf.qos(), }) } } impl Emitable for VfInfoVlan { fn buffer_len(&self) -> usize { VF_INFO_VLAN_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = VfInfoVlanBuffer::new(buffer); buffer.set_vf_id(self.vf_id); buffer.set_vlan_id(self.vlan_id); buffer.set_qos(self.qos); } } netlink-packet-route-0.21.0/src/link/stats.rs000064400000000000000000000117601046102023000172060ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; pub(crate) const LINK_STATS_LEN: usize = 96; #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[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, } 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.21.0/src/link/stats64.rs000064400000000000000000000123571046102023000173630ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; pub(crate) const LINK_STATS64_LEN: usize = 200; 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), rx_otherhost_dropped: (u64, 192..200), }); #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[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, pub rx_otherhost_dropped: 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(), rx_otherhost_dropped: buf.rx_otherhost_dropped(), }) } } 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); buffer.set_rx_otherhost_dropped(self.rx_otherhost_dropped); } } netlink-packet-route-0.21.0/src/link/tests/bond.rs000064400000000000000000000224411046102023000201320ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::link::link_flag::LinkFlags; use crate::link::{ BondArpValidate, BondMode, BondPortState, InfoBond, InfoBondPort, InfoData, InfoKind, InfoPortData, InfoPortKind, LinkAttribute, LinkHeader, LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, Map, MiiStatus, State, }; use crate::{AddressFamily, RouteNetlinkMessage}; #[test] fn test_bond_link_info() { let raw: Vec = vec![ 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x43, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x00, // length 204 0x12, 0x00, // IFLA_LINKINFO 18 0x09, 0x00, // length 9 0x01, 0x00, // IFLA_INFO_KIND 1 0x62, 0x6f, 0x6e, 0x64, 0x00, // 'bond\0' 0x00, 0x00, 0x00, // padding 0xbc, 0x00, // length 188 0x02, 0x00, // IFLA_INFO_DATA 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x13, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x1e, 0x00, 0x02, 0x00, 0x00, 0x00, ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 24, link_layer_type: LinkLayerType::Ether, flags: LinkFlags::Broadcast | LinkFlags::Controller | LinkFlags::LowerUp | LinkFlags::Multicast | LinkFlags::Running | LinkFlags::Up, change_mask: LinkFlags::empty(), }, attributes: vec![LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::Bond), LinkInfo::Data(InfoData::Bond(vec![ InfoBond::Mode(BondMode::BalanceRr), InfoBond::MiiMon(0), InfoBond::UpDelay(0), InfoBond::DownDelay(0), InfoBond::PeerNotifDelay(0), InfoBond::UseCarrier(1), InfoBond::ArpInterval(0), InfoBond::ArpValidate(BondArpValidate::None), InfoBond::ArpAllTargets(0), InfoBond::PrimaryReselect(0), InfoBond::FailOverMac(0), InfoBond::XmitHashPolicy(0), InfoBond::ResendIgmp(1), InfoBond::NumPeerNotif(1), InfoBond::AllPortsActive(0), InfoBond::MinLinks(0), InfoBond::LpInterval(1), InfoBond::PacketsPerPort(1), InfoBond::AdLacpActive(1), InfoBond::AdLacpRate(0), InfoBond::AdSelect(0), InfoBond::TlbDynamicLb(1), InfoBond::MissedMax(2), ])), ])], }; assert_eq!( expected, LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } #[test] fn test_bond_port_link_info() { let raw: Vec = vec![ 0x00, 0x00, 0x01, 0x00, 0x15, 0x00, 0x00, 0x00, 0x43, 0x18, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x12, 0x00, 0x09, 0x00, 0x01, 0x00, 0x76, 0x65, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x04, 0x00, 0x62, 0x6f, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x05, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, 0x23, 0x45, 0x67, 0x89, 0x1a, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 21, link_layer_type: LinkLayerType::Ether, flags: LinkFlags::Broadcast | LinkFlags::LowerUp | LinkFlags::Multicast | LinkFlags::Port | LinkFlags::Running | LinkFlags::Up, change_mask: LinkFlags::empty(), }, attributes: vec![LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::Veth), LinkInfo::PortKind(InfoPortKind::Bond), LinkInfo::PortData(InfoPortData::BondPort(vec![ InfoBondPort::BondPortState(BondPortState::Active), InfoBondPort::MiiStatus(MiiStatus::Up), InfoBondPort::LinkFailureCount(0), InfoBondPort::PermHwaddr(vec![ 0x00, 0x23, 0x45, 0x67, 0x89, 0x1a, ]), InfoBondPort::QueueId(0), InfoBondPort::Prio(0), ])), ])], }; assert_eq!( expected, LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } #[test] fn test_bond_arp_validate() { let raw: Vec = vec![ 0x00, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x03, 0x00, 0x62, 0x6f, 0x6e, 0x64, 0x30, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x05, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0xdc, 0x05, 0x00, 0x00, 0x08, 0x00, 0x32, 0x00, 0x44, 0x00, 0x00, 0x00, 0x08, 0x00, 0x33, 0x00, 0xff, 0xff, 0x00, 0x00, 0x08, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1f, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x28, 0x00, 0xff, 0xff, 0x00, 0x00, 0x08, 0x00, 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x20, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x06, 0x00, 0x6e, 0x6f, 0x6f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x92, 0xb3, 0xba, 0x20, 0xd7, 0xa0, 0x00, 0x00, 0x0a, 0x00, 0x02, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 5, link_layer_type: LinkLayerType::Ether, flags: LinkFlags::Broadcast | LinkFlags::Controller | LinkFlags::Multicast, change_mask: LinkFlags::empty(), }, attributes: vec![ LinkAttribute::IfName("bond0".into()), LinkAttribute::TxQueueLen(1000), LinkAttribute::OperState(State::Down), LinkAttribute::Mode(0), LinkAttribute::Mtu(1500), LinkAttribute::MinMtu(68), LinkAttribute::MaxMtu(65535), LinkAttribute::Group(0), LinkAttribute::Promiscuity(0), LinkAttribute::NumTxQueues(16), LinkAttribute::GsoMaxSegs(65535), LinkAttribute::GsoMaxSize(65536), LinkAttribute::NumRxQueues(16), LinkAttribute::Carrier(0), LinkAttribute::Qdisc("noop".into()), LinkAttribute::CarrierChanges(1), LinkAttribute::CarrierUpCount(0), LinkAttribute::CarrierDownCount(1), LinkAttribute::ProtoDown(0), LinkAttribute::Map(Map { memory_start: 0, memory_end: 0, base_address: 0, irq: 0, dma: 0, port: 0, }), LinkAttribute::Address(vec![0x92, 0xb3, 0xba, 0x20, 0xd7, 0xa0]), LinkAttribute::Broadcast(vec![0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), ], }; let packet = RouteNetlinkMessage::NewLink(expected); let mut buf = vec![0u8; packet.buffer_len()]; packet.emit(&mut buf); assert_eq!(raw, buf); } netlink-packet-route-0.21.0/src/link/tests/bridge.rs000064400000000000000000000753761046102023000204630ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::{DefaultNla, NlaBuffer}, Emitable, Parseable, }; use crate::link::{ af_spec::VecAfSpecBridge, link_flag::LinkFlags, AfSpecBridge, AfSpecInet, AfSpecInet6, AfSpecUnspec, BridgeFlag, BridgeId, BridgeMode, BridgePortMulticastRouter, BridgePortState, BridgeVlanInfo, BridgeVlanInfoFlags, BridgeVlanTunnelInfo, Inet6CacheInfo, Inet6DevConf, Inet6IfaceFlags, InetDevConf, InfoBridge, InfoBridgePort, InfoData, InfoKind, InfoPortData, InfoPortKind, LinkAttribute, LinkHeader, LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, LinkXdp, Map, State, Stats, Stats64, XdpAttached, }; use crate::AddressFamily; #[test] fn test_parse_link_bridge_no_extention_mask() { let raw = vec![ 0x00, 0x00, 0x01, 0x00, 0x35, 0x00, 0x00, 0x00, 0x43, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00, 0x62, 0x72, 0x30, 0x00, 0x08, 0x00, 0x0d, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x05, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0xdc, 0x05, 0x00, 0x00, 0x08, 0x00, 0x32, 0x00, 0x44, 0x00, 0x00, 0x00, 0x08, 0x00, 0x33, 0x00, 0xff, 0xff, 0x00, 0x00, 0x08, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x28, 0x00, 0xff, 0xff, 0x00, 0x00, 0x08, 0x00, 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x3b, 0x00, 0xf8, 0xff, 0x07, 0x00, 0x08, 0x00, 0x3c, 0x00, 0xff, 0xff, 0x00, 0x00, 0x08, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x6e, 0x6f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x00, 0x08, 0x00, 0x23, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x2f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x23, 0x45, 0x67, 0x89, 0x1c, 0x00, 0x00, 0x0a, 0x00, 0x02, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xcc, 0x00, 0x17, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x07, 0x00, 0x36, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x88, 0x14, 0x00, 0x00, 0x42, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x2b, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x01, 0x12, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x00, 0x00, 0x9c, 0x01, 0x02, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x13, 0x00, 0xa6, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0xdb, 0x05, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0xc7, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00, 0xcf, 0x07, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x2f, 0x75, 0x00, 0x00, 0x08, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x80, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x80, 0x00, 0x00, 0x23, 0x45, 0x67, 0x89, 0x1c, 0x0c, 0x00, 0x0a, 0x00, 0x80, 0x00, 0x00, 0x23, 0x45, 0x67, 0x89, 0x1c, 0x06, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x14, 0x00, 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x81, 0x00, 0x00, 0x00, 0x06, 0x00, 0x27, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x17, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1b, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x1c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x2b, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x2c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x1e, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x1f, 0x00, 0x8f, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x20, 0x00, 0x9b, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x21, 0x00, 0xd3, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x22, 0x00, 0xe7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x23, 0x00, 0x34, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x01, 0x1a, 0x00, 0x8c, 0x00, 0x02, 0x00, 0x88, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x01, 0x0a, 0x00, 0x08, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x80, 0x14, 0x00, 0x05, 0x00, 0xff, 0xff, 0x00, 0x00, 0xe7, 0xc4, 0x92, 0x01, 0x18, 0x92, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0xf0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xdc, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xa0, 0x0f, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3a, 0x09, 0x00, 0x80, 0x51, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xee, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x3e, 0x80, ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 53, link_layer_type: LinkLayerType::Ether, flags: LinkFlags::Broadcast | LinkFlags::LowerUp | LinkFlags::Multicast | LinkFlags::Running | LinkFlags::Up, change_mask: LinkFlags::empty(), }, attributes: vec![ LinkAttribute::IfName("br0".into()), LinkAttribute::TxQueueLen(1000), LinkAttribute::OperState(State::Up), LinkAttribute::Mode(0), LinkAttribute::Mtu(1500), LinkAttribute::MinMtu(68), LinkAttribute::MaxMtu(65535), LinkAttribute::Group(0), LinkAttribute::Promiscuity(0), LinkAttribute::Other(DefaultNla::new(61, vec![0, 0, 0, 0])), LinkAttribute::NumTxQueues(1), LinkAttribute::GsoMaxSegs(65535), LinkAttribute::GsoMaxSize(65536), LinkAttribute::Other(DefaultNla::new(58, vec![0, 0, 1, 0])), LinkAttribute::Other(DefaultNla::new(63, vec![0, 0, 1, 0])), LinkAttribute::Other(DefaultNla::new(64, vec![0, 0, 1, 0])), LinkAttribute::Other(DefaultNla::new(59, vec![248, 255, 7, 0])), LinkAttribute::Other(DefaultNla::new(60, vec![255, 255, 0, 0])), LinkAttribute::NumRxQueues(1), LinkAttribute::Carrier(1), LinkAttribute::Qdisc("noqueue".to_string()), LinkAttribute::CarrierChanges(2), LinkAttribute::CarrierUpCount(1), LinkAttribute::CarrierDownCount(1), LinkAttribute::ProtoDown(0), LinkAttribute::Map(Map { memory_start: 0, memory_end: 0, base_address: 0, irq: 0, dma: 0, port: 0, }), LinkAttribute::Address(vec![0x00, 0x23, 0x45, 0x67, 0x89, 0x1c]), LinkAttribute::Broadcast(vec![0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), LinkAttribute::Stats64(Stats64 { rx_packets: 54, tx_packets: 31, rx_bytes: 5256, tx_bytes: 3394, rx_errors: 0, tx_errors: 0, rx_dropped: 0, tx_dropped: 0, multicast: 54, collisions: 0, rx_length_errors: 0, rx_over_errors: 0, rx_crc_errors: 0, rx_frame_errors: 0, rx_fifo_errors: 0, rx_missed_errors: 0, tx_aborted_errors: 0, tx_carrier_errors: 0, tx_fifo_errors: 0, tx_heartbeat_errors: 0, tx_window_errors: 0, rx_compressed: 0, tx_compressed: 0, rx_nohandler: 0, rx_otherhost_dropped: 0, }), LinkAttribute::Stats(Stats { rx_packets: 54, tx_packets: 31, rx_bytes: 5256, tx_bytes: 3394, rx_errors: 0, tx_errors: 0, rx_dropped: 0, tx_dropped: 0, multicast: 54, collisions: 0, rx_length_errors: 0, rx_over_errors: 0, rx_crc_errors: 0, rx_frame_errors: 0, rx_fifo_errors: 0, rx_missed_errors: 0, tx_aborted_errors: 0, tx_carrier_errors: 0, tx_fifo_errors: 0, tx_heartbeat_errors: 0, tx_window_errors: 0, rx_compressed: 0, tx_compressed: 0, rx_nohandler: 0, }), LinkAttribute::Xdp(vec![LinkXdp::Attached(XdpAttached::None)]), LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::Bridge), LinkInfo::Data(InfoData::Bridge(vec![ InfoBridge::HelloTimer(0), InfoBridge::TcnTimer(0), InfoBridge::TopologyChangeTimer(0), InfoBridge::GcTimer(14246), InfoBridge::ForwardDelay(1499), InfoBridge::HelloTime(199), InfoBridge::MaxAge(1999), InfoBridge::AgeingTime(29999), InfoBridge::StpState(0), InfoBridge::Priority(32768), InfoBridge::VlanFiltering(false), InfoBridge::GroupFwdMask(0), InfoBridge::BridgeId(BridgeId { priority: 0x8000, address: [0x00, 0x23, 0x45, 0x67, 0x89, 0x1c], }), InfoBridge::RootId(BridgeId { priority: 0x8000, address: [0x00, 0x23, 0x45, 0x67, 0x89, 0x1c], }), InfoBridge::RootPort(0), InfoBridge::RootPathCost(0), InfoBridge::TopologyChange(0), InfoBridge::TopologyChangeDetected(0), InfoBridge::GroupAddr([0x01, 0x80, 0xc2, 0x00, 0x00, 0x00]), InfoBridge::MultiBoolOpt(30064771072), InfoBridge::VlanProtocol(33024), InfoBridge::VlanDefaultPvid(1), InfoBridge::VlanStatsEnabled(0), InfoBridge::VlanStatsPerHost(0), InfoBridge::MulticastRouter(1), InfoBridge::MulticastSnooping(1), InfoBridge::MulticastQueryUseIfaddr(0), InfoBridge::MulticastQuerier(0), InfoBridge::MulticastStatsEnabled(0), InfoBridge::MulticastHashElasticity(16), InfoBridge::MulticastHashMax(4096), 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), ])), ]), LinkAttribute::AfSpecUnspec(vec![ AfSpecUnspec::Inet(vec![AfSpecInet::DevConf(InetDevConf { forwarding: 1, mc_forwarding: 0, proxy_arp: 0, accept_redirects: 1, secure_redirects: 1, send_redirects: 1, shared_media: 1, rp_filter: 2, accept_source_route: 0, bootp_relay: 0, log_martians: 0, tag: 0, arpfilter: 0, medium_id: 0, noxfrm: 0, nopolicy: 0, force_igmp_version: 0, arp_announce: 0, arp_ignore: 0, promote_secondaries: 1, 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, bc_forwarding: 0, arp_evict_nocarrier: 1, })]), AfSpecUnspec::Inet6(vec![ AfSpecInet6::Flags( Inet6IfaceFlags::RsSent | Inet6IfaceFlags::Ready, ), AfSpecInet6::CacheInfo(Inet6CacheInfo { max_reasm_len: 65535, tstamp: 26395879, reachable_time: 37400, retrans_time: 1000, }), AfSpecInet6::DevConf(Inet6DevConf { forwarding: 0, hoplimit: 64, mtu6: 1500, accept_ra: 1, accept_redirects: 1, autoconf: 1, dad_transmits: 1, rtr_solicits: -1, rtr_solicit_interval: 4000, rtr_solicit_delay: 1000, use_tempaddr: 0, 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, rpl_seg_enabled: 0, ra_defrtr_metric: 1024, ioam6_enabled: 0, ioam6_id: 65535, ioam6_id_wide: -1, ndisc_evict_nocarrier: 1, accept_untracked_na: 0, accept_ra_min_lft: 0, }), ]), ]), LinkAttribute::Other(DefaultNla::new(32830, vec![])), ], }; assert_eq!( expected, LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } #[test] fn test_af_spec_bridge() { // The nlmon cannot capture AF_BRIDGE data, this is debug print of // example `dump_packet_link_bridge_vlan` after command: // `bridge vlan add vid 2-4094 dev eth2` let raw: Vec = vec![ 0x08, 0x00, 0x02, 0x00, 0x06, 0x00, 0x01, 0x00, 0x08, 0x00, 0x02, 0x00, 0x08, 0x00, 0x02, 0x00, 0x08, 0x00, 0x02, 0x00, 0x10, 0x00, 0xfe, 0x0f, ]; let expected = vec![ AfSpecBridge::VlanInfo(BridgeVlanInfo { flags: BridgeVlanInfoFlags::Pvid | BridgeVlanInfoFlags::Untagged, vid: 1, }), AfSpecBridge::VlanInfo(BridgeVlanInfo { flags: BridgeVlanInfoFlags::RangeBegin, vid: 2, }), AfSpecBridge::VlanInfo(BridgeVlanInfo { flags: BridgeVlanInfoFlags::RangeEnd, vid: 4094, }), ]; assert_eq!( VecAfSpecBridge::parse(&NlaBuffer::new(&raw)).unwrap().0, expected ); } #[test] fn test_bridge_port_link_info() { let raw = vec![ 0x00, 0x00, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x01, 0x12, 0x00, 0x09, 0x00, 0x01, 0x00, 0x76, 0x65, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x04, 0x00, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x00, 0x00, 0x48, 0x01, 0x05, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x1e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x80, 0x00, 0x52, 0x54, 0x00, 0xde, 0x0d, 0x2e, 0x0c, 0x00, 0x0e, 0x00, 0x80, 0x00, 0x52, 0x54, 0x00, 0xde, 0x0d, 0x2e, 0x06, 0x00, 0x0f, 0x00, 0x01, 0x80, 0x00, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x11, 0x00, 0x01, 0x80, 0x00, 0x00, 0x06, 0x00, 0x12, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x19, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x25, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 7, link_layer_type: LinkLayerType::Ether, flags: LinkFlags::Broadcast | LinkFlags::Multicast, change_mask: LinkFlags::empty(), }, attributes: vec![LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::Veth), LinkInfo::PortKind(InfoPortKind::Bridge), LinkInfo::PortData(InfoPortData::BridgePort(vec![ InfoBridgePort::State(BridgePortState::Disabled), InfoBridgePort::Priority(32), InfoBridgePort::Cost(2), InfoBridgePort::HairpinMode(false), InfoBridgePort::Guard(false), InfoBridgePort::Protect(false), InfoBridgePort::FastLeave(false), InfoBridgePort::MulticastToUnicast(false), InfoBridgePort::Learning(true), InfoBridgePort::UnicastFlood(true), InfoBridgePort::MulticastFlood(true), InfoBridgePort::BroadcastFlood(true), InfoBridgePort::ProxyARP(false), InfoBridgePort::ProxyARPWifi(false), InfoBridgePort::RootId(BridgeId { priority: 0x8000, address: [0x52, 0x54, 0x00, 0xde, 0x0d, 0x2e], }), InfoBridgePort::BridgeId(BridgeId { priority: 0x8000, address: [0x52, 0x54, 0x00, 0xde, 0x0d, 0x2e], }), InfoBridgePort::DesignatedPort(32769), InfoBridgePort::DesignatedCost(0), InfoBridgePort::PortId(32769), InfoBridgePort::PortNumber(1), InfoBridgePort::TopologyChangeAck(false), InfoBridgePort::ConfigPending(false), InfoBridgePort::VlanTunnel(false), InfoBridgePort::GroupFwdMask(0), InfoBridgePort::NeighSupress(false), InfoBridgePort::MrpRingOpen(false), InfoBridgePort::MrpInOpen(false), InfoBridgePort::Isolated(false), InfoBridgePort::Locked(false), InfoBridgePort::Mab(false), InfoBridgePort::MessageAgeTimer(0), InfoBridgePort::ForwardDelayTimer(0), InfoBridgePort::HoldTimer(0), InfoBridgePort::MulticastRouter( BridgePortMulticastRouter::TempQuery, ), InfoBridgePort::MulticastEhtHostsLimit(512), InfoBridgePort::MulticastEhtHostsCnt(0), InfoBridgePort::MulticastNGroups(0), InfoBridgePort::MulticastMaxGroups(0), ])), ])], }; assert_eq!( expected, LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } // Veth does not support changing bridge port mode, this is wireshark capture // data of generated by `sudo bridge link set dev eth1 hwmode vepa` sending // from user space to linux kernel #[test] fn test_af_spec_bridge_mode() { let raw: Vec = vec![ 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, ]; let expected = vec![ AfSpecBridge::Flags(BridgeFlag::LowerDev), AfSpecBridge::Mode(BridgeMode::Vepa), ]; assert_eq!( VecAfSpecBridge::parse(&NlaBuffer::new(&raw)).unwrap().0, expected ); } // Wireshark capture of nlmon for kernel reply of command `bridge vlan // tunnelshow dev eth1`. // The VLAN tunnel was setup by: // bridge vlan add dev eth1 vid 100 pvid // bridge vlan add dev eth1 vid 111 // bridge link set dev eth1 vlan_tunnel on // bridge vlan add dev eth1 vid 100 tunnel_info id 101 // bridge vlan add dev eth1 vid 111 tunnel_info id 120 #[test] fn test_af_spec_bridge_vlan_tunnel_info() { let raw: Vec = vec![ 0x08, 0x00, 0x02, 0x00, 0x04, 0x00, 0x01, 0x00, 0x08, 0x00, 0x02, 0x00, 0x02, 0x00, 0x64, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x1c, 0x00, 0x03, 0x00, 0x08, 0x00, 0x01, 0x00, 0x65, 0x00, 0x00, 0x00, 0x06, 0x00, 0x02, 0x00, 0x64, 0x00, 0x00, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x03, 0x00, 0x08, 0x00, 0x01, 0x00, 0x78, 0x00, 0x00, 0x00, 0x06, 0x00, 0x02, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let expected = vec![ AfSpecBridge::VlanInfo(BridgeVlanInfo { flags: BridgeVlanInfoFlags::Untagged, vid: 1, }), AfSpecBridge::VlanInfo(BridgeVlanInfo { flags: BridgeVlanInfoFlags::Pvid, vid: 100, }), AfSpecBridge::VlanInfo(BridgeVlanInfo { flags: BridgeVlanInfoFlags::empty(), vid: 111, }), AfSpecBridge::VlanTunnelInfo(vec![ BridgeVlanTunnelInfo::Id(101), BridgeVlanTunnelInfo::Vid(100), BridgeVlanTunnelInfo::Flags(BridgeVlanInfoFlags::empty()), ]), AfSpecBridge::VlanTunnelInfo(vec![ BridgeVlanTunnelInfo::Id(120), BridgeVlanTunnelInfo::Vid(111), BridgeVlanTunnelInfo::Flags(BridgeVlanInfoFlags::empty()), ]), ]; assert_eq!( VecAfSpecBridge::parse(&NlaBuffer::new(&raw)).unwrap().0, expected ); } netlink-packet-route-0.21.0/src/link/tests/geneve.rs000064400000000000000000000047541046102023000204700ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::Ipv6Addr; use std::str::FromStr; use netlink_packet_utils::{Emitable, Parseable}; use crate::link::link_flag::LinkFlags; use crate::link::{ GeneveDf, InfoData, InfoGeneve, InfoKind, LinkAttribute, LinkHeader, LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, }; use crate::AddressFamily; #[test] fn test_geneve_link_info() { let raw: Vec = vec![ 0x00, 0x00, 0xfe, 0xff, 0xc0, 0x69, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x12, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x67, 0x65, 0x6e, 0x65, 0x76, 0x65, 0x00, 0x00, 0x64, 0x00, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x07, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x00, 0x01, 0xe2, 0x40, 0x05, 0x00, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x11, 0x5c, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0e, 0x00, ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 27072, link_layer_type: LinkLayerType::None, flags: LinkFlags::Pointopoint | LinkFlags::Noarp, change_mask: LinkFlags::empty(), }, attributes: vec![LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::Geneve), LinkInfo::Data(InfoData::Geneve(vec![ InfoGeneve::Id(42), InfoGeneve::Remote6(Ipv6Addr::from_str("2001:db8::1").unwrap()), InfoGeneve::UdpZeroCsum6Tx(false), InfoGeneve::Ttl(10), InfoGeneve::Tos(18), InfoGeneve::Label(123456), InfoGeneve::Df(GeneveDf::Set), InfoGeneve::Port(4444), InfoGeneve::UdpZeroCsum6Rx(false), InfoGeneve::TtlInherit(false), InfoGeneve::InnerProtoInherit, ])), ])], }; assert_eq!( expected, LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/link/tests/hsr.rs000064400000000000000000000036461046102023000200120ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::link::link_flag::LinkFlags; use crate::link::{ HsrProtocol, InfoData, InfoHsr, InfoKind, LinkAttribute, LinkHeader, LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, }; use crate::AddressFamily; #[test] fn test_parsing_link_hsr() { let raw = vec![ 0x00, 0x00, 0x01, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x43, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x12, 0x00, 0x08, 0x00, 0x01, 0x00, 0x68, 0x73, 0x72, 0x00, 0x30, 0x00, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x01, 0x15, 0x4e, 0x00, 0x01, 0x90, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x09, 0xfc, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 45, link_layer_type: LinkLayerType::Ether, flags: LinkFlags::Broadcast | LinkFlags::LowerUp | LinkFlags::Multicast | LinkFlags::Running | LinkFlags::Up, change_mask: LinkFlags::empty(), }, attributes: vec![LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::Hsr), LinkInfo::Data(InfoData::Hsr(vec![ InfoHsr::Port1(44), InfoHsr::Port2(42), InfoHsr::SupervisionAddr([0x01, 0x15, 0x4e, 0x00, 0x01, 0x90]), InfoHsr::SeqNr(64521), InfoHsr::Protocol(HsrProtocol::Hsr), ])), ])], }; assert_eq!( expected, LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/link/tests/ipvlan.rs000064400000000000000000000054111046102023000204770ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::link::link_flag::LinkFlags; use crate::link::{ InfoData, InfoIpVlan, InfoKind, IpVlanFlags, IpVlanMode, LinkAttribute, LinkHeader, LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, }; use crate::AddressFamily; #[test] fn test_ipvlan_link_info() { let raw: Vec = vec![ 0x00, 0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x12, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x69, 0x70, 0x76, 0x6c, 0x61, 0x6e, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 18, link_layer_type: LinkLayerType::Ether, flags: LinkFlags::Broadcast | LinkFlags::Multicast, change_mask: LinkFlags::empty(), }, attributes: vec![LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::IpVlan), LinkInfo::Data(InfoData::IpVlan(vec![ InfoIpVlan::Mode(IpVlanMode::L2), InfoIpVlan::Flags(IpVlanFlags::Vepa), ])), ])], }; assert_eq!( expected, LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } #[test] fn test_ipvlan_bridge_link_info() { let raw: Vec = vec![ 0x00, 0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x12, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x69, 0x70, 0x76, 0x6c, 0x61, 0x6e, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 18, link_layer_type: LinkLayerType::Ether, flags: LinkFlags::Broadcast | LinkFlags::Multicast, change_mask: LinkFlags::empty(), }, attributes: vec![LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::IpVlan), LinkInfo::Data(InfoData::IpVlan(vec![ InfoIpVlan::Mode(IpVlanMode::L2), InfoIpVlan::Flags(IpVlanFlags::default()), ])), ])], }; assert_eq!( expected, LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/link/tests/ipvtap.rs000064400000000000000000000030361046102023000205120ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::link::link_flag::LinkFlags; use crate::link::{ InfoData, InfoIpVtap, InfoKind, IpVtapFlags, IpVtapMode, LinkAttribute, LinkHeader, LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, }; use crate::AddressFamily; #[test] fn test_ipvtap_link_info() { let raw: Vec = vec![ 0x00, 0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x12, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x69, 0x70, 0x76, 0x74, 0x61, 0x70, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 18, link_layer_type: LinkLayerType::Ether, flags: LinkFlags::Broadcast | LinkFlags::Multicast, change_mask: LinkFlags::empty(), }, attributes: vec![LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::IpVtap), LinkInfo::Data(InfoData::IpVtap(vec![ InfoIpVtap::Mode(IpVtapMode::L2), InfoIpVtap::Flags(IpVtapFlags::Vepa), ])), ])], }; assert_eq!( expected, LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/link/tests/loopback.rs000064400000000000000000000174111046102023000210030ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::{DefaultNla, NlaBuffer}, Emitable, Parseable, }; use crate::link::{ af_spec::VecAfSpecUnspec, AfSpecInet, AfSpecInet6, AfSpecUnspec, Inet6CacheInfo, Inet6DevConf, Inet6IfaceFlags, InetDevConf, }; #[test] fn test_link_loopback() { // This is nlmon capture for `ip link show lo` on Linux kernel 6.5.8 let raw: Vec = vec![ 0x0c, 0x00, // length 12 0x2d, 0x00, // AF_MCTP 45 0x08, 0x00, // length 8 0x01, 0x00, // IFLA_MCTP_NET 1 0x01, 0x00, 0x00, 0x00, // u32 value 1 0x8c, 0x00, // length 140 0x02, 0x00, // AF_INET 2 0x88, 0x00, // length 136 0x01, 0x00, // IFLA_INET_CONF 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x01, // length 272 0x0a, 0x00, // AF_INET 6 0x08, 0x00, // length 8 0x01, 0x00, // IFLA_INET6_FLAGS 1 0x00, 0x00, 0x00, 0x80, // IF_READY: 0x80000000 0x14, 0x00, // length 20 0x05, 0x00, // IFLA_INET6_CACHEINFO 5 0xff, 0xff, 0x00, 0x00, // max_reasm_len: 65535 0xb2, 0x00, 0x00, 0x00, // tstamp: 178 0x05, 0x75, 0x00, 0x00, // reachable_time: 29957 0xe8, 0x03, 0x00, 0x00, // retrans_time: 1000 0xf0, 0x00, // length 240 0x02, 0x00, // IFLA_INET6_CONF 2 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xa0, 0x0f, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x80, 0x3a, 0x09, 0x00, 0x80, 0x51, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xee, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // End of IFLA_INET6_CONF ]; let expected = vec![ AfSpecUnspec::Other(DefaultNla::new( 45, vec![8u8, 0, 1, 0, 1, 0, 0, 0], )), AfSpecUnspec::Inet(vec![AfSpecInet::DevConf(InetDevConf { forwarding: 1, mc_forwarding: 0, proxy_arp: 0, accept_redirects: 1, secure_redirects: 1, send_redirects: 1, shared_media: 1, rp_filter: 2, accept_source_route: 0, 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: 1, 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, bc_forwarding: 0, arp_evict_nocarrier: 1, })]), AfSpecUnspec::Inet6(vec![ AfSpecInet6::Flags(Inet6IfaceFlags::Ready), AfSpecInet6::CacheInfo(Inet6CacheInfo { max_reasm_len: 65535, tstamp: 178, reachable_time: 29957, retrans_time: 1000, }), AfSpecInet6::DevConf(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, rpl_seg_enabled: 0, ra_defrtr_metric: 1024, ioam6_enabled: 0, ioam6_id: 65535, ioam6_id_wide: -1, ndisc_evict_nocarrier: 1, accept_untracked_na: 0, accept_ra_min_lft: 0, }), ]), ]; assert_eq!( VecAfSpecUnspec::parse(&NlaBuffer::new(&raw)).unwrap().0, expected ); let mut buffer = vec![0; expected.as_slice().buffer_len()]; expected.as_slice().emit(&mut buffer); assert_eq!(buffer.as_slice(), raw); } netlink-packet-route-0.21.0/src/link/tests/macsec.rs000064400000000000000000000053551046102023000204500ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::link::link_flag::LinkFlags; use crate::link::{ InfoData, InfoKind, InfoMacSec, LinkAttribute, LinkHeader, LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, MacSecCipherId, MacSecOffload, MacSecValidate, }; use crate::AddressFamily; #[test] fn test_macsec_link_info() { let raw: Vec = vec![ 0x00, 0x00, 0x01, 0x00, 0x09, 0x00, 0x00, 0x00, 0x43, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x12, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x6d, 0x61, 0x63, 0x73, 0x65, 0x63, 0x00, 0x00, 0x6c, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x01, 0x00, 0xf2, 0xca, 0xda, 0x49, 0x34, 0x51, 0x00, 0x01, 0x05, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x02, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 9, link_layer_type: LinkLayerType::Ether, flags: LinkFlags::Broadcast | LinkFlags::LowerUp | LinkFlags::Multicast | LinkFlags::Running | LinkFlags::Up, change_mask: LinkFlags::empty(), }, attributes: vec![LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::MacSec), LinkInfo::Data(InfoData::MacSec(vec![ InfoMacSec::Sci(72146879057152754), InfoMacSec::IcvLen(16), #[allow(deprecated)] InfoMacSec::CipherSuite(MacSecCipherId::DefaultGcmAes128), InfoMacSec::EncodingSa(0), InfoMacSec::Encrypt(1), InfoMacSec::Protect(1), InfoMacSec::IncSci(1), InfoMacSec::Es(0), InfoMacSec::Scb(0), InfoMacSec::ReplayProtect(0), InfoMacSec::Validation(MacSecValidate::Strict), InfoMacSec::Offload(MacSecOffload::Off), ])), ])], }; assert_eq!( expected, LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/link/tests/macvlan.rs000064400000000000000000000044741046102023000206370ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::link::link_flag::LinkFlags; use crate::link::{ InfoData, InfoKind, InfoMacVlan, LinkAttribute, LinkHeader, LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, MacVlanMode, }; use crate::AddressFamily; #[test] fn test_macvlan_link_info() { let raw: Vec = vec![ 0x00, 0x00, 0x01, 0x00, 0x17, 0x00, 0x00, 0x00, 0x43, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x12, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x6d, 0x61, 0x63, 0x76, 0x6c, 0x61, 0x6e, 0x00, 0x48, 0x00, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x06, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, 0x23, 0x45, 0x67, 0x89, 0x1d, 0x00, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, 0x23, 0x45, 0x67, 0x89, 0x1c, 0x00, 0x00, 0x08, 0x00, 0x07, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0xe8, 0x03, 0x00, 0x00, ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 23, link_layer_type: LinkLayerType::Ether, flags: LinkFlags::Broadcast | LinkFlags::LowerUp | LinkFlags::Multicast | LinkFlags::Running | LinkFlags::Up, change_mask: LinkFlags::empty(), }, attributes: vec![LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::MacVlan), LinkInfo::Data(InfoData::MacVlan(vec![ InfoMacVlan::Mode(MacVlanMode::Source), InfoMacVlan::Flags(0), InfoMacVlan::MacAddrCount(2), InfoMacVlan::MacAddrData(vec![ InfoMacVlan::MacAddr([0, 35, 69, 103, 137, 29]), InfoMacVlan::MacAddr([0, 35, 69, 103, 137, 28]), ]), InfoMacVlan::BcQueueLen(1000), InfoMacVlan::BcQueueLenUsed(1000), ])), ])], }; assert_eq!( expected, LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/link/tests/macvtap.rs000064400000000000000000000415721046102023000206510ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{nla::DefaultNla, Emitable, Parseable}; use crate::link::link_flag::LinkFlags; use crate::link::{ AfSpecInet, AfSpecInet6, AfSpecUnspec, Inet6CacheInfo, Inet6DevConf, Inet6IfaceFlags, InetDevConf, InfoData, InfoKind, InfoMacVtap, LinkAttribute, LinkHeader, LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, LinkXdp, MacVtapMode, Map, State, Stats, Stats64, XdpAttached, }; use crate::AddressFamily; #[test] fn test_macvtap_link_info() { let raw: Vec = vec![ 0x00, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x03, 0x00, 0x6d, 0x74, 0x61, 0x70, 0x30, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d, 0x00, 0xf4, 0x01, 0x00, 0x00, 0x05, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0xdc, 0x05, 0x00, 0x00, 0x08, 0x00, 0x32, 0x00, 0x44, 0x00, 0x00, 0x00, 0x08, 0x00, 0x33, 0x00, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x28, 0x00, 0xff, 0xff, 0x00, 0x00, 0x08, 0x00, 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x3c, 0x00, 0xff, 0xff, 0x00, 0x00, 0x08, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x06, 0x00, 0x66, 0x71, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x92, 0x41, 0xed, 0x4c, 0x60, 0x97, 0x00, 0x00, 0x0a, 0x00, 0x02, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xcc, 0x00, 0x17, 0x00, 0x14, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0xa3, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x07, 0x00, 0x14, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x9c, 0xa3, 0x04, 0x00, 0x81, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x2b, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x12, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x6d, 0x61, 0x63, 0x76, 0x74, 0x61, 0x70, 0x00, 0x2c, 0x00, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x07, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x08, 0x00, 0x05, 0x00, 0x03, 0x00, 0x00, 0x00, 0xa0, 0x01, 0x1a, 0x00, 0x8c, 0x00, 0x02, 0x00, 0x88, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x01, 0x0a, 0x00, 0x08, 0x00, 0x01, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x14, 0x00, 0x05, 0x00, 0xff, 0xff, 0x00, 0x00, 0x08, 0xb1, 0x10, 0x01, 0xfd, 0xa4, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0xf0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xdc, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xa0, 0x0f, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3a, 0x09, 0x00, 0x80, 0x51, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xee, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x3e, 0x80, ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 12, link_layer_type: LinkLayerType::Ether, flags: LinkFlags::Broadcast | LinkFlags::Multicast, change_mask: LinkFlags::empty(), }, attributes: vec![ LinkAttribute::IfName("mtap0".to_string()), LinkAttribute::TxQueueLen(500), LinkAttribute::OperState(State::Down), LinkAttribute::Mode(0), LinkAttribute::Mtu(1500), LinkAttribute::MinMtu(68), LinkAttribute::MaxMtu(2304), LinkAttribute::Group(0), LinkAttribute::Promiscuity(0), LinkAttribute::Other(DefaultNla::new(61, vec![0, 0, 0, 0])), LinkAttribute::NumTxQueues(1), LinkAttribute::GsoMaxSegs(65535), LinkAttribute::GsoMaxSize(65536), LinkAttribute::Other(DefaultNla::new(58, vec![0, 0, 1, 0])), LinkAttribute::Other(DefaultNla::new(63, vec![0, 0, 1, 0])), LinkAttribute::Other(DefaultNla::new(64, vec![0, 0, 1, 0])), LinkAttribute::Other(DefaultNla::new(59, vec![0, 0, 1, 0])), LinkAttribute::Other(DefaultNla::new(60, vec![255, 255, 0, 0])), LinkAttribute::NumRxQueues(1), LinkAttribute::Carrier(1), LinkAttribute::Qdisc("fq_codel".to_string()), LinkAttribute::CarrierChanges(0), LinkAttribute::CarrierUpCount(0), LinkAttribute::CarrierDownCount(0), LinkAttribute::ProtoDown(0), LinkAttribute::Map(Map { memory_start: 0, memory_end: 0, base_address: 0, irq: 0, dma: 0, port: 0, }), LinkAttribute::Address(vec![146, 65, 237, 76, 96, 151]), LinkAttribute::Broadcast(vec![255, 255, 255, 255, 255, 255]), LinkAttribute::Stats64(Stats64 { rx_packets: 532, tx_packets: 50, rx_bytes: 304028, tx_bytes: 6017, rx_errors: 0, tx_errors: 0, rx_dropped: 0, tx_dropped: 0, multicast: 532, collisions: 0, rx_length_errors: 0, rx_over_errors: 0, rx_crc_errors: 0, rx_frame_errors: 0, rx_fifo_errors: 0, rx_missed_errors: 0, tx_aborted_errors: 0, tx_carrier_errors: 0, tx_fifo_errors: 0, tx_heartbeat_errors: 0, tx_window_errors: 0, rx_compressed: 0, tx_compressed: 0, rx_nohandler: 0, rx_otherhost_dropped: 0, }), LinkAttribute::Stats(Stats { rx_packets: 532, tx_packets: 50, rx_bytes: 304028, tx_bytes: 6017, rx_errors: 0, tx_errors: 0, rx_dropped: 0, tx_dropped: 0, multicast: 532, collisions: 0, rx_length_errors: 0, rx_over_errors: 0, rx_crc_errors: 0, rx_frame_errors: 0, rx_fifo_errors: 0, rx_missed_errors: 0, tx_aborted_errors: 0, tx_carrier_errors: 0, tx_fifo_errors: 0, tx_heartbeat_errors: 0, tx_window_errors: 0, rx_compressed: 0, tx_compressed: 0, rx_nohandler: 0, }), LinkAttribute::Xdp(vec![LinkXdp::Attached(XdpAttached::None)]), LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::MacVtap), LinkInfo::Data(InfoData::MacVtap(vec![ InfoMacVtap::Mode(MacVtapMode::Private), InfoMacVtap::Flags(0), InfoMacVtap::MacAddrCount(0), InfoMacVtap::BcQueueLen(1000), InfoMacVtap::BcQueueLenUsed(1000), ])), ]), LinkAttribute::Link(3), LinkAttribute::AfSpecUnspec(vec![ AfSpecUnspec::Inet(vec![AfSpecInet::DevConf(InetDevConf { forwarding: 1, mc_forwarding: 0, proxy_arp: 0, accept_redirects: 1, secure_redirects: 1, send_redirects: 1, shared_media: 1, rp_filter: 2, accept_source_route: 0, bootp_relay: 0, log_martians: 0, tag: 0, arpfilter: 0, medium_id: 0, noxfrm: 0, nopolicy: 0, force_igmp_version: 0, arp_announce: 0, arp_ignore: 0, promote_secondaries: 1, 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, bc_forwarding: 0, arp_evict_nocarrier: 1, })]), AfSpecUnspec::Inet6(vec![ AfSpecInet6::Flags( Inet6IfaceFlags::Otherconf | Inet6IfaceFlags::RaManaged, ), AfSpecInet6::CacheInfo(Inet6CacheInfo { max_reasm_len: 65535, tstamp: 17871112, reachable_time: 42237, retrans_time: 1000, }), AfSpecInet6::DevConf(Inet6DevConf { forwarding: 0, hoplimit: 64, mtu6: 1500, accept_ra: 1, accept_redirects: 1, autoconf: 1, dad_transmits: 1, rtr_solicits: -1, rtr_solicit_interval: 4000, rtr_solicit_delay: 1000, use_tempaddr: 0, 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, rpl_seg_enabled: 0, ra_defrtr_metric: 1024, ioam6_enabled: 0, ioam6_id: 65535, ioam6_id_wide: -1, ndisc_evict_nocarrier: 1, accept_untracked_na: 0, accept_ra_min_lft: 0, }), ]), ]), // TODO: Need to parse NLA_F_NESTED|IFLA_DEVLINK_PORT LinkAttribute::Other(DefaultNla::new(32830, Vec::new())), ], }; assert_eq!( expected, LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/link/tests/message.rs000064400000000000000000000157051046102023000206410ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::traits::{Emitable, ParseableParametrized}; use crate::link::link_flag::LinkFlags; use crate::link::{ LinkAttribute, LinkHeader, LinkLayerType, LinkMessage, LinkMessageBuffer, State, }; use crate::AddressFamily; static LINK_MSG: [u8; 96] = [ 0x00, // interface family AF_UNSPEC 0x00, // reserved 0x04, 0x03, // link layer type 772 = loopback 0x01, 0x00, 0x00, 0x00, // interface index = 1 0x49, 0x00, 0x01, 0x00, // flags: UP|LOOPBACK|RUNNING|LOWERUP 0x00, 0x00, 0x00, 0x00, // reserved 2 (aka device change flag) // attributes 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 link_message_packet_header_read() { let packet = LinkMessageBuffer::new(&LINK_MSG[0..16]); assert_eq!(packet.interface_family(), AddressFamily::Unspec.into()); assert_eq!(packet.reserved_1(), 0); assert_eq!(packet.link_layer_type(), LinkLayerType::Loopback.into()); assert_eq!(packet.link_index(), 1); assert_eq!( packet.flags(), (LinkFlags::Loopback | LinkFlags::LowerUp | LinkFlags::Running | LinkFlags::Up) .bits() ); assert_eq!(packet.change_mask(), 0); } #[test] fn link_message_packet_header_build() { let mut buf = vec![0xff; 16]; { let mut packet = LinkMessageBuffer::new(&mut buf); packet.set_interface_family(AddressFamily::Unspec.into()); packet.set_reserved_1(0); packet.set_link_layer_type(LinkLayerType::Loopback.into()); packet.set_link_index(1); packet.set_flags( (LinkFlags::Loopback | LinkFlags::LowerUp | LinkFlags::Running | LinkFlags::Up) .bits(), ); packet.set_change_mask(0); } assert_eq!(&buf[..], &LINK_MSG[0..16]); } #[test] fn link_mssage_packet_attributes_read() { let packet = LinkMessageBuffer::new(&LINK_MSG[..]); assert_eq!(packet.attributes().count(), 10); let mut attributes = packet.attributes(); // device name L=7,T=3,V=lo let nla = attributes.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 = LinkAttribute::parse_with_param(&nla, AddressFamily::Inet).unwrap(); assert_eq!(parsed, LinkAttribute::IfName(String::from("lo"))); // TxQueue length L=8,T=13,V=1000 let nla = attributes.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 = LinkAttribute::parse_with_param(&nla, AddressFamily::Inet).unwrap(); assert_eq!(parsed, LinkAttribute::TxQueueLen(1000)); // OperState L=5,T=16,V=0 (unknown) let nla = attributes.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 = LinkAttribute::parse_with_param(&nla, AddressFamily::Inet).unwrap(); assert_eq!(parsed, LinkAttribute::OperState(State::Unknown)); // Link mode L=5,T=17,V=0 let nla = attributes.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 = LinkAttribute::parse_with_param(&nla, AddressFamily::Inet).unwrap(); assert_eq!(parsed, LinkAttribute::Mode(0)); // MTU L=8,T=4,V=65536 let nla = attributes.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 = LinkAttribute::parse_with_param(&nla, AddressFamily::Inet).unwrap(); assert_eq!(parsed, LinkAttribute::Mtu(65_536)); // 0x00, 0x00, 0x00, 0x00, // Group L=8,T=27,V=9 let nla = attributes.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 = LinkAttribute::parse_with_param(&nla, AddressFamily::Inet).unwrap(); assert_eq!(parsed, LinkAttribute::Group(0)); // Promiscuity L=8,T=30,V=0 let nla = attributes.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 = LinkAttribute::parse_with_param(&nla, AddressFamily::Inet).unwrap(); assert_eq!(parsed, LinkAttribute::Promiscuity(0)); // Number of Tx Queues L=8,T=31,V=1 // 0x01, 0x00, 0x00, 0x00 let nla = attributes.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 = LinkAttribute::parse_with_param(&nla, AddressFamily::Inet).unwrap(); assert_eq!(parsed, LinkAttribute::NumTxQueues(1)); } #[test] fn link_message_emit() { let header = LinkHeader { link_layer_type: LinkLayerType::Loopback, index: 1, flags: LinkFlags::Loopback | LinkFlags::LowerUp | LinkFlags::Running | LinkFlags::Up, interface_family: AddressFamily::Unspec, ..Default::default() }; let attributes = vec![ LinkAttribute::IfName("lo".into()), LinkAttribute::TxQueueLen(1000), LinkAttribute::OperState(State::Unknown), LinkAttribute::Mode(0), LinkAttribute::Mtu(0x1_0000), LinkAttribute::Group(0), LinkAttribute::Promiscuity(0), LinkAttribute::NumTxQueues(1), LinkAttribute::GsoMaxSegs(0xffff), LinkAttribute::GsoMaxSize(0x1_0000), ]; let packet = LinkMessage { header, attributes }; let mut buf = [0; 96]; assert_eq!(packet.buffer_len(), 96); packet.emit(&mut buf[..]); assert_eq!(buf, &LINK_MSG[..96]); } netlink-packet-route-0.21.0/src/link/tests/mod.rs000064400000000000000000000011021046102023000177560ustar 00000000000000// SPDX-License-Identifier: MIT #[cfg(test)] mod bond; #[cfg(test)] mod bridge; #[cfg(test)] mod geneve; #[cfg(test)] mod hsr; #[cfg(test)] mod ipvlan; #[cfg(test)] mod ipvtap; #[cfg(test)] mod loopback; #[cfg(test)] mod macsec; #[cfg(test)] mod macvlan; #[cfg(test)] mod macvtap; #[cfg(test)] mod message; #[cfg(test)] mod prop_list; #[cfg(test)] mod sriov; #[cfg(test)] mod statistics; #[cfg(test)] mod veth; #[cfg(test)] mod vlan; #[cfg(test)] mod vrf; #[cfg(test)] mod vxlan; #[cfg(test)] mod xdp; #[cfg(test)] mod xfrm; //TODO(Gris Ge): capture netlink message for ipoib netlink-packet-route-0.21.0/src/link/tests/prop_list.rs000064400000000000000000000030541046102023000212220ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::link::link_flag::LinkFlags; use crate::link::{ LinkAttribute, LinkHeader, LinkLayerType, LinkMessage, LinkMessageBuffer, Prop, }; use crate::AddressFamily; #[test] fn test_wlan0_with_prop_altname() { // nlmon dump of `ip link show wlan0` with two alt_name for wlan0 with // IFLA_PROP_LIST only let raw = vec![ 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x43, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x34, 0x80, 0x0e, 0x00, 0x35, 0x00, 0x77, 0x6c, 0x70, 0x30, 0x73, 0x32, 0x30, 0x66, 0x33, 0x00, 0x00, 0x00, 0x09, 0x00, 0x35, 0x00, 0x77, 0x69, 0x66, 0x69, 0x00, 0x00, 0x00, 0x00, ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 2, link_layer_type: LinkLayerType::Ether, flags: LinkFlags::Broadcast | LinkFlags::LowerUp | LinkFlags::Multicast | LinkFlags::Running | LinkFlags::Up, change_mask: LinkFlags::empty(), }, attributes: vec![LinkAttribute::PropList(vec![ Prop::AltIfName("wlp0s20f3".to_string()), Prop::AltIfName("wifi".to_string()), ])], }; assert_eq!( expected, LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/link/tests/sriov.rs000064400000000000000000000177251046102023000203630ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{nla::NlaBuffer, Emitable, ParseableParametrized}; use crate::link::{ LinkAttribute, LinkVfInfo, VfInfo, VfInfoBroadcast, VfInfoLinkState, VfInfoMac, VfInfoRate, VfInfoRssQueryEn, VfInfoSpoofCheck, VfInfoTrust, VfInfoTxRate, VfInfoVlan, VfLinkState, VfStats, VfVlan, VfVlanInfo, VlanProtocol, }; use crate::AddressFamily; // Wireshark capture of nlmon on a PF NIC with 2 SR-IOV VF enabled. // Only the IFLA_VFINFO_LIST included. #[test] fn test_parsing_link_sriov() { let raw = vec![ 0x54, 0x02, 0x16, 0x00, 0x28, 0x01, 0x01, 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x60, 0x71, 0x72, 0xbd, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x0d, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x64, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x01, 0x01, 0x00, 0x28, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xde, 0x48, 0xe1, 0xe0, 0xbb, 0xec, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x0d, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x64, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let expected = LinkAttribute::VfInfoList(vec![ LinkVfInfo(vec![ VfInfo::Mac(VfInfoMac::new(0, &[78, 96, 113, 114, 189, 29])), VfInfo::Broadcast(VfInfoBroadcast::new(&[ 255, 255, 255, 255, 255, 255, ])), VfInfo::Vlan(VfInfoVlan { vf_id: 0, vlan_id: 0, qos: 0, }), VfInfo::Rate(VfInfoRate { vf_id: 0, min_tx_rate: 0, max_tx_rate: 0, }), VfInfo::TxRate(VfInfoTxRate { vf_id: 0, rate: 0 }), VfInfo::SpoofCheck(VfInfoSpoofCheck { vf_id: 0, enabled: true, }), VfInfo::LinkState(VfInfoLinkState { vf_id: 0, state: VfLinkState::Auto, }), VfInfo::RssQueryEn(VfInfoRssQueryEn { vf_id: 0, enabled: false, }), VfInfo::Trust(VfInfoTrust { vf_id: 0, enabled: false, }), VfInfo::VlanList(vec![VfVlan::Info(VfVlanInfo { vf_id: 0, vlan_id: 0, qos: 0, protocol: VlanProtocol::Ieee8021Q, })]), VfInfo::Stats(vec![ VfStats::RxPackets(0), VfStats::TxPackets(0), VfStats::RxBytes(0), VfStats::TxBytes(0), VfStats::Broadcast(0), VfStats::Multicast(0), VfStats::RxDropped(0), VfStats::TxDropped(0), ]), ]), LinkVfInfo(vec![ VfInfo::Mac(VfInfoMac::new(1, &[222, 72, 225, 224, 187, 236])), VfInfo::Broadcast(VfInfoBroadcast::new(&[ 255, 255, 255, 255, 255, 255, ])), VfInfo::Vlan(VfInfoVlan { vf_id: 1, vlan_id: 0, qos: 0, }), VfInfo::Rate(VfInfoRate { vf_id: 1, min_tx_rate: 0, max_tx_rate: 0, }), VfInfo::TxRate(VfInfoTxRate { vf_id: 1, rate: 0 }), VfInfo::SpoofCheck(VfInfoSpoofCheck { vf_id: 1, enabled: true, }), VfInfo::LinkState(VfInfoLinkState { vf_id: 1, state: VfLinkState::Auto, }), VfInfo::RssQueryEn(VfInfoRssQueryEn { vf_id: 1, enabled: false, }), VfInfo::Trust(VfInfoTrust { vf_id: 1, enabled: false, }), VfInfo::VlanList(vec![VfVlan::Info(VfVlanInfo { vf_id: 1, vlan_id: 0, qos: 0, protocol: VlanProtocol::Ieee8021Q, })]), VfInfo::Stats(vec![ VfStats::RxPackets(0), VfStats::TxPackets(0), VfStats::RxBytes(0), VfStats::TxBytes(0), VfStats::Broadcast(0), VfStats::Multicast(0), VfStats::RxDropped(0), VfStats::TxDropped(0), ]), ]), ]); assert_eq!( expected, LinkAttribute::parse_with_param( &NlaBuffer::new(&raw), AddressFamily::Unspec ) .unwrap(), ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/link/tests/statistics.rs000064400000000000000000001115071046102023000214040ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{nla::DefaultNla, Emitable, Parseable}; use crate::link::link_flag::LinkFlags; use crate::link::{ AfSpecInet, AfSpecInet6, AfSpecUnspec, Icmp6Stats, Inet6CacheInfo, Inet6DevConf, Inet6IfaceFlags, Inet6Stats, InetDevConf, LinkAttribute, LinkHeader, LinkLayerType, LinkMessage, LinkMessageBuffer, LinkXdp, Map, Prop, State, Stats, Stats64, XdpAttached, }; use crate::AddressFamily; // tshark capture of command `ip -s link show ens3` #[test] fn test_parsing_link_statistics_on_kernel_4_18() { let raw = vec![ 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x43, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x03, 0x00, 0x65, 0x6e, 0x73, 0x33, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x05, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0xdc, 0x05, 0x00, 0x00, 0x08, 0x00, 0x32, 0x00, 0x44, 0x00, 0x00, 0x00, 0x08, 0x00, 0x33, 0x00, 0xff, 0xff, 0x00, 0x00, 0x08, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x28, 0x00, 0xff, 0xff, 0x00, 0x00, 0x08, 0x00, 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x06, 0x00, 0x66, 0x71, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x23, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x2f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x52, 0x54, 0x00, 0xf3, 0xc5, 0xcf, 0x00, 0x00, 0x0a, 0x00, 0x02, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xc4, 0x00, 0x17, 0x00, 0x8b, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0xb0, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0xd6, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x07, 0x00, 0x8b, 0x6d, 0x00, 0x00, 0xf6, 0x0d, 0x00, 0x00, 0x91, 0xb0, 0x6a, 0x00, 0x2d, 0xd6, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x2b, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x36, 0x00, 0x52, 0x54, 0x00, 0xf3, 0xc5, 0xcf, 0x00, 0x00, 0xf8, 0x02, 0x1a, 0x00, 0x88, 0x00, 0x02, 0x00, 0x84, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x02, 0x0a, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x14, 0x00, 0x05, 0x00, 0xff, 0xff, 0x00, 0x00, 0xce, 0x72, 0x3e, 0x00, 0x2c, 0x70, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0xd0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xdc, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xa0, 0x0f, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3a, 0x09, 0x00, 0x80, 0x51, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xee, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x03, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0xca, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x34, 0x80, 0x0b, 0x00, 0x35, 0x00, 0x65, 0x6e, 0x70, 0x30, 0x73, 0x33, 0x00, 0x00, 0x0c, 0x00, 0x38, 0x00, 0x76, 0x69, 0x72, 0x74, 0x69, 0x6f, 0x30, 0x00, 0x0b, 0x00, 0x39, 0x00, 0x76, 0x69, 0x72, 0x74, 0x69, 0x6f, 0x00, 0x00, ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 2, link_layer_type: LinkLayerType::Ether, flags: LinkFlags::Broadcast | LinkFlags::LowerUp | LinkFlags::Multicast | LinkFlags::Running | LinkFlags::Up, change_mask: LinkFlags::empty(), }, attributes: vec![ LinkAttribute::IfName("ens3".into()), LinkAttribute::TxQueueLen(1000), LinkAttribute::OperState(State::Up), LinkAttribute::Mode(0), LinkAttribute::Mtu(1500), LinkAttribute::MinMtu(68), LinkAttribute::MaxMtu(65535), LinkAttribute::Group(0), LinkAttribute::Promiscuity(0), LinkAttribute::NumTxQueues(1), LinkAttribute::GsoMaxSegs(65535), LinkAttribute::GsoMaxSize(65536), LinkAttribute::NumRxQueues(1), LinkAttribute::Carrier(1), LinkAttribute::Qdisc("fq_codel".into()), LinkAttribute::CarrierChanges(2), LinkAttribute::ProtoDown(0), LinkAttribute::CarrierUpCount(1), LinkAttribute::CarrierDownCount(1), LinkAttribute::Map(Map { memory_start: 0, memory_end: 0, base_address: 0, irq: 0, dma: 0, port: 0, }), LinkAttribute::Address(vec![82, 84, 0, 243, 197, 207]), LinkAttribute::Broadcast(vec![255, 255, 255, 255, 255, 255]), LinkAttribute::Stats64(Stats64 { rx_packets: 28043, tx_packets: 3574, rx_bytes: 6992017, tx_bytes: 316973, rx_errors: 0, tx_errors: 0, rx_dropped: 20486, tx_dropped: 0, multicast: 0, collisions: 0, rx_length_errors: 0, rx_over_errors: 0, rx_crc_errors: 0, rx_frame_errors: 0, rx_fifo_errors: 0, rx_missed_errors: 0, tx_aborted_errors: 0, tx_carrier_errors: 0, tx_fifo_errors: 0, tx_heartbeat_errors: 0, tx_window_errors: 0, rx_compressed: 0, tx_compressed: 0, rx_nohandler: 0, rx_otherhost_dropped: 0, }), LinkAttribute::Stats(Stats { rx_packets: 28043, tx_packets: 3574, rx_bytes: 6992017, tx_bytes: 316973, rx_errors: 0, tx_errors: 0, rx_dropped: 20486, tx_dropped: 0, multicast: 0, collisions: 0, rx_length_errors: 0, rx_over_errors: 0, rx_crc_errors: 0, rx_frame_errors: 0, rx_fifo_errors: 0, rx_missed_errors: 0, tx_aborted_errors: 0, tx_carrier_errors: 0, tx_fifo_errors: 0, tx_heartbeat_errors: 0, tx_window_errors: 0, rx_compressed: 0, tx_compressed: 0, rx_nohandler: 0, }), LinkAttribute::Xdp(vec![LinkXdp::Attached(XdpAttached::None)]), LinkAttribute::PermAddress(vec![82, 84, 0, 243, 197, 207]), LinkAttribute::AfSpecUnspec(vec![ AfSpecUnspec::Inet(vec![AfSpecInet::DevConf(InetDevConf { forwarding: 0, mc_forwarding: 0, proxy_arp: 0, accept_redirects: 0, secure_redirects: 1, send_redirects: 0, shared_media: 1, rp_filter: 0, accept_source_route: 1, bootp_relay: 0, log_martians: 0, tag: 0, arpfilter: 0, medium_id: 0, noxfrm: 0, nopolicy: 0, 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, bc_forwarding: 0, arp_evict_nocarrier: 0, })]), AfSpecUnspec::Inet6(vec![ AfSpecInet6::Flags(Inet6IfaceFlags::Ready), AfSpecInet6::CacheInfo(Inet6CacheInfo { max_reasm_len: 65535, tstamp: 4092622, reachable_time: 28716, retrans_time: 1000, }), AfSpecInet6::DevConf(Inet6DevConf { forwarding: 0, hoplimit: 64, mtu6: 1500, accept_ra: 0, accept_redirects: 0, autoconf: 1, dad_transmits: 1, rtr_solicits: -1, rtr_solicit_interval: 4000, rtr_solicit_delay: 1000, use_tempaddr: 0, 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: 1, disable_policy: 0, accept_ra_rt_info_min_plen: 0, ndisc_tclass: 0, rpl_seg_enabled: 0, ra_defrtr_metric: 0, ioam6_enabled: 0, ioam6_id: 0, ioam6_id_wide: 0, ndisc_evict_nocarrier: 0, accept_untracked_na: 0, accept_ra_min_lft: 0, }), AfSpecInet6::Stats(Inet6Stats { num: 37, in_pkts: 1524, in_octets: 182870, in_delivers: 0, out_forw_datagrams: 0, out_pkts: 6, out_octets: 528, in_hdr_errors: 0, in_too_big_errors: 0, in_no_routes: 0, in_addr_errors: 0, in_unknown_protos: 0, in_truncated_pkts: 0, in_discards: 1516, 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: 2, out_mcast_pkts: 6, in_bcast_pkts: 0, out_bcast_pkts: 0, in_mcast_octets: 242, out_mcast_octets: 528, in_bcast_octets: 0, out_bcast_octets: 0, in_csum_errors: 0, in_no_ect_pkts: 8, in_ect1_pkts: 0, in_ect0_pkts: 0, in_ce_pkts: 0, }), AfSpecInet6::Icmp6Stats(Icmp6Stats { num: 6, in_msgs: 0, in_errors: 0, out_msgs: 6, out_errors: 0, csum_errors: 0, }), AfSpecInet6::Token(std::net::Ipv6Addr::UNSPECIFIED), AfSpecInet6::AddrGenMode(1), ]), ]), LinkAttribute::PropList(vec![Prop::AltIfName("enp0s3".into())]), LinkAttribute::Other(DefaultNla::new( 56, vec![118, 105, 114, 116, 105, 111, 48, 0], )), LinkAttribute::Other(DefaultNla::new( 57, vec![118, 105, 114, 116, 105, 111, 0], )), ], }; assert_eq!( expected, LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() ); // We do not test emit here as old kernel has smaller buffer size of stats } // tshark capture of command `ip -s link show wlan0` on kernel 6.6 #[test] fn test_parsing_link_statistics() { let raw = vec![ 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x43, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x03, 0x00, 0x77, 0x6c, 0x61, 0x6e, 0x30, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x05, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x11, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0xdc, 0x05, 0x00, 0x00, 0x08, 0x00, 0x32, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x33, 0x00, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x28, 0x00, 0xff, 0xff, 0x00, 0x00, 0x08, 0x00, 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x3c, 0x00, 0xff, 0xff, 0x00, 0x00, 0x08, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x6e, 0x6f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x00, 0x08, 0x00, 0x23, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x2f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x10, 0xa5, 0x1d, 0x6f, 0xd2, 0x18, 0x00, 0x00, 0x0a, 0x00, 0x02, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xcc, 0x00, 0x17, 0x00, 0x50, 0xb4, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x89, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xc9, 0x91, 0x07, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x6e, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x07, 0x00, 0x50, 0xb4, 0x02, 0x00, 0x80, 0x89, 0x01, 0x00, 0x42, 0xc9, 0x91, 0x07, 0x4d, 0x6e, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x2b, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x36, 0x00, 0x10, 0xa5, 0x1d, 0x6f, 0xd2, 0x18, 0x00, 0x00, 0xa0, 0x01, 0x1a, 0x00, 0x8c, 0x00, 0x02, 0x00, 0x88, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x01, 0x0a, 0x00, 0x08, 0x00, 0x01, 0x00, 0xf0, 0x00, 0x00, 0x80, 0x14, 0x00, 0x05, 0x00, 0xff, 0xff, 0x00, 0x00, 0xea, 0x04, 0x00, 0x00, 0x1a, 0x5e, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0xf0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xdc, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xa0, 0x0f, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3a, 0x09, 0x00, 0x80, 0x51, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xee, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x34, 0x80, 0x0e, 0x00, 0x35, 0x00, 0x77, 0x6c, 0x70, 0x30, 0x73, 0x32, 0x30, 0x66, 0x33, 0x00, 0x00, 0x00, 0x11, 0x00, 0x38, 0x00, 0x30, 0x30, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x3a, 0x31, 0x34, 0x2e, 0x33, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x39, 0x00, 0x70, 0x63, 0x69, 0x00, 0x04, 0x00, 0x3e, 0x80, ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 3, link_layer_type: LinkLayerType::Ether, flags: LinkFlags::Broadcast | LinkFlags::LowerUp | LinkFlags::Multicast | LinkFlags::Running | LinkFlags::Up, change_mask: LinkFlags::empty(), }, attributes: vec![ LinkAttribute::IfName("wlan0".into()), LinkAttribute::TxQueueLen(1000), LinkAttribute::OperState(State::Up), LinkAttribute::Mode(1), LinkAttribute::Mtu(1500), LinkAttribute::MinMtu(256), LinkAttribute::MaxMtu(2304), LinkAttribute::Group(0), LinkAttribute::Promiscuity(0), LinkAttribute::Other(DefaultNla::new(61, vec![0, 0, 0, 0])), LinkAttribute::NumTxQueues(1), LinkAttribute::GsoMaxSegs(65535), LinkAttribute::GsoMaxSize(65536), LinkAttribute::Other(DefaultNla::new(58, vec![0, 0, 1, 0])), LinkAttribute::Other(DefaultNla::new(63, vec![0, 0, 1, 0])), LinkAttribute::Other(DefaultNla::new(64, vec![0, 0, 1, 0])), LinkAttribute::Other(DefaultNla::new(59, vec![0, 0, 1, 0])), LinkAttribute::Other(DefaultNla::new(60, vec![255, 255, 0, 0])), LinkAttribute::NumRxQueues(1), LinkAttribute::Carrier(1), LinkAttribute::Qdisc("noqueue".into()), LinkAttribute::CarrierChanges(2), LinkAttribute::CarrierUpCount(1), LinkAttribute::CarrierDownCount(1), LinkAttribute::ProtoDown(0), LinkAttribute::Map(Map { memory_start: 0, memory_end: 0, base_address: 0, irq: 0, dma: 0, port: 0, }), LinkAttribute::Address(vec![16, 165, 29, 111, 210, 24]), LinkAttribute::Broadcast(vec![255, 255, 255, 255, 255, 255]), LinkAttribute::Stats64(Stats64 { rx_packets: 177232, tx_packets: 100736, rx_bytes: 126994754, tx_bytes: 18116173, rx_errors: 0, tx_errors: 0, rx_dropped: 0, tx_dropped: 0, multicast: 0, collisions: 0, rx_length_errors: 0, rx_over_errors: 0, rx_crc_errors: 0, rx_frame_errors: 0, rx_fifo_errors: 0, rx_missed_errors: 0, tx_aborted_errors: 0, tx_carrier_errors: 0, tx_fifo_errors: 0, tx_heartbeat_errors: 0, tx_window_errors: 0, rx_compressed: 0, tx_compressed: 0, rx_nohandler: 0, rx_otherhost_dropped: 0, }), LinkAttribute::Stats(Stats { rx_packets: 177232, tx_packets: 100736, rx_bytes: 126994754, tx_bytes: 18116173, rx_errors: 0, tx_errors: 0, rx_dropped: 0, tx_dropped: 0, multicast: 0, collisions: 0, rx_length_errors: 0, rx_over_errors: 0, rx_crc_errors: 0, rx_frame_errors: 0, rx_fifo_errors: 0, rx_missed_errors: 0, tx_aborted_errors: 0, tx_carrier_errors: 0, tx_fifo_errors: 0, tx_heartbeat_errors: 0, tx_window_errors: 0, rx_compressed: 0, tx_compressed: 0, rx_nohandler: 0, }), LinkAttribute::NumVf(0), LinkAttribute::Xdp(vec![LinkXdp::Attached(XdpAttached::None)]), LinkAttribute::PermAddress(vec![16, 165, 29, 111, 210, 24]), LinkAttribute::AfSpecUnspec(vec![ AfSpecUnspec::Inet(vec![AfSpecInet::DevConf(InetDevConf { forwarding: 1, mc_forwarding: 0, proxy_arp: 0, accept_redirects: 1, secure_redirects: 1, send_redirects: 1, shared_media: 1, rp_filter: 2, accept_source_route: 0, bootp_relay: 0, log_martians: 0, tag: 0, arpfilter: 0, medium_id: 0, noxfrm: 0, nopolicy: 0, force_igmp_version: 0, arp_announce: 0, arp_ignore: 0, promote_secondaries: 1, 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, bc_forwarding: 0, arp_evict_nocarrier: 1, })]), AfSpecUnspec::Inet6(vec![ AfSpecInet6::Flags( Inet6IfaceFlags::Otherconf | Inet6IfaceFlags::RaManaged | Inet6IfaceFlags::RaRcvd | Inet6IfaceFlags::RsSent | Inet6IfaceFlags::Ready, ), AfSpecInet6::CacheInfo(Inet6CacheInfo { max_reasm_len: 65535, tstamp: 1258, reachable_time: 24090, retrans_time: 1000, }), AfSpecInet6::DevConf(Inet6DevConf { forwarding: 0, hoplimit: 64, mtu6: 1500, accept_ra: 1, accept_redirects: 1, autoconf: 1, dad_transmits: 1, rtr_solicits: -1, rtr_solicit_interval: 4000, rtr_solicit_delay: 1000, use_tempaddr: 0, 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, rpl_seg_enabled: 0, ra_defrtr_metric: 1024, ioam6_enabled: 0, ioam6_id: 65535, ioam6_id_wide: -1, ndisc_evict_nocarrier: 1, accept_untracked_na: 0, accept_ra_min_lft: 0, }), ]), ]), LinkAttribute::PropList(vec![Prop::AltIfName("wlp0s20f3".into())]), LinkAttribute::Other(DefaultNla::new( 56, vec![48, 48, 48, 48, 58, 48, 48, 58, 49, 52, 46, 51, 0], )), LinkAttribute::Other(DefaultNla::new(57, vec![112, 99, 105, 0])), LinkAttribute::Other(DefaultNla::new( 32830, // NLA_F_NESTED|IFLA_DEVLINK_PORT vec![], )), ], }; assert_eq!( expected, LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/link/tests/veth.rs000064400000000000000000000076011046102023000201570ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::link::link_flag::LinkFlags; use crate::link::{ InfoData, InfoKind, InfoVeth, LinkAttribute, LinkHeader, LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, }; use crate::AddressFamily; #[test] fn test_veth_get_link_info() { let raw: Vec = vec![ 0x00, 0x00, // AF_UNSPEC and reserved 0x01, 0x00, // Link layer type ethernet(1) 0x19, 0x00, 0x00, 0x00, // iface index 25 0x43, 0x10, 0x01, 0x00, // flags 0x00, 0x00, 0x00, 0x00, // changed flags0 0x10, 0x00, // length 16 0x12, 0x00, // IFLA_LINKINFO 18 0x09, 0x00, // length 09 0x01, 0x00, // IFLA_INFO_KIND 0x76, 0x65, 0x74, 0x68, 0x00, // 'bond\0' 0x00, 0x00, 0x00, // padding ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 25, link_layer_type: LinkLayerType::Ether, flags: LinkFlags::Broadcast | LinkFlags::LowerUp | LinkFlags::Multicast | LinkFlags::Running | LinkFlags::Up, change_mask: LinkFlags::empty(), }, attributes: vec![LinkAttribute::LinkInfo(vec![LinkInfo::Kind( InfoKind::Veth, )])], }; assert_eq!( expected, LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } #[test] fn test_crate_veth() { // With `iproute 6.5.0`, the IFLA_INFO_KIND will not use NULL terminated // string. // This is bug of iproute: https://issues.redhat.com/browse/RHEL-14964 // The correct way is NULL terminated `IFLA_INFO_KIND`, hence below packet // is different from what iproute generate. let raw: Vec = vec![ 0x00, // interface family AF_UNSPEC 0x00, // reserved 0x00, 0x00, // link layer type 0 0x00, 0x00, 0x00, 0x00, // iface index 0 0x00, 0x00, 0x00, 0x00, // device flags 0 0x00, 0x00, 0x00, 0x00, // change flags 0 0x0a, 0x00, // length 10 0x03, 0x00, // IFLA_IFNAME 0x76, 0x65, 0x74, 0x68, 0x31, 0x00, 0x00, 0x00, // "veth0\0" 0x38, 0x00, // length 56 0x12, 0x00, // IFLA_LINKINFO 18 0x09, 0x00, // length 9 0x01, 0x00, // IFLA_INFO_KIND 1 0x76, 0x65, 0x74, 0x68, 0x00, // 'veth\0' 0x00, 0x00, 0x00, // padding 0x28, 0x00, // length 40 0x02, 0x00, // IFLA_INFO_DATA 2 0x24, 0x00, // length 36 0x01, 0x00, // VETH_INFO_PEER 1 0x00, // Netlink Message header: family AF_UNSPEC 0x00, // reserved 0x00, 0x00, // link layer type 0 0x00, 0x00, 0x00, 0x00, // iface index 0 0x00, 0x00, 0x00, 0x00, // device flags 0 0x00, 0x00, 0x00, 0x00, // change flags 0 0x0d, 0x00, // length 16 0x03, 0x00, // IFLA_IFNAME 3 0x76, 0x65, 0x74, 0x68, 0x31, 0x2d, 0x65, 0x70, 0x00, // "veth1-ep\0" 0x00, 0x00, 0x00, // padding ]; let expected = LinkMessage { attributes: vec![ LinkAttribute::IfName("veth1".to_string()), LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::Veth), LinkInfo::Data(InfoData::Veth(InfoVeth::Peer(LinkMessage { attributes: vec![LinkAttribute::IfName( "veth1-ep".to_string(), )], ..Default::default() }))), ]), ], ..Default::default() }; assert_eq!( expected, LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/link/tests/vlan.rs000064400000000000000000000055441046102023000201550ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::link::link_flag::LinkFlags; use crate::link::{ InfoData, InfoKind, InfoVlan, LinkAttribute, LinkHeader, LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, VlanProtocol, VlanQosMapping, }; use crate::AddressFamily; #[test] fn test_parsing_link_vlan() { let raw = vec![ 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x00, 0x00, 0x43, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // change flags 0 0x50, 0x00, // length 80 0x12, 0x00, // IFLA_LINKINFO 18 0x09, 0x00, // length 0x01, 0x00, // IFLA_INFO_KIND 1 0x76, 0x6c, 0x61, 0x6e, 0x00, // 'vlan\0' 0x00, 0x00, 0x00, // padding 0x40, 0x00, // length 64 0x02, 0x00, // IFLA_INFO_DATA 2 0x06, 0x00, // length 06 0x05, 0x00, // IFLA_VLAN_PROTOCOL 5 0x81, 0x00, // big endian 0x8100 ETH_P_8021Q 0x00, 0x00, // padding 0x06, 0x00, // length 06 0x01, 0x00, // IFLA_VLAN_ID 1 0x65, 0x00, // VLAN ID 101 0x00, 0x00, // padding 0x0c, 0x00, // length 12 0x02, 0x00, // IFLA_VLAN_FLAGS 2 0x01, 0x00, 0x00, 0x00, // flags VLAN_FLAG_REORDER_HDR(1) 0xff, 0xff, 0xff, 0xff, // mask 0x10, 0x00, // length 16 0x04, 0x00, // IFLA_VLAN_INGRESS_QOS 4 0x0c, 0x00, // length 12 0x01, 0x00, // IFLA_VLAN_QOS_MAPPING 1 0x06, 0x00, 0x00, 0x00, // from 6 0x07, 0x00, 0x00, 0x00, // to 7 0x10, 0x00, // length 16 0x03, 0x00, // IFLA_VLAN_EGRESS_QOS 3 0x0c, 0x00, // length 12 0x01, 0x00, // IFLA_VLAN_QOS_MAPPING 1 0x04, 0x00, 0x00, 0x00, // from 4 0x05, 0x00, 0x00, 0x00, // to 5 ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 34, link_layer_type: LinkLayerType::Ether, flags: LinkFlags::Broadcast | LinkFlags::LowerUp | LinkFlags::Multicast | LinkFlags::Running | LinkFlags::Up, change_mask: LinkFlags::empty(), }, attributes: vec![LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::Vlan), LinkInfo::Data(InfoData::Vlan(vec![ InfoVlan::Protocol(VlanProtocol::Ieee8021Q), InfoVlan::Id(101), InfoVlan::Flags((1, 4294967295)), InfoVlan::IngressQos(vec![VlanQosMapping::Mapping(6, 7)]), InfoVlan::EgressQos(vec![VlanQosMapping::Mapping(4, 5)]), ])), ])], }; assert_eq!( expected, LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/link/tests/vrf.rs000064400000000000000000000441751046102023000200150ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::Ipv6Addr; use netlink_packet_utils::nla::DefaultNla; use netlink_packet_utils::{Emitable, Parseable}; use crate::link::link_flag::LinkFlags; use crate::link::link_info::InfoVrfPort; use crate::link::{ AfSpecInet, AfSpecInet6, AfSpecUnspec, Icmp6Stats, Inet6CacheInfo, Inet6DevConf, Inet6IfaceFlags, Inet6Stats, InetDevConf, InfoData, InfoKind, InfoPortData, InfoPortKind, InfoVrf, LinkAttribute, LinkHeader, LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, LinkXdp, Map, State, Stats, Stats64, XdpAttached, }; use crate::AddressFamily; #[test] fn test_parsing_link_vrf() { let raw = vec![ 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x00, 0x00, 0xc1, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x12, 0x00, 0x08, 0x00, 0x01, 0x00, 0x76, 0x72, 0x66, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x00, 0x00, ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 34, link_layer_type: LinkLayerType::Ether, flags: LinkFlags::Controller | LinkFlags::LowerUp | LinkFlags::Noarp | LinkFlags::Running | LinkFlags::Up, change_mask: LinkFlags::empty(), }, attributes: vec![LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::Vrf), LinkInfo::Data(InfoData::Vrf(vec![InfoVrf::TableId(10)])), ])], }; assert_eq!( expected, LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } /// This link correspond to a veth link attached to a VRF, created as /// follow: /// /// ```no_rust /// sudo ip link add vrf-blue type vrf table 10 /// sudo ip link set dev vrf-blue up /// sudo ip link add veth20 type veth /// sudo ip link set veth20 master vrf-blue /// ``` #[test] fn test_link_info_with_ifla_vrf_port_table() { let data = vec![ 0x00, 0x00, // interface family unspec 0x01, 0x00, // link layer type ethernet 0x23, 0x00, 0x00, 0x00, // index 35 0x02, 0x10, 0x00, 0x00, // flags: broadcast, multicast 0x00, 0x00, 0x00, 0x00, // change flags 0x0b, 0x00, 0x03, 0x00, 0x76, 0x65, 0x74, 0x68, 0x32, 0x30, 0x00, 0x00, 0x08, 0x00, 0x0d, 0x00, 0xe8, 0x03, 0x00, 0x00, // tx queue len: 1000 0x05, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, // oper state: down 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, // link mode: 0 0x08, 0x00, 0x04, 0x00, 0xdc, 0x05, 0x00, 0x00, // mtu: 1500 0x08, 0x00, 0x32, 0x00, 0x44, 0x00, 0x00, 0x00, // min mtu: 68 0x08, 0x00, 0x33, 0x00, 0xff, 0xff, 0x00, 0x00, // max mtu: 65535 0x08, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, // group: 0 0x08, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, // promisc: 0 0x08, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, // unparsed (all multi count: 0) 0x08, 0x00, 0x1f, 0x00, 0x1a, 0x00, 0x00, 0x00, // number of tx queues: 26 0x08, 0x00, 0x28, 0x00, 0xff, 0xff, 0x00, 0x00, // Max GSO segment count: 65535 0x08, 0x00, 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, // Max GSO size: 65536 0x08, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x01, 0x00, // unparsed (GRO max size) 0x08, 0x00, 0x3b, 0x00, 0xf8, 0xff, 0x07, 0x00, // unparsed (TSO max size) 0x08, 0x00, 0x3c, 0x00, 0xff, 0xff, 0x00, 0x00, // unparsed (TSO max segments) 0x08, 0x00, 0x20, 0x00, 0x1a, 0x00, 0x00, 0x00, // number of rx queues: 26 0x08, 0x00, 0x0a, 0x00, 0x21, 0x00, 0x00, 0x00, // controller 33 (master) 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, // carrier: not restricted 0x09, 0x00, 0x06, 0x00, 0x6e, 0x6f, 0x6f, 0x70, 0x00, 0x00, 0x00, 0x00, // queue discipline: noop 0x08, 0x00, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, // carrier changes: 1 0x08, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, // carrier up count: 0 0x08, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0x00, // carrier down count: 1 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, // proto down: 0 // Map 0x24, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // end map 0x0a, 0x00, 0x01, 0x00, 0x82, 0x84, 0x7b, 0x35, 0x68, 0x99, 0x00, 0x00, // hw address: 82:84:7b:35:68:99 0x0a, 0x00, 0x02, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, // hw broadcast address // Stats64 0xcc, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Stats 0x64, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x2b, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // XDP: attached: none // Link Info 0x24, 0x00, 0x12, 0x00, 0x09, 0x00, 0x01, 0x00, 0x76, 0x65, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, // Kind: veth 0x08, 0x00, 0x04, 0x00, 0x76, 0x72, 0x66, 0x00, 0x0c, 0x00, 0x05, 0x00, // Port kind: vrf 0x08, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x00, 0x00, // port data: table ID 10 0x08, 0x00, 0x05, 0x00, 0x22, 0x00, 0x00, 0x00, // Link: 34 // AF spec header 0x1c, 0x03, 0x1a, 0x00, // AF spec inet header 0x8c, 0x00, 0x02, 0x00, // DevConf 0x88, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // AF spec inet6 header 0x8c, 0x02, 0x0a, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // Flags // Cache info 0x14, 0x00, 0x05, 0x00, 0xff, 0xff, 0x00, 0x00, 0x59, 0x28, 0x18, 0x00, 0x00, 0x84, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, // net6 dev conf 0xf0, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xdc, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xa0, 0x0f, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3a, 0x09, 0x00, 0x80, 0x51, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xee, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // inet6 stats 0x2c, 0x01, 0x03, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // icmp6 stats 0x34, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // token: :: 0x14, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // addr gen mode: 0 0x05, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 35, link_layer_type: LinkLayerType::Ether, flags: LinkFlags::Broadcast | LinkFlags::Multicast, change_mask: LinkFlags::empty(), }, attributes: vec![ LinkAttribute::IfName("veth20".to_string()), LinkAttribute::TxQueueLen(1000), LinkAttribute::OperState(State::Down), LinkAttribute::Mode(0), LinkAttribute::Mtu(1500), LinkAttribute::MinMtu(68), LinkAttribute::MaxMtu(65535), LinkAttribute::Group(0), LinkAttribute::Promiscuity(0), LinkAttribute::Other(DefaultNla::new(61, vec![0, 0, 0, 0])), LinkAttribute::NumTxQueues(26), LinkAttribute::GsoMaxSegs(65535), LinkAttribute::GsoMaxSize(65536), LinkAttribute::Other(DefaultNla::new(58, vec![0, 0, 1, 0])), LinkAttribute::Other(DefaultNla::new(59, vec![248, 255, 7, 0])), LinkAttribute::Other(DefaultNla::new(60, vec![255, 255, 0, 0])), LinkAttribute::NumRxQueues(26), LinkAttribute::Controller(33), LinkAttribute::Carrier(0), LinkAttribute::Qdisc("noop".to_string()), LinkAttribute::CarrierChanges(1), LinkAttribute::CarrierUpCount(0), LinkAttribute::CarrierDownCount(1), LinkAttribute::ProtoDown(0), LinkAttribute::Map(Map::default()), LinkAttribute::Address(vec![130, 132, 123, 53, 104, 153]), LinkAttribute::Broadcast(vec![255, 255, 255, 255, 255, 255]), LinkAttribute::Stats64(Stats64::default()), LinkAttribute::Stats(Stats::default()), LinkAttribute::Xdp(vec![LinkXdp::Attached(XdpAttached::None)]), LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::Veth), LinkInfo::PortKind(InfoPortKind::Vrf), LinkInfo::PortData(InfoPortData::VrfPort(vec![ InfoVrfPort::TableId(10), ])), ]), LinkAttribute::Link(34), LinkAttribute::AfSpecUnspec(vec![ AfSpecUnspec::Inet(vec![AfSpecInet::DevConf(InetDevConf { forwarding: 1, secure_redirects: 1, send_redirects: 1, shared_media: 1, accept_source_route: 1, igmpv2_unsolicited_report_interval: 10000, igmpv3_unsolicited_report_interval: 1000, arp_evict_nocarrier: 1, ..Default::default() })]), AfSpecUnspec::Inet6(vec![ AfSpecInet6::Flags(Inet6IfaceFlags::empty()), AfSpecInet6::CacheInfo(Inet6CacheInfo { max_reasm_len: 65535, tstamp: 1583193, reachable_time: 33792, retrans_time: 1000, }), AfSpecInet6::DevConf(Inet6DevConf { forwarding: 1, hoplimit: 64, mtu6: 1500, accept_ra: 1, autoconf: 1, dad_transmits: 1, rtr_solicits: -1, rtr_solicit_interval: 4000, rtr_solicit_delay: 1000, temp_valid_lft: 604800, temp_prefered_lft: 86400, regen_max_retry: 3, max_desync_factor: 600, max_addresses: 16, accept_ra_defrtr: 1, accept_ra_pinfo: 1, accept_ra_rtr_pref: 1, rtr_probe_interval: 60000, mldv1_unsolicited_report_interval: 10000, mldv2_unsolicited_report_interval: 1000, suppress_frag_ndisc: 1, accept_ra_min_hop_limit: 1, rtr_solicit_max_interval: 3600000, enhanced_dad: 1, ra_defrtr_metric: 1024, ioam6_id: 65535, ioam6_id_wide: -1, ndisc_evict_nocarrier: 1, accept_ra_mtu: 1, accept_dad: 1, ..Default::default() }), AfSpecInet6::Stats(Inet6Stats { num: 37, ..Default::default() }), AfSpecInet6::Icmp6Stats(Icmp6Stats { num: 6, ..Default::default() }), AfSpecInet6::Token(Ipv6Addr::UNSPECIFIED), AfSpecInet6::AddrGenMode(0), ]), ]), ], }; let link = LinkMessage::parse(&LinkMessageBuffer::new(&data)).unwrap(); assert_eq!(expected, link); let _buf = vec![0; data.len()]; // FIXME: the packet we write is not a perfect match with the // packet we received from the kernel. // // link.emit(&mut buf[..]); // assert_eq!(data, buf); } netlink-packet-route-0.21.0/src/link/tests/vxlan.rs000064400000000000000000000616601046102023000203460ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::{Ipv4Addr, Ipv6Addr}; use std::str::FromStr; use netlink_packet_utils::{nla::DefaultNla, Emitable, Parseable}; use crate::link::link_flag::LinkFlags; use crate::link::{ AfSpecInet, AfSpecInet6, AfSpecUnspec, Inet6CacheInfo, Inet6DevConf, Inet6IfaceFlags, InetDevConf, InfoData, InfoKind, InfoVxlan, LinkAttribute, LinkHeader, LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, LinkXdp, Map, State, Stats, Stats64, XdpAttached, }; use crate::AddressFamily; #[test] fn test_parsing_link_vxlan() { let raw = vec![ 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x43, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x30, 0x00, 0x00, 0x08, 0x00, 0x0d, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0xaa, 0x05, 0x00, 0x00, 0x08, 0x00, 0x32, 0x00, 0x44, 0x00, 0x00, 0x00, 0x08, 0x00, 0x33, 0x00, 0xff, 0xff, 0x00, 0x00, 0x08, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x28, 0x00, 0xff, 0xff, 0x00, 0x00, 0x08, 0x00, 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x3b, 0x00, 0xf8, 0xff, 0x07, 0x00, 0x08, 0x00, 0x3c, 0x00, 0xff, 0xff, 0x00, 0x00, 0x08, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x6e, 0x6f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x00, 0x08, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x23, 0x45, 0x67, 0x89, 0x1c, 0x00, 0x00, 0x0a, 0x00, 0x02, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xcc, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x2b, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x12, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, 0x65, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x03, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x01, 0x01, 0x01, 0x01, 0x05, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0f, 0x00, 0x12, 0xb5, 0x00, 0x00, 0x05, 0x00, 0x12, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x01, 0x1a, 0x00, 0x8c, 0x00, 0x02, 0x00, 0x88, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x01, 0x0a, 0x00, 0x08, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x80, 0x14, 0x00, 0x05, 0x00, 0xff, 0xff, 0x00, 0x00, 0xb0, 0x1c, 0x00, 0x00, 0x0e, 0x8c, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0xf0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xaa, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xa0, 0x0f, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3a, 0x09, 0x00, 0x80, 0x51, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xee, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x3e, 0x80, ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 16, link_layer_type: LinkLayerType::Ether, flags: LinkFlags::Broadcast | LinkFlags::LowerUp | LinkFlags::Multicast | LinkFlags::Running | LinkFlags::Up, change_mask: LinkFlags::empty(), }, attributes: vec![ LinkAttribute::IfName("vxlan0".into()), LinkAttribute::TxQueueLen(1000), LinkAttribute::OperState(State::Unknown), LinkAttribute::Mode(0), LinkAttribute::Mtu(1450), LinkAttribute::MinMtu(68), LinkAttribute::MaxMtu(65535), LinkAttribute::Group(0), LinkAttribute::Promiscuity(0), LinkAttribute::Other(DefaultNla::new(61, vec![0, 0, 0, 0])), LinkAttribute::NumTxQueues(1), LinkAttribute::GsoMaxSegs(65535), LinkAttribute::GsoMaxSize(65536), LinkAttribute::Other(DefaultNla::new(58, vec![0, 0, 1, 0])), LinkAttribute::Other(DefaultNla::new(63, vec![0, 0, 1, 0])), LinkAttribute::Other(DefaultNla::new(64, vec![0, 0, 1, 0])), LinkAttribute::Other(DefaultNla::new(59, vec![248, 255, 7, 0])), LinkAttribute::Other(DefaultNla::new(60, vec![255, 255, 0, 0])), LinkAttribute::NumRxQueues(1), LinkAttribute::Carrier(1), LinkAttribute::Qdisc("noqueue".to_string()), LinkAttribute::CarrierChanges(0), LinkAttribute::CarrierUpCount(0), LinkAttribute::CarrierDownCount(0), LinkAttribute::ProtoDown(0), LinkAttribute::Map(Map { memory_start: 0, memory_end: 0, base_address: 0, irq: 0, dma: 0, port: 0, }), LinkAttribute::Address(vec![0x00, 0x23, 0x45, 0x67, 0x89, 0x1c]), LinkAttribute::Broadcast(vec![0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), LinkAttribute::Stats64(Stats64 { rx_packets: 0, tx_packets: 0, rx_bytes: 0, tx_bytes: 0, rx_errors: 0, tx_errors: 698, rx_dropped: 0, tx_dropped: 0, multicast: 0, collisions: 0, rx_length_errors: 0, rx_over_errors: 0, rx_crc_errors: 0, rx_frame_errors: 0, rx_fifo_errors: 0, rx_missed_errors: 0, tx_aborted_errors: 0, tx_carrier_errors: 698, tx_fifo_errors: 0, tx_heartbeat_errors: 0, tx_window_errors: 0, rx_compressed: 0, tx_compressed: 0, rx_nohandler: 0, rx_otherhost_dropped: 0, }), LinkAttribute::Stats(Stats { rx_packets: 0, tx_packets: 0, rx_bytes: 0, tx_bytes: 0, rx_errors: 0, tx_errors: 698, rx_dropped: 0, tx_dropped: 0, multicast: 0, collisions: 0, rx_length_errors: 0, rx_over_errors: 0, rx_crc_errors: 0, rx_frame_errors: 0, rx_fifo_errors: 0, rx_missed_errors: 0, tx_aborted_errors: 0, tx_carrier_errors: 698, tx_fifo_errors: 0, tx_heartbeat_errors: 0, tx_window_errors: 0, rx_compressed: 0, tx_compressed: 0, rx_nohandler: 0, }), LinkAttribute::Xdp(vec![LinkXdp::Attached(XdpAttached::None)]), LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::Vxlan), LinkInfo::Data(InfoData::Vxlan(vec![ InfoVxlan::Id(101), InfoVxlan::Group(Ipv4Addr::from_str("8.8.8.8").unwrap()), InfoVxlan::Link(13), InfoVxlan::Local(Ipv4Addr::from_str("1.1.1.1").unwrap()), InfoVxlan::Ttl(0), InfoVxlan::TtlInherit(false), InfoVxlan::Tos(0), InfoVxlan::Df(0), InfoVxlan::Label(0), InfoVxlan::Learning(true), InfoVxlan::Proxy(false), InfoVxlan::Rsc(false), InfoVxlan::L2Miss(false), InfoVxlan::L3Miss(false), InfoVxlan::CollectMetadata(false), InfoVxlan::Ageing(300), InfoVxlan::Limit(0), InfoVxlan::Port(4789), InfoVxlan::UDPCsum(true), InfoVxlan::UDPZeroCsumTX(false), InfoVxlan::UDPZeroCsumRX(false), InfoVxlan::RemCsumTX(false), InfoVxlan::RemCsumRX(false), InfoVxlan::Localbypass(true), InfoVxlan::PortRange((0, 0)), ])), ]), LinkAttribute::AfSpecUnspec(vec![ AfSpecUnspec::Inet(vec![AfSpecInet::DevConf(InetDevConf { forwarding: 1, mc_forwarding: 0, proxy_arp: 0, accept_redirects: 1, secure_redirects: 1, send_redirects: 1, shared_media: 1, rp_filter: 2, accept_source_route: 0, bootp_relay: 0, log_martians: 0, tag: 0, arpfilter: 0, medium_id: 0, noxfrm: 0, nopolicy: 0, force_igmp_version: 0, arp_announce: 0, arp_ignore: 0, promote_secondaries: 1, 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, bc_forwarding: 0, arp_evict_nocarrier: 1, })]), AfSpecUnspec::Inet6(vec![ AfSpecInet6::Flags( Inet6IfaceFlags::RsSent | Inet6IfaceFlags::Ready, ), AfSpecInet6::CacheInfo(Inet6CacheInfo { max_reasm_len: 65535, tstamp: 7344, reachable_time: 35854, retrans_time: 1000, }), AfSpecInet6::DevConf(Inet6DevConf { forwarding: 0, hoplimit: 64, mtu6: 1450, accept_ra: 1, accept_redirects: 1, autoconf: 1, dad_transmits: 1, rtr_solicits: -1, rtr_solicit_interval: 4000, rtr_solicit_delay: 1000, use_tempaddr: 0, 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, rpl_seg_enabled: 0, ra_defrtr_metric: 1024, ioam6_enabled: 0, ioam6_id: 65535, ioam6_id_wide: -1, ndisc_evict_nocarrier: 1, accept_untracked_na: 0, accept_ra_min_lft: 0, }), ]), ]), LinkAttribute::Other(DefaultNla::new(32830, vec![])), ], }; assert_eq!( expected, LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } #[test] fn test_parsing_link_vxlan_ipv6() { let raw = vec![ 0x00, 0x00, 0x01, 0x00, 0xbe, 0x69, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x31, 0x00, 0x00, 0x08, 0x00, 0x0d, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x05, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x96, 0x05, 0x00, 0x00, 0x08, 0x00, 0x32, 0x00, 0x44, 0x00, 0x00, 0x00, 0x08, 0x00, 0x33, 0x00, 0xff, 0xff, 0x00, 0x00, 0x08, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x28, 0x00, 0xff, 0xff, 0x00, 0x00, 0x08, 0x00, 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x06, 0x00, 0x6e, 0x6f, 0x6f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x00, 0x0a, 0x00, 0x02, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0c, 0x00, 0x2b, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x12, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x00, 0x00, 0x00, 0xec, 0x00, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x10, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x11, 0x00, 0xfd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x05, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0f, 0x00, 0x21, 0x18, 0x00, 0x00, 0x05, 0x00, 0x12, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 27070, link_layer_type: LinkLayerType::Ether, flags: LinkFlags::Broadcast | LinkFlags::Multicast, change_mask: LinkFlags::empty(), }, attributes: vec![ LinkAttribute::IfName("vxlan1".into()), LinkAttribute::TxQueueLen(1000), LinkAttribute::OperState(State::Down), LinkAttribute::Mode(0), LinkAttribute::Mtu(1430), LinkAttribute::MinMtu(68), LinkAttribute::MaxMtu(65535), LinkAttribute::Group(0), LinkAttribute::Promiscuity(0), LinkAttribute::NumTxQueues(1), LinkAttribute::GsoMaxSegs(65535), LinkAttribute::GsoMaxSize(65536), LinkAttribute::NumRxQueues(1), LinkAttribute::Carrier(1), LinkAttribute::Qdisc("noop".to_string()), LinkAttribute::CarrierChanges(0), LinkAttribute::CarrierUpCount(0), LinkAttribute::CarrierDownCount(0), LinkAttribute::ProtoDown(0), LinkAttribute::Address(vec![0x00, 0x01, 0x02, 0x03, 0x04, 0x05]), LinkAttribute::Broadcast(vec![0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), LinkAttribute::Xdp(vec![LinkXdp::Attached(XdpAttached::None)]), LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::Vxlan), LinkInfo::Data(InfoData::Vxlan(vec![ InfoVxlan::Id(12), InfoVxlan::Group6(Ipv6Addr::from_str("ff00::1").unwrap()), InfoVxlan::Link(2), InfoVxlan::Local6(Ipv6Addr::from_str("fd01::2").unwrap()), InfoVxlan::Ttl(0), InfoVxlan::TtlInherit(false), InfoVxlan::Tos(0), InfoVxlan::Df(0), InfoVxlan::Label(0), InfoVxlan::Other(DefaultNla::new(32, vec![0, 0, 0, 0])), InfoVxlan::Learning(true), InfoVxlan::Proxy(false), InfoVxlan::Rsc(false), InfoVxlan::L2Miss(false), InfoVxlan::L3Miss(false), InfoVxlan::CollectMetadata(false), InfoVxlan::Ageing(300), InfoVxlan::Limit(0), InfoVxlan::Port(8472), InfoVxlan::UDPCsum(true), InfoVxlan::UDPZeroCsumTX(false), InfoVxlan::UDPZeroCsumRX(false), InfoVxlan::RemCsumTX(false), InfoVxlan::RemCsumRX(false), InfoVxlan::Localbypass(true), InfoVxlan::PortRange((0, 0)), ])), ]), ], }; assert_eq!( expected, LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/link/tests/xdp.rs000064400000000000000000000127401046102023000200040ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::{DefaultNla, NlaBuffer}, Emitable, Parseable, }; use crate::link::{xdp::VecLinkXdp, LinkXdp, XdpAttached}; 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 = VecLinkXdp::parse(&nla).unwrap().0; let expected = vec![ LinkXdp::Attached(XdpAttached::None), LinkXdp::Attached(XdpAttached::Driver), LinkXdp::Attached(XdpAttached::SocketBuffer), LinkXdp::Attached(XdpAttached::Hardware), LinkXdp::Attached(XdpAttached::Multiple), LinkXdp::Attached(XdpAttached::Other(252)), ]; assert_eq!(expected, parsed); } #[test] fn emit_xdp_attached() { // None let nlas = vec![LinkXdp::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![LinkXdp::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![LinkXdp::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![LinkXdp::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![LinkXdp::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![LinkXdp::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 = VecLinkXdp::parse(&nla).unwrap().0; let expected = vec![ LinkXdp::Fd(29856), LinkXdp::Flags(0), LinkXdp::ProgId(103), LinkXdp::DrvProgId(101), LinkXdp::SkbProgId(101), LinkXdp::HwProgId(101), LinkXdp::ExpectedFd(29857), LinkXdp::Other( DefaultNla::parse(&NlaBuffer::new(&XDP[56..64])).unwrap(), ), LinkXdp::Other(DefaultNla::parse(&NlaBuffer::new(&XDP[64..])).unwrap()), ]; assert_eq!(expected, parsed); } #[test] fn emit_xdp() { let nlas = vec![ LinkXdp::Fd(29856), LinkXdp::Flags(0), LinkXdp::ProgId(103), LinkXdp::DrvProgId(101), LinkXdp::SkbProgId(101), LinkXdp::HwProgId(101), LinkXdp::ExpectedFd(29857), LinkXdp::Other( DefaultNla::parse(&NlaBuffer::new(&XDP[56..64])).unwrap(), ), LinkXdp::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.21.0/src/link/tests/xfrm.rs000064400000000000000000000030441046102023000201620ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::link::link_flag::LinkFlags; use crate::link::{ InfoData, InfoKind, InfoXfrm, LinkAttribute, LinkHeader, LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, }; use crate::AddressFamily; #[test] fn test_parsing_link_xfrm() { let raw = vec![ 0x00, 0x00, 0xfe, 0xff, 0x28, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x12, 0x00, 0x09, 0x00, 0x01, 0x00, 0x78, 0x66, 0x72, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, ]; let expected = LinkMessage { header: LinkHeader { interface_family: AddressFamily::Unspec, index: 40, link_layer_type: LinkLayerType::None, flags: LinkFlags::LowerUp | LinkFlags::Noarp | LinkFlags::Running | LinkFlags::Up, change_mask: LinkFlags::empty(), }, attributes: vec![LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::Xfrm), LinkInfo::Data(InfoData::Xfrm(vec![ InfoXfrm::Link(2), InfoXfrm::IfId(10), ])), ])], }; assert_eq!( expected, LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/link/vlan_protocol.rs000064400000000000000000000024571046102023000207340ustar 00000000000000// SPDX-License-Identifier: MIT const ETH_P_8021Q: u16 = 0x8100; const ETH_P_8021AD: u16 = 0x88A8; #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] #[non_exhaustive] #[repr(u16)] // VLAN protocol seldom add new, so no Other for this enum. pub enum VlanProtocol { #[default] Ieee8021Q = ETH_P_8021Q, Ieee8021Ad = ETH_P_8021AD, } impl From for VlanProtocol { fn from(d: u16) -> Self { match d { ETH_P_8021Q => Self::Ieee8021Q, ETH_P_8021AD => Self::Ieee8021Ad, _ => { log::warn!( "BUG: Got unknown VLAN protocol {}, treating as {}", d, Self::Ieee8021Q ); Self::Ieee8021Q } } } } impl From for u16 { fn from(v: VlanProtocol) -> u16 { match v { VlanProtocol::Ieee8021Q => ETH_P_8021Q, VlanProtocol::Ieee8021Ad => ETH_P_8021AD, } } } impl std::fmt::Display for VlanProtocol { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{}", match self { VlanProtocol::Ieee8021Q => "802.1q", VlanProtocol::Ieee8021Ad => "802.1ad", } ) } } netlink-packet-route-0.21.0/src/link/wireless.rs000064400000000000000000000011751046102023000177040ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{DecodeError, Emitable, Parseable}; // Place holder for kernel code is `struct iw_event` #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct LinkWirelessEvent(Vec); impl + ?Sized> Parseable for LinkWirelessEvent { fn parse(buf: &T) -> Result { Ok(LinkWirelessEvent(buf.as_ref().to_vec())) } } impl Emitable for LinkWirelessEvent { fn buffer_len(&self) -> usize { self.0.len() } fn emit(&self, buffer: &mut [u8]) { buffer.copy_from_slice(self.0.as_slice()) } } netlink-packet-route-0.21.0/src/link/xdp.rs000064400000000000000000000147541046102023000166510ustar 00000000000000// SPDX-License-Identifier: MIT use std::{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, parse_u8}, DecodeError, Parseable, }; const IFLA_XDP_FD: u32 = 1; const IFLA_XDP_ATTACHED: u32 = 2; const IFLA_XDP_FLAGS: u32 = 3; const IFLA_XDP_PROG_ID: u32 = 4; const IFLA_XDP_DRV_PROG_ID: u32 = 5; const IFLA_XDP_SKB_PROG_ID: u32 = 6; const IFLA_XDP_HW_PROG_ID: u32 = 7; const IFLA_XDP_EXPECTED_FD: u32 = 8; const XDP_ATTACHED_NONE: u8 = 0; const XDP_ATTACHED_DRV: u8 = 1; const XDP_ATTACHED_SKB: u8 = 2; const XDP_ATTACHED_HW: u8 = 3; const XDP_ATTACHED_MULTI: u8 = 4; #[non_exhaustive] #[derive(Debug, PartialEq, Eq, Clone)] pub enum LinkXdp { Fd(RawFd), Attached(XdpAttached), Flags(u32), ProgId(u32), DrvProgId(u32), SkbProgId(u32), HwProgId(u32), ExpectedFd(u32), Other(DefaultNla), } impl Nla for LinkXdp { fn value_len(&self) -> usize { match self { Self::Fd(_) => size_of::(), Self::Attached(_) => size_of::(), Self::Flags(_) => size_of::(), Self::ProgId(_) => size_of::(), Self::DrvProgId(_) => size_of::(), Self::SkbProgId(_) => size_of::(), Self::HwProgId(_) => size_of::(), Self::ExpectedFd(_) => size_of::(), Self::Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Fd(ref value) => NativeEndian::write_i32(buffer, *value), Self::Attached(ref value) => buffer[0] = value.as_u8(), Self::Flags(ref value) => NativeEndian::write_u32(buffer, *value), Self::ProgId(ref value) => NativeEndian::write_u32(buffer, *value), Self::DrvProgId(ref value) => { NativeEndian::write_u32(buffer, *value) } Self::SkbProgId(ref value) => { NativeEndian::write_u32(buffer, *value) } Self::HwProgId(ref value) => { NativeEndian::write_u32(buffer, *value) } Self::ExpectedFd(ref value) => { NativeEndian::write_u32(buffer, *value) } Self::Other(ref nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Fd(_) => IFLA_XDP_FD as u16, Self::Attached(_) => IFLA_XDP_ATTACHED as u16, Self::Flags(_) => IFLA_XDP_FLAGS as u16, Self::ProgId(_) => IFLA_XDP_PROG_ID as u16, Self::DrvProgId(_) => IFLA_XDP_DRV_PROG_ID as u16, Self::SkbProgId(_) => IFLA_XDP_SKB_PROG_ID as u16, Self::HwProgId(_) => IFLA_XDP_HW_PROG_ID as u16, Self::ExpectedFd(_) => IFLA_XDP_EXPECTED_FD as u16, Self::Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for LinkXdp { fn parse(nla: &NlaBuffer<&'a T>) -> Result { let payload = nla.value(); Ok(match nla.kind() as u32 { IFLA_XDP_FD => Self::Fd( parse_i32(payload).context("invalid IFLA_XDP_FD value")?, ), IFLA_XDP_ATTACHED => { let err = "invalid IFLA_XDP_ATTACHED value"; let value = parse_u8(payload).context(err)?; Self::Attached(XdpAttached::try_from(value).context(err)?) } IFLA_XDP_FLAGS => Self::Flags( parse_u32(payload).context("invalid IFLA_XDP_FLAGS value")?, ), IFLA_XDP_PROG_ID => Self::ProgId( parse_u32(payload).context("invalid IFLA_XDP_PROG_ID value")?, ), IFLA_XDP_DRV_PROG_ID => Self::DrvProgId( parse_u32(payload).context("invalid IFLA_XDP_PROG_ID value")?, ), IFLA_XDP_SKB_PROG_ID => Self::SkbProgId( parse_u32(payload).context("invalid IFLA_XDP_PROG_ID value")?, ), IFLA_XDP_HW_PROG_ID => Self::HwProgId( parse_u32(payload).context("invalid IFLA_XDP_PROG_ID value")?, ), IFLA_XDP_EXPECTED_FD => Self::ExpectedFd( parse_u32(payload).context("invalid IFLA_XDP_PROG_ID value")?, ), _ => Self::Other( DefaultNla::parse(nla) .context(format!("unknown NLA type {}", nla.kind()))?, ), }) } } pub(crate) struct VecLinkXdp(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 VecLinkXdp { 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?; res.push(LinkXdp::parse(&nla)?); } Ok(VecLinkXdp(res)) } } #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] 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, } } } netlink-packet-route-0.21.0/src/message.rs000064400000000000000000000625071046102023000165440ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_core::{ NetlinkDeserializable, NetlinkHeader, NetlinkPayload, NetlinkSerializable, }; use netlink_packet_utils::{ DecodeError, Emitable, Parseable, ParseableParametrized, }; use crate::tc::{TcActionMessage, TcActionMessageBuffer}; use crate::{ address::{AddressHeader, AddressMessage, AddressMessageBuffer}, link::{LinkMessage, LinkMessageBuffer}, neighbour::{NeighbourMessage, NeighbourMessageBuffer}, neighbour_table::{NeighbourTableMessage, NeighbourTableMessageBuffer}, nsid::{NsidMessage, NsidMessageBuffer}, prefix::{PrefixMessage, PrefixMessageBuffer}, route::{RouteHeader, RouteMessage, RouteMessageBuffer}, rule::{RuleMessage, RuleMessageBuffer}, tc::{TcMessage, TcMessageBuffer}, }; const RTM_NEWLINK: u16 = 16; const RTM_DELLINK: u16 = 17; const RTM_GETLINK: u16 = 18; const RTM_SETLINK: u16 = 19; const RTM_NEWADDR: u16 = 20; const RTM_DELADDR: u16 = 21; const RTM_GETADDR: u16 = 22; const RTM_NEWROUTE: u16 = 24; const RTM_DELROUTE: u16 = 25; const RTM_GETROUTE: u16 = 26; const RTM_NEWNEIGH: u16 = 28; const RTM_DELNEIGH: u16 = 29; const RTM_GETNEIGH: u16 = 30; const RTM_NEWRULE: u16 = 32; const RTM_DELRULE: u16 = 33; const RTM_GETRULE: u16 = 34; const RTM_NEWQDISC: u16 = 36; const RTM_DELQDISC: u16 = 37; const RTM_GETQDISC: u16 = 38; const RTM_NEWTCLASS: u16 = 40; const RTM_DELTCLASS: u16 = 41; const RTM_GETTCLASS: u16 = 42; const RTM_NEWTFILTER: u16 = 44; const RTM_DELTFILTER: u16 = 45; const RTM_GETTFILTER: u16 = 46; const RTM_NEWACTION: u16 = 48; const RTM_DELACTION: u16 = 49; const RTM_GETACTION: u16 = 50; const RTM_NEWPREFIX: u16 = 52; // const RTM_GETMULTICAST: u16 = 58; // const RTM_GETANYCAST: u16 = 62; const RTM_NEWNEIGHTBL: u16 = 64; const RTM_GETNEIGHTBL: u16 = 66; const RTM_SETNEIGHTBL: u16 = 67; // const RTM_NEWNDUSEROPT: u16 = 68; // const RTM_NEWADDRLABEL: u16 = 72; // const RTM_DELADDRLABEL: u16 = 73; // const RTM_GETADDRLABEL: u16 = 74; // const RTM_GETDCB: u16 = 78; // const RTM_SETDCB: u16 = 79; // const RTM_NEWNETCONF: u16 = 80; // const RTM_DELNETCONF: u16 = 81; // const RTM_GETNETCONF: u16 = 82; // const RTM_NEWMDB: u16 = 84; // const RTM_DELMDB: u16 = 85; // const RTM_GETMDB: u16 = 86; const RTM_NEWNSID: u16 = 88; const RTM_DELNSID: u16 = 89; const RTM_GETNSID: u16 = 90; // const RTM_NEWSTATS: u16 = 92; // const RTM_GETSTATS: u16 = 94; // const RTM_NEWCACHEREPORT: u16 = 96; const RTM_NEWCHAIN: u16 = 100; const RTM_DELCHAIN: u16 = 101; const RTM_GETCHAIN: u16 = 102; const RTM_NEWLINKPROP: u16 = 108; const RTM_DELLINKPROP: u16 = 109; buffer!(RouteNetlinkMessageBuffer); impl<'a, T: AsRef<[u8]> + ?Sized> ParseableParametrized, u16> for RouteNetlinkMessage { fn parse_with_param( buf: &RouteNetlinkMessageBuffer<&'a T>, message_type: u16, ) -> Result { 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].into(); msg } else { return Err(e); } } }; match message_type { RTM_NEWLINK => RouteNetlinkMessage::NewLink(msg), RTM_GETLINK => RouteNetlinkMessage::GetLink(msg), RTM_DELLINK => RouteNetlinkMessage::DelLink(msg), RTM_SETLINK => RouteNetlinkMessage::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(), attributes: vec![], }; msg.header.family = buf.inner()[0].into(); msg } else { return Err(e); } } }; match message_type { RTM_NEWADDR => RouteNetlinkMessage::NewAddress(msg), RTM_GETADDR => RouteNetlinkMessage::GetAddress(msg), RTM_DELADDR => RouteNetlinkMessage::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 => RouteNetlinkMessage::GetNeighbour(msg), RTM_NEWNEIGH => RouteNetlinkMessage::NewNeighbour(msg), RTM_DELNEIGH => RouteNetlinkMessage::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 => { RouteNetlinkMessage::GetNeighbourTable(msg) } RTM_NEWNEIGHTBL => { RouteNetlinkMessage::NewNeighbourTable(msg) } RTM_SETNEIGHTBL => { RouteNetlinkMessage::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(), attributes: vec![], }; msg.header.address_family = buf.inner()[0].into(); msg } else { return Err(e); } } }; match message_type { RTM_NEWROUTE => RouteNetlinkMessage::NewRoute(msg), RTM_GETROUTE => RouteNetlinkMessage::GetRoute(msg), RTM_DELROUTE => RouteNetlinkMessage::DelRoute(msg), _ => unreachable!(), } } // Prefix messages RTM_NEWPREFIX => { let err = "invalid prefix message"; RouteNetlinkMessage::NewPrefix( PrefixMessage::parse( &PrefixMessageBuffer::new_checked(&buf.inner()) .context(err)?, ) .context(err)?, ) } 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 => RouteNetlinkMessage::NewRule(msg), RTM_DELRULE => RouteNetlinkMessage::DelRule(msg), RTM_GETRULE => RouteNetlinkMessage::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 => { RouteNetlinkMessage::NewQueueDiscipline(msg) } RTM_DELQDISC => { RouteNetlinkMessage::DelQueueDiscipline(msg) } RTM_GETQDISC => { RouteNetlinkMessage::GetQueueDiscipline(msg) } RTM_NEWTCLASS => RouteNetlinkMessage::NewTrafficClass(msg), RTM_DELTCLASS => RouteNetlinkMessage::DelTrafficClass(msg), RTM_GETTCLASS => RouteNetlinkMessage::GetTrafficClass(msg), RTM_NEWTFILTER => { RouteNetlinkMessage::NewTrafficFilter(msg) } RTM_DELTFILTER => { RouteNetlinkMessage::DelTrafficFilter(msg) } RTM_GETTFILTER => { RouteNetlinkMessage::GetTrafficFilter(msg) } RTM_NEWCHAIN => RouteNetlinkMessage::NewTrafficChain(msg), RTM_DELCHAIN => RouteNetlinkMessage::DelTrafficChain(msg), RTM_GETCHAIN => RouteNetlinkMessage::GetTrafficChain(msg), _ => unreachable!(), } } RTM_NEWACTION | RTM_DELACTION | RTM_GETACTION => { let err = "invalid tc action message"; let msg = TcActionMessage::parse( &TcActionMessageBuffer::new_checked(&buf.inner()) .context(err)?, ) .context(err)?; match message_type { RTM_NEWACTION => RouteNetlinkMessage::NewTrafficAction(msg), RTM_DELACTION => RouteNetlinkMessage::DelTrafficAction(msg), RTM_GETACTION => RouteNetlinkMessage::GetTrafficAction(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 => RouteNetlinkMessage::NewNsId(msg), RTM_DELNSID => RouteNetlinkMessage::DelNsId(msg), RTM_GETNSID => RouteNetlinkMessage::GetNsId(msg), _ => unreachable!(), } } _ => { return Err( format!("Unknown message type: {message_type}").into() ) } }; Ok(message) } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum RouteNetlinkMessage { 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), NewPrefix(PrefixMessage), NewQueueDiscipline(TcMessage), DelQueueDiscipline(TcMessage), GetQueueDiscipline(TcMessage), NewTrafficClass(TcMessage), DelTrafficClass(TcMessage), GetTrafficClass(TcMessage), NewTrafficFilter(TcMessage), DelTrafficFilter(TcMessage), GetTrafficFilter(TcMessage), NewTrafficAction(TcActionMessage), DelTrafficAction(TcActionMessage), GetTrafficAction(TcActionMessage), NewTrafficChain(TcMessage), DelTrafficChain(TcMessage), GetTrafficChain(TcMessage), NewNsId(NsidMessage), DelNsId(NsidMessage), GetNsId(NsidMessage), NewRule(RuleMessage), DelRule(RuleMessage), GetRule(RuleMessage), } impl RouteNetlinkMessage { pub fn is_new_link(&self) -> bool { matches!(self, RouteNetlinkMessage::NewLink(_)) } pub fn is_del_link(&self) -> bool { matches!(self, RouteNetlinkMessage::DelLink(_)) } pub fn is_get_link(&self) -> bool { matches!(self, RouteNetlinkMessage::GetLink(_)) } pub fn is_set_link(&self) -> bool { matches!(self, RouteNetlinkMessage::SetLink(_)) } pub fn is_new_address(&self) -> bool { matches!(self, RouteNetlinkMessage::NewAddress(_)) } pub fn is_del_address(&self) -> bool { matches!(self, RouteNetlinkMessage::DelAddress(_)) } pub fn is_get_address(&self) -> bool { matches!(self, RouteNetlinkMessage::GetAddress(_)) } pub fn is_get_neighbour(&self) -> bool { matches!(self, RouteNetlinkMessage::GetNeighbour(_)) } pub fn is_new_route(&self) -> bool { matches!(self, RouteNetlinkMessage::NewRoute(_)) } pub fn is_new_neighbour(&self) -> bool { matches!(self, RouteNetlinkMessage::NewNeighbour(_)) } pub fn is_get_route(&self) -> bool { matches!(self, RouteNetlinkMessage::GetRoute(_)) } pub fn is_del_neighbour(&self) -> bool { matches!(self, RouteNetlinkMessage::DelNeighbour(_)) } pub fn is_new_neighbour_table(&self) -> bool { matches!(self, RouteNetlinkMessage::NewNeighbourTable(_)) } pub fn is_get_neighbour_table(&self) -> bool { matches!(self, RouteNetlinkMessage::GetNeighbourTable(_)) } pub fn is_set_neighbour_table(&self) -> bool { matches!(self, RouteNetlinkMessage::SetNeighbourTable(_)) } pub fn is_del_route(&self) -> bool { matches!(self, RouteNetlinkMessage::DelRoute(_)) } pub fn is_new_qdisc(&self) -> bool { matches!(self, RouteNetlinkMessage::NewQueueDiscipline(_)) } pub fn is_del_qdisc(&self) -> bool { matches!(self, RouteNetlinkMessage::DelQueueDiscipline(_)) } pub fn is_get_qdisc(&self) -> bool { matches!(self, RouteNetlinkMessage::GetQueueDiscipline(_)) } pub fn is_new_class(&self) -> bool { matches!(self, RouteNetlinkMessage::NewTrafficClass(_)) } pub fn is_del_class(&self) -> bool { matches!(self, RouteNetlinkMessage::DelTrafficClass(_)) } pub fn is_get_class(&self) -> bool { matches!(self, RouteNetlinkMessage::GetTrafficClass(_)) } pub fn is_new_filter(&self) -> bool { matches!(self, RouteNetlinkMessage::NewTrafficFilter(_)) } pub fn is_del_filter(&self) -> bool { matches!(self, RouteNetlinkMessage::DelTrafficFilter(_)) } pub fn is_get_filter(&self) -> bool { matches!(self, RouteNetlinkMessage::GetTrafficFilter(_)) } pub fn is_new_action(&self) -> bool { matches!(self, RouteNetlinkMessage::NewTrafficAction(_)) } pub fn is_del_action(&self) -> bool { matches!(self, RouteNetlinkMessage::DelTrafficAction(_)) } pub fn is_get_action(&self) -> bool { matches!(self, RouteNetlinkMessage::GetTrafficAction(_)) } pub fn is_new_chain(&self) -> bool { matches!(self, RouteNetlinkMessage::NewTrafficChain(_)) } pub fn is_del_chain(&self) -> bool { matches!(self, RouteNetlinkMessage::DelTrafficChain(_)) } pub fn is_get_chain(&self) -> bool { matches!(self, RouteNetlinkMessage::GetTrafficChain(_)) } pub fn is_new_nsid(&self) -> bool { matches!(self, RouteNetlinkMessage::NewNsId(_)) } pub fn is_get_nsid(&self) -> bool { matches!(self, RouteNetlinkMessage::GetNsId(_)) } pub fn is_del_nsid(&self) -> bool { matches!(self, RouteNetlinkMessage::DelNsId(_)) } pub fn is_get_rule(&self) -> bool { matches!(self, RouteNetlinkMessage::GetRule(_)) } pub fn is_new_rule(&self) -> bool { matches!(self, RouteNetlinkMessage::NewRule(_)) } pub fn is_del_rule(&self) -> bool { matches!(self, RouteNetlinkMessage::DelRule(_)) } pub fn message_type(&self) -> u16 { use self::RouteNetlinkMessage::*; 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, NewPrefix(_) => RTM_NEWPREFIX, 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, NewTrafficAction(_) => RTM_NEWACTION, DelTrafficAction(_) => RTM_DELACTION, GetTrafficAction(_) => RTM_GETACTION, 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 RouteNetlinkMessage { #[rustfmt::skip] fn buffer_len(&self) -> usize { use self::RouteNetlinkMessage::*; 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(), NewPrefix(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(), | NewTrafficAction(ref msg) | DelTrafficAction(ref msg) | GetTrafficAction(ref msg) => msg.buffer_len(), } } #[rustfmt::skip] fn emit(&self, buffer: &mut [u8]) { use self::RouteNetlinkMessage::*; 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), | NewPrefix(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), | NewTrafficAction(ref msg) | DelTrafficAction(ref msg) | GetTrafficAction(ref msg) => msg.emit(buffer), } } } impl NetlinkSerializable for RouteNetlinkMessage { 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 RouteNetlinkMessage { type Error = DecodeError; fn deserialize( header: &NetlinkHeader, payload: &[u8], ) -> Result { let buf = RouteNetlinkMessageBuffer::new(payload); match RouteNetlinkMessage::parse_with_param(&buf, header.message_type) { Err(e) => Err(e), Ok(message) => Ok(message), } } } impl From for NetlinkPayload { fn from(message: RouteNetlinkMessage) -> Self { NetlinkPayload::InnerMessage(message) } } netlink-packet-route-0.21.0/src/neighbour/address.rs000064400000000000000000000030611046102023000205150ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::{Ipv4Addr, Ipv6Addr}; use netlink_packet_utils::{DecodeError, Emitable}; use crate::{ ip::{ emit_ip_to_buffer, parse_ipv4_addr, parse_ipv6_addr, IPV4_ADDR_LEN, IPV6_ADDR_LEN, }, AddressFamily, }; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum NeighbourAddress { Inet(Ipv4Addr), Inet6(Ipv6Addr), Other(Vec), } impl NeighbourAddress { pub(crate) fn parse_with_param( address_family: AddressFamily, payload: &[u8], ) -> Result { Ok(match address_family { AddressFamily::Inet => Self::Inet(parse_ipv4_addr(payload)?), AddressFamily::Inet6 => Self::Inet6(parse_ipv6_addr(payload)?), _ => Self::Other(payload.to_vec()), }) } } impl Emitable for NeighbourAddress { fn buffer_len(&self) -> usize { match self { Self::Inet(_) => IPV4_ADDR_LEN, Self::Inet6(_) => IPV6_ADDR_LEN, Self::Other(v) => v.len(), } } fn emit(&self, buffer: &mut [u8]) { match self { Self::Inet(v) => emit_ip_to_buffer(&((*v).into()), buffer), Self::Inet6(v) => emit_ip_to_buffer(&((*v).into()), buffer), Self::Other(v) => buffer.copy_from_slice(v.as_slice()), } } } impl From for NeighbourAddress { fn from(v: Ipv4Addr) -> Self { Self::Inet(v) } } impl From for NeighbourAddress { fn from(v: Ipv6Addr) -> Self { Self::Inet6(v) } } netlink-packet-route-0.21.0/src/neighbour/attribute.rs000064400000000000000000000130731046102023000210770ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{BigEndian, ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::{parse_u16, parse_u16_be, parse_u32}, DecodeError, Emitable, Parseable, ParseableParametrized, }; use super::{NeighbourAddress, NeighbourCacheInfo, NeighbourCacheInfoBuffer}; use crate::{route::RouteProtocol, AddressFamily}; const NDA_DST: u16 = 1; const NDA_LLADDR: u16 = 2; const NDA_CACHEINFO: u16 = 3; const NDA_PROBES: u16 = 4; const NDA_VLAN: u16 = 5; const NDA_PORT: u16 = 6; const NDA_VNI: u16 = 7; const NDA_IFINDEX: u16 = 8; // Kernel constant name is NDA_MASTER const NDA_CONTROLLER: u16 = 9; const NDA_LINK_NETNSID: u16 = 10; const NDA_SRC_VNI: u16 = 11; const NDA_PROTOCOL: u16 = 12; // const NDA_NH_ID: u16 = 13; // const NDA_FDB_EXT_ATTRS: u16 = 14; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum NeighbourAttribute { Destination(NeighbourAddress), LinkLocalAddress(Vec), CacheInfo(NeighbourCacheInfo), Probes(u32), Vlan(u16), Port(u16), Vni(u32), IfIndex(u32), Controller(u32), LinkNetNsId(u32), SourceVni(u32), Protocol(RouteProtocol), Other(DefaultNla), } impl Nla for NeighbourAttribute { fn value_len(&self) -> usize { match self { Self::LinkLocalAddress(bytes) => bytes.len(), Self::Destination(v) => v.buffer_len(), Self::CacheInfo(v) => v.buffer_len(), Self::Vlan(_) | Self::Port(_) => 2, Self::Protocol(v) => v.buffer_len(), Self::Probes(_) | Self::LinkNetNsId(_) | Self::Controller(_) | Self::Vni(_) | Self::IfIndex(_) | Self::SourceVni(_) => 4, Self::Other(attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Destination(v) => v.emit(buffer), Self::LinkLocalAddress(bytes) => { buffer.copy_from_slice(bytes.as_slice()) } Self::CacheInfo(v) => v.emit(buffer), Self::Vlan(value) => NativeEndian::write_u16(buffer, *value), Self::Port(value) => BigEndian::write_u16(buffer, *value), Self::Probes(value) | Self::LinkNetNsId(value) | Self::Controller(value) | Self::Vni(value) | Self::IfIndex(value) | Self::SourceVni(value) => NativeEndian::write_u32(buffer, *value), Self::Protocol(v) => v.emit(buffer), Self::Other(attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Destination(_) => NDA_DST, Self::LinkLocalAddress(_) => NDA_LLADDR, Self::CacheInfo(_) => NDA_CACHEINFO, Self::Probes(_) => NDA_PROBES, Self::Vlan(_) => NDA_VLAN, Self::Port(_) => NDA_PORT, Self::Vni(_) => NDA_VNI, Self::IfIndex(_) => NDA_IFINDEX, Self::Controller(_) => NDA_CONTROLLER, Self::LinkNetNsId(_) => NDA_LINK_NETNSID, Self::SourceVni(_) => NDA_SRC_VNI, Self::Protocol(_) => NDA_PROTOCOL, Self::Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> ParseableParametrized, AddressFamily> for NeighbourAttribute { fn parse_with_param( buf: &NlaBuffer<&'a T>, address_family: AddressFamily, ) -> Result { let payload = buf.value(); Ok(match buf.kind() { NDA_DST => Self::Destination( NeighbourAddress::parse_with_param(address_family, payload) .context(format!("invalid NDA_DST value {:?}", payload))?, ), NDA_LLADDR => Self::LinkLocalAddress(payload.to_vec()), NDA_CACHEINFO => Self::CacheInfo( NeighbourCacheInfo::parse( &NeighbourCacheInfoBuffer::new_checked(payload).context( format!("invalid NDA_CACHEINFO value {:?}", payload), )?, ) .context(format!( "invalid NDA_CACHEINFO value {:?}", payload ))?, ), NDA_PROBES => { Self::Probes(parse_u32(payload).context(format!( "invalid NDA_PROBES value {:?}", payload ))?) } NDA_VLAN => Self::Vlan(parse_u16(payload)?), NDA_PORT => Self::Port( parse_u16_be(payload) .context(format!("invalid NDA_PORT value {payload:?}"))?, ), NDA_VNI => Self::Vni(parse_u32(payload)?), NDA_IFINDEX => Self::IfIndex(parse_u32(payload)?), NDA_CONTROLLER => Self::Controller(parse_u32(payload).context( format!("invalid NDA_CONTROLLER value {payload:?}"), )?), NDA_LINK_NETNSID => Self::LinkNetNsId(parse_u32(payload).context( format!("invalid NDA_LINK_NETNSID value {payload:?}"), )?), NDA_SRC_VNI => Self::SourceVni(parse_u32(payload)?), NDA_PROTOCOL => { Self::Protocol(RouteProtocol::parse(payload).context( format!("invalid NDA_PROTOCOL value {:?}", payload), )?) } _ => Self::Other( DefaultNla::parse(buf) .context("invalid link NLA value (unknown type)")?, ), }) } } netlink-packet-route-0.21.0/src/neighbour/cache_info.rs000064400000000000000000000023461046102023000211530ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] pub struct NeighbourCacheInfo { pub confirmed: u32, pub used: u32, pub updated: u32, pub refcnt: u32, } const NEIGHBOUR_CACHE_INFO_LEN: usize = 16; buffer!(NeighbourCacheInfoBuffer(NEIGHBOUR_CACHE_INFO_LEN) { confirmed: (u32, 0..4), used: (u32, 4..8), updated: (u32, 8..12), refcnt: (u32, 12..16), }); impl> Parseable> for NeighbourCacheInfo { fn parse(buf: &NeighbourCacheInfoBuffer) -> Result { Ok(Self { confirmed: buf.confirmed(), used: buf.used(), updated: buf.updated(), refcnt: buf.refcnt(), }) } } impl Emitable for NeighbourCacheInfo { fn buffer_len(&self) -> usize { NEIGHBOUR_CACHE_INFO_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = NeighbourCacheInfoBuffer::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.21.0/src/neighbour/flags.rs000064400000000000000000000014061046102023000201650ustar 00000000000000// SPDX-License-Identifier: MIT const NTF_USE: u8 = 1 << 0; const NTF_SELF: u8 = 1 << 1; // Kernel constant name is NTF_MASTER const NTF_CONTROLLER: u8 = 1 << 2; const NTF_PROXY: u8 = 1 << 3; const NTF_EXT_LEARNED: u8 = 1 << 4; const NTF_OFFLOADED: u8 = 1 << 5; const NTF_STICKY: u8 = 1 << 6; const NTF_ROUTER: u8 = 1 << 7; bitflags! { #[derive(Clone, Eq, PartialEq, Debug, Copy, Default)] #[non_exhaustive] pub struct NeighbourFlags: u8 { const Use = NTF_USE; const Own = NTF_SELF; const Controller = NTF_CONTROLLER; const Proxy = NTF_PROXY; const ExtLearned = NTF_EXT_LEARNED; const Offloaded = NTF_OFFLOADED; const Sticky = NTF_STICKY; const Router = NTF_ROUTER; const _ = !0; } } netlink-packet-route-0.21.0/src/neighbour/header.rs000064400000000000000000000053721046102023000203270ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::{NlaBuffer, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; use super::{flags::NeighbourFlags, NeighbourState}; use crate::{route::RouteType, AddressFamily}; 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), kind: (u8, 11), payload:(slice, NEIGHBOUR_HEADER_LEN..), }); impl<'a, T: AsRef<[u8]> + ?Sized> NeighbourMessageBuffer<&'a T> { pub fn attributes( &self, ) -> impl Iterator, DecodeError>> { NlasIterator::new(self.payload()) } } /// 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. // Linux kernel struct `struct ndmsg` #[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct NeighbourHeader { pub family: AddressFamily, pub ifindex: u32, /// Neighbour cache entry state. pub state: NeighbourState, /// Neighbour cache entry flags. It should be set to a combination /// of the `NTF_*` constants pub flags: NeighbourFlags, /// Neighbour cache entry type. It should be set to one of the /// `NDA_*` constants. pub kind: RouteType, } impl> Parseable> for NeighbourHeader { fn parse(buf: &NeighbourMessageBuffer) -> Result { Ok(Self { family: buf.family().into(), ifindex: buf.ifindex(), state: buf.state().into(), flags: NeighbourFlags::from_bits_retain(buf.flags()), kind: buf.kind().into(), }) } } 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.into()); packet.set_ifindex(self.ifindex); packet.set_state(self.state.into()); packet.set_flags(self.flags.bits()); packet.set_kind(self.kind.into()); } } netlink-packet-route-0.21.0/src/neighbour/kind.rs000064400000000000000000000033261046102023000200210ustar 00000000000000// SPDX-License-Identifier: MIT const NUD_INCOMPLETE: u16 = 0x01; const NUD_REACHABLE: u16 = 0x02; const NUD_STALE: u16 = 0x04; const NUD_DELAY: u16 = 0x08; const NUD_PROBE: u16 = 0x10; const NUD_FAILED: u16 = 0x20; const NUD_NOARP: u16 = 0x40; const NUD_PERMANENT: u16 = 0x80; const NUD_NONE: u16 = 0x00; /// Types that can be set in a `RTM_GETROUTE` /// ([`NeightbourNetlinkMessage::GetNeightbour`]) message. #[derive(Clone, Eq, PartialEq, Debug, Copy)] #[non_exhaustive] pub enum NeightbourType { Incomplete, Reachable, Stale, Delay, Probe, Failed, Noarp, Permanent, None, Other(u16), } impl From for u16 { fn from(v: NeightbourType) -> u16 { match v { NeightbourType::Incomplete => NUD_INCOMPLETE, NeightbourType::Reachable => NUD_REACHABLE, NeightbourType::Stale => NUD_STALE, NeightbourType::Delay => NUD_DELAY, NeightbourType::Probe => NUD_PROBE, NeightbourType::Failed => NUD_FAILED, NeightbourType::Noarp => NUD_NOARP, NeightbourType::Permanent => NUD_PERMANENT, NeightbourType::None => NUD_NONE, NeightbourType::Other(d) => d, } } } impl From for NeightbourType { fn from(d: u16) -> Self { match d { NUD_INCOMPLETE => Self::Incomplete, NUD_REACHABLE => Self::Reachable, NUD_STALE => Self::Stale, NUD_DELAY => Self::Delay, NUD_PROBE => Self::Probe, NUD_FAILED => Self::Failed, NUD_NOARP => Self::Noarp, NUD_PERMANENT => Self::Permanent, NUD_NONE => Self::None, _ => Self::Other(d), } } } netlink-packet-route-0.21.0/src/neighbour/message.rs000064400000000000000000000036321046102023000205200ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ traits::{Emitable, Parseable, ParseableParametrized}, DecodeError, }; use super::{ super::AddressFamily, NeighbourAttribute, NeighbourHeader, NeighbourMessageBuffer, }; #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct NeighbourMessage { pub header: NeighbourHeader, pub attributes: Vec, } impl Emitable for NeighbourMessage { fn buffer_len(&self) -> usize { self.header.buffer_len() + self.attributes.as_slice().buffer_len() } fn emit(&self, buffer: &mut [u8]) { self.header.emit(buffer); self.attributes .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 { let header = NeighbourHeader::parse(buf) .context("failed to parse neighbour message header")?; let address_family = header.family; Ok(NeighbourMessage { header, attributes: Vec::::parse_with_param( buf, address_family, ) .context("failed to parse neighbour message NLAs")?, }) } } impl<'a, T: AsRef<[u8]> + 'a> ParseableParametrized, AddressFamily> for Vec { fn parse_with_param( buf: &NeighbourMessageBuffer<&'a T>, address_family: AddressFamily, ) -> Result { let mut attributes = vec![]; for nla_buf in buf.attributes() { attributes.push(NeighbourAttribute::parse_with_param( &nla_buf?, address_family, )?); } Ok(attributes) } } netlink-packet-route-0.21.0/src/neighbour/mod.rs000064400000000000000000000007521046102023000176530ustar 00000000000000// SPDX-License-Identifier: MIT mod address; mod attribute; mod cache_info; mod flags; mod header; mod message; mod state; #[cfg(test)] mod tests; pub use self::address::NeighbourAddress; pub use self::attribute::NeighbourAttribute; pub use self::cache_info::{NeighbourCacheInfo, NeighbourCacheInfoBuffer}; pub use self::flags::NeighbourFlags; pub use self::header::{NeighbourHeader, NeighbourMessageBuffer}; pub use self::message::NeighbourMessage; pub use self::state::NeighbourState; netlink-packet-route-0.21.0/src/neighbour/state.rs000064400000000000000000000044311046102023000202120ustar 00000000000000// SPDX-License-Identifier: MIT const NUD_INCOMPLETE: u16 = 0x01; const NUD_REACHABLE: u16 = 0x02; const NUD_STALE: u16 = 0x04; const NUD_DELAY: u16 = 0x08; const NUD_PROBE: u16 = 0x10; const NUD_FAILED: u16 = 0x20; const NUD_NOARP: u16 = 0x40; const NUD_PERMANENT: u16 = 0x80; const NUD_NONE: u16 = 0x00; #[derive(Clone, Eq, PartialEq, Debug, Copy, Default)] #[non_exhaustive] pub enum NeighbourState { Incomplete, Reachable, Stale, Delay, Probe, Failed, Noarp, Permanent, #[default] None, Other(u16), } impl From for u16 { fn from(v: NeighbourState) -> u16 { match v { NeighbourState::Incomplete => NUD_INCOMPLETE, NeighbourState::Reachable => NUD_REACHABLE, NeighbourState::Stale => NUD_STALE, NeighbourState::Delay => NUD_DELAY, NeighbourState::Probe => NUD_PROBE, NeighbourState::Failed => NUD_FAILED, NeighbourState::Noarp => NUD_NOARP, NeighbourState::Permanent => NUD_PERMANENT, NeighbourState::None => NUD_NONE, NeighbourState::Other(d) => d, } } } impl From for NeighbourState { fn from(d: u16) -> Self { match d { NUD_INCOMPLETE => Self::Incomplete, NUD_REACHABLE => Self::Reachable, NUD_STALE => Self::Stale, NUD_DELAY => Self::Delay, NUD_PROBE => Self::Probe, NUD_FAILED => Self::Failed, NUD_NOARP => Self::Noarp, NUD_PERMANENT => Self::Permanent, NUD_NONE => Self::None, _ => Self::Other(d), } } } impl std::fmt::Display for NeighbourState { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Incomplete => write!(f, "incomplete"), Self::Reachable => write!(f, "reachable"), Self::Stale => write!(f, "stale"), Self::Delay => write!(f, "delay"), Self::Probe => write!(f, "probe"), Self::Failed => write!(f, "failed"), Self::Noarp => write!(f, "noarp"), Self::Permanent => write!(f, "permanent"), Self::None => write!(f, "none"), Self::Other(d) => write!(f, "other({d}"), } } } netlink-packet-route-0.21.0/src/neighbour/tests/bridge.rs000064400000000000000000000023561046102023000214740ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::{ neighbour::{ flags::NeighbourFlags, NeighbourAttribute, NeighbourHeader, NeighbourMessage, NeighbourMessageBuffer, NeighbourState, }, route::RouteType, AddressFamily, }; // wireshark capture(netlink message header removed) of nlmon against command: // ip -f bridge neighbour show #[test] fn test_bridge_neighbour_show() { let raw = vec![ 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01, 0x00, 0x00, ]; let expected = NeighbourMessage { header: NeighbourHeader { family: AddressFamily::Bridge, ifindex: 3, state: NeighbourState::Permanent, flags: NeighbourFlags::Own, kind: RouteType::Unspec, }, attributes: vec![NeighbourAttribute::LinkLocalAddress(vec![ 1, 0, 94, 0, 0, 1, ])], }; assert_eq!( expected, NeighbourMessage::parse(&NeighbourMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/neighbour/tests/ip.rs000064400000000000000000000127451046102023000206530ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::{Ipv4Addr, Ipv6Addr}; use std::str::FromStr; use netlink_packet_utils::{Emitable, Parseable}; use crate::neighbour::flags::NeighbourFlags; use crate::{ neighbour::{ NeighbourAttribute, NeighbourCacheInfo, NeighbourHeader, NeighbourMessage, NeighbourMessageBuffer, NeighbourState, }, route::{RouteProtocol, RouteType}, AddressFamily, }; // wireshark capture(netlink message header removed) of nlmon against command: // ip -4 neighbour show #[test] fn test_ipv4_neighbour_show() { let raw = vec![ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x08, 0x00, 0x01, 0x00, 0xac, 0x11, 0x02, 0x01, 0x0a, 0x00, 0x02, 0x00, 0x1c, 0x69, 0x7a, 0x07, 0xc3, 0x36, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x03, 0x00, 0x71, 0x01, 0x00, 0x00, 0xcb, 0x0d, 0x1f, 0x00, 0xcb, 0x0d, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, ]; let expected = NeighbourMessage { header: NeighbourHeader { family: AddressFamily::Inet, ifindex: 3, state: NeighbourState::Reachable, flags: NeighbourFlags::empty(), kind: RouteType::Unicast, }, attributes: vec![ NeighbourAttribute::Destination( Ipv4Addr::from_str("172.17.2.1").unwrap().into(), ), NeighbourAttribute::LinkLocalAddress(vec![ 28, 105, 122, 7, 195, 54, ]), NeighbourAttribute::Probes(1), NeighbourAttribute::CacheInfo(NeighbourCacheInfo { confirmed: 369, used: 2035147, updated: 2035147, refcnt: 1, }), ], }; assert_eq!( expected, NeighbourMessage::parse(&NeighbourMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } // wireshark capture(netlink message header removed) of nlmon against command: // ip -6 neighbour show #[test] fn test_ipv6_neighbour_show() { let raw = vec![ 0x0a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x80, 0x01, 0x14, 0x00, 0x01, 0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x69, 0x7a, 0xff, 0xfe, 0x07, 0xc3, 0x36, 0x0a, 0x00, 0x02, 0x00, 0x1c, 0x69, 0x7a, 0x07, 0xc3, 0x36, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x03, 0x00, 0x61, 0x76, 0x00, 0x00, 0x61, 0x76, 0x00, 0x00, 0x8c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let expected = NeighbourMessage { header: NeighbourHeader { family: AddressFamily::Inet6, ifindex: 3, state: NeighbourState::Stale, flags: NeighbourFlags::Router, kind: RouteType::Unicast, }, attributes: vec![ NeighbourAttribute::Destination( Ipv6Addr::from_str("fe80::1e69:7aff:fe07:c336") .unwrap() .into(), ), NeighbourAttribute::LinkLocalAddress(vec![ 28, 105, 122, 7, 195, 54, ]), NeighbourAttribute::Probes(1), NeighbourAttribute::CacheInfo(NeighbourCacheInfo { confirmed: 30305, used: 30305, updated: 25996, refcnt: 0, }), ], }; assert_eq!( expected, NeighbourMessage::parse(&NeighbourMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } // Setup // ip neighbo add 172.17.2.99 dev wlan0 lladdr 00:11:22:33:44:55 \ // protocol dhcp // wireshark capture(netlink message header removed) of nlmon against command: // ip -4 neighbour show #[test] fn test_ipv4_neighbour_protocol_show() { let raw = vec![ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x01, 0x08, 0x00, 0x01, 0x00, 0xac, 0x11, 0x02, 0x63, 0x0a, 0x00, 0x02, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x03, 0x00, 0x4d, 0x0b, 0x00, 0x00, 0x4d, 0x0b, 0x00, 0x00, 0x4d, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x00, 0x00, ]; let expected = NeighbourMessage { header: NeighbourHeader { family: AddressFamily::Inet, ifindex: 3, state: NeighbourState::Permanent, flags: NeighbourFlags::empty(), kind: RouteType::Unicast, }, attributes: vec![ NeighbourAttribute::Destination( Ipv4Addr::from_str("172.17.2.99").unwrap().into(), ), NeighbourAttribute::LinkLocalAddress(vec![ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, ]), NeighbourAttribute::Probes(0), NeighbourAttribute::CacheInfo(NeighbourCacheInfo { confirmed: 2893, used: 2893, updated: 2893, refcnt: 0, }), NeighbourAttribute::Protocol(RouteProtocol::Dhcp), ], }; assert_eq!( expected, NeighbourMessage::parse(&NeighbourMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/neighbour/tests/mod.rs000064400000000000000000000001171046102023000210100ustar 00000000000000// SPDX-License-Identifier: MIT #[cfg(test)] mod bridge; #[cfg(test)] mod ip; netlink-packet-route-0.21.0/src/neighbour_table/attribute.rs000064400000000000000000000115311046102023000222430ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::{parse_string, parse_u32, parse_u64}, DecodeError, Emitable, Parseable, }; use super::{ param::VecNeighbourTableParameter, NeighbourTableConfig, NeighbourTableConfigBuffer, NeighbourTableParameter, NeighbourTableStats, NeighbourTableStatsBuffer, }; const NDTA_NAME: u16 = 1; const NDTA_THRESH1: u16 = 2; const NDTA_THRESH2: u16 = 3; const NDTA_THRESH3: u16 = 4; const NDTA_CONFIG: u16 = 5; const NDTA_PARMS: u16 = 6; const NDTA_STATS: u16 = 7; const NDTA_GC_INTERVAL: u16 = 8; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum NeighbourTableAttribute { Parms(Vec), Name(String), Threshold1(u32), Threshold2(u32), Threshold3(u32), Config(NeighbourTableConfig), Stats(NeighbourTableStats), GcInterval(u64), Other(DefaultNla), } impl Nla for NeighbourTableAttribute { fn value_len(&self) -> usize { match self { Self::Parms(v) => v.as_slice().buffer_len(), Self::Stats(v) => v.buffer_len(), Self::Config(v) => v.buffer_len(), // strings: +1 because we need to append a nul byte Self::Name(s) => s.len() + 1, Self::Threshold1(_) | Self::Threshold2(_) | Self::Threshold3(_) => { 4 } Self::GcInterval(_) => 8, Self::Other(attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Parms(v) => v.as_slice().emit(buffer), Self::Stats(v) => v.emit(buffer), Self::Config(v) => v.emit(buffer), Self::Name(string) => { buffer[..string.len()].copy_from_slice(string.as_bytes()); buffer[string.len()] = 0; } Self::GcInterval(value) => NativeEndian::write_u64(buffer, *value), Self::Threshold1(value) | Self::Threshold2(value) | Self::Threshold3(value) => { NativeEndian::write_u32(buffer, *value) } Self::Other(attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Name(_) => NDTA_NAME, Self::Config(_) => NDTA_CONFIG, Self::Stats(_) => NDTA_STATS, Self::Parms(_) => NDTA_PARMS, Self::GcInterval(_) => NDTA_GC_INTERVAL, Self::Threshold1(_) => NDTA_THRESH1, Self::Threshold2(_) => NDTA_THRESH2, Self::Threshold3(_) => NDTA_THRESH3, Self::Other(attr) => attr.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for NeighbourTableAttribute { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { NDTA_NAME => Self::Name( parse_string(payload).context("invalid NDTA_NAME value")?, ), NDTA_CONFIG => Self::Config( NeighbourTableConfig::parse( &NeighbourTableConfigBuffer::new_checked(payload) .context(format!("invalid NDTA_CONFIG {payload:?}"))?, ) .context(format!("invalid NDTA_CONFIG {payload:?}"))?, ), NDTA_STATS => Self::Stats( NeighbourTableStats::parse( &NeighbourTableStatsBuffer::new_checked(payload) .context(format!("invalid NDTA_STATS {payload:?}"))?, ) .context(format!("invalid NDTA_STATS {payload:?}"))?, ), NDTA_PARMS => { let err = |payload| format!("invalid NDTA_PARMS {payload:?}"); Self::Parms( VecNeighbourTableParameter::parse( &NlaBuffer::new_checked(payload) .context(err(payload))?, ) .context(err(payload))? .0, ) } NDTA_GC_INTERVAL => Self::GcInterval( parse_u64(payload).context("invalid NDTA_GC_INTERVAL value")?, ), NDTA_THRESH1 => Self::Threshold1( parse_u32(payload).context("invalid NDTA_THRESH1 value")?, ), NDTA_THRESH2 => Self::Threshold2( parse_u32(payload).context("invalid NDTA_THRESH2 value")?, ), NDTA_THRESH3 => Self::Threshold3( parse_u32(payload).context("invalid NDTA_THRESH3 value")?, ), kind => Self::Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } netlink-packet-route-0.21.0/src/neighbour_table/config.rs000064400000000000000000000037261046102023000215140ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] pub struct NeighbourTableConfig { 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!(NeighbourTableConfigBuffer(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 NeighbourTableConfig { fn parse(buf: &NeighbourTableConfigBuffer) -> 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 NeighbourTableConfig { fn buffer_len(&self) -> usize { CONFIG_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = NeighbourTableConfigBuffer::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.21.0/src/neighbour_table/header.rs000064400000000000000000000024411046102023000214700ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::{NlaBuffer, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; use crate::AddressFamily; 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 attributes( &self, ) -> impl Iterator, DecodeError>> { NlasIterator::new(self.payload()) } } // kernel code is `struct rtgenmsg` #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] pub struct NeighbourTableHeader { pub family: AddressFamily, } impl> Parseable> for NeighbourTableHeader { fn parse( buf: &NeighbourTableMessageBuffer, ) -> Result { Ok(Self { family: buf.family().into(), }) } } 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.into()); } } netlink-packet-route-0.21.0/src/neighbour_table/message.rs000064400000000000000000000032731046102023000216700ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; use super::{ NeighbourTableAttribute, NeighbourTableHeader, NeighbourTableMessageBuffer, }; #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct NeighbourTableMessage { pub header: NeighbourTableHeader, pub attributes: Vec, } impl Emitable for NeighbourTableMessage { fn buffer_len(&self) -> usize { self.header.buffer_len() + self.attributes.as_slice().buffer_len() } fn emit(&self, buffer: &mut [u8]) { self.header.emit(buffer); self.attributes .as_slice() .emit(&mut buffer[self.header.buffer_len()..]); } } 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")?, attributes: 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 attributes = vec![]; for nla_buf in buf.attributes() { attributes.push(NeighbourTableAttribute::parse(&nla_buf?)?); } Ok(attributes) } } netlink-packet-route-0.21.0/src/neighbour_table/mod.rs000064400000000000000000000007711046102023000210230ustar 00000000000000// SPDX-License-Identifier: MIT mod attribute; mod config; mod header; mod message; pub(crate) mod param; mod stats; #[cfg(test)] mod tests; pub use self::attribute::NeighbourTableAttribute; pub use self::config::{NeighbourTableConfig, NeighbourTableConfigBuffer}; pub use self::header::{NeighbourTableHeader, NeighbourTableMessageBuffer}; pub use self::message::NeighbourTableMessage; pub use self::param::NeighbourTableParameter; pub use self::stats::{NeighbourTableStats, NeighbourTableStatsBuffer}; netlink-packet-route-0.21.0/src/neighbour_table/param.rs000064400000000000000000000212261046102023000213420ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, parsers::{parse_u32, parse_u64}, DecodeError, Parseable, }; const NDTPA_IFINDEX: u16 = 1; const NDTPA_REFCNT: u16 = 2; const NDTPA_REACHABLE_TIME: u16 = 3; const NDTPA_BASE_REACHABLE_TIME: u16 = 4; const NDTPA_RETRANS_TIME: u16 = 5; const NDTPA_GC_STALETIME: u16 = 6; const NDTPA_DELAY_PROBE_TIME: u16 = 7; const NDTPA_QUEUE_LEN: u16 = 8; const NDTPA_APP_PROBES: u16 = 9; const NDTPA_UCAST_PROBES: u16 = 10; const NDTPA_MCAST_PROBES: u16 = 11; const NDTPA_ANYCAST_DELAY: u16 = 12; const NDTPA_PROXY_DELAY: u16 = 13; const NDTPA_PROXY_QLEN: u16 = 14; const NDTPA_LOCKTIME: u16 = 15; const NDTPA_QUEUE_LENBYTES: u16 = 16; const NDTPA_MCAST_REPROBES: u16 = 17; // const NDTPA_PAD: u16 = 18; const NDTPA_INTERVAL_PROBE_TIME_MS: u16 = 19; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum NeighbourTableParameter { Ifindex(u32), ReferenceCount(u32), ReachableTime(u64), BaseReachableTime(u64), RetransTime(u64), GcStaletime(u64), DelayProbeTime(u64), QueueLen(u32), AppProbes(u32), UcastProbes(u32), McastProbes(u32), AnycastDelay(u64), ProxyDelay(u64), ProxyQlen(u32), Locktime(u64), QueueLenbytes(u32), McastReprobes(u32), IntervalProbeTimeMs(u64), Other(DefaultNla), } impl Nla for NeighbourTableParameter { fn value_len(&self) -> usize { match self { Self::Ifindex(_) | Self::ReferenceCount(_) | Self::QueueLen(_) | Self::AppProbes(_) | Self::UcastProbes(_) | Self::McastProbes(_) | Self::ProxyQlen(_) | Self::QueueLenbytes(_) | Self::McastReprobes(_) => 4, Self::ReachableTime(_) | Self::BaseReachableTime(_) | Self::RetransTime(_) | Self::GcStaletime(_) | Self::DelayProbeTime(_) | Self::AnycastDelay(_) | Self::ProxyDelay(_) | Self::Locktime(_) | Self::IntervalProbeTimeMs(_) => 8, Self::Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Ifindex(v) | Self::ReferenceCount(v) | Self::QueueLen(v) | Self::AppProbes(v) | Self::UcastProbes(v) | Self::McastProbes(v) | Self::ProxyQlen(v) | Self::QueueLenbytes(v) | Self::McastReprobes(v) => NativeEndian::write_u32(buffer, *v), Self::ReachableTime(v) | Self::BaseReachableTime(v) | Self::RetransTime(v) | Self::GcStaletime(v) | Self::DelayProbeTime(v) | Self::AnycastDelay(v) | Self::ProxyDelay(v) | Self::Locktime(v) | Self::IntervalProbeTimeMs(v) => { NativeEndian::write_u64(buffer, *v) } Self::Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Ifindex(_) => NDTPA_IFINDEX, Self::ReferenceCount(_) => NDTPA_REFCNT, Self::ReachableTime(_) => NDTPA_REACHABLE_TIME, Self::BaseReachableTime(_) => NDTPA_BASE_REACHABLE_TIME, Self::RetransTime(_) => NDTPA_RETRANS_TIME, Self::GcStaletime(_) => NDTPA_GC_STALETIME, Self::DelayProbeTime(_) => NDTPA_DELAY_PROBE_TIME, Self::QueueLen(_) => NDTPA_QUEUE_LEN, Self::AppProbes(_) => NDTPA_APP_PROBES, Self::UcastProbes(_) => NDTPA_UCAST_PROBES, Self::McastProbes(_) => NDTPA_MCAST_PROBES, Self::AnycastDelay(_) => NDTPA_ANYCAST_DELAY, Self::ProxyDelay(_) => NDTPA_PROXY_DELAY, Self::ProxyQlen(_) => NDTPA_PROXY_QLEN, Self::Locktime(_) => NDTPA_LOCKTIME, Self::QueueLenbytes(_) => NDTPA_QUEUE_LENBYTES, Self::McastReprobes(_) => NDTPA_MCAST_REPROBES, Self::IntervalProbeTimeMs(_) => NDTPA_INTERVAL_PROBE_TIME_MS, Self::Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for NeighbourTableParameter { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { NDTPA_IFINDEX => { Self::Ifindex(parse_u32(payload).context(format!( "invalid NDTPA_IFINDEX value {payload:?}" ))?) } NDTPA_REFCNT => { Self::ReferenceCount(parse_u32(payload).context(format!( "invalid NDTPA_REFCNT value {payload:?}" ))?) } NDTPA_REACHABLE_TIME => { Self::ReachableTime(parse_u64(payload).context(format!( "invalid NDTPA_REACHABLE_TIME value {payload:?}" ))?) } NDTPA_BASE_REACHABLE_TIME => { Self::BaseReachableTime(parse_u64(payload).context(format!( "invalid NDTPA_BASE_REACHABLE_TIME value {payload:?}" ))?) } NDTPA_RETRANS_TIME => { Self::RetransTime(parse_u64(payload).context(format!( "invalid NDTPA_RETRANS_TIME value {payload:?}" ))?) } NDTPA_GC_STALETIME => { Self::GcStaletime(parse_u64(payload).context(format!( "invalid NDTPA_GC_STALE_TIME value {payload:?}" ))?) } NDTPA_DELAY_PROBE_TIME => { Self::DelayProbeTime(parse_u64(payload).context(format!( "invalid NDTPA_DELAY_PROBE_TIME value {payload:?}" ))?) } NDTPA_QUEUE_LEN => Self::QueueLen(parse_u32(payload).context( format!("invalid NDTPA_QUEUE_LEN value {payload:?}"), )?), NDTPA_APP_PROBES => Self::AppProbes(parse_u32(payload).context( format!("invalid NDTPA_APP_PROBES value {payload:?}"), )?), NDTPA_UCAST_PROBES => { Self::UcastProbes(parse_u32(payload).context(format!( "invalid NDTPA_UCAST_PROBES value {payload:?}" ))?) } NDTPA_MCAST_PROBES => { Self::McastProbes(parse_u32(payload).context(format!( "invalid NDTPA_MCAST_PROBES value {payload:?}" ))?) } NDTPA_ANYCAST_DELAY => { Self::AnycastDelay(parse_u64(payload).context(format!( "invalid NDTPA_ANYCAST_DELAY value {payload:?}" ))?) } NDTPA_PROXY_DELAY => Self::ProxyDelay(parse_u64(payload).context( format!("invalid NDTPA_PROXY_DELAY value {payload:?}"), )?), NDTPA_PROXY_QLEN => Self::ProxyQlen(parse_u32(payload).context( format!("invalid NDTPA_PROXY_QLEN value {payload:?}"), )?), NDTPA_LOCKTIME => Self::Locktime(parse_u64(payload).context( format!("invalid NDTPA_LOCKTIME value {payload:?}"), )?), NDTPA_QUEUE_LENBYTES => { Self::QueueLenbytes(parse_u32(payload).context(format!( "invalid NDTPA_QUEUE_LENBYTES value {payload:?}" ))?) } NDTPA_MCAST_REPROBES => { Self::McastReprobes(parse_u32(payload).context(format!( "invalid NDTPA_MCAST_PROBES value {payload:?}" ))?) } NDTPA_INTERVAL_PROBE_TIME_MS => Self::IntervalProbeTimeMs( parse_u64(payload).context(format!( "invalid NDTPA_INTERVAL_PROBE_TIME_MS value {payload:?}" ))?, ), _ => Self::Other(DefaultNla::parse(buf).context(format!( "invalid NDTA_PARMS attribute {payload:?}" ))?), }) } } #[derive(Debug, PartialEq, Eq, Clone)] pub(crate) struct VecNeighbourTableParameter( pub(crate) Vec, ); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecNeighbourTableParameter { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; let err = "invalid NDTA_PARMS attribute"; for nla in NlasIterator::new(buf.into_inner()) { let nla = nla.context(err)?; nlas.push(NeighbourTableParameter::parse(&nla).context(err)?); } Ok(Self(nlas)) } } netlink-packet-route-0.21.0/src/neighbour_table/stats.rs000064400000000000000000000046471046102023000214100ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] pub struct NeighbourTableStats { 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 table_fulls: u64, } pub const STATS_LEN: usize = 88; buffer!(NeighbourTableStatsBuffer(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), table_fulls: (u64, 80..88), }); impl> Parseable> for NeighbourTableStats { fn parse(buf: &NeighbourTableStatsBuffer) -> 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(), table_fulls: buf.table_fulls(), }) } } impl Emitable for NeighbourTableStats { fn buffer_len(&self) -> usize { STATS_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = NeighbourTableStatsBuffer::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); buffer.set_table_fulls(self.table_fulls); } } netlink-packet-route-0.21.0/src/neighbour_table/tests.rs000064400000000000000000000210701046102023000214010ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::{ neighbour_table::{ NeighbourTableAttribute, NeighbourTableConfig, NeighbourTableHeader, NeighbourTableMessage, NeighbourTableMessageBuffer, NeighbourTableParameter, NeighbourTableStats, }, AddressFamily, }; // There is no need to do ipv6 test as it is almost identical to ipv4 for // neighbour_table. // Wireshark nlmon capture of `examples/dump_neighbour_tables.rs` #[test] fn test_ipv4_neighbour_table() { let raw = vec![ 0x02, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x61, 0x72, 0x70, 0x5f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x06, 0x00, 0x08, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x40, 0x03, 0x00, 0x08, 0x00, 0x08, 0x00, 0x65, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x40, 0x00, 0x00, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00, 0x93, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x30, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x60, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x88, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x05, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0f, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x13, 0x00, 0x88, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let expected = NeighbourTableMessage { header: NeighbourTableHeader { family: AddressFamily::Inet, }, attributes: vec![ NeighbourTableAttribute::Name("arp_cache".to_string()), NeighbourTableAttribute::Parms(vec![ NeighbourTableParameter::Ifindex(16), NeighbourTableParameter::ReferenceCount(1), NeighbourTableParameter::QueueLenbytes(212992), NeighbourTableParameter::QueueLen(101), NeighbourTableParameter::ProxyQlen(64), NeighbourTableParameter::AppProbes(0), NeighbourTableParameter::UcastProbes(3), NeighbourTableParameter::McastProbes(3), NeighbourTableParameter::McastReprobes(0), NeighbourTableParameter::ReachableTime(18067), NeighbourTableParameter::BaseReachableTime(30000), NeighbourTableParameter::GcStaletime(60000), NeighbourTableParameter::DelayProbeTime(5000), NeighbourTableParameter::RetransTime(1000), NeighbourTableParameter::AnycastDelay(1000), NeighbourTableParameter::ProxyDelay(800), NeighbourTableParameter::Locktime(1000), NeighbourTableParameter::IntervalProbeTimeMs(5000), ]), ], }; assert_eq!( expected, NeighbourTableMessage::parse(&NeighbourTableMessageBuffer::new(&raw)) .unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } // Wireshark nlmon capture of `examples/dump_neighbour_tables.rs` #[test] fn test_ipv4_neighbour_table_stats_config() { let raw = vec![ 0x02, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x61, 0x72, 0x70, 0x5f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x30, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x80, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x24, 0x00, 0x05, 0x00, 0x04, 0x00, 0x98, 0x01, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x02, 0xac, 0x02, 0x6d, 0x66, 0x01, 0x00, 0xd5, 0x7e, 0xc0, 0xe4, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x07, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x06, 0x00, 0x08, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x40, 0x03, 0x00, 0x08, 0x00, 0x08, 0x00, 0x65, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x40, 0x00, 0x00, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00, 0xaa, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x30, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x60, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x88, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x05, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0f, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x13, 0x00, 0x88, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let expected = NeighbourTableMessage { header: NeighbourTableHeader { family: AddressFamily::Inet, }, attributes: vec![ NeighbourTableAttribute::Name("arp_cache".to_string()), NeighbourTableAttribute::GcInterval(30000), NeighbourTableAttribute::Threshold1(128), NeighbourTableAttribute::Threshold2(512), NeighbourTableAttribute::Threshold3(1024), NeighbourTableAttribute::Config(NeighbourTableConfig { key_len: 4, entry_size: 408, entries: 12, last_flush: 44827194, last_rand: 91757, hash_rand: 3837820629, hash_mask: 15, hash_chain_gc: 0, proxy_qlen: 0, }), NeighbourTableAttribute::Stats(NeighbourTableStats { allocs: 17, destroys: 5, hash_grows: 1, res_failed: 16, lookups: 15626, hits: 9228, multicast_probes_received: 0, unicast_probes_received: 0, periodic_gc_runs: 2884, forced_gc_runs: 0, table_fulls: 0, }), NeighbourTableAttribute::Parms(vec![ NeighbourTableParameter::ReferenceCount(1), NeighbourTableParameter::QueueLenbytes(212992), NeighbourTableParameter::QueueLen(101), NeighbourTableParameter::ProxyQlen(64), NeighbourTableParameter::AppProbes(0), NeighbourTableParameter::UcastProbes(3), NeighbourTableParameter::McastProbes(3), NeighbourTableParameter::McastReprobes(0), NeighbourTableParameter::ReachableTime(38570), NeighbourTableParameter::BaseReachableTime(30000), NeighbourTableParameter::GcStaletime(60000), NeighbourTableParameter::DelayProbeTime(5000), NeighbourTableParameter::RetransTime(1000), NeighbourTableParameter::AnycastDelay(1000), NeighbourTableParameter::ProxyDelay(800), NeighbourTableParameter::Locktime(1000), NeighbourTableParameter::IntervalProbeTimeMs(5000), ]), ], }; assert_eq!( expected, NeighbourTableMessage::parse(&NeighbourTableMessageBuffer::new(&raw)) .unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/nsid/attribute.rs000064400000000000000000000051471046102023000200550ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::{parse_i32, parse_u32}, traits::Parseable, DecodeError, }; const NETNSA_NSID: u16 = 1; const NETNSA_PID: u16 = 2; const NETNSA_FD: u16 = 3; const NETNSA_TARGET_NSID: u16 = 4; const NETNSA_CURRENT_NSID: u16 = 5; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum NsidAttribute { /// -1 means not assigned Id(i32), Pid(u32), Fd(u32), TargetNsid(i32), CurrentNsid(i32), Other(DefaultNla), } impl Nla for NsidAttribute { fn value_len(&self) -> usize { match self { Self::Id(_) | Self::Pid(_) | Self::Fd(_) | Self::TargetNsid(_) | Self::CurrentNsid(_) => 4, Self::Other(attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Fd(v) | Self::Pid(v) => NativeEndian::write_u32(buffer, *v), Self::Id(v) | Self::TargetNsid(v) | Self::CurrentNsid(v) => { NativeEndian::write_i32(buffer, *v) } Self::Other(attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Id(_) => NETNSA_NSID, Self::Pid(_) => NETNSA_PID, Self::Fd(_) => NETNSA_FD, Self::TargetNsid(_) => NETNSA_TARGET_NSID, Self::CurrentNsid(_) => NETNSA_CURRENT_NSID, Self::Other(attr) => attr.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for NsidAttribute { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { NETNSA_NSID => { Self::Id(parse_i32(payload).context("invalid NETNSA_NSID")?) } NETNSA_PID => { Self::Pid(parse_u32(payload).context("invalid NETNSA_PID")?) } NETNSA_FD => { Self::Fd(parse_u32(payload).context("invalid NETNSA_FD")?) } NETNSA_TARGET_NSID => Self::TargetNsid( parse_i32(payload).context("invalid NETNSA_TARGET_NSID")?, ), NETNSA_CURRENT_NSID => Self::CurrentNsid( parse_i32(payload).context("invalid NETNSA_CURRENT_NSID")?, ), kind => Self::Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } netlink-packet-route-0.21.0/src/nsid/header.rs000064400000000000000000000021401046102023000172700ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::{NlaBuffer, NlasIterator}, DecodeError, Emitable, Parseable, }; use crate::AddressFamily; const NSID_HEADER_LEN: usize = 4; buffer!(NsidMessageBuffer(NSID_HEADER_LEN) { family: (u8, 0), payload: (slice, NSID_HEADER_LEN..), }); impl<'a, T: AsRef<[u8]> + ?Sized> NsidMessageBuffer<&'a T> { pub fn attributes( &self, ) -> impl Iterator, DecodeError>> { NlasIterator::new(self.payload()) } } #[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct NsidHeader { pub family: AddressFamily, } 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_family(self.family.into()); } } impl> Parseable> for NsidHeader { fn parse(buf: &NsidMessageBuffer) -> Result { Ok(NsidHeader { family: buf.family().into(), }) } } netlink-packet-route-0.21.0/src/nsid/message.rs000064400000000000000000000027251046102023000174750ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; use crate::nsid::{NsidAttribute, NsidHeader, NsidMessageBuffer}; #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct NsidMessage { pub header: NsidHeader, pub attributes: 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")?, attributes: 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 attributes = vec![]; for nla_buf in buf.attributes() { attributes.push(NsidAttribute::parse(&nla_buf?)?); } Ok(attributes) } } impl Emitable for NsidMessage { fn buffer_len(&self) -> usize { self.header.buffer_len() + self.attributes.as_slice().buffer_len() } fn emit(&self, buffer: &mut [u8]) { self.header.emit(buffer); self.attributes .as_slice() .emit(&mut buffer[self.header.buffer_len()..]); } } netlink-packet-route-0.21.0/src/nsid/mod.rs000064400000000000000000000003451046102023000166240ustar 00000000000000// SPDX-License-Identifier: MIT mod attribute; mod header; mod message; #[cfg(test)] mod tests; pub use self::attribute::NsidAttribute; pub use self::header::{NsidHeader, NsidMessageBuffer}; pub use self::message::NsidMessage; netlink-packet-route-0.21.0/src/nsid/tests.rs000064400000000000000000000043311046102023000172060ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::{ nsid::{NsidAttribute, NsidHeader, NsidMessage, NsidMessageBuffer}, AddressFamily, }; // Setup // ip netns add abc // ip netns set abc 99 // wireshark capture(netlink message header removed) of nlmon against command: // ip netns list #[test] fn test_ip_netns_query_reply() { let raw = vec![ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x63, 0x00, 0x00, 0x00, ]; let expected = NsidMessage { header: NsidHeader { family: AddressFamily::Unspec, }, attributes: vec![NsidAttribute::Id(99)], }; assert_eq!( expected, NsidMessage::parse(&NsidMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } // Setup // ip netns add abc // ip netns set abc 99 // wireshark capture(netlink message header removed) of nlmon against command: // ip netns list #[test] fn test_ip_netns_query() { let raw = vec![ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, ]; let expected = NsidMessage { header: NsidHeader { family: AddressFamily::Unspec, }, attributes: vec![NsidAttribute::Fd(6)], }; assert_eq!( expected, NsidMessage::parse(&NsidMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } // wireshark capture(netlink message header removed) of nlmon against command: // ip netns list-id target-nsid 99 #[test] fn test_ip_netns_query_target_ns_id() { let raw = vec![ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, ]; let expected = NsidMessage { header: NsidHeader { family: AddressFamily::Unspec, }, attributes: vec![NsidAttribute::TargetNsid(99)], }; assert_eq!( expected, NsidMessage::parse(&NsidMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/prefix/attribute.rs000064400000000000000000000041201046102023000204030ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::Ipv6Addr; use anyhow::Context; use netlink_packet_utils::{ nla::{self, DefaultNla, NlaBuffer}, traits::Parseable, DecodeError, Emitable, }; use super::cache_info::{CacheInfo, CacheInfoBuffer}; const PREFIX_ADDRESS: u16 = 1; const PREFIX_CACHEINFO: u16 = 2; #[derive(Debug, PartialEq, Eq, Clone)] pub enum PrefixAttribute { Address(Ipv6Addr), CacheInfo(CacheInfo), Other(DefaultNla), } impl nla::Nla for PrefixAttribute { fn value_len(&self) -> usize { match *self { Self::Address(_) => 16, Self::CacheInfo(ref cache_info) => cache_info.buffer_len(), Self::Other(ref attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match *self { Self::Address(ref addr) => buffer.copy_from_slice(&addr.octets()), Self::CacheInfo(ref cache_info) => cache_info.emit(buffer), Self::Other(ref attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { match *self { Self::Address(_) => PREFIX_ADDRESS, Self::CacheInfo(_) => PREFIX_CACHEINFO, Self::Other(ref nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for PrefixAttribute { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); match buf.kind() { PREFIX_ADDRESS => { if let Ok(payload) = TryInto::<[u8; 16]>::try_into(payload) { Ok(Self::Address(Ipv6Addr::from(payload))) } else { Err(DecodeError::from(format!("Invalid PREFIX_ADDRESS, unexpected payload length: {:?}", payload))) } } PREFIX_CACHEINFO => Ok(Self::CacheInfo( CacheInfo::parse(&CacheInfoBuffer::new(payload)).context( format!("Invalid PREFIX_CACHEINFO: {:?}", payload), )?, )), _ => Ok(Self::Other(DefaultNla::parse(buf)?)), } } } netlink-packet-route-0.21.0/src/prefix/cache_info.rs000064400000000000000000000017161046102023000204660ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{traits::Parseable, DecodeError, Emitable}; #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub struct CacheInfo { pub preferred_time: u32, pub valid_time: u32, } const CACHE_INFO_LEN: usize = 8; buffer!(CacheInfoBuffer(CACHE_INFO_LEN) { preferred_time: (u32, 0..4), valid_time: (u32, 4..8), }); impl> Parseable> for CacheInfo { fn parse(buf: &CacheInfoBuffer) -> Result { Ok(CacheInfo { preferred_time: buf.preferred_time(), valid_time: buf.valid_time(), }) } } 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_preferred_time(self.preferred_time); buffer.set_valid_time(self.valid_time); } } netlink-packet-route-0.21.0/src/prefix/header.rs000064400000000000000000000024321046102023000176340ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::{NlaBuffer, NlasIterator}, DecodeError, Emitable, }; const PREFIX_HEADER_LEN: usize = 12; buffer!(PrefixMessageBuffer(PREFIX_HEADER_LEN) { prefix_family: (u8, 0), pad1: (u8, 1), pad2: (u16, 2..4), ifindex: (i32, 4..8), prefix_type: (u8, 8), prefix_len: (u8, 9), flags: (u8, 10), pad3: (u8, 11), payload: (slice, PREFIX_HEADER_LEN..), }); impl<'a, T: AsRef<[u8]> + ?Sized> PrefixMessageBuffer<&'a T> { pub fn nlas( &self, ) -> impl Iterator, DecodeError>> { NlasIterator::new(self.payload()) } } #[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct PrefixHeader { pub prefix_family: u8, pub ifindex: i32, pub prefix_type: u8, pub prefix_len: u8, pub flags: u8, } impl Emitable for PrefixHeader { fn buffer_len(&self) -> usize { PREFIX_HEADER_LEN } fn emit(&self, buffer: &mut [u8]) { let mut packet = PrefixMessageBuffer::new(buffer); packet.set_prefix_family(self.prefix_family); packet.set_ifindex(self.ifindex); packet.set_prefix_type(self.prefix_type); packet.set_prefix_len(self.prefix_len); packet.set_flags(self.flags); } } netlink-packet-route-0.21.0/src/prefix/message.rs000064400000000000000000000035641046102023000200370ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; use super::{ attribute::PrefixAttribute, header::{PrefixHeader, PrefixMessageBuffer}, }; #[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct PrefixMessage { pub header: PrefixHeader, pub attributes: Vec, } impl Emitable for PrefixMessage { fn buffer_len(&self) -> usize { self.header.buffer_len() + self.attributes.as_slice().buffer_len() } fn emit(&self, buffer: &mut [u8]) { self.header.emit(buffer); self.attributes .as_slice() .emit(&mut buffer[self.header.buffer_len()..]); } } impl> Parseable> for PrefixHeader { fn parse(buf: &PrefixMessageBuffer) -> Result { Ok(Self { prefix_family: buf.prefix_family(), ifindex: buf.ifindex(), prefix_type: buf.prefix_type(), prefix_len: buf.prefix_len(), flags: buf.flags(), }) } } impl<'a, T: AsRef<[u8]> + 'a> Parseable> for PrefixMessage { fn parse(buf: &PrefixMessageBuffer<&'a T>) -> Result { Ok(Self { header: PrefixHeader::parse(buf) .context("failed to parse prefix message header")?, attributes: Vec::::parse(buf) .context("failed to parse prefix message attributes")?, }) } } impl<'a, T: AsRef<[u8]> + 'a> Parseable> for Vec { fn parse(buf: &PrefixMessageBuffer<&'a T>) -> Result { let mut nlas = vec![]; for nla_buf in buf.nlas() { nlas.push(PrefixAttribute::parse(&nla_buf?)?); } Ok(nlas) } } netlink-packet-route-0.21.0/src/prefix/mod.rs000064400000000000000000000002671046102023000171670ustar 00000000000000// SPDX-License-Identifier: MIT mod attribute; mod cache_info; mod header; mod message; #[cfg(test)] mod tests; pub use header::PrefixMessageBuffer; pub use message::PrefixMessage; netlink-packet-route-0.21.0/src/prefix/tests.rs000064400000000000000000000031751046102023000175530ustar 00000000000000// SPDX-License-Identifier: MIT use std::{net::Ipv6Addr, str::FromStr}; use netlink_packet_utils::{Emitable, Parseable}; use crate::prefix::{ attribute::PrefixAttribute, cache_info::CacheInfo, header::PrefixHeader, PrefixMessage, PrefixMessageBuffer, }; #[test] fn test_new_prefix() { #[rustfmt::skip] let data = vec![ // prefixmsg // AF_INET6 + padding 0x0a, 0x00, 0x00, 0x00, // ifindex 0x02, 0x00, 0x00, 0x00, // type, prefix length, flags, padding 0x03, 0x40, 0x01, 0x00, // PREFIX_ADDRESS attribute 0x14, 0x00, 0x01, 0x00, 0xfc, 0x00, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // PREFIX_CACHEINFO attribute 0x0c, 0x00, 0x02, 0x00, 0xff, 0xff, 0xff, 0xfa, 0xff, 0xff, 0xff, 0xff, ]; let actual = PrefixMessage::parse(&PrefixMessageBuffer::new(&data)) .expect("Generated PrefixMessage"); let expected = PrefixMessage { header: PrefixHeader { prefix_family: libc::AF_INET6 as u8, ifindex: 2, prefix_type: 3, prefix_len: 64, flags: 1, }, attributes: vec![ PrefixAttribute::Address( Ipv6Addr::from_str("fc00:a0a::1").expect("Ipv6Addr"), ), PrefixAttribute::CacheInfo(CacheInfo { preferred_time: 0xfaffffff, valid_time: 0xffffffff, }), ], }; assert_eq!(expected, actual); let mut buf = vec![0; 44]; expected.emit(&mut buf); assert_eq!(data, buf); } netlink-packet-route-0.21.0/src/route/address.rs000064400000000000000000000041531046102023000176740ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use netlink_packet_utils::{DecodeError, Emitable}; use crate::{ ip::{ emit_ip_to_buffer, parse_ipv4_addr, parse_ipv6_addr, IPV4_ADDR_LEN, IPV6_ADDR_LEN, }, route::MplsLabel, AddressFamily, }; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum RouteAddress { Inet(Ipv4Addr), Inet6(Ipv6Addr), Mpls(MplsLabel), Other(Vec), } impl From for RouteAddress { fn from(ip: IpAddr) -> Self { match ip { IpAddr::V4(ipv4) => Self::Inet(ipv4), IpAddr::V6(ipv6) => Self::Inet6(ipv6), } } } impl RouteAddress { pub(crate) fn parse( address_family: AddressFamily, payload: &[u8], ) -> Result { Ok(match address_family { AddressFamily::Inet => Self::Inet(parse_ipv4_addr(payload)?), AddressFamily::Inet6 => Self::Inet6(parse_ipv6_addr(payload)?), #[cfg(any(target_os = "linux", target_os = "fuchsia"))] AddressFamily::Mpls => Self::Mpls(MplsLabel::parse(payload)?), _ => Self::Other(payload.to_vec()), }) } } impl Emitable for RouteAddress { fn buffer_len(&self) -> usize { match self { Self::Inet(_) => IPV4_ADDR_LEN, Self::Inet6(_) => IPV6_ADDR_LEN, Self::Mpls(v) => v.buffer_len(), Self::Other(v) => v.len(), } } fn emit(&self, buffer: &mut [u8]) { match self { Self::Inet(v) => emit_ip_to_buffer(&((*v).into()), buffer), Self::Inet6(v) => emit_ip_to_buffer(&((*v).into()), buffer), Self::Mpls(v) => v.emit(buffer), Self::Other(v) => buffer.copy_from_slice(v.as_slice()), } } } impl From for RouteAddress { fn from(v: Ipv4Addr) -> Self { Self::Inet(v) } } impl From for RouteAddress { fn from(v: Ipv6Addr) -> Self { Self::Inet6(v) } } impl From for RouteAddress { fn from(v: MplsLabel) -> Self { Self::Mpls(v) } } netlink-packet-route-0.21.0/src/route/attribute.rs000064400000000000000000000276671046102023000202710ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::{parse_u16, parse_u32, parse_u64, parse_u8}, traits::{Emitable, Parseable, ParseableParametrized}, DecodeError, }; use super::{ super::AddressFamily, lwtunnel::VecRouteLwTunnelEncap, metrics::VecRouteMetric, mpls::VecMplsLabel, MplsLabel, RouteAddress, RouteCacheInfo, RouteCacheInfoBuffer, RouteLwEnCapType, RouteLwTunnelEncap, RouteMetric, RouteMfcStats, RouteMfcStatsBuffer, RouteMplsTtlPropagation, RouteNextHop, RouteNextHopBuffer, RoutePreference, RouteRealm, RouteType, RouteVia, RouteViaBuffer, }; const RTA_DST: u16 = 1; const RTA_SRC: u16 = 2; const RTA_IIF: u16 = 3; const RTA_OIF: u16 = 4; const RTA_GATEWAY: u16 = 5; const RTA_PRIORITY: u16 = 6; const RTA_PREFSRC: u16 = 7; const RTA_METRICS: u16 = 8; const RTA_MULTIPATH: u16 = 9; // const RTA_PROTOINFO: u16 = 10; // linux kernel said `no longer used` const RTA_FLOW: u16 = 11; const RTA_CACHEINFO: u16 = 12; // const RTA_SESSION: u16 = 13; // linux kernel said `no longer used` // const RTA_MP_ALGO: u16 = 14; // linux kernel said `no longer used` const RTA_TABLE: u16 = 15; const RTA_MARK: u16 = 16; const RTA_MFC_STATS: u16 = 17; const RTA_VIA: u16 = 18; const RTA_NEWDST: u16 = 19; const RTA_PREF: u16 = 20; pub(crate) const RTA_ENCAP_TYPE: u16 = 21; const RTA_ENCAP: u16 = 22; const RTA_EXPIRES: u16 = 23; const RTA_UID: u16 = 25; const RTA_TTL_PROPAGATE: u16 = 26; // TODO // const RTA_IP_PROTO:u16 = 27; // const RTA_SPORT:u16 = 28; // const RTA_DPORT:u16 = 29; // const RTA_NH_ID:u16 = 30; /// Netlink attributes for `RTM_NEWROUTE`, `RTM_DELROUTE`, /// `RTM_GETROUTE` netlink messages. #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum RouteAttribute { Metrics(Vec), MfcStats(RouteMfcStats), MultiPath(Vec), CacheInfo(RouteCacheInfo), Destination(RouteAddress), Source(RouteAddress), Gateway(RouteAddress), PrefSource(RouteAddress), Via(RouteVia), /// Only for MPLS for destination label(u32) to forward the packet with NewDestination(Vec), Preference(RoutePreference), EncapType(RouteLwEnCapType), Encap(Vec), // The RTA_EXPIRES holds different data type in kernel 6.5.8. // For non-multipath route, it is u32 and only used for modifying routes. // For multipath route, it is u64 for querying only. /// This is only for non-multicast route Expires(u32), /// This is only for multicast route MulticastExpires(u64), Uid(u32), TtlPropagate(RouteMplsTtlPropagation), Iif(u32), Oif(u32), Priority(u32), /// IPv4 Realm Realm(RouteRealm), Table(u32), Mark(u32), Other(DefaultNla), } impl Nla for RouteAttribute { fn value_len(&self) -> usize { match self { Self::Destination(addr) | Self::PrefSource(addr) | Self::Gateway(addr) | Self::Source(addr) => addr.buffer_len(), Self::Via(v) => v.buffer_len(), Self::NewDestination(v) => VecMplsLabel(v.clone()).buffer_len(), Self::Encap(v) => v.as_slice().buffer_len(), Self::TtlPropagate(_) => 1, Self::CacheInfo(cache_info) => cache_info.buffer_len(), Self::MfcStats(stats) => stats.buffer_len(), Self::Metrics(metrics) => metrics.as_slice().buffer_len(), Self::MultiPath(next_hops) => { next_hops.iter().map(|nh| nh.buffer_len()).sum() } Self::Preference(_) => 1, Self::EncapType(v) => v.buffer_len(), Self::Realm(v) => v.buffer_len(), Self::Uid(_) | Self::Expires(_) | Self::Iif(_) | Self::Oif(_) | Self::Priority(_) | Self::Table(_) | Self::Mark(_) => 4, Self::MulticastExpires(_) => 8, Self::Other(attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Destination(addr) | Self::PrefSource(addr) | Self::Source(addr) | Self::Gateway(addr) => addr.emit(buffer), Self::Via(v) => v.emit(buffer), Self::NewDestination(v) => VecMplsLabel(v.to_vec()).emit(buffer), Self::Encap(nlas) => nlas.as_slice().emit(buffer), Self::TtlPropagate(v) => buffer[0] = u8::from(*v), Self::Preference(p) => buffer[0] = (*p).into(), Self::CacheInfo(cache_info) => cache_info.emit(buffer), Self::MfcStats(stats) => stats.emit(buffer), Self::Metrics(metrics) => metrics.as_slice().emit(buffer), Self::MultiPath(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 } } Self::EncapType(v) => v.emit(buffer), Self::Uid(value) | Self::Expires(value) | Self::Iif(value) | Self::Oif(value) | Self::Priority(value) | Self::Table(value) | Self::Mark(value) => NativeEndian::write_u32(buffer, *value), Self::Realm(v) => v.emit(buffer), Self::MulticastExpires(value) => { NativeEndian::write_u64(buffer, *value) } Self::Other(attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Destination(_) => RTA_DST, Self::Source(_) => RTA_SRC, Self::Iif(_) => RTA_IIF, Self::Oif(_) => RTA_OIF, Self::Gateway(_) => RTA_GATEWAY, Self::Priority(_) => RTA_PRIORITY, Self::PrefSource(_) => RTA_PREFSRC, Self::Metrics(_) => RTA_METRICS, Self::MultiPath(_) => RTA_MULTIPATH, Self::Realm(_) => RTA_FLOW, Self::CacheInfo(_) => RTA_CACHEINFO, Self::Table(_) => RTA_TABLE, Self::Mark(_) => RTA_MARK, Self::MfcStats(_) => RTA_MFC_STATS, Self::Via(_) => RTA_VIA, Self::NewDestination(_) => RTA_NEWDST, Self::Preference(_) => RTA_PREF, Self::EncapType(_) => RTA_ENCAP_TYPE, Self::Encap(_) => RTA_ENCAP, Self::Expires(_) => RTA_EXPIRES, Self::MulticastExpires(_) => RTA_EXPIRES, Self::Uid(_) => RTA_UID, Self::TtlPropagate(_) => RTA_TTL_PROPAGATE, Self::Other(ref attr) => attr.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> ParseableParametrized< NlaBuffer<&'a T>, (AddressFamily, RouteType, RouteLwEnCapType), > for RouteAttribute { fn parse_with_param( buf: &NlaBuffer<&'a T>, (address_family, route_type, encap_type): ( AddressFamily, RouteType, RouteLwEnCapType, ), ) -> Result { let payload = buf.value(); Ok(match buf.kind() { RTA_DST => { Self::Destination(RouteAddress::parse(address_family, payload)?) } RTA_SRC => { Self::Source(RouteAddress::parse(address_family, payload)?) } RTA_GATEWAY => { Self::Gateway(RouteAddress::parse(address_family, payload)?) } RTA_PREFSRC => { Self::PrefSource(RouteAddress::parse(address_family, payload)?) } RTA_VIA => Self::Via( RouteVia::parse( &RouteViaBuffer::new_checked(payload).context(format!( "Invalid RTA_VIA value {:?}", payload ))?, ) .context(format!("Invalid RTA_VIA value {:?}", payload))?, ), RTA_NEWDST => Self::NewDestination( VecMplsLabel::parse(payload) .context(format!("Invalid RTA_NEWDST value {:?}", payload))? .0, ), RTA_PREF => Self::Preference(parse_u8(payload)?.into()), RTA_ENCAP => Self::Encap( VecRouteLwTunnelEncap::parse_with_param(buf, encap_type)?.0, ), RTA_EXPIRES => { if route_type == RouteType::Multicast { Self::MulticastExpires(parse_u64(payload).context( format!( "invalid RTA_EXPIRES (multicast) value {:?}", payload ), )?) } else { Self::Expires(parse_u32(payload).context(format!( "invalid RTA_EXPIRES value {:?}", payload ))?) } } RTA_UID => Self::Uid( parse_u32(payload) .context(format!("invalid RTA_UID value {:?}", payload))?, ), RTA_TTL_PROPAGATE => Self::TtlPropagate( RouteMplsTtlPropagation::from(parse_u8(payload).context( format!("invalid RTA_TTL_PROPAGATE {:?}", payload), )?), ), RTA_ENCAP_TYPE => Self::EncapType(RouteLwEnCapType::from( parse_u16(payload).context("invalid RTA_ENCAP_TYPE value")?, )), RTA_IIF => { Self::Iif(parse_u32(payload).context("invalid RTA_IIF value")?) } RTA_OIF => { Self::Oif(parse_u32(payload).context("invalid RTA_OIF value")?) } RTA_PRIORITY => Self::Priority( parse_u32(payload).context("invalid RTA_PRIORITY value")?, ), RTA_FLOW => Self::Realm( RouteRealm::parse(payload).context("invalid RTA_FLOW value")?, ), RTA_TABLE => Self::Table( parse_u32(payload).context("invalid RTA_TABLE value")?, ), RTA_MARK => Self::Mark( parse_u32(payload).context("invalid RTA_MARK value")?, ), RTA_CACHEINFO => Self::CacheInfo( RouteCacheInfo::parse( &RouteCacheInfoBuffer::new_checked(payload) .context("invalid RTA_CACHEINFO value")?, ) .context("invalid RTA_CACHEINFO value")?, ), RTA_MFC_STATS => Self::MfcStats( RouteMfcStats::parse( &RouteMfcStatsBuffer::new_checked(payload) .context("invalid RTA_MFC_STATS value")?, ) .context("invalid RTA_MFC_STATS value")?, ), RTA_METRICS => Self::Metrics( VecRouteMetric::parse(payload) .context("invalid RTA_METRICS value")? .0, ), RTA_MULTIPATH => { let mut next_hops = vec![]; let mut buf = payload; loop { let nh_buf = RouteNextHopBuffer::new_checked(&buf) .context("invalid RTA_MULTIPATH value")?; let len = nh_buf.length() as usize; let nh = RouteNextHop::parse_with_param( &nh_buf, (address_family, route_type, encap_type), ) .context("invalid RTA_MULTIPATH value")?; next_hops.push(nh); if buf.len() == len { break; } buf = &buf[len..]; } Self::MultiPath(next_hops) } _ => Self::Other( DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, ), }) } } netlink-packet-route-0.21.0/src/route/cache_info.rs000064400000000000000000000031321046102023000203210ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] pub struct RouteCacheInfo { 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, } const CACHE_INFO_LEN: usize = 32; buffer!(RouteCacheInfoBuffer(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 RouteCacheInfo { fn parse(buf: &RouteCacheInfoBuffer) -> 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 RouteCacheInfo { fn buffer_len(&self) -> usize { CACHE_INFO_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = RouteCacheInfoBuffer::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.21.0/src/route/flags.rs000064400000000000000000000025151046102023000173430ustar 00000000000000// SPDX-License-Identifier: MIT use super::next_hops::{ RTNH_F_DEAD, RTNH_F_LINKDOWN, RTNH_F_OFFLOAD, RTNH_F_ONLINK, RTNH_F_PERVASIVE, RTNH_F_TRAP, RTNH_F_UNRESOLVED, }; const RTM_F_NOTIFY: u32 = 0x100; const RTM_F_CLONED: u32 = 0x200; const RTM_F_EQUALIZE: u32 = 0x400; const RTM_F_PREFIX: u32 = 0x800; const RTM_F_LOOKUP_TABLE: u32 = 0x1000; const RTM_F_FIB_MATCH: u32 = 0x2000; const RTM_F_OFFLOAD: u32 = 0x4000; const RTM_F_TRAP: u32 = 0x8000; const RTM_F_OFFLOAD_FAILED: u32 = 0x20000000; bitflags! { #[derive(Clone, Eq, PartialEq, Debug, Copy, Default)] #[non_exhaustive] pub struct RouteFlags: u32 { const Dead = RTNH_F_DEAD as u32; const Pervasive = RTNH_F_PERVASIVE as u32; const Onlink = RTNH_F_ONLINK as u32; const Offload = RTNH_F_OFFLOAD as u32; const Linkdown = RTNH_F_LINKDOWN as u32; const Unresolved = RTNH_F_UNRESOLVED as u32; const Trap = RTNH_F_TRAP as u32; const Notify = RTM_F_NOTIFY; const Cloned = RTM_F_CLONED; const Equalize = RTM_F_EQUALIZE; const Prefix = RTM_F_PREFIX; const LookupTable = RTM_F_LOOKUP_TABLE; const FibMatch = RTM_F_FIB_MATCH; const RtOffload = RTM_F_OFFLOAD; const RtTrap = RTM_F_TRAP; const OffloadFailed = RTM_F_OFFLOAD_FAILED; const _ = !0; } } netlink-packet-route-0.21.0/src/route/header.rs000064400000000000000000000303401046102023000174740ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::{NlaBuffer, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; use super::{super::AddressFamily, flags::RouteFlags}; 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 attributes( &self, ) -> impl Iterator, DecodeError>> { NlasIterator::new(self.payload()) } } /// High level representation of `RTM_GETROUTE`, `RTM_ADDROUTE`, `RTM_DELROUTE` /// messages headers. #[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct RouteHeader { /// Address family of the route: either [AddressFamily::Inet] for IPv4, /// or [AddressFamily::Inet6] for IPv6. pub address_family: AddressFamily, /// Prefix length of the destination subnet. pub destination_prefix_length: u8, /// Prefix length of the source address. pub source_prefix_length: u8, /// Type of service. pub tos: u8, /// Routing table ID. pub table: u8, /// Route Protocol pub protocol: RouteProtocol, /// Route scope pub scope: RouteScope, /// Route type. pub kind: RouteType, /// Flags when querying the kernel with a `RTM_GETROUTE` message. pub flags: RouteFlags, } impl RouteHeader { pub const RT_TABLE_MAIN: u8 = 254; pub const RT_TABLE_UNSPEC: u8 = 0; } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for RouteHeader { fn parse(buf: &RouteMessageBuffer<&'a T>) -> Result { Ok(RouteHeader { address_family: buf.address_family().into(), destination_prefix_length: buf.destination_prefix_length(), source_prefix_length: buf.source_prefix_length(), tos: buf.tos(), table: buf.table(), protocol: buf.protocol().into(), scope: buf.scope().into(), kind: buf.kind().into(), flags: RouteFlags::from_bits_retain(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.into()); 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.into()); buffer.set_scope(self.scope.into()); buffer.set_kind(self.kind.into()); buffer.set_flags(self.flags.bits()); } } #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub enum RouteProtocol { Unspec, IcmpRedirect, Kernel, Boot, Static, Gated, Ra, Mrt, Zebra, Bird, DnRouted, Xorp, Ntk, Dhcp, Mrouted, KeepAlived, Babel, Bgp, Isis, Ospf, Rip, Eigrp, Other(u8), } const RTPROT_UNSPEC: u8 = 0; const RTPROT_REDIRECT: u8 = 1; const RTPROT_KERNEL: u8 = 2; const RTPROT_BOOT: u8 = 3; const RTPROT_STATIC: u8 = 4; const RTPROT_GATED: u8 = 8; const RTPROT_RA: u8 = 9; const RTPROT_MRT: u8 = 10; const RTPROT_ZEBRA: u8 = 11; const RTPROT_BIRD: u8 = 12; const RTPROT_DNROUTED: u8 = 13; const RTPROT_XORP: u8 = 14; const RTPROT_NTK: u8 = 15; const RTPROT_DHCP: u8 = 16; const RTPROT_MROUTED: u8 = 17; const RTPROT_KEEPALIVED: u8 = 18; const RTPROT_BABEL: u8 = 42; const RTPROT_BGP: u8 = 186; const RTPROT_ISIS: u8 = 187; const RTPROT_OSPF: u8 = 188; const RTPROT_RIP: u8 = 189; const RTPROT_EIGRP: u8 = 192; impl From for u8 { fn from(t: RouteProtocol) -> u8 { match t { RouteProtocol::Unspec => RTPROT_UNSPEC, RouteProtocol::IcmpRedirect => RTPROT_REDIRECT, RouteProtocol::Kernel => RTPROT_KERNEL, RouteProtocol::Boot => RTPROT_BOOT, RouteProtocol::Static => RTPROT_STATIC, RouteProtocol::Gated => RTPROT_GATED, RouteProtocol::Ra => RTPROT_RA, RouteProtocol::Mrt => RTPROT_MRT, RouteProtocol::Zebra => RTPROT_ZEBRA, RouteProtocol::Bird => RTPROT_BIRD, RouteProtocol::DnRouted => RTPROT_DNROUTED, RouteProtocol::Xorp => RTPROT_XORP, RouteProtocol::Ntk => RTPROT_NTK, RouteProtocol::Dhcp => RTPROT_DHCP, RouteProtocol::Mrouted => RTPROT_MROUTED, RouteProtocol::KeepAlived => RTPROT_KEEPALIVED, RouteProtocol::Babel => RTPROT_BABEL, RouteProtocol::Bgp => RTPROT_BGP, RouteProtocol::Isis => RTPROT_ISIS, RouteProtocol::Ospf => RTPROT_OSPF, RouteProtocol::Rip => RTPROT_RIP, RouteProtocol::Eigrp => RTPROT_EIGRP, RouteProtocol::Other(d) => d, } } } impl From for RouteProtocol { fn from(d: u8) -> Self { match d { RTPROT_UNSPEC => RouteProtocol::Unspec, RTPROT_REDIRECT => RouteProtocol::IcmpRedirect, RTPROT_KERNEL => RouteProtocol::Kernel, RTPROT_BOOT => RouteProtocol::Boot, RTPROT_STATIC => RouteProtocol::Static, RTPROT_GATED => RouteProtocol::Gated, RTPROT_RA => RouteProtocol::Ra, RTPROT_MRT => RouteProtocol::Mrt, RTPROT_ZEBRA => RouteProtocol::Zebra, RTPROT_BIRD => RouteProtocol::Bird, RTPROT_DNROUTED => RouteProtocol::DnRouted, RTPROT_XORP => RouteProtocol::Xorp, RTPROT_NTK => RouteProtocol::Ntk, RTPROT_DHCP => RouteProtocol::Dhcp, RTPROT_MROUTED => RouteProtocol::Mrouted, RTPROT_KEEPALIVED => RouteProtocol::KeepAlived, RTPROT_BABEL => RouteProtocol::Babel, RTPROT_BGP => RouteProtocol::Bgp, RTPROT_ISIS => RouteProtocol::Isis, RTPROT_OSPF => RouteProtocol::Ospf, RTPROT_RIP => RouteProtocol::Rip, RTPROT_EIGRP => RouteProtocol::Eigrp, _ => RouteProtocol::Other(d), } } } impl std::fmt::Display for RouteProtocol { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Unspec => write!(f, "unspec"), Self::IcmpRedirect => write!(f, "icmp_redirect"), Self::Kernel => write!(f, "kernel"), Self::Boot => write!(f, "boot"), Self::Static => write!(f, "static"), Self::Gated => write!(f, "gated"), Self::Ra => write!(f, "ra"), Self::Mrt => write!(f, "merit_mrt"), Self::Zebra => write!(f, "zebra"), Self::Bird => write!(f, "bird"), Self::DnRouted => write!(f, "decnet_routing_daemon"), Self::Xorp => write!(f, "xorp"), Self::Ntk => write!(f, "netsukuku"), Self::Dhcp => write!(f, "Dhcp"), Self::Mrouted => write!(f, "multicast_daemon"), Self::KeepAlived => write!(f, "keepalived_daemon"), Self::Babel => write!(f, "babel"), Self::Bgp => write!(f, "bgp"), Self::Isis => write!(f, "isis"), Self::Ospf => write!(f, "ospf"), Self::Rip => write!(f, "rip"), Self::Eigrp => write!(f, "eigrp"), Self::Other(v) => write!(f, "other({v})"), } } } impl Default for RouteProtocol { fn default() -> Self { Self::Unspec } } impl Parseable<[u8]> for RouteProtocol { fn parse(buf: &[u8]) -> Result { if buf.len() == 1 { Ok(Self::from(buf[0])) } else { Err(DecodeError::from(format!( "Expecting single u8 for route protocol, but got {:?}", buf ))) } } } impl Emitable for RouteProtocol { fn buffer_len(&self) -> usize { 1 } fn emit(&self, buffer: &mut [u8]) { buffer[0] = u8::from(*self); } } const RT_SCOPE_UNIVERSE: u8 = 0; const RT_SCOPE_SITE: u8 = 200; const RT_SCOPE_LINK: u8 = 253; const RT_SCOPE_HOST: u8 = 254; const RT_SCOPE_NOWHERE: u8 = 255; #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub enum RouteScope { Universe, Site, Link, Host, NoWhere, Other(u8), } impl From for u8 { fn from(v: RouteScope) -> Self { match v { RouteScope::Universe => RT_SCOPE_UNIVERSE, RouteScope::Site => RT_SCOPE_SITE, RouteScope::Link => RT_SCOPE_LINK, RouteScope::Host => RT_SCOPE_HOST, RouteScope::NoWhere => RT_SCOPE_NOWHERE, RouteScope::Other(s) => s, } } } impl From for RouteScope { fn from(d: u8) -> Self { match d { RT_SCOPE_UNIVERSE => RouteScope::Universe, RT_SCOPE_SITE => RouteScope::Site, RT_SCOPE_LINK => RouteScope::Link, RT_SCOPE_HOST => RouteScope::Host, RT_SCOPE_NOWHERE => RouteScope::NoWhere, _ => RouteScope::Other(d), } } } impl Default for RouteScope { fn default() -> Self { Self::Universe } } impl std::fmt::Display for RouteScope { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Universe => write!(f, "universe"), Self::Site => write!(f, "site"), Self::Link => write!(f, "link"), Self::Host => write!(f, "host"), Self::NoWhere => write!(f, "no_where"), Self::Other(s) => write!(f, "other({s})"), } } } #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub enum RouteType { /// Unknown Unspec, /// Gateway or direct route Unicast, /// Accept locally Local, /// Accept locally as broadcast, send as broadcast Broadcast, /// Accept locally as broadcast, but send as unicast Anycast, /// Multicast route Multicast, /// Drop BlackHole, /// Destination is unreachable Unreachable, /// Administratively prohibited Prohibit, /// Not in this table Throw, /// Translate this address Nat, /// Use external resolver ExternalResolve, Other(u8), } const RTN_UNSPEC: u8 = 0; const RTN_UNICAST: u8 = 1; const RTN_LOCAL: u8 = 2; const RTN_BROADCAST: u8 = 3; const RTN_ANYCAST: u8 = 4; const RTN_MULTICAST: u8 = 5; const RTN_BLACKHOLE: u8 = 6; const RTN_UNREACHABLE: u8 = 7; const RTN_PROHIBIT: u8 = 8; const RTN_THROW: u8 = 9; const RTN_NAT: u8 = 10; const RTN_XRESOLVE: u8 = 11; impl From for RouteType { fn from(d: u8) -> Self { match d { RTN_UNSPEC => Self::Unspec, RTN_UNICAST => Self::Unicast, RTN_LOCAL => Self::Local, RTN_BROADCAST => Self::Broadcast, RTN_ANYCAST => Self::Anycast, RTN_MULTICAST => Self::Multicast, RTN_BLACKHOLE => Self::BlackHole, RTN_UNREACHABLE => Self::Unreachable, RTN_PROHIBIT => Self::Prohibit, RTN_THROW => Self::Throw, RTN_NAT => Self::Nat, RTN_XRESOLVE => Self::ExternalResolve, _ => Self::Other(d), } } } impl Default for RouteType { fn default() -> Self { Self::Unspec } } impl From for u8 { fn from(v: RouteType) -> Self { match v { RouteType::Unspec => RTN_UNSPEC, RouteType::Unicast => RTN_UNICAST, RouteType::Local => RTN_LOCAL, RouteType::Broadcast => RTN_BROADCAST, RouteType::Anycast => RTN_ANYCAST, RouteType::Multicast => RTN_MULTICAST, RouteType::BlackHole => RTN_BLACKHOLE, RouteType::Unreachable => RTN_UNREACHABLE, RouteType::Prohibit => RTN_PROHIBIT, RouteType::Throw => RTN_THROW, RouteType::Nat => RTN_NAT, RouteType::ExternalResolve => RTN_XRESOLVE, RouteType::Other(d) => d, } } } netlink-packet-route-0.21.0/src/route/lwtunnel.rs000064400000000000000000000121251046102023000201150ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, traits::{Emitable, Parseable, ParseableParametrized}, DecodeError, }; use super::RouteMplsIpTunnel; const LWTUNNEL_ENCAP_NONE: u16 = 0; const LWTUNNEL_ENCAP_MPLS: u16 = 1; const LWTUNNEL_ENCAP_IP: u16 = 2; const LWTUNNEL_ENCAP_ILA: u16 = 3; const LWTUNNEL_ENCAP_IP6: u16 = 4; const LWTUNNEL_ENCAP_SEG6: u16 = 5; const LWTUNNEL_ENCAP_BPF: u16 = 6; const LWTUNNEL_ENCAP_SEG6_LOCAL: u16 = 7; const LWTUNNEL_ENCAP_RPL: u16 = 8; const LWTUNNEL_ENCAP_IOAM6: u16 = 9; const LWTUNNEL_ENCAP_XFRM: u16 = 10; #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] #[non_exhaustive] pub enum RouteLwEnCapType { #[default] None, Mpls, Ip, Ila, Ip6, Seg6, Bpf, Seg6Local, Rpl, Ioam6, Xfrm, Other(u16), } impl From for RouteLwEnCapType { fn from(d: u16) -> Self { match d { LWTUNNEL_ENCAP_NONE => Self::None, LWTUNNEL_ENCAP_MPLS => Self::Mpls, LWTUNNEL_ENCAP_IP => Self::Ip, LWTUNNEL_ENCAP_ILA => Self::Ila, LWTUNNEL_ENCAP_IP6 => Self::Ip6, LWTUNNEL_ENCAP_SEG6 => Self::Seg6, LWTUNNEL_ENCAP_BPF => Self::Bpf, LWTUNNEL_ENCAP_SEG6_LOCAL => Self::Seg6Local, LWTUNNEL_ENCAP_RPL => Self::Rpl, LWTUNNEL_ENCAP_IOAM6 => Self::Ioam6, LWTUNNEL_ENCAP_XFRM => Self::Xfrm, _ => Self::Other(d), } } } impl From for u16 { fn from(v: RouteLwEnCapType) -> u16 { match v { RouteLwEnCapType::None => LWTUNNEL_ENCAP_NONE, RouteLwEnCapType::Mpls => LWTUNNEL_ENCAP_MPLS, RouteLwEnCapType::Ip => LWTUNNEL_ENCAP_IP, RouteLwEnCapType::Ila => LWTUNNEL_ENCAP_ILA, RouteLwEnCapType::Ip6 => LWTUNNEL_ENCAP_IP6, RouteLwEnCapType::Seg6 => LWTUNNEL_ENCAP_SEG6, RouteLwEnCapType::Bpf => LWTUNNEL_ENCAP_BPF, RouteLwEnCapType::Seg6Local => LWTUNNEL_ENCAP_SEG6_LOCAL, RouteLwEnCapType::Rpl => LWTUNNEL_ENCAP_RPL, RouteLwEnCapType::Ioam6 => LWTUNNEL_ENCAP_IOAM6, RouteLwEnCapType::Xfrm => LWTUNNEL_ENCAP_XFRM, RouteLwEnCapType::Other(d) => d, } } } impl Emitable for RouteLwEnCapType { fn buffer_len(&self) -> usize { 2 } fn emit(&self, buffer: &mut [u8]) { buffer.copy_from_slice(&(u16::from(*self).to_ne_bytes())) } } impl std::fmt::Display for RouteLwEnCapType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::None => write!(f, "none"), Self::Mpls => write!(f, "mpls"), Self::Ip => write!(f, "ip"), Self::Ila => write!(f, "ila"), Self::Ip6 => write!(f, "ip6"), Self::Seg6 => write!(f, "seg6"), Self::Bpf => write!(f, "bpf"), Self::Seg6Local => write!(f, "seg6_local"), Self::Rpl => write!(f, "rpl"), Self::Ioam6 => write!(f, "ioam6"), Self::Xfrm => write!(f, "xfrm"), Self::Other(d) => write!(f, "other({d})"), } } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum RouteLwTunnelEncap { Mpls(RouteMplsIpTunnel), Other(DefaultNla), } impl Nla for RouteLwTunnelEncap { fn value_len(&self) -> usize { match self { Self::Mpls(v) => v.value_len(), Self::Other(v) => v.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Mpls(v) => v.emit_value(buffer), Self::Other(v) => v.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Mpls(v) => v.kind(), Self::Other(v) => v.kind(), } } } impl<'a, T> ParseableParametrized, RouteLwEnCapType> for RouteLwTunnelEncap where T: AsRef<[u8]> + ?Sized, { fn parse_with_param( buf: &NlaBuffer<&'a T>, kind: RouteLwEnCapType, ) -> Result { Ok(match kind { RouteLwEnCapType::Mpls => { Self::Mpls(RouteMplsIpTunnel::parse(buf)?) } _ => Self::Other(DefaultNla::parse(buf)?), }) } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub(crate) struct VecRouteLwTunnelEncap(pub(crate) Vec); impl<'a, T> ParseableParametrized, RouteLwEnCapType> for VecRouteLwTunnelEncap where T: AsRef<[u8]> + ?Sized, { fn parse_with_param( buf: &NlaBuffer<&'a T>, kind: RouteLwEnCapType, ) -> Result { let mut ret = Vec::new(); for nla in NlasIterator::new(buf.value()) { let nla = nla.context(format!("Invalid RTA_ENCAP for kind: {kind}"))?; ret.push(RouteLwTunnelEncap::parse_with_param(&nla, kind).context( format!("Failed to parse RTA_ENCAP for kind: {kind}",), )?) } Ok(Self(ret)) } } netlink-packet-route-0.21.0/src/route/message.rs000064400000000000000000000053261046102023000176760ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ traits::{Emitable, Parseable, ParseableParametrized}, DecodeError, }; use super::{ super::AddressFamily, attribute::RTA_ENCAP_TYPE, RouteAttribute, RouteHeader, RouteLwEnCapType, RouteMessageBuffer, RouteType, }; #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct RouteMessage { pub header: RouteHeader, pub attributes: Vec, } impl Emitable for RouteMessage { fn buffer_len(&self) -> usize { self.header.buffer_len() + self.attributes.as_slice().buffer_len() } fn emit(&self, buffer: &mut [u8]) { self.header.emit(buffer); self.attributes .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 { let header = RouteHeader::parse(buf) .context("failed to parse route message header")?; let address_family = header.address_family; let route_type = header.kind; Ok(RouteMessage { header, attributes: Vec::::parse_with_param( buf, (address_family, route_type), ) .context("failed to parse route message NLAs")?, }) } } impl<'a, T: AsRef<[u8]> + 'a> ParseableParametrized, (AddressFamily, RouteType)> for Vec { fn parse_with_param( buf: &RouteMessageBuffer<&'a T>, (address_family, route_type): (AddressFamily, RouteType), ) -> Result { let mut attributes = vec![]; let mut encap_type = RouteLwEnCapType::None; // The RTA_ENCAP_TYPE is provided __after__ RTA_ENCAP, we should find // RTA_ENCAP_TYPE first. for nla_buf in buf.attributes() { let nla = match nla_buf { Ok(n) => n, Err(_) => continue, }; if nla.kind() == RTA_ENCAP_TYPE { if let Ok(RouteAttribute::EncapType(v)) = RouteAttribute::parse_with_param( &nla, (address_family, route_type, encap_type), ) { encap_type = v; break; } } } for nla_buf in buf.attributes() { attributes.push(RouteAttribute::parse_with_param( &nla_buf?, (address_family, route_type, encap_type), )?); } Ok(attributes) } } netlink-packet-route-0.21.0/src/route/metrics.rs000064400000000000000000000152351046102023000177200ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use std::mem::size_of; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, parsers::parse_u32, traits::Parseable, DecodeError, }; const RTAX_LOCK: u16 = 1; const RTAX_MTU: u16 = 2; const RTAX_WINDOW: u16 = 3; const RTAX_RTT: u16 = 4; const RTAX_RTTVAR: u16 = 5; const RTAX_SSTHRESH: u16 = 6; const RTAX_CWND: u16 = 7; const RTAX_ADVMSS: u16 = 8; const RTAX_REORDERING: u16 = 9; const RTAX_HOPLIMIT: u16 = 10; const RTAX_INITCWND: u16 = 11; const RTAX_FEATURES: u16 = 12; const RTAX_RTO_MIN: u16 = 13; const RTAX_INITRWND: u16 = 14; const RTAX_QUICKACK: u16 = 15; const RTAX_CC_ALGO: u16 = 16; const RTAX_FASTOPEN_NO_COOKIE: u16 = 17; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum RouteMetric { 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 RouteMetric { fn value_len(&self) -> usize { match self { Self::Lock(_) | Self::Mtu(_) | Self::Window(_) | Self::Rtt(_) | Self::RttVar(_) | Self::SsThresh(_) | Self::Cwnd(_) | Self::Advmss(_) | Self::Reordering(_) | Self::Hoplimit(_) | Self::InitCwnd(_) | Self::Features(_) | Self::RtoMin(_) | Self::InitRwnd(_) | Self::QuickAck(_) | Self::CcAlgo(_) | Self::FastopenNoCookie(_) => size_of::(), Self::Other(attr) => attr.value_len(), } } #[rustfmt::skip] fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Lock(value) | Self:: Mtu(value) | Self:: Window(value) | Self:: Rtt(value) | Self:: RttVar(value) | Self:: SsThresh(value) | Self:: Cwnd(value) | Self:: Advmss(value) | Self:: Reordering(value) | Self:: Hoplimit(value) | Self:: InitCwnd(value) | Self:: Features(value) | Self:: RtoMin(value) | Self:: InitRwnd(value) | Self:: QuickAck(value) | Self:: CcAlgo(value) | Self:: FastopenNoCookie(value) => NativeEndian::write_u32(buffer, *value), Self::Other(attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Lock(_) => RTAX_LOCK, Self::Mtu(_) => RTAX_MTU, Self::Window(_) => RTAX_WINDOW, Self::Rtt(_) => RTAX_RTT, Self::RttVar(_) => RTAX_RTTVAR, Self::SsThresh(_) => RTAX_SSTHRESH, Self::Cwnd(_) => RTAX_CWND, Self::Advmss(_) => RTAX_ADVMSS, Self::Reordering(_) => RTAX_REORDERING, Self::Hoplimit(_) => RTAX_HOPLIMIT, Self::InitCwnd(_) => RTAX_INITCWND, Self::Features(_) => RTAX_FEATURES, Self::RtoMin(_) => RTAX_RTO_MIN, Self::InitRwnd(_) => RTAX_INITRWND, Self::QuickAck(_) => RTAX_QUICKACK, Self::CcAlgo(_) => RTAX_CC_ALGO, Self::FastopenNoCookie(_) => RTAX_FASTOPEN_NO_COOKIE, Self::Other(attr) => attr.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for RouteMetric { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { RTAX_LOCK => Self::Lock( parse_u32(payload).context("invalid RTAX_LOCK value")?, ), RTAX_MTU => { Self::Mtu(parse_u32(payload).context("invalid RTAX_MTU value")?) } RTAX_WINDOW => Self::Window( parse_u32(payload).context("invalid RTAX_WINDOW value")?, ), RTAX_RTT => { Self::Rtt(parse_u32(payload).context("invalid RTAX_RTT value")?) } RTAX_RTTVAR => Self::RttVar( parse_u32(payload).context("invalid RTAX_RTTVAR value")?, ), RTAX_SSTHRESH => Self::SsThresh( parse_u32(payload).context("invalid RTAX_SSTHRESH value")?, ), RTAX_CWND => Self::Cwnd( parse_u32(payload).context("invalid RTAX_CWND value")?, ), RTAX_ADVMSS => Self::Advmss( parse_u32(payload).context("invalid RTAX_ADVMSS value")?, ), RTAX_REORDERING => Self::Reordering( parse_u32(payload).context("invalid RTAX_REORDERING value")?, ), RTAX_HOPLIMIT => Self::Hoplimit( parse_u32(payload).context("invalid RTAX_HOPLIMIT value")?, ), RTAX_INITCWND => Self::InitCwnd( parse_u32(payload).context("invalid RTAX_INITCWND value")?, ), RTAX_FEATURES => Self::Features( parse_u32(payload).context("invalid RTAX_FEATURES value")?, ), RTAX_RTO_MIN => Self::RtoMin( parse_u32(payload).context("invalid RTAX_RTO_MIN value")?, ), RTAX_INITRWND => Self::InitRwnd( parse_u32(payload).context("invalid RTAX_INITRWND value")?, ), RTAX_QUICKACK => Self::QuickAck( parse_u32(payload).context("invalid RTAX_QUICKACK value")?, ), RTAX_CC_ALGO => Self::CcAlgo( parse_u32(payload).context("invalid RTAX_CC_ALGO value")?, ), RTAX_FASTOPEN_NO_COOKIE => Self::FastopenNoCookie( parse_u32(payload) .context("invalid RTAX_FASTOPEN_NO_COOKIE value")?, ), _ => Self::Other( DefaultNla::parse(buf) .context("invalid NLA value (unknown type) value")?, ), }) } } pub(crate) struct VecRouteMetric(pub(crate) Vec); impl + ?Sized> Parseable for VecRouteMetric { fn parse(payload: &T) -> Result { let mut nlas = vec![]; for nla in NlasIterator::new(payload) { let nla = nla.context("Invalid RTA_METRICS")?; nlas.push(RouteMetric::parse(&nla).context("Invalid RTA_METRICS")?); } Ok(Self(nlas)) } } netlink-packet-route-0.21.0/src/route/mfc_stats.rs000064400000000000000000000021071046102023000202270ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] pub struct RouteMfcStats { pub packets: u64, pub bytes: u64, pub wrong_if: u64, } const MFC_STATS_LEN: usize = 24; buffer!(RouteMfcStatsBuffer(MFC_STATS_LEN) { packets: (u64, 0..8), bytes: (u64, 8..16), wrong_if: (u64, 16..24), }); impl> Parseable> for RouteMfcStats { fn parse( buf: &RouteMfcStatsBuffer, ) -> Result { Ok(RouteMfcStats { packets: buf.packets(), bytes: buf.bytes(), wrong_if: buf.wrong_if(), }) } } impl Emitable for RouteMfcStats { fn buffer_len(&self) -> usize { MFC_STATS_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = RouteMfcStatsBuffer::new(buffer); buffer.set_packets(self.packets); buffer.set_bytes(self.bytes); buffer.set_wrong_if(self.wrong_if); } } netlink-packet-route-0.21.0/src/route/mod.rs000064400000000000000000000017651046102023000170340ustar 00000000000000// SPDX-License-Identifier: MIT mod address; mod attribute; mod cache_info; mod flags; mod header; mod lwtunnel; mod message; pub(crate) mod metrics; mod mfc_stats; mod mpls; mod next_hops; mod preference; mod realm; mod via; #[cfg(test)] mod tests; pub use self::address::RouteAddress; pub use self::attribute::RouteAttribute; pub use self::cache_info::{RouteCacheInfo, RouteCacheInfoBuffer}; pub use self::header::{ RouteHeader, RouteMessageBuffer, RouteProtocol, RouteScope, RouteType, }; pub use self::lwtunnel::{RouteLwEnCapType, RouteLwTunnelEncap}; pub use self::message::RouteMessage; pub use self::metrics::RouteMetric; pub use self::mfc_stats::{RouteMfcStats, RouteMfcStatsBuffer}; pub use self::mpls::{MplsLabel, RouteMplsIpTunnel, RouteMplsTtlPropagation}; pub use self::next_hops::{ RouteNextHop, RouteNextHopBuffer, RouteNextHopFlags, }; pub use self::preference::RoutePreference; pub use self::realm::RouteRealm; pub use self::via::{RouteVia, RouteViaBuffer}; pub use flags::RouteFlags; netlink-packet-route-0.21.0/src/route/mpls.rs000064400000000000000000000132571046102023000172270ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::parse_u8, traits::{Emitable, Parseable}, DecodeError, }; const MPLS_IPTUNNEL_DST: u16 = 1; const MPLS_IPTUNNEL_TTL: u16 = 2; /// Netlink attributes for `RTA_ENCAP` with `RTA_ENCAP_TYPE` set to /// `LWTUNNEL_ENCAP_MPLS`. #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum RouteMplsIpTunnel { Destination(Vec), Ttl(u8), Other(DefaultNla), } impl Nla for RouteMplsIpTunnel { fn value_len(&self) -> usize { match self { Self::Destination(v) => VecMplsLabel(v.to_vec()).buffer_len(), Self::Ttl(_) => 1, Self::Other(attr) => attr.value_len(), } } fn kind(&self) -> u16 { match self { Self::Destination(_) => MPLS_IPTUNNEL_DST, Self::Ttl(_) => MPLS_IPTUNNEL_TTL, Self::Other(attr) => attr.kind(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Destination(v) => VecMplsLabel(v.to_vec()).emit(buffer), Self::Ttl(ttl) => buffer[0] = *ttl, Self::Other(attr) => attr.emit_value(buffer), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for RouteMplsIpTunnel { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { MPLS_IPTUNNEL_DST => Self::Destination( VecMplsLabel::parse(payload) .context(format!( "invalid MPLS_IPTUNNEL_DST value {:?}", payload ))? .0, ), MPLS_IPTUNNEL_TTL => Self::Ttl( parse_u8(payload).context("invalid MPLS_IPTUNNEL_TTL value")?, ), _ => Self::Other( DefaultNla::parse(buf) .context("invalid NLA value (unknown type) value")?, ), }) } } const MPLS_LS_LABEL_MASK: u32 = 0xFFFFF000; const MPLS_LS_LABEL_SHIFT: u32 = 12; const MPLS_LS_TC_MASK: u32 = 0x00000E00; const MPLS_LS_TC_SHIFT: u32 = 9; const MPLS_LS_S_MASK: u32 = 0x00000100; const MPLS_LS_S_SHIFT: u32 = 8; const MPLS_LS_TTL_MASK: u32 = 0x000000FF; const MPLS_LS_TTL_SHIFT: u32 = 0; #[derive(Debug, PartialEq, Eq, Clone, Copy)] /// MPLS label defined in RFC 3032 and updated by RFC 5462 pub struct MplsLabel { /// label, 20 bytes pub label: u32, /// Traffic Class, 3 bits pub traffic_class: u8, /// Bottom of Stack, 1 bit pub bottom_of_stack: bool, /// Time to Live pub ttl: u8, } impl MplsLabel { pub(crate) fn parse(payload: &[u8]) -> Result { if payload.len() == 4 { Ok(Self::from(u32::from_be_bytes([ payload[0], payload[1], payload[2], payload[3], ]))) } else { Err(DecodeError::from(format!( "Invalid u8 array length {}, expecting \ 4 bytes for MPLS label, got {:?}", payload.len(), payload, ))) } } } impl Emitable for MplsLabel { fn buffer_len(&self) -> usize { 4 } fn emit(&self, buffer: &mut [u8]) { buffer.copy_from_slice(u32::from(*self).to_be_bytes().as_slice()) } } impl From for MplsLabel { fn from(d: u32) -> Self { let label = (d & MPLS_LS_LABEL_MASK) >> MPLS_LS_LABEL_SHIFT; let traffic_class = ((d & MPLS_LS_TC_MASK) >> MPLS_LS_TC_SHIFT) as u8; let bottom_of_stack = (d & MPLS_LS_S_MASK) > 0; let ttl = (d & MPLS_LS_TTL_MASK) as u8; Self { label, traffic_class, bottom_of_stack, ttl, } } } impl From for u32 { fn from(v: MplsLabel) -> u32 { v.label << MPLS_LS_LABEL_SHIFT | (v.traffic_class as u32) << MPLS_LS_TC_SHIFT | (v.bottom_of_stack as u32) << MPLS_LS_S_SHIFT | (v.ttl as u32) << MPLS_LS_TTL_SHIFT } } pub(crate) struct VecMplsLabel(pub(crate) Vec); impl VecMplsLabel { pub(crate) fn parse(payload: &[u8]) -> Result { let mut labels = vec![]; let mut i: usize = 0; while i + 4 <= payload.len() { labels.push(MplsLabel::parse(&payload[i..i + 4])?); i += 4; } Ok(Self(labels)) } } impl Emitable for VecMplsLabel { fn buffer_len(&self) -> usize { self.0.len() * 4 } fn emit(&self, buffer: &mut [u8]) { for (i, label) in self.0.iter().enumerate() { label.emit(&mut buffer[i * 4..i * 4 + 4]); } } } const MPLS_TTL_PROP_DEFAULT: u8 = 0; const MPLS_TTL_PROP_ENABLED: u8 = 1; const MPLS_TTL_PROP_DISABLED: u8 = 2; #[derive(Debug, PartialEq, Eq, Clone, Default, Copy)] #[non_exhaustive] pub enum RouteMplsTtlPropagation { #[default] Default, Enabled, Disabled, Other(u8), } impl From for RouteMplsTtlPropagation { fn from(d: u8) -> Self { match d { MPLS_TTL_PROP_DEFAULT => Self::Default, MPLS_TTL_PROP_ENABLED => Self::Enabled, MPLS_TTL_PROP_DISABLED => Self::Disabled, _ => Self::Other(d), } } } impl From for u8 { fn from(v: RouteMplsTtlPropagation) -> u8 { match v { RouteMplsTtlPropagation::Default => MPLS_TTL_PROP_DEFAULT, RouteMplsTtlPropagation::Enabled => MPLS_TTL_PROP_ENABLED, RouteMplsTtlPropagation::Disabled => MPLS_TTL_PROP_DISABLED, RouteMplsTtlPropagation::Other(d) => d, } } } netlink-packet-route-0.21.0/src/route/next_hops.rs000064400000000000000000000113321046102023000202530ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{NlaBuffer, NlasIterator}, traits::{Emitable, ParseableParametrized}, DecodeError, }; use super::{ super::AddressFamily, RouteAttribute, RouteLwEnCapType, RouteType, }; pub(crate) const RTNH_F_DEAD: u8 = 1; pub(crate) const RTNH_F_PERVASIVE: u8 = 2; pub(crate) const RTNH_F_ONLINK: u8 = 4; pub(crate) const RTNH_F_OFFLOAD: u8 = 8; pub(crate) const RTNH_F_LINKDOWN: u8 = 16; pub(crate) const RTNH_F_UNRESOLVED: u8 = 32; pub(crate) const RTNH_F_TRAP: u8 = 64; bitflags! { #[derive(Clone, Eq, PartialEq, Debug, Copy, Default)] #[non_exhaustive] pub struct RouteNextHopFlags: u8 { const Dead = RTNH_F_DEAD; const Pervasive = RTNH_F_PERVASIVE; const Onlink = RTNH_F_ONLINK; const Offload = RTNH_F_OFFLOAD; const Linkdown = RTNH_F_LINKDOWN; const Unresolved = RTNH_F_UNRESOLVED; const Trap = RTNH_F_TRAP; const _ = !0; } } const PAYLOAD_OFFSET: usize = 8; buffer!(RouteNextHopBuffer { length: (u16, 0..2), flags: (u8, 2), hops: (u8, 3), interface_index: (u32, 4..8), payload: (slice, PAYLOAD_OFFSET..), }); impl> RouteNextHopBuffer { 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 RouteNextHopBuffer: length {len} < {PAYLOAD_OFFSET}" ) .into()); } if len < self.length() as usize { return Err(format!( "invalid RouteNextHopBuffer: length {} < {}", len, self.length(), ) .into()); } Ok(()) } } impl<'a, T: AsRef<[u8]> + ?Sized> RouteNextHopBuffer<&'a T> { pub fn attributes( &self, ) -> impl Iterator, DecodeError>> { NlasIterator::new( &self.payload()[..(self.length() as usize - PAYLOAD_OFFSET)], ) } } #[derive(Debug, Clone, Eq, PartialEq, Default)] #[non_exhaustive] pub struct RouteNextHop { /// Next-hop flags pub flags: RouteNextHopFlags, /// Next-hop priority pub hops: u8, /// Interface index for the next-hop pub interface_index: u32, /// Attributes pub attributes: Vec, } impl<'a, T: AsRef<[u8]>> ParseableParametrized< RouteNextHopBuffer<&'a T>, (AddressFamily, RouteType, RouteLwEnCapType), > for RouteNextHop { fn parse_with_param( buf: &RouteNextHopBuffer<&T>, (address_family, route_type, encap_type): ( AddressFamily, RouteType, RouteLwEnCapType, ), ) -> Result { let attributes = Vec::::parse_with_param( &RouteNextHopBuffer::new_checked(buf.buffer) .context("cannot parse route attributes in next-hop")?, (address_family, route_type, encap_type), ) .context("cannot parse route attributes in next-hop")?; Ok(RouteNextHop { flags: RouteNextHopFlags::from_bits_retain(buf.flags()), hops: buf.hops(), interface_index: buf.interface_index(), attributes, }) } } impl<'a, T: AsRef<[u8]> + 'a> ParseableParametrized< RouteNextHopBuffer<&'a T>, (AddressFamily, RouteType, RouteLwEnCapType), > for Vec { fn parse_with_param( buf: &RouteNextHopBuffer<&'a T>, (address_family, route_type, encap_type): ( AddressFamily, RouteType, RouteLwEnCapType, ), ) -> Result { let mut nlas = vec![]; for nla_buf in buf.attributes() { nlas.push(RouteAttribute::parse_with_param( &nla_buf?, (address_family, route_type, encap_type), )?); } Ok(nlas) } } impl Emitable for RouteNextHop { fn buffer_len(&self) -> usize { // len, flags, hops and interface id fields PAYLOAD_OFFSET + self.attributes.as_slice().buffer_len() } fn emit(&self, buffer: &mut [u8]) { let mut nh_buffer = RouteNextHopBuffer::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_index(self.interface_index); self.attributes.as_slice().emit(nh_buffer.payload_mut()) } } netlink-packet-route-0.21.0/src/route/preference.rs000064400000000000000000000022701046102023000203630ustar 00000000000000// SPDX-License-Identifier: MIT const ICMPV6_ROUTER_PREF_LOW: u8 = 0x3; const ICMPV6_ROUTER_PREF_MEDIUM: u8 = 0x0; const ICMPV6_ROUTER_PREF_HIGH: u8 = 0x1; const ICMPV6_ROUTER_PREF_INVALID: u8 = 0x2; #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub enum RoutePreference { Low, Medium, High, Invalid, Other(u8), } impl From for u8 { fn from(v: RoutePreference) -> Self { match v { RoutePreference::Low => ICMPV6_ROUTER_PREF_LOW, RoutePreference::Medium => ICMPV6_ROUTER_PREF_MEDIUM, RoutePreference::High => ICMPV6_ROUTER_PREF_HIGH, RoutePreference::Invalid => ICMPV6_ROUTER_PREF_INVALID, RoutePreference::Other(s) => s, } } } impl From for RoutePreference { fn from(d: u8) -> Self { match d { ICMPV6_ROUTER_PREF_LOW => Self::Low, ICMPV6_ROUTER_PREF_MEDIUM => Self::Medium, ICMPV6_ROUTER_PREF_HIGH => Self::High, ICMPV6_ROUTER_PREF_INVALID => Self::Invalid, _ => Self::Other(d), } } } impl Default for RoutePreference { fn default() -> Self { Self::Invalid } } netlink-packet-route-0.21.0/src/route/realm.rs000064400000000000000000000020561046102023000173470ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{DecodeError, Emitable}; const RULE_REALM_LEN: usize = 4; #[derive(Clone, Eq, PartialEq, Debug, Copy)] pub struct RouteRealm { pub source: u16, pub destination: u16, } impl RouteRealm { pub(crate) fn parse(buf: &[u8]) -> Result { let all = u32::from_ne_bytes([buf[0], buf[1], buf[2], buf[3]]); if buf.len() == RULE_REALM_LEN { Ok(Self { source: (all >> 16) as u16, destination: (all & 0xFFFF) as u16, }) } else { Err(DecodeError::from(format!( "Invalid rule port range data, expecting \ {RULE_REALM_LEN} u8 array, but got {:?}", buf ))) } } } impl Emitable for RouteRealm { fn buffer_len(&self) -> usize { RULE_REALM_LEN } fn emit(&self, buffer: &mut [u8]) { let all = (self.source as u32) << 16 | self.destination as u32; buffer.copy_from_slice(&all.to_ne_bytes()); } } netlink-packet-route-0.21.0/src/route/tests/cache_info.rs000064400000000000000000000072661046102023000214770ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::Ipv6Addr; use std::str::FromStr; use netlink_packet_utils::traits::{Emitable, Parseable}; use crate::route::flags::RouteFlags; use crate::route::{ RouteAttribute, RouteCacheInfo, RouteHeader, RouteMessage, RouteMessageBuffer, RouteMetric, RoutePreference, RouteProtocol, RouteScope, RouteType, }; use crate::AddressFamily; #[test] // wireshark capture(netlink message header removed) of nlmon against command: // ip -6 route show dev wlan0 fn test_ipv6_route_cache_info() { let raw = vec![ 0x0a, // Address family inet6(10) 0x00, // destination prefix length 0 0x00, // source prefix length 0 0x00, // tos 0xfe, // table 254(main) 0x09, // protocol RTPROT_RA 9 0x00, // scope RT_SCOPE_UNIVERSE 0 0x01, // type RTN_UNICAST 1 0x00, 0x00, 0x00, 0x00, // flags 0 0x08, 0x00, // length 8 0x0f, 0x00, // RTA_TABLE 15 0xfe, 0x00, 0x00, 0x00, // table u32 254 0x14, 0x00, // length 20 0x08, 0x00, // RTA_METRICS 8 0x08, 0x00, // length 8 0x02, 0x00, // RTAX_MTU 2 0x98, 0x05, 0x00, 0x00, // MTU 1432 0x08, 0x00, // length 8 0x0a, 0x00, // RTAX_HOPLIMIT 10 0xfe, 0x00, 0x00, 0x00, // hop limit 254 0x08, 0x00, // length 8 0x06, 0x00, // RTA_PRIORITY 6 0x00, 0x04, 0x00, 0x00, // 1024 0x14, 0x00, // length 20 0x05, 0x00, // RTA_GATEWAY 5 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd6, 0x38, 0x9c, 0xff, 0xfe, 0x01, 0xe8, 0x52, // ipv6 addr 0x08, 0x00, // length 8 0x04, 0x00, // RTA_OIF 4 0x02, 0x00, 0x00, 0x00, // oif 2 0x24, 0x00, // length 36 0x0c, 0x00, // RTA_CACHEINFO 0x00, 0x00, 0x00, 0x00, // rta_clntref 0x00, 0x00, 0x00, 0x00, // rta_lastuse 0x29, 0x29, 0x05, 0x00, // rta_expires 0x00, 0x00, 0x00, 0x00, // rta_error 0x00, 0x00, 0x00, 0x00, // rta_id 0x00, 0x00, 0x00, 0x00, // rta_ts 0x00, 0x00, 0x00, 0x00, // rta_tsage 0x00, 0x00, 0x00, 0x00, // padding 0x05, 0x00, // length 5 0x14, 0x00, // RTA_PREF 0x01, // 1 ICMPV6_ROUTER_PREF_HIGH 0x00, 0x00, 0x00, // padding ]; let expected = RouteMessage { header: RouteHeader { address_family: AddressFamily::Inet6, destination_prefix_length: 0, source_prefix_length: 0, tos: 0, table: 254, protocol: RouteProtocol::Ra, scope: RouteScope::Universe, kind: RouteType::Unicast, flags: RouteFlags::empty(), }, attributes: vec![ RouteAttribute::Table(254), RouteAttribute::Metrics(vec![ RouteMetric::Mtu(1432), RouteMetric::Hoplimit(254), ]), RouteAttribute::Priority(1024), RouteAttribute::Gateway( Ipv6Addr::from_str("fe80::d638:9cff:fe01:e852") .unwrap() .into(), ), RouteAttribute::Oif(2), RouteAttribute::CacheInfo(RouteCacheInfo { clntref: 0, last_use: 0, expires: 338217, error: 0, used: 0, id: 0, ts: 0, ts_age: 0, }), RouteAttribute::Preference(RoutePreference::High), ], }; assert_eq!( expected, RouteMessage::parse(&RouteMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/route/tests/expires.rs000064400000000000000000000033111046102023000210630ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::Ipv6Addr; use std::str::FromStr; use netlink_packet_utils::traits::{Emitable, Parseable}; use crate::route::flags::RouteFlags; use crate::route::{ RouteAttribute, RouteHeader, RouteMessage, RouteMessageBuffer, RouteProtocol, RouteScope, RouteType, }; use crate::AddressFamily; #[test] // wireshark capture(netlink message header removed) of nlmon against command: // ip route add 2001:db8:1::/64 dev wlan0 expires 3000 fn test_ipv6_route_expires() { let raw = vec![ 0x0a, 0x40, 0x00, 0x00, 0xfe, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x17, 0x00, 0xb8, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, ]; let expected = RouteMessage { header: RouteHeader { address_family: AddressFamily::Inet6, destination_prefix_length: 64, source_prefix_length: 0, tos: 0, table: 254, protocol: RouteProtocol::Boot, scope: RouteScope::Universe, kind: RouteType::Unicast, flags: RouteFlags::empty(), }, attributes: vec![ RouteAttribute::Destination( Ipv6Addr::from_str("2001:db8:1::").unwrap().into(), ), RouteAttribute::Expires(3000), RouteAttribute::Oif(2), ], }; assert_eq!( expected, RouteMessage::parse(&RouteMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/route/tests/loopback.rs000064400000000000000000000122571046102023000212070ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::traits::{Emitable, Parseable}; use std::net::{Ipv4Addr, Ipv6Addr}; use crate::route::flags::RouteFlags; use crate::route::{ RouteAttribute, RouteCacheInfo, RouteHeader, RouteMessage, RouteMessageBuffer, RoutePreference, RouteProtocol, RouteScope, RouteType, }; use crate::AddressFamily; #[test] // wireshark capture(netlink message header removed) of nlmon against command: // ip -4 route show dev lo table local fn test_ipv4_route_loopback() { let raw = vec![ 0x02, 0x08, 0x00, 0x00, 0xff, 0x02, 0xfe, 0x02, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x00, 0x01, 0x08, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, ]; let expected = RouteMessage { header: RouteHeader { address_family: AddressFamily::Inet, destination_prefix_length: 8, source_prefix_length: 0, tos: 0, table: 255, protocol: RouteProtocol::Kernel, scope: RouteScope::Host, kind: RouteType::Local, flags: RouteFlags::empty(), }, attributes: vec![ RouteAttribute::Table(255), RouteAttribute::Destination( Ipv4Addr::from([127u8, 0, 0, 0]).into(), ), RouteAttribute::PrefSource(Ipv4Addr::from([127u8, 0, 0, 1]).into()), RouteAttribute::Oif(1), ], }; assert_eq!( expected, RouteMessage::parse(&RouteMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } #[test] // wireshark capture(netlink message header removed) of nlmon against command: // ip -4 route show dev lo table local fn test_ipv4_route_loopback_broadcast() { let raw = vec![ 0x02, 0x20, 0x00, 0x00, 0xff, 0x02, 0xfd, 0x03, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x7f, 0xff, 0xff, 0xff, 0x08, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x00, 0x01, 0x08, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, ]; let expected = RouteMessage { header: RouteHeader { address_family: AddressFamily::Inet, destination_prefix_length: 32, source_prefix_length: 0, tos: 0, table: 255, protocol: RouteProtocol::Kernel, scope: RouteScope::Link, kind: RouteType::Broadcast, flags: RouteFlags::empty(), }, attributes: vec![ RouteAttribute::Table(255), RouteAttribute::Destination( Ipv4Addr::from([127u8, 255, 255, 255]).into(), ), RouteAttribute::PrefSource(Ipv4Addr::from([127u8, 0, 0, 1]).into()), RouteAttribute::Oif(1), ], }; assert_eq!( expected, RouteMessage::parse(&RouteMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } #[test] // wireshark capture(netlink message header removed) of nlmon against command: // ip -6 route show dev lo table local fn test_ipv6_route_loopback() { let raw = vec![ 0x0a, 0x80, 0x00, 0x00, 0xff, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let expected = RouteMessage { header: RouteHeader { address_family: AddressFamily::Inet6, destination_prefix_length: 128, source_prefix_length: 0, tos: 0, table: 255, protocol: RouteProtocol::Kernel, scope: RouteScope::Universe, kind: RouteType::Local, flags: RouteFlags::empty(), }, attributes: vec![ RouteAttribute::Table(255), RouteAttribute::Destination(Ipv6Addr::LOCALHOST.into()), RouteAttribute::Priority(0), RouteAttribute::Oif(1), RouteAttribute::CacheInfo(RouteCacheInfo { clntref: 0, last_use: 0, expires: 0, error: 0, used: 0, id: 0, ts: 0, ts_age: 0, }), RouteAttribute::Preference(RoutePreference::Medium), ], }; assert_eq!( expected, RouteMessage::parse(&RouteMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/route/tests/mod.rs000064400000000000000000000004101046102023000201600ustar 00000000000000// SPDX-License-Identifier: MIT #[cfg(test)] mod cache_info; #[cfg(test)] mod expires; #[cfg(test)] mod loopback; #[cfg(test)] mod mpls; #[cfg(test)] mod multipath; #[cfg(test)] mod realm; #[cfg(test)] mod route_flags; #[cfg(test)] mod uid; #[cfg(test)] mod via; netlink-packet-route-0.21.0/src/route/tests/mpls.rs000064400000000000000000000301411046102023000203600ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::{Ipv4Addr, Ipv6Addr}; use std::str::FromStr; use netlink_packet_utils::traits::{Emitable, Parseable}; use crate::route::flags::RouteFlags; use crate::route::{ MplsLabel, RouteAddress, RouteAttribute, RouteCacheInfo, RouteHeader, RouteLwEnCapType, RouteLwTunnelEncap, RouteMessage, RouteMessageBuffer, RouteMplsIpTunnel, RouteMplsTtlPropagation, RoutePreference, RouteProtocol, RouteScope, RouteType, }; use crate::AddressFamily; // Setup: // ip link add dummy1 type dummy // ip link set dummy1 up // ip addr add 192.0.2.1/24 dev dummy1 // modprobe mpls_iptunnel // sysctl -w net.mpls.platform_labels=65535 // ip route add 198.51.100.1/32 encap mpls 100 ttl 25 \ // via inet 192.0.2.1 dev dummy1 // wireshark capture(netlink message header removed) of nlmon against command: // ip route show dev dummy1 #[test] fn test_mpls_route_to_ipv4() { let raw = vec![ 0x02, 0x20, 0x00, 0x00, 0xfe, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0xc6, 0x33, 0x64, 0x01, 0x08, 0x00, 0x05, 0x00, 0xc0, 0x00, 0x02, 0x01, 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x16, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x06, 0x41, 0x00, 0x05, 0x00, 0x02, 0x00, 0x19, 0x00, 0x00, 0x00, 0x06, 0x00, 0x15, 0x00, 0x01, 0x00, 0x00, 0x00, ]; let expected = RouteMessage { header: RouteHeader { address_family: AddressFamily::Inet, destination_prefix_length: 32, source_prefix_length: 0, tos: 0, table: 254, protocol: RouteProtocol::Boot, scope: RouteScope::Universe, kind: RouteType::Unicast, flags: RouteFlags::empty(), }, attributes: vec![ RouteAttribute::Table(254), RouteAttribute::Destination( Ipv4Addr::from_str("198.51.100.1").unwrap().into(), ), RouteAttribute::Gateway( Ipv4Addr::from_str("192.0.2.1").unwrap().into(), ), RouteAttribute::Oif(10), RouteAttribute::Encap(vec![ RouteLwTunnelEncap::Mpls(RouteMplsIpTunnel::Destination(vec![ MplsLabel { label: 100, traffic_class: 0, bottom_of_stack: true, ttl: 0, }, ])), RouteLwTunnelEncap::Mpls(RouteMplsIpTunnel::Ttl(25)), ]), RouteAttribute::EncapType(RouteLwEnCapType::Mpls), ], }; assert_eq!( expected, RouteMessage::parse(&RouteMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } // Setup: // ip link add dummy1 type dummy // ip link set dummy1 up // ip addr add 2001:db8:1::1/64 dev dummy1 // modprobe mpls_iptunnel // sysctl -w net.mpls.platform_labels=65535 // ip route add 2001:db8:2::/64 encap mpls 200 \ // via inet6 2001:db8:1::2 dev dummy1 // wireshark capture(netlink message header removed) of nlmon against command: // ip -6 route show dev dummy1 #[test] fn test_ipv6_to_mpls_route() { let raw = vec![ 0x0a, 0x40, 0x00, 0x00, 0xfe, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x14, 0x00, 0x05, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x0c, 0x81, 0x00, 0x06, 0x00, 0x15, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let expected = RouteMessage { header: RouteHeader { address_family: AddressFamily::Inet6, destination_prefix_length: 64, source_prefix_length: 0, tos: 0, table: 254, protocol: RouteProtocol::Boot, scope: RouteScope::Universe, kind: RouteType::Unicast, flags: RouteFlags::empty(), }, attributes: vec![ RouteAttribute::Table(254), RouteAttribute::Destination( Ipv6Addr::from_str("2001:db8:2::").unwrap().into(), ), RouteAttribute::Priority(1024), RouteAttribute::Gateway( Ipv6Addr::from_str("2001:db8:1::2").unwrap().into(), ), RouteAttribute::Oif(7), RouteAttribute::Encap(vec![RouteLwTunnelEncap::Mpls( RouteMplsIpTunnel::Destination(vec![MplsLabel { label: 200, traffic_class: 0, bottom_of_stack: true, ttl: 0, }]), )]), RouteAttribute::EncapType(RouteLwEnCapType::Mpls), RouteAttribute::CacheInfo(RouteCacheInfo { clntref: 0, last_use: 0, expires: 0, error: 0, used: 0, id: 0, ts: 0, ts_age: 0, }), RouteAttribute::Preference(RoutePreference::Medium), ], }; assert_eq!( expected, RouteMessage::parse(&RouteMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } // Setup: // ip link add dummy1 type dummy // ip link set dummy1 up // ip addr add 2001:db8:1::1/64 dev dummy1 // modprobe mpls_iptunnel // sysctl -w net.mpls.platform_labels=65535 // ip -f mpls route add 300 via inet6 2001:db8:1::2 dev dummy1 // wireshark capture(netlink message header removed) of nlmon against command: // ip -f mpls route show dev dummy1 #[test] fn test_mpls_route_to_ipv6() { let raw = vec![ 0x1c, 0x14, 0x00, 0x00, 0xfe, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x12, 0xc1, 0x00, 0x16, 0x00, 0x12, 0x00, 0x0a, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, ]; let expected = RouteMessage { header: RouteHeader { address_family: AddressFamily::Mpls, destination_prefix_length: 20, source_prefix_length: 0, tos: 0, table: 254, protocol: RouteProtocol::Boot, scope: RouteScope::Universe, kind: RouteType::Unicast, flags: RouteFlags::empty(), }, attributes: vec![ RouteAttribute::Destination(RouteAddress::Mpls(MplsLabel { label: 300, traffic_class: 0, bottom_of_stack: true, ttl: 0, })), RouteAttribute::Via( Ipv6Addr::from_str("2001:db8:1::2").unwrap().into(), ), RouteAttribute::Oif(7), ], }; assert_eq!( expected, RouteMessage::parse(&RouteMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } // Setup: // ip link add dummy1 type dummy // ip link set dummy1 up // ip addr add 2001:db8:1::1/64 dev dummy1 // modprobe mpls_iptunnel // sysctl -w net.mpls.platform_labels=65535 // ip -f mpls route add 100 as 200 via inet6 2001:db8:1::2 dev dummy1 // wireshark capture(netlink message header removed) of nlmon against command: // ip -f mpls route show dev dummy1 #[test] fn test_mpls_route_relable_new_dst() { let raw = vec![ 0x1c, 0x14, 0x00, 0x00, 0xfe, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x06, 0x41, 0x00, 0x08, 0x00, 0x13, 0x00, 0x00, 0x0c, 0x81, 0x00, 0x16, 0x00, 0x12, 0x00, 0x0a, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, ]; let expected = RouteMessage { header: RouteHeader { address_family: AddressFamily::Mpls, destination_prefix_length: 20, source_prefix_length: 0, tos: 0, table: 254, protocol: RouteProtocol::Boot, scope: RouteScope::Universe, kind: RouteType::Unicast, flags: RouteFlags::empty(), }, attributes: vec![ RouteAttribute::Destination(RouteAddress::Mpls(MplsLabel { label: 100, traffic_class: 0, bottom_of_stack: true, ttl: 0, })), RouteAttribute::NewDestination(vec![MplsLabel { label: 200, traffic_class: 0, bottom_of_stack: true, ttl: 0, }]), RouteAttribute::Via( Ipv6Addr::from_str("2001:db8:1::2").unwrap().into(), ), RouteAttribute::Oif(9), ], }; assert_eq!( expected, RouteMessage::parse(&RouteMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } // Setup: // ip link add dummy1 type dummy // ip link set dummy1 up // ip addr add 2001:db8:1::1/64 dev dummy1 // modprobe mpls_iptunnel // sysctl -w net.mpls.platform_labels=65535 // ip -f mpls route add 100 ttl-propagate enabled as 200 \ // via inet6 2001:db8:1::2 dev dummy1 // wireshark capture(netlink message header removed) of nlmon against command: // ip -6 route show dev dummy1 #[test] fn test_mpls_ttl_propagate() { let raw = vec![ 0x1c, 0x14, 0x00, 0x00, 0xfe, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x06, 0x41, 0x00, 0x05, 0x00, 0x1a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x13, 0x00, 0x00, 0x0c, 0x81, 0x00, 0x16, 0x00, 0x12, 0x00, 0x0a, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, ]; let expected = RouteMessage { header: RouteHeader { address_family: AddressFamily::Mpls, destination_prefix_length: 20, source_prefix_length: 0, tos: 0, table: 254, protocol: RouteProtocol::Boot, scope: RouteScope::Universe, kind: RouteType::Unicast, flags: RouteFlags::empty(), }, attributes: vec![ RouteAttribute::Destination(RouteAddress::Mpls(MplsLabel { label: 100, traffic_class: 0, bottom_of_stack: true, ttl: 0, })), RouteAttribute::TtlPropagate(RouteMplsTtlPropagation::Enabled), RouteAttribute::NewDestination(vec![MplsLabel { label: 200, traffic_class: 0, bottom_of_stack: true, ttl: 0, }]), RouteAttribute::Via( Ipv6Addr::from_str("2001:db8:1::2").unwrap().into(), ), RouteAttribute::Oif(8), ], }; assert_eq!( expected, RouteMessage::parse(&RouteMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/route/tests/multipath.rs000064400000000000000000000000401046102023000214070ustar 00000000000000// SPDX-License-Identifier: MIT netlink-packet-route-0.21.0/src/route/tests/realm.rs000064400000000000000000000052541046102023000205140ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::Ipv4Addr; use std::str::FromStr; use netlink_packet_utils::traits::{Emitable, Parseable}; use crate::route::flags::RouteFlags; use crate::route::{ RouteAttribute, RouteCacheInfo, RouteHeader, RouteMessage, RouteMessageBuffer, RouteProtocol, RouteRealm, RouteScope, RouteType, }; use crate::AddressFamily; // Setup // ip route add 192.0.2.1 dev lo realm 250/254 // wireshark capture(netlink message header removed) of nlmon against command: // ip route get 192.0.2.1 #[test] fn test_ipv4_route_realm() { let raw = vec![ 0x02, 0x20, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, 0x08, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0xc0, 0x00, 0x02, 0x01, 0x08, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0b, 0x00, 0xfe, 0x00, 0xfa, 0x00, 0x08, 0x00, 0x07, 0x00, 0xac, 0x11, 0x02, 0x0c, 0x08, 0x00, 0x19, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x24, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let expected = RouteMessage { header: RouteHeader { address_family: AddressFamily::Inet, destination_prefix_length: 32, source_prefix_length: 0, tos: 0, table: 254, protocol: RouteProtocol::Unspec, scope: RouteScope::Universe, kind: RouteType::Unicast, flags: RouteFlags::Cloned | RouteFlags::from_bits_retain(0x80000000), }, attributes: vec![ RouteAttribute::Table(254), RouteAttribute::Destination( Ipv4Addr::from_str("192.0.2.1").unwrap().into(), ), RouteAttribute::Oif(1), RouteAttribute::Realm(RouteRealm { source: 250, destination: 254, }), RouteAttribute::PrefSource( Ipv4Addr::from_str("172.17.2.12").unwrap().into(), ), RouteAttribute::Uid(1000), RouteAttribute::CacheInfo(RouteCacheInfo { clntref: 2, last_use: 0, expires: 0, error: 0, used: 0, id: 0, ts: 0, ts_age: 0, }), ], }; assert_eq!( expected, RouteMessage::parse(&RouteMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/route/tests/route_flags.rs000064400000000000000000000041441046102023000217230ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::Ipv6Addr; use std::str::FromStr; use netlink_packet_utils::traits::{Emitable, Parseable}; use crate::route::flags::RouteFlags; use crate::route::{ RouteAttribute, RouteHeader, RouteMessage, RouteMessageBuffer, RouteNextHopBuffer, RouteProtocol, RouteScope, RouteType, }; use crate::AddressFamily; // wireshark capture(netlink message header removed) of nlmon against command: // ip route add 2001:db8:1::/64 dev lo onlink #[test] fn test_ipv6_add_route_onlink() { let raw = vec![ 0x0a, 0x40, 0x00, 0x00, 0xfe, 0x03, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, ]; let expected = RouteMessage { header: RouteHeader { address_family: AddressFamily::Inet6, destination_prefix_length: 64, source_prefix_length: 0, tos: 0, table: 254, protocol: RouteProtocol::Boot, scope: RouteScope::Universe, kind: RouteType::Unicast, flags: RouteFlags::Onlink, }, attributes: vec![ RouteAttribute::Destination( Ipv6Addr::from_str("2001:db8:1::").unwrap().into(), ), RouteAttribute::Oif(1), ], }; assert_eq!( expected, RouteMessage::parse(&RouteMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } // Verify that [`RouteNextHopBuffer`] rejects the buffer when provided with // an invalid length. #[test] fn test_next_hop_max_buffer_len() { // Route next-hop buffer layout: // |byte0|byte1|byte2|byte3|byte4|byte5|byte6|byte7|bytes8+| // |-----|-----|-----|-----|-----|-----|-----|-----|-------| // | length |flags|hops | Interface Index |Payload| let buffer = [0xff, 0xff, 0, 0, 0, 0, 0, 0]; assert!(RouteNextHopBuffer::new_checked(buffer).is_err()); } netlink-packet-route-0.21.0/src/route/tests/uid.rs000064400000000000000000000050351046102023000201720ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::Ipv4Addr; use std::str::FromStr; use netlink_packet_utils::traits::{Emitable, Parseable}; use crate::route::flags::RouteFlags; use crate::route::{ RouteAttribute, RouteCacheInfo, RouteHeader, RouteMessage, RouteMessageBuffer, RouteProtocol, RouteScope, RouteType, }; use crate::AddressFamily; // wireshark capture(netlink message header removed) of nlmon against command: // ip route get 127.0.0.1 #[test] fn test_ipv4_route_uid() { let raw = vec![ 0x02, 0x20, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x80, 0x08, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x7f, 0x00, 0x00, 0x01, 0x08, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x00, 0x01, 0x08, 0x00, 0x19, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x24, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x52, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let expected = RouteMessage { header: RouteHeader { address_family: AddressFamily::Inet, destination_prefix_length: 32, source_prefix_length: 0, tos: 0, table: 254, protocol: RouteProtocol::Unspec, scope: RouteScope::Universe, kind: RouteType::Local, flags: RouteFlags::Cloned | // TODO(Gris Ge): Waiting reply from kernel team for this // value. RouteFlags::from_bits_retain(0x80000000), }, attributes: vec![ RouteAttribute::Table(254), RouteAttribute::Destination( Ipv4Addr::from_str("127.0.0.1").unwrap().into(), ), RouteAttribute::Oif(1), RouteAttribute::PrefSource( Ipv4Addr::from_str("127.0.0.1").unwrap().into(), ), RouteAttribute::Uid(1000), RouteAttribute::CacheInfo(RouteCacheInfo { clntref: 2, last_use: 39762, expires: 0, error: 0, used: 0, id: 0, ts: 0, ts_age: 0, }), ], }; assert_eq!( expected, RouteMessage::parse(&RouteMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/route/tests/via.rs000064400000000000000000000037151046102023000201730ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::{Ipv4Addr, Ipv6Addr}; use std::str::FromStr; use netlink_packet_utils::traits::{Emitable, Parseable}; use crate::route::flags::RouteFlags; use crate::route::{ RouteAttribute, RouteHeader, RouteMessage, RouteMessageBuffer, RouteProtocol, RouteScope, RouteType, RouteVia, }; use crate::AddressFamily; // Setup: // ip route add 192.0.2.1 via inet6 2001:db8:1:: dev lo // wireshark capture(netlink message header removed) of nlmon against command: // ip route show dev lo #[test] fn test_ipv4_route_via() { let raw = vec![ 0x02, 0x20, 0x00, 0x00, 0xfe, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0xc0, 0x00, 0x02, 0x01, 0x16, 0x00, 0x12, 0x00, 0x0a, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, ]; let expected = RouteMessage { header: RouteHeader { address_family: AddressFamily::Inet, destination_prefix_length: 32, source_prefix_length: 0, tos: 0, table: 254, protocol: RouteProtocol::Boot, scope: RouteScope::Universe, kind: RouteType::Unicast, flags: RouteFlags::empty(), }, attributes: vec![ RouteAttribute::Table(254), RouteAttribute::Destination( Ipv4Addr::from_str("192.0.2.1").unwrap().into(), ), RouteAttribute::Via(RouteVia::Inet6( Ipv6Addr::from_str("2001:db8:1::").unwrap(), )), RouteAttribute::Oif(1), ], }; assert_eq!( expected, RouteMessage::parse(&RouteMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/route/via.rs000064400000000000000000000053001046102023000170210ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::{Ipv4Addr, Ipv6Addr}; use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; use crate::ip::{ parse_ipv4_addr, parse_ipv6_addr, IPV4_ADDR_LEN, IPV6_ADDR_LEN, }; use crate::AddressFamily; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] // Kernel representative is `struct rtvia` // In Linux kernel 6.18, MPLS route also use `AF_PACKET` // and MPLS route. Even the MPLS is using AF_PACKET, so we cannot simply // treat `RouteVia` as `IpAddr`. pub enum RouteVia { Inet(Ipv4Addr), Inet6(Ipv6Addr), #[cfg(any(target_os = "linux", target_os = "fuchsia"))] Packet(Vec), Other((AddressFamily, Vec)), } const RTVIA_LEN: usize = 2; buffer!(RouteViaBuffer(RTVIA_LEN) { address_family: (u16, 0..2), address: (slice, RTVIA_LEN..), }); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for RouteVia { fn parse(buf: &RouteViaBuffer<&'a T>) -> Result { let address_family: AddressFamily = (buf.address_family() as u8).into(); Ok(match address_family { AddressFamily::Inet => Self::Inet(parse_ipv4_addr(buf.address())?), AddressFamily::Inet6 => { Self::Inet6(parse_ipv6_addr(buf.address())?) } #[cfg(any(target_os = "linux", target_os = "fuchsia"))] AddressFamily::Packet => Self::Packet(buf.address().to_vec()), _ => Self::Other((address_family, buf.address().to_vec())), }) } } impl Emitable for RouteVia { fn buffer_len(&self) -> usize { match self { Self::Inet(_) => IPV4_ADDR_LEN + 2, Self::Inet6(_) => IPV6_ADDR_LEN + 2, #[cfg(any(target_os = "linux", target_os = "fuchsia"))] Self::Packet(a) => a.len() + 2, Self::Other((_, a)) => a.len() + 2, } } fn emit(&self, buffer: &mut [u8]) { let mut buffer = RouteViaBuffer::new(buffer); let (address_family, addr) = match self { Self::Inet(ip) => (AddressFamily::Inet, ip.octets().to_vec()), Self::Inet6(ip) => (AddressFamily::Inet6, ip.octets().to_vec()), #[cfg(any(target_os = "linux", target_os = "fuchsia"))] Self::Packet(a) => (AddressFamily::Packet, a.to_vec()), Self::Other((f, a)) => (*f, a.to_vec()), }; buffer.set_address_family(u8::from(address_family).into()); buffer.address_mut().copy_from_slice(addr.as_slice()); } } impl From for RouteVia { fn from(v: Ipv4Addr) -> Self { Self::Inet(v) } } impl From for RouteVia { fn from(v: Ipv6Addr) -> Self { Self::Inet6(v) } } netlink-packet-route-0.21.0/src/rule/action.rs000064400000000000000000000026601046102023000173360ustar 00000000000000// SPDX-License-Identifier: MIT const FR_ACT_UNSPEC: u8 = 0; const FR_ACT_TO_TBL: u8 = 1; const FR_ACT_GOTO: u8 = 2; const FR_ACT_NOP: u8 = 3; // const FR_ACT_RES3: u8 = 4; // const FR_ACT_RES4: u8 = 5; const FR_ACT_BLACKHOLE: u8 = 6; const FR_ACT_UNREACHABLE: u8 = 7; const FR_ACT_PROHIBIT: u8 = 8; #[derive(Eq, PartialEq, Debug, Clone, Copy, Default)] #[non_exhaustive] pub enum RuleAction { #[default] Unspec, ToTable, Goto, Nop, Blackhole, Unreachable, Prohibit, Other(u8), } impl From for RuleAction { fn from(d: u8) -> Self { match d { FR_ACT_UNSPEC => Self::Unspec, FR_ACT_TO_TBL => Self::ToTable, FR_ACT_GOTO => Self::Goto, FR_ACT_NOP => Self::Nop, FR_ACT_BLACKHOLE => Self::Blackhole, FR_ACT_UNREACHABLE => Self::Unreachable, FR_ACT_PROHIBIT => Self::Prohibit, _ => Self::Other(d), } } } impl From for u8 { fn from(v: RuleAction) -> u8 { match v { RuleAction::Unspec => FR_ACT_UNSPEC, RuleAction::ToTable => FR_ACT_TO_TBL, RuleAction::Goto => FR_ACT_GOTO, RuleAction::Nop => FR_ACT_NOP, RuleAction::Blackhole => FR_ACT_BLACKHOLE, RuleAction::Unreachable => FR_ACT_UNREACHABLE, RuleAction::Prohibit => FR_ACT_PROHIBIT, RuleAction::Other(d) => d, } } } netlink-packet-route-0.21.0/src/rule/attribute.rs000064400000000000000000000177331046102023000200730ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::IpAddr; use anyhow::Context; use netlink_packet_utils::{ byteorder::{ByteOrder, NativeEndian}, nla::{DefaultNla, Nla, NlaBuffer}, parsers::{parse_string, parse_u32, parse_u8}, DecodeError, Emitable, Parseable, }; use crate::{ ip::{emit_ip_addr, ip_addr_len, parse_ip_addr, IpProtocol}, route::{RouteProtocol, RouteRealm}, rule::{RulePortRange, RuleUidRange}, }; const FRA_DST: u16 = 1; const FRA_SRC: u16 = 2; const FRA_IIFNAME: u16 = 3; const FRA_GOTO: u16 = 4; // const FRA_UNUSED2: u16 = 5; const FRA_PRIORITY: u16 = 6; // const FRA_UNUSED3: u16 = 7; // const FRA_UNUSED4: u16 = 8; // const FRA_UNUSED5: u16 = 9; const FRA_FWMARK: u16 = 10; const FRA_FLOW: u16 = 11; const FRA_TUN_ID: u16 = 12; const FRA_SUPPRESS_IFGROUP: u16 = 13; const FRA_SUPPRESS_PREFIXLEN: u16 = 14; const FRA_TABLE: u16 = 15; const FRA_FWMASK: u16 = 16; const FRA_OIFNAME: u16 = 17; // const FRA_PAD: u16 = 18; const FRA_L3MDEV: u16 = 19; const FRA_UID_RANGE: u16 = 20; const FRA_PROTOCOL: u16 = 21; const FRA_IP_PROTO: u16 = 22; const FRA_SPORT_RANGE: u16 = 23; const FRA_DPORT_RANGE: u16 = 24; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum RuleAttribute { /// destination address Destination(IpAddr), /// source address Source(IpAddr), /// input interface name Iifname(String), /// The priority number of another rule for [super::RuleAction::Goto] Goto(u32), Priority(u32), FwMark(u32), FwMask(u32), /// IPv4 route realm Realm(RouteRealm), TunId(u32), SuppressIfGroup(u32), SuppressPrefixLen(u32), Table(u32), /// output interface name Oifname(String), L3MDev(bool), UidRange(RuleUidRange), Protocol(RouteProtocol), IpProtocol(IpProtocol), SourcePortRange(RulePortRange), DestinationPortRange(RulePortRange), Other(DefaultNla), } impl Nla for RuleAttribute { fn value_len(&self) -> usize { match self { Self::Destination(ip) | Self::Source(ip) => ip_addr_len(ip), Self::UidRange(v) => v.buffer_len(), Self::SourcePortRange(v) | Self::DestinationPortRange(v) => { v.buffer_len() } Self::Iifname(s) | Self::Oifname(s) => s.as_bytes().len() + 1, Self::Priority(_) | Self::FwMark(_) | Self::FwMask(_) | Self::TunId(_) | Self::Goto(_) | Self::SuppressIfGroup(_) | Self::SuppressPrefixLen(_) | Self::Table(_) => 4, Self::Realm(v) => v.buffer_len(), Self::L3MDev(_) | Self::Protocol(_) | Self::IpProtocol(_) => 1, Self::Other(attr) => attr.value_len(), } } fn kind(&self) -> u16 { match self { Self::Destination(_) => FRA_DST, Self::Source(_) => FRA_SRC, Self::Iifname(_) => FRA_IIFNAME, Self::Goto(_) => FRA_GOTO, Self::Priority(_) => FRA_PRIORITY, Self::FwMark(_) => FRA_FWMARK, Self::FwMask(_) => FRA_FWMASK, Self::Realm(_) => FRA_FLOW, Self::TunId(_) => FRA_TUN_ID, Self::SuppressIfGroup(_) => FRA_SUPPRESS_IFGROUP, Self::SuppressPrefixLen(_) => FRA_SUPPRESS_PREFIXLEN, Self::Table(_) => FRA_TABLE, Self::Oifname(_) => FRA_OIFNAME, Self::L3MDev(_) => FRA_L3MDEV, Self::UidRange(_) => FRA_UID_RANGE, Self::Protocol(_) => FRA_PROTOCOL, Self::IpProtocol(_) => FRA_IP_PROTO, Self::SourcePortRange(_) => FRA_SPORT_RANGE, Self::DestinationPortRange(_) => FRA_DPORT_RANGE, Self::Other(attr) => attr.kind(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Destination(ip) | Self::Source(ip) => { emit_ip_addr(ip, buffer) } Self::SourcePortRange(v) | Self::DestinationPortRange(v) => { v.emit(buffer) } Self::UidRange(v) => v.emit(buffer), Self::Iifname(s) | Self::Oifname(s) => { buffer[..s.len()].copy_from_slice(s.as_bytes()) } Self::Realm(v) => v.emit(buffer), Self::Priority(value) | Self::FwMark(value) | Self::FwMask(value) | Self::TunId(value) | Self::Goto(value) | Self::SuppressIfGroup(value) | Self::SuppressPrefixLen(value) | Self::Table(value) => NativeEndian::write_u32(buffer, *value), Self::L3MDev(value) => buffer[0] = (*value).into(), Self::IpProtocol(value) => buffer[0] = i32::from(*value) as u8, Self::Protocol(value) => buffer[0] = u8::from(*value), Self::Other(attr) => attr.emit_value(buffer), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for RuleAttribute { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { FRA_DST => Self::Destination( parse_ip_addr(payload) .context(format!("Invalid FRA_DST value {payload:?}"))?, ), FRA_SRC => Self::Source( parse_ip_addr(payload) .context(format!("Invalid FRA_SRC value {payload:?}"))?, ), FRA_IIFNAME => Self::Iifname( parse_string(payload).context("invalid FRA_IIFNAME value")?, ), FRA_GOTO => Self::Goto( parse_u32(payload).context("invalid FRA_GOTO value")?, ), FRA_PRIORITY => Self::Priority( parse_u32(payload).context("invalid FRA_PRIORITY value")?, ), FRA_FWMARK => Self::FwMark( parse_u32(payload).context("invalid FRA_FWMARK value")?, ), FRA_FLOW => Self::Realm( RouteRealm::parse(payload).context("invalid FRA_FLOW value")?, ), FRA_TUN_ID => Self::TunId( parse_u32(payload).context("invalid FRA_TUN_ID value")?, ), FRA_SUPPRESS_IFGROUP => Self::SuppressIfGroup( parse_u32(payload) .context("invalid FRA_SUPPRESS_IFGROUP value")?, ), FRA_SUPPRESS_PREFIXLEN => Self::SuppressPrefixLen( parse_u32(payload) .context("invalid FRA_SUPPRESS_PREFIXLEN value")?, ), FRA_TABLE => Self::Table( parse_u32(payload).context("invalid FRA_TABLE value")?, ), FRA_FWMASK => Self::FwMask( parse_u32(payload).context("invalid FRA_FWMASK value")?, ), FRA_OIFNAME => Self::Oifname( parse_string(payload).context("invalid FRA_OIFNAME value")?, ), FRA_L3MDEV => Self::L3MDev( parse_u8(payload).context("invalid FRA_L3MDEV value")? > 0, ), FRA_UID_RANGE => Self::UidRange( RuleUidRange::parse(payload) .context("invalid FRA_UID_RANGE value")?, ), FRA_PROTOCOL => Self::Protocol( parse_u8(payload) .context("invalid FRA_PROTOCOL value")? .into(), ), FRA_IP_PROTO => Self::IpProtocol(IpProtocol::from( parse_u8(payload).context("invalid FRA_IP_PROTO value")? as i32, )), FRA_SPORT_RANGE => Self::SourcePortRange( RulePortRange::parse(payload) .context("invalid FRA_SPORT_RANGE value")?, ), FRA_DPORT_RANGE => Self::DestinationPortRange( RulePortRange::parse(payload) .context("invalid FRA_DPORT_RANGE value")?, ), _ => Self::Other( DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, ), }) } } netlink-packet-route-0.21.0/src/rule/flags.rs000064400000000000000000000013651046102023000171560ustar 00000000000000// SPDX-License-Identifier: MIT const FIB_RULE_PERMANENT: u32 = 0x00000001; const FIB_RULE_INVERT: u32 = 0x00000002; const FIB_RULE_UNRESOLVED: u32 = 0x00000004; const FIB_RULE_IIF_DETACHED: u32 = 0x00000008; const FIB_RULE_DEV_DETACHED: u32 = FIB_RULE_IIF_DETACHED; const FIB_RULE_OIF_DETACHED: u32 = 0x00000010; bitflags! { #[derive(Clone, Eq, PartialEq, Debug, Copy, Default)] #[non_exhaustive] pub struct RuleFlags: u32 { const Permanent = FIB_RULE_PERMANENT; const Invert = FIB_RULE_INVERT; const Unresolved = FIB_RULE_UNRESOLVED; const IifDetached = FIB_RULE_IIF_DETACHED; const DevDetached = FIB_RULE_DEV_DETACHED; const OifDetached = FIB_RULE_OIF_DETACHED; const _ = !0; } } netlink-packet-route-0.21.0/src/rule/header.rs000064400000000000000000000037751046102023000173210ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::{NlaBuffer, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; use super::{super::AddressFamily, flags::RuleFlags, RuleAction}; 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 attributes( &self, ) -> impl Iterator, DecodeError>> { NlasIterator::new(self.payload()) } } // Linux kernel code `struct fib_rule_hdr` #[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct RuleHeader { pub family: AddressFamily, pub dst_len: u8, pub src_len: u8, pub tos: u8, pub table: u8, pub action: RuleAction, pub flags: RuleFlags, } 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.into()); packet.set_dst_len(self.dst_len); packet.set_src_len(self.src_len); packet.set_table(self.table); packet.set_tos(self.tos); packet.set_action(self.action.into()); packet.set_flags(self.flags.bits()); } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for RuleHeader { fn parse(buf: &RuleMessageBuffer<&'a T>) -> Result { Ok(RuleHeader { family: buf.family().into(), dst_len: buf.dst_len(), src_len: buf.src_len(), tos: buf.tos(), table: buf.table(), action: buf.action().into(), flags: RuleFlags::from_bits_retain(buf.flags()), }) } } netlink-packet-route-0.21.0/src/rule/message.rs000064400000000000000000000027331046102023000175060ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; use super::{RuleAttribute, RuleHeader, RuleMessageBuffer}; #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct RuleMessage { pub header: RuleHeader, pub attributes: Vec, } impl Emitable for RuleMessage { fn buffer_len(&self) -> usize { self.header.buffer_len() + self.attributes.as_slice().buffer_len() } fn emit(&self, buffer: &mut [u8]) { self.header.emit(buffer); self.attributes .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 attributes = Vec::::parse(buf) .context("failed to parse link message NLAs")?; Ok(RuleMessage { header, attributes }) } } impl<'a, T: AsRef<[u8]> + 'a> Parseable> for Vec { fn parse(buf: &RuleMessageBuffer<&'a T>) -> Result { let mut attributes = vec![]; for nla_buf in buf.attributes() { attributes.push(RuleAttribute::parse(&nla_buf?)?); } Ok(attributes) } } netlink-packet-route-0.21.0/src/rule/mod.rs000064400000000000000000000006551046102023000166420ustar 00000000000000// SPDX-License-Identifier: MIT mod action; mod attribute; mod flags; mod header; mod message; mod port_range; #[cfg(test)] mod tests; mod uid_range; pub use self::action::RuleAction; pub use self::attribute::RuleAttribute; pub use self::flags::RuleFlags; pub use self::header::{RuleHeader, RuleMessageBuffer}; pub use self::message::RuleMessage; pub use self::port_range::RulePortRange; pub use self::uid_range::RuleUidRange; netlink-packet-route-0.21.0/src/rule/port_range.rs000064400000000000000000000020261046102023000202150ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{DecodeError, Emitable}; const RULE_PORT_RANGE_LEN: usize = 4; #[derive(Clone, Eq, PartialEq, Debug, Copy)] pub struct RulePortRange { pub start: u16, pub end: u16, } impl RulePortRange { pub(crate) fn parse(buf: &[u8]) -> Result { if buf.len() == RULE_PORT_RANGE_LEN { Ok(Self { start: u16::from_ne_bytes([buf[0], buf[1]]), end: u16::from_ne_bytes([buf[2], buf[3]]), }) } else { Err(DecodeError::from(format!( "Invalid rule port range data, expecting \ {RULE_PORT_RANGE_LEN} u8 array, but got {:?}", buf ))) } } } impl Emitable for RulePortRange { fn buffer_len(&self) -> usize { RULE_PORT_RANGE_LEN } fn emit(&self, buffer: &mut [u8]) { buffer[0..2].copy_from_slice(&self.start.to_ne_bytes()); buffer[2..4].copy_from_slice(&self.end.to_ne_bytes()); } } netlink-packet-route-0.21.0/src/rule/tests/fw_mark.rs000064400000000000000000000064761046102023000206620ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::{ route::RouteProtocol, rule::{ flags::RuleFlags, RuleAction, RuleAttribute, RuleHeader, RuleMessage, RuleMessageBuffer, }, AddressFamily, }; // Setup: // ip rule add priority 1001 fwmark 0x20 suppress_prefixlength 8 // wireshark capture(netlink message header removed) of nlmon against command: // ip -4 rule show priority 1001 #[test] fn test_ipv4_fwmark_suppress_prefixlength() { let raw = vec![ 0x02, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x06, 0x00, 0xe9, 0x03, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x10, 0x00, 0xff, 0xff, 0xff, 0xff, ]; let expected = RuleMessage { header: RuleHeader { family: AddressFamily::Inet, dst_len: 0, src_len: 0, tos: 0, table: 254, action: RuleAction::ToTable, flags: RuleFlags::empty(), }, attributes: vec![ RuleAttribute::Table(254), RuleAttribute::SuppressPrefixLen(8), RuleAttribute::Protocol(RouteProtocol::Unspec), RuleAttribute::Priority(1001), RuleAttribute::FwMark(0x20), RuleAttribute::FwMask(0xffffffff), ], }; assert_eq!( expected, RuleMessage::parse(&RuleMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } // Setup: // ip -6 rule add priority 1002 fwmark 0x20 suppress_ifgroup 89 // wireshark capture(netlink message header removed) of nlmon against command: // ip -6 rule show priority 1002 #[test] fn test_ipv6_fwmark_suppress_ifgroup() { let raw = vec![ 0x0a, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x00, 0xff, 0xff, 0xff, 0xff, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x06, 0x00, 0xea, 0x03, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x10, 0x00, 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x0d, 0x00, 0x59, 0x00, 0x00, 0x00, ]; let expected = RuleMessage { header: RuleHeader { family: AddressFamily::Inet6, dst_len: 0, src_len: 0, tos: 0, table: 254, action: RuleAction::ToTable, flags: RuleFlags::empty(), }, attributes: vec![ RuleAttribute::Table(254), RuleAttribute::SuppressPrefixLen(0xffffffff), RuleAttribute::Protocol(RouteProtocol::Unspec), RuleAttribute::Priority(1002), RuleAttribute::FwMark(0x20), RuleAttribute::FwMask(0xffffffff), RuleAttribute::SuppressIfGroup(89), ], }; assert_eq!( expected, RuleMessage::parse(&RuleMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/rule/tests/iif_oif.rs000064400000000000000000000075041046102023000206310ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::Ipv4Addr; use std::str::FromStr; use netlink_packet_utils::{Emitable, Parseable}; use crate::{ route::RouteProtocol, rule::{ flags::RuleFlags, RuleAction, RuleAttribute, RuleHeader, RuleMessage, RuleMessageBuffer, }, AddressFamily, IpProtocol, }; // Setup: // ip rule add priority 9000 from 192.0.2.1 to 203.0.113.1 \ // iif lo oif lo protocol dhcp prohibit // wireshark capture(netlink message header removed) of nlmon against command: // ip -4 rule show priority 9000 #[test] fn test_ipv4_iif_oif_prohibit() { let raw = vec![ 0x02, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x00, 0xff, 0xff, 0xff, 0xff, 0x05, 0x00, 0x15, 0x00, 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x03, 0x00, 0x6c, 0x6f, 0x00, 0x00, 0x07, 0x00, 0x11, 0x00, 0x6c, 0x6f, 0x00, 0x00, 0x08, 0x00, 0x06, 0x00, 0x28, 0x23, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0xcb, 0x00, 0x71, 0x01, 0x08, 0x00, 0x02, 0x00, 0xc0, 0x00, 0x02, 0x01, ]; let expected = RuleMessage { header: RuleHeader { family: AddressFamily::Inet, dst_len: 32, src_len: 32, tos: 0, table: 0, action: RuleAction::Prohibit, flags: RuleFlags::empty(), }, attributes: vec![ RuleAttribute::Table(0), RuleAttribute::SuppressPrefixLen(0xffffffff), RuleAttribute::Protocol(RouteProtocol::Dhcp), RuleAttribute::Iifname("lo".to_string()), RuleAttribute::Oifname("lo".to_string()), RuleAttribute::Priority(9000), RuleAttribute::Destination( Ipv4Addr::from_str("203.0.113.1").unwrap().into(), ), RuleAttribute::Source( Ipv4Addr::from_str("192.0.2.1").unwrap().into(), ), ], }; assert_eq!( expected, RuleMessage::parse(&RuleMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } // Setup: // ip -6 rule add priority 9001 iif lo oif lo ipproto icmp \ // protocol bgp table 500 // wireshark capture(netlink message header removed) of nlmon against command: // ip -6 rule show priority 9000 #[test] fn test_ipv6_iif_oif_ipproto() { let raw = vec![ 0x0a, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0xf4, 0x01, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x00, 0xff, 0xff, 0xff, 0xff, 0x05, 0x00, 0x15, 0x00, 0xba, 0x00, 0x00, 0x00, 0x07, 0x00, 0x03, 0x00, 0x6c, 0x6f, 0x00, 0x00, 0x07, 0x00, 0x11, 0x00, 0x6c, 0x6f, 0x00, 0x00, 0x08, 0x00, 0x06, 0x00, 0x29, 0x23, 0x00, 0x00, 0x05, 0x00, 0x16, 0x00, 0x01, 0x00, 0x00, 0x00, ]; let expected = RuleMessage { header: RuleHeader { family: AddressFamily::Inet6, dst_len: 0, src_len: 0, tos: 0, table: 252, action: RuleAction::ToTable, flags: RuleFlags::empty(), }, attributes: vec![ RuleAttribute::Table(500), RuleAttribute::SuppressPrefixLen(0xffffffff), RuleAttribute::Protocol(RouteProtocol::Bgp), RuleAttribute::Iifname("lo".to_string()), RuleAttribute::Oifname("lo".to_string()), RuleAttribute::Priority(9001), RuleAttribute::IpProtocol(IpProtocol::Icmp), ], }; assert_eq!( expected, RuleMessage::parse(&RuleMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/rule/tests/l3mdev.rs000064400000000000000000000062541046102023000204200ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::{ route::RouteProtocol, rule::{ flags::RuleFlags, RuleAction, RuleAttribute, RuleHeader, RuleMessage, RuleMessageBuffer, RuleUidRange, }, AddressFamily, }; // Setup: // ip rule add l3mdev priority 1999 // wireshark capture(netlink message header removed) of nlmon against command: // ip -4 rule show priority 1999 #[test] fn test_ipv4_l3mdev() { let raw = vec![ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x00, 0xff, 0xff, 0xff, 0xff, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x06, 0x00, 0xcf, 0x07, 0x00, 0x00, 0x05, 0x00, 0x13, 0x00, 0x01, 0x00, 0x00, 0x00, ]; let expected = RuleMessage { header: RuleHeader { family: AddressFamily::Inet, dst_len: 0, src_len: 0, tos: 0, table: 0, action: RuleAction::ToTable, flags: RuleFlags::empty(), }, attributes: vec![ RuleAttribute::Table(0), RuleAttribute::SuppressPrefixLen(0xffffffff), RuleAttribute::Protocol(RouteProtocol::Unspec), RuleAttribute::Priority(1999), RuleAttribute::L3MDev(true), ], }; assert_eq!( expected, RuleMessage::parse(&RuleMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } // Setup: // ip -6 rule add priority 2999 l3mdev uidrange 1000-1999 // wireshark capture(netlink message header removed) of nlmon against command: // ip -6 rule show priority 2999 #[test] fn test_ipv6_l3mdev_uid() { let raw = vec![ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x00, 0xff, 0xff, 0xff, 0xff, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x06, 0x00, 0xb7, 0x0b, 0x00, 0x00, 0x05, 0x00, 0x13, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0xe8, 0x03, 0x00, 0x00, 0xcf, 0x07, 0x00, 0x00, ]; let expected = RuleMessage { header: RuleHeader { family: AddressFamily::Inet6, dst_len: 0, src_len: 0, tos: 0, table: 0, action: RuleAction::ToTable, flags: RuleFlags::empty(), }, attributes: vec![ RuleAttribute::Table(0), RuleAttribute::SuppressPrefixLen(0xffffffff), RuleAttribute::Protocol(RouteProtocol::Unspec), RuleAttribute::Priority(2999), RuleAttribute::L3MDev(true), RuleAttribute::UidRange(RuleUidRange { start: 1000, end: 1999, }), ], }; assert_eq!( expected, RuleMessage::parse(&RuleMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/rule/tests/mod.rs000064400000000000000000000003061046102023000177750ustar 00000000000000// SPDX-License-Identifier: MIT #[cfg(test)] mod fw_mark; #[cfg(test)] mod iif_oif; #[cfg(test)] mod l3mdev; #[cfg(test)] mod on_boot_rules; #[cfg(test)] mod sport_dport; #[cfg(test)] mod src_dst; netlink-packet-route-0.21.0/src/rule/tests/on_boot_rules.rs000064400000000000000000000051651046102023000220770ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::{ route::RouteProtocol, rule::{ flags::RuleFlags, RuleAction, RuleAttribute, RuleHeader, RuleMessage, RuleMessageBuffer, }, AddressFamily, }; // wireshark capture(netlink message header removed) of nlmon against command: // ip -4 rule show #[test] fn test_ipv4_rule() { let raw = vec![ 0x02, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x00, 0xff, 0xff, 0xff, 0xff, 0x05, 0x00, 0x15, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x06, 0x00, 0xfe, 0x7f, 0x00, 0x00, ]; let expected = RuleMessage { header: RuleHeader { family: AddressFamily::Inet, dst_len: 0, src_len: 0, tos: 0, table: 254, action: RuleAction::ToTable, flags: RuleFlags::empty(), }, attributes: vec![ RuleAttribute::Table(254), RuleAttribute::SuppressPrefixLen(0xffffffff), RuleAttribute::Protocol(RouteProtocol::Kernel), RuleAttribute::Priority(32766), ], }; assert_eq!( expected, RuleMessage::parse(&RuleMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } // wireshark capture(netlink message header removed) of nlmon against command: // ip -6 rule show #[test] fn test_ipv6_rule() { let raw = vec![ 0x0a, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x00, 0xff, 0xff, 0xff, 0xff, 0x05, 0x00, 0x15, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x06, 0x00, 0xfe, 0x7f, 0x00, 0x00, ]; let expected = RuleMessage { header: RuleHeader { family: AddressFamily::Inet6, dst_len: 0, src_len: 0, tos: 0, table: 254, action: RuleAction::ToTable, flags: RuleFlags::empty(), }, attributes: vec![ RuleAttribute::Table(254), RuleAttribute::SuppressPrefixLen(0xffffffff), RuleAttribute::Protocol(RouteProtocol::Kernel), RuleAttribute::Priority(32766), ], }; assert_eq!( expected, RuleMessage::parse(&RuleMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/rule/tests/sport_dport.rs000064400000000000000000000103501046102023000215750ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::{ route::{RouteProtocol, RouteRealm}, rule::{ flags::RuleFlags, RuleAction, RuleAttribute, RuleHeader, RuleMessage, RuleMessageBuffer, RulePortRange, }, AddressFamily, IpProtocol, }; // Setup: // ip rule add priority 1009 sport 80 dport 8080 ipproto tcp realms 199 // wireshark capture(netlink message header removed) of nlmon against command: // ip -4 rule show priority 1009 #[test] fn test_ipv4_tcp_sport_dport_realm() { let raw = vec![ 0x02, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x00, 0xff, 0xff, 0xff, 0xff, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x06, 0x00, 0xf1, 0x03, 0x00, 0x00, 0x08, 0x00, 0x17, 0x00, 0x50, 0x00, 0x50, 0x00, 0x08, 0x00, 0x18, 0x00, 0x90, 0x1f, 0x90, 0x1f, 0x05, 0x00, 0x16, 0x00, 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0b, 0x00, 0xc7, 0x00, 0x00, 0x00, ]; let expected = RuleMessage { header: RuleHeader { family: AddressFamily::Inet, dst_len: 0, src_len: 0, tos: 0, table: 254, action: RuleAction::ToTable, flags: RuleFlags::empty(), }, attributes: vec![ RuleAttribute::Table(254), RuleAttribute::SuppressPrefixLen(0xffffffff), RuleAttribute::Protocol(RouteProtocol::Unspec), RuleAttribute::Priority(1009), RuleAttribute::SourcePortRange(RulePortRange { start: 80, end: 80, }), RuleAttribute::DestinationPortRange(RulePortRange { start: 8080, end: 8080, }), RuleAttribute::IpProtocol(IpProtocol::Tcp), RuleAttribute::Realm(RouteRealm { source: 0, destination: 199, }), ], }; assert_eq!( expected, RuleMessage::parse(&RuleMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } // Setup: // ip -4 rule add priority 1020 sport 80-8080 dport 8080-9090 \ // ipproto udp realms 199/200 // wireshark capture(netlink message header removed) of nlmon against command: // ip -4 rule show priority 1020 #[test] fn test_ipv4_udp_sport_range_dport_range_reals_src_dst() { let raw = vec![ 0x02, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x00, 0xff, 0xff, 0xff, 0xff, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x06, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x08, 0x00, 0x17, 0x00, 0x50, 0x00, 0x90, 0x1f, 0x08, 0x00, 0x18, 0x00, 0x90, 0x1f, 0x82, 0x23, 0x05, 0x00, 0x16, 0x00, 0x11, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0b, 0x00, 0xc8, 0x00, 0xc7, 0x00, ]; let expected = RuleMessage { header: RuleHeader { family: AddressFamily::Inet, dst_len: 0, src_len: 0, tos: 0, table: 254, action: RuleAction::ToTable, flags: RuleFlags::empty(), }, attributes: vec![ RuleAttribute::Table(254), RuleAttribute::SuppressPrefixLen(0xffffffff), RuleAttribute::Protocol(RouteProtocol::Unspec), RuleAttribute::Priority(1020), RuleAttribute::SourcePortRange(RulePortRange { start: 80, end: 8080, }), RuleAttribute::DestinationPortRange(RulePortRange { start: 8080, end: 9090, }), RuleAttribute::IpProtocol(IpProtocol::Udp), RuleAttribute::Realm(RouteRealm { source: 199, destination: 200, }), ], }; assert_eq!( expected, RuleMessage::parse(&RuleMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/rule/tests/src_dst.rs000064400000000000000000000075371046102023000206740ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::{Ipv4Addr, Ipv6Addr}; use std::str::FromStr; use netlink_packet_utils::{Emitable, Parseable}; use crate::rule::flags::RuleFlags; use crate::{ route::RouteProtocol, rule::{ RuleAction, RuleAttribute, RuleHeader, RuleMessage, RuleMessageBuffer, }, AddressFamily, }; // Setup: // ip rule add priority 1000 from 192.0.2.1 to 203.0.113.1 blackhole // wireshark capture(netlink message header removed) of nlmon against command: // ip -4 rule show priority 1000 #[test] fn test_ipv4_src_dst_blackhole() { let raw = vec![ 0x02, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x00, 0xff, 0xff, 0xff, 0xff, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x06, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0xcb, 0x00, 0x71, 0x01, 0x08, 0x00, 0x02, 0x00, 0xc0, 0x00, 0x02, 0x01, ]; let expected = RuleMessage { header: RuleHeader { family: AddressFamily::Inet, dst_len: 32, src_len: 32, tos: 0, table: 0, action: RuleAction::Blackhole, flags: RuleFlags::empty(), }, attributes: vec![ RuleAttribute::Table(0), RuleAttribute::SuppressPrefixLen(0xffffffff), RuleAttribute::Protocol(RouteProtocol::Unspec), RuleAttribute::Priority(1000), RuleAttribute::Destination( Ipv4Addr::from_str("203.0.113.1").unwrap().into(), ), RuleAttribute::Source( Ipv4Addr::from_str("192.0.2.1").unwrap().into(), ), ], }; assert_eq!( expected, RuleMessage::parse(&RuleMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } // Setup: // ip -6 rule add priority 20000 from 2001:db8:1::1 to 2001:db8:2::1 \ // goto 32766 // wireshark capture(netlink message header removed) of nlmon against command: // ip -6 rule show priority 20000 #[test] fn test_ipv6_src_dst_goto() { let raw = vec![ 0x0a, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x00, 0xff, 0xff, 0xff, 0xff, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x06, 0x00, 0x20, 0x4e, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x02, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ]; let expected = RuleMessage { header: RuleHeader { family: AddressFamily::Inet6, dst_len: 128, src_len: 128, tos: 0, table: 0, action: RuleAction::Goto, flags: RuleFlags::empty(), }, attributes: vec![ RuleAttribute::Table(0), RuleAttribute::SuppressPrefixLen(0xffffffff), RuleAttribute::Protocol(RouteProtocol::Unspec), RuleAttribute::Priority(20000), RuleAttribute::Goto(32766), RuleAttribute::Destination( Ipv6Addr::from_str("2001:db8:2::1").unwrap().into(), ), RuleAttribute::Source( Ipv6Addr::from_str("2001:db8:1::1").unwrap().into(), ), ], }; assert_eq!( expected, RuleMessage::parse(&RuleMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/rule/uid_range.rs000064400000000000000000000020571046102023000200160ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{DecodeError, Emitable}; const RULE_UID_RANGE_LEN: usize = 8; #[derive(Clone, Eq, PartialEq, Debug, Copy)] pub struct RuleUidRange { pub start: u32, pub end: u32, } impl RuleUidRange { pub(crate) fn parse(buf: &[u8]) -> Result { if buf.len() == RULE_UID_RANGE_LEN { Ok(Self { start: u32::from_ne_bytes([buf[0], buf[1], buf[2], buf[3]]), end: u32::from_ne_bytes([buf[4], buf[5], buf[6], buf[7]]), }) } else { Err(DecodeError::from(format!( "Invalid rule port range data, expecting \ {RULE_UID_RANGE_LEN} u8 array, but got {:?}", buf ))) } } } impl Emitable for RuleUidRange { fn buffer_len(&self) -> usize { RULE_UID_RANGE_LEN } fn emit(&self, buffer: &mut [u8]) { buffer[0..4].copy_from_slice(&self.start.to_ne_bytes()); buffer[4..8].copy_from_slice(&self.end.to_ne_bytes()); } } netlink-packet-route-0.21.0/src/tc/actions/action.rs000064400000000000000000000451161046102023000204400ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::nla::NLA_F_NESTED; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, parsers::{parse_string, parse_u32}, traits::{Emitable, Parseable, ParseableParametrized}, DecodeError, }; use crate::tc::TcStats2; use super::{ TcActionMirror, TcActionMirrorOption, TcActionNat, TcActionNatOption, }; /// TODO: determine when and why to use this as opposed to the buffer's `kind`. const TCA_ACT_TAB: u16 = 1; /// [`TcAction`] is a netlink message attribute that describes a [tc-action]. /// /// [tc-action]: https://man7.org/linux/man-pages/man8/tc-actions.8.html #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct TcAction { /// Table id. /// Corresponds to the [`Kind`] of the action. /// /// [`Kind`]: crate::tc::TcActionAttribute::Kind pub tab: u16, /// Attributes of the action. pub attributes: Vec, } impl Default for TcAction { fn default() -> Self { Self { tab: TCA_ACT_TAB, attributes: Vec::new(), } } } impl Nla for TcAction { fn value_len(&self) -> usize { self.attributes.as_slice().buffer_len() } fn emit_value(&self, buffer: &mut [u8]) { self.attributes.as_slice().emit(buffer); } fn kind(&self) -> u16 { self.tab } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for TcAction { fn parse(buf: &NlaBuffer<&'a T>) -> Result { // We need to find the `Kind` attribute before we can parse the others, // as kind is used in calls to parse_with_param for the other // attributes. // Messages of this type which do not specify [`Kind`], or which specify // `Kind` more than once are malformed and should be rejected. // We cannot ensure that `Kind` will be the first attribute in the // `attributes` `Vec` (although it usually is). // As a result, we need to determine `Kind` first, then parse the rest // of the attributes. let kind = match NlasIterator::new(buf.value()) .filter_map(|nla| { let nla = match nla { Ok(nla) => nla, Err(e) => { return Some( Err(e).context("failed to parse action nla"), ) } }; match nla.kind() { TCA_ACT_KIND => Some( parse_string(nla.value()) .context("failed to parse TCA_ACT_KIND"), ), _ => None, } }) .collect::, _>>() { Ok(kinds) => { if kinds.is_empty() { return Err(DecodeError::from("Missing TCA_ACT_KIND")); } if kinds.len() > 1 { return Err(DecodeError::from("Duplicate TCA_ACT_KIND")); } kinds[0].clone() } Err(e) => return Err(DecodeError::from(e.to_string())), }; let attributes = NlasIterator::new(buf.value()) .map(|nla| { TcActionAttribute::parse_with_param(&nla?, kind.as_str()) }) .collect::, _>>()?; Ok(Self { tab: buf.kind(), attributes, }) } } const TCA_ACT_KIND: u16 = 1; const TCA_ACT_OPTIONS: u16 = 2; const TCA_ACT_INDEX: u16 = 3; const TCA_ACT_STATS: u16 = 4; // const TCA_ACT_PAD: u16 = 5; const TCA_ACT_COOKIE: u16 = 6; // const TCA_ACT_FLAGS: u16 = 7; // const TCA_ACT_HW_STATS: u16 = 8; // const TCA_ACT_USED_HW_STATS: u16 = 9; const TCA_ACT_IN_HW_COUNT: u16 = 10; /// Attributes of a traffic control action. #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum TcActionAttribute { /// The [`Kind`] (general type or class) of the action (e.g. "mirred", /// "nat"). /// /// [`Kind`]: #variant.Kind Kind(String), /// Parameters of the action. Options(Vec), /// Index of the action. /// /// This is used to identify the action in the kernel. /// Each action [`Kind`] has a unique table of actions. /// That is, each action [`Kind`] has its own set of [`Index`] values. /// /// If [`Index`] is zero on action creation, /// the kernel will assign a unique index to the new action. /// The combination of [`Kind`] and [`Index`] can then be used to identify /// and interact with the action in the future. /// /// For example, one action can be used by multiple different filters by /// referencing the action's [`Index`] when creating that filter. /// Such multiply referenced actions will aggregate their statistics. /// /// The kernel will reject attempts to delete an action if it is in use by /// a filter. /// Remove all referencing filters before deleting the action. /// /// [`Kind`]: #variant.Kind /// [`Index`]: #variant.Index Index(u32), /// Statistics about the action (e.g., number of bytes and or packets /// processed). Stats(Vec), /// [`Cookie`] is an attribute which _is not interpreted by the kernel at /// all_ and may be used to store up to 16 bytes of arbitrary data on /// an action in the kernel. /// Userspace processes may then use this data to store additional /// information about the action or to correlate actions with other /// data. /// /// [`Cookie`]: #variant.Cookie Cookie(Vec), /// Number of times the action has been installed in hardware. InHwCount(u32), /// Other attributes unknown at the time of writing or not yet supported by /// this library. Other(DefaultNla), } impl Nla for TcActionAttribute { fn value_len(&self) -> usize { match self { Self::Cookie(bytes) => bytes.len(), Self::Kind(k) => k.len() + 1, Self::Options(opt) => opt.as_slice().buffer_len(), Self::Index(_) | Self::InHwCount(_) => 4, Self::Stats(s) => s.as_slice().buffer_len(), Self::Other(attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Cookie(bytes) => buffer.copy_from_slice(bytes.as_slice()), Self::Kind(string) => { buffer[..string.as_bytes().len()] .copy_from_slice(string.as_bytes()); buffer[string.as_bytes().len()] = 0; } Self::Options(opt) => opt.as_slice().emit(buffer), Self::Index(value) | Self::InHwCount(value) => { NativeEndian::write_u32(buffer, *value); } Self::Stats(s) => s.as_slice().emit(buffer), Self::Other(attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Kind(_) => TCA_ACT_KIND, Self::Options(_) => TCA_ACT_OPTIONS | NLA_F_NESTED, Self::Index(_) => TCA_ACT_INDEX, Self::Stats(_) => TCA_ACT_STATS, Self::Cookie(_) => TCA_ACT_COOKIE, Self::InHwCount(_) => TCA_ACT_IN_HW_COUNT, Self::Other(nla) => nla.kind(), } } } impl<'a, T, P> ParseableParametrized, P> for TcActionAttribute where T: AsRef<[u8]> + ?Sized, P: AsRef, { fn parse_with_param( buf: &NlaBuffer<&'a T>, kind: P, ) -> Result { Ok(match buf.kind() { TCA_ACT_KIND => { let buf_value = buf.value(); TcActionAttribute::Kind( parse_string(buf_value) .context("failed to parse TCA_ACT_KIND")?, ) } TCA_ACT_OPTIONS => TcActionAttribute::Options( NlasIterator::new(buf.value()) .map(|nla| { let nla = nla.context("invalid TCA_ACT_OPTIONS")?; TcActionOption::parse_with_param(&nla, kind.as_ref()) .context("failed to parse TCA_ACT_OPTIONS") }) .collect::, _>>()?, ), TCA_ACT_INDEX => TcActionAttribute::Index( parse_u32(buf.value()) .context("failed to parse TCA_ACT_INDEX")?, ), TCA_ACT_STATS => TcActionAttribute::Stats( NlasIterator::new(buf.value()) .map(|nla| { let nla = nla.context("invalid TCA_ACT_STATS")?; TcStats2::parse_with_param(&nla, kind.as_ref()) .context("failed to parse TCA_ACT_STATS") }) .collect::, _>>()?, ), TCA_ACT_COOKIE => TcActionAttribute::Cookie(buf.value().to_vec()), TCA_ACT_IN_HW_COUNT => TcActionAttribute::InHwCount( parse_u32(buf.value()) .context("failed to parse TCA_ACT_IN_HW_COUNT")?, ), _ => TcActionAttribute::Other( DefaultNla::parse(buf).context("failed to parse action nla")?, ), }) } } /// [`TcActionOption`] is a netlink message attribute that describes an option /// of a [tc-actions] action. /// /// This enum is non-exhaustive as new action types may be added to the kernel /// at any time. /// Only a small subset of possible actions are currently supported. /// /// [tc-actions]: https://man7.org/linux/man-pages/man8/tc-actions.8.html #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum TcActionOption { /// Mirror options. /// /// These options can be used to mirror (copy) or redirect frames / packets /// to another network interface. Mirror(TcActionMirrorOption), /// NAT options. /// /// These options type can be used to perform network address translation. Nat(TcActionNatOption), /// Other action types not yet supported by this library. Other(DefaultNla), } impl Nla for TcActionOption { fn value_len(&self) -> usize { match self { Self::Mirror(nla) => nla.value_len(), Self::Nat(nla) => nla.value_len(), Self::Other(nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Mirror(nla) => nla.emit_value(buffer), Self::Nat(nla) => nla.emit_value(buffer), Self::Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Mirror(nla) => nla.kind(), Self::Nat(nla) => nla.kind(), Self::Other(nla) => nla.kind(), } } } impl<'a, T, S> ParseableParametrized, S> for TcActionOption where T: AsRef<[u8]> + ?Sized, S: AsRef, { fn parse_with_param( buf: &NlaBuffer<&'a T>, kind: S, ) -> Result { Ok(match kind.as_ref() { TcActionMirror::KIND => Self::Mirror( TcActionMirrorOption::parse(buf) .context("failed to parse mirror action")?, ), TcActionNat::KIND => Self::Nat( TcActionNatOption::parse(buf) .context("failed to parse nat action")?, ), _ => Self::Other( DefaultNla::parse(buf) .context("failed to parse action options")?, ), }) } } /// Generic traffic control action parameters. /// /// This structure is used to describe attributes common to all traffic control /// actions. /// /// See [`#define tc_gen` in `linux/pkt_cls.h`][`tc_gen`]. /// /// [`tc_gen`]: https://elixir.bootlin.com/linux/v6.8.9/source/include/uapi/linux/pkt_cls.h#L179 #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] #[non_exhaustive] pub struct TcActionGeneric { /// The [`index`] of the action is a unique identifier used to track /// actions installed in the kernel. /// /// Each action type (e.g. [`mirror`] or [`nat`]) has its own independent /// [`index`] space. /// If you assign the [`index`] field to `0` when creating an action, the /// kernel will assign a unique [`index`] to the new action. /// /// [`mirror`]: struct.TcActionMirror.html /// [`nat`]: struct.TcActionNat.html /// [`index`]: #structfield.index pub index: u32, /// NOTE: I cannot find any documentation on this field nor any place /// where it is used in iproute2 or the Linux kernel. /// The [`capab`] field is part of the [`#define tc_gen`] in the kernel, /// and that `#define` is used in many places, /// but I don't see any place using the [`capab`] field in any way. /// I may be looking in the wrong place or missing something. /// /// [`#define tc_gen`]: https://elixir.bootlin.com/linux/v6.8.9/source/include/uapi/linux/pkt_cls.h#L179 /// [`capab`]: #structfield.capab pub capab: u32, /// Action type. pub action: TcActionType, /// Reference count of this action. /// /// This refers to the number of times this action is referenced within the /// kernel. /// Actions are cleaned up (deleted) when [`refcnt`] reaches 0. /// /// If you create an action on its own (i.e., not associated with a /// filter), the [`refcnt`] will be 1. /// If that action is then associated with a filter, the [`refcnt`] will be /// 2. /// If you then delete that filter, the [`refcnt`] will be 1 and the action /// will remain until you explicitly delete it (which is only possible /// when the [`refcnt`] is 1 and the [`bindcnt`] is 0). /// /// If you were to create an action indirectly (e.g., as part of creating a /// filter) then the [`refcnt`] will still be 1 (along with the /// [`bindcnt`]). /// If you then create another filter that references the same action, the /// [`refcnt`] will be 2 (along with the [`bindcnt`]). /// /// If you then deleted both of those actions, /// the [`refcnt`] would be 0 and the action would be removed from the /// kernel. /// /// [`refcnt`]: #structfield.refcnt /// [`bindcnt`]: #structfield.bindcnt pub refcnt: i32, /// Bind count of this action. /// /// The number of filters that reference (bind to) this action. pub bindcnt: i32, } impl TcActionGeneric { pub(crate) const BUF_LEN: usize = 20; } buffer!(TcActionGenericBuffer(TcActionGeneric::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 TcActionGeneric { fn buffer_len(&self) -> usize { Self::BUF_LEN } fn emit(&self, buffer: &mut [u8]) { let mut packet = TcActionGenericBuffer::new(buffer); packet.set_index(self.index); packet.set_capab(self.capab); packet.set_action(self.action.into()); packet.set_refcnt(self.refcnt); packet.set_bindcnt(self.bindcnt); } } impl> Parseable> for TcActionGeneric { fn parse(buf: &TcActionGenericBuffer) -> Result { Ok(Self { index: buf.index(), capab: buf.capab(), action: buf.action().into(), refcnt: buf.refcnt(), bindcnt: buf.bindcnt(), }) } } const TC_ACT_UNSPEC: i32 = -1; const TC_ACT_OK: i32 = 0; const TC_ACT_RECLASSIFY: i32 = 1; const TC_ACT_SHOT: i32 = 2; const TC_ACT_PIPE: i32 = 3; const TC_ACT_STOLEN: i32 = 4; const TC_ACT_QUEUED: i32 = 5; const TC_ACT_REPEAT: i32 = 6; const TC_ACT_REDIRECT: i32 = 7; const TC_ACT_TRAP: i32 = 8; /// Generic traffic control action types. /// /// These are the possible "outcomes" for a packet after an action is applied to /// it. /// /// This enum is non-exhaustive as new action types may be added to the kernel /// at any time. #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] #[non_exhaustive] pub enum TcActionType { /// No specific outcome specified (i.e., take the default for that action). #[default] Unspec, /// Terminates packet processing and allows the packet to proceed. Ok, /// Terminates packet processing and restart packet classification. Reclassify, /// Drop the packet. Shot, /// Pipe the packet to the next action (if any). Pipe, /// The packet is removed from this processing pipeline and returned to /// another. /// This happens, for example, when using the "mirred" redirect action. Stolen, /// Queue the packet for later processing. Queued, /// Repeat the action. /// /// > TODO: confirm this. I have not used this action before and its /// > semantics are unclear. Repeat, /// Redirect the packet. /// /// > TODO: confirm semantics of this action. It is unclear how /// > [`Redirect`] differs from [`Stolen`]. /// /// [`Stolen`]: #variant.Stolen /// [`Redirect`]: #variant.Redirect Redirect, /// Transition packet processing from the hardware to software. /// /// If this action is encountered by in software, it is equivalent to /// [`Shot`]. /// /// [`Shot`]: #variant.Shot Trap, /// Other action types not known at the time of writing or not yet /// supported by this library. Other(i32), } impl From for TcActionType { fn from(d: i32) -> Self { match d { TC_ACT_UNSPEC => Self::Unspec, TC_ACT_OK => Self::Ok, TC_ACT_RECLASSIFY => Self::Reclassify, TC_ACT_SHOT => Self::Shot, TC_ACT_PIPE => Self::Pipe, TC_ACT_STOLEN => Self::Stolen, TC_ACT_QUEUED => Self::Queued, TC_ACT_REPEAT => Self::Repeat, TC_ACT_REDIRECT => Self::Redirect, TC_ACT_TRAP => Self::Trap, _ => Self::Other(d), } } } impl From for i32 { fn from(v: TcActionType) -> i32 { match v { TcActionType::Unspec => TC_ACT_UNSPEC, TcActionType::Ok => TC_ACT_OK, TcActionType::Reclassify => TC_ACT_RECLASSIFY, TcActionType::Shot => TC_ACT_SHOT, TcActionType::Pipe => TC_ACT_PIPE, TcActionType::Stolen => TC_ACT_STOLEN, TcActionType::Queued => TC_ACT_QUEUED, TcActionType::Repeat => TC_ACT_REPEAT, TcActionType::Redirect => TC_ACT_REDIRECT, TcActionType::Trap => TC_ACT_TRAP, TcActionType::Other(d) => d, } } } netlink-packet-route-0.21.0/src/tc/actions/header.rs000064400000000000000000000026371046102023000204140ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::nla::{NlaBuffer, NlasIterator}; use netlink_packet_utils::{DecodeError, Emitable, Parseable}; use crate::AddressFamily; const TCA_HEADER_LEN: usize = 4; buffer!(TcActionMessageBuffer(TCA_HEADER_LEN) { family: (u8, 0), pad1: (u8, 1), pad2: (u16, 2..TCA_HEADER_LEN), payload: (slice, TCA_HEADER_LEN..), }); impl<'a, T: AsRef<[u8]> + ?Sized> TcActionMessageBuffer<&'a T> { /// Returns an iterator over the attributes of a `TcActionMessageBuffer`. pub fn attributes( &self, ) -> impl Iterator, DecodeError>> { NlasIterator::new(self.payload()) } } /// Header for a traffic control action message. #[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct TcActionMessageHeader { /// Address family (usually `AddressFamily::Unspec`). pub family: AddressFamily, } impl Emitable for TcActionMessageHeader { fn buffer_len(&self) -> usize { TCA_HEADER_LEN } fn emit(&self, buffer: &mut [u8]) { let mut packet = TcActionMessageBuffer::new(buffer); packet.set_family(self.family.into()); } } impl> Parseable> for TcActionMessageHeader { fn parse(buf: &TcActionMessageBuffer) -> Result { Ok(TcActionMessageHeader { family: buf.family().into(), }) } } netlink-packet-route-0.21.0/src/tc/actions/message.rs000064400000000000000000000210521046102023000206000ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::nla::{DefaultNla, NlaBuffer}; use netlink_packet_utils::nla::{Nla, NlasIterator}; use netlink_packet_utils::{DecodeError, Emitable, Parseable}; use crate::tc::actions::{TcActionMessageBuffer, TcActionMessageHeader}; use crate::tc::TcAction; /// Message to describe [tc-actions] /// /// [tc-actions]: https://man7.org/linux/man-pages/man8/tc-actions.8.html #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct TcActionMessage { /// Header of the message. pub header: TcActionMessageHeader, /// Attributes of the message. pub attributes: Vec, } const TCA_ACT_FLAG_LARGE_DUMP_ON: u32 = 1 << 0; const TCA_ACT_FLAG_TERSE_DUMP: u32 = 1 << 1; bitflags! { /// Flags to configure action dumps (list operations). #[derive(Debug, PartialEq, Eq, Clone, Copy, Default, PartialOrd, Ord, Hash)] #[non_exhaustive] pub struct TcActionMessageFlags: u32 { /// If set, this flag enables more than `TCA_ACT_MAX_PRIO` actions in a single /// actions listing operation. const LargeDump = TCA_ACT_FLAG_LARGE_DUMP_ON; /// If set, this flag restricts an action dump to only include essential /// details. const TerseDump = TCA_ACT_FLAG_TERSE_DUMP; const _ = !0; } } /// [`TcActionMessageFlagsWithSelector`] sets the [`TcActionMessageFlags`] which /// are to be included in an operation, based on the accompanying [`flags`] and /// [`selector`] fields. /// /// [`flags`]: #structfield.flags /// [`selector`]: #structfield.selector #[derive(Debug, PartialEq, Eq, Clone, Copy, Default, PartialOrd, Ord, Hash)] pub struct TcActionMessageFlagsWithSelector { /// A bitmask of [`TcActionMessageFlags`] to be associated with an /// operation. pub flags: TcActionMessageFlags, /// A bitmask to determine which flags are to be included in an operation. /// /// Any flags which are set in the [`flags`] field but which are not set in /// the [`selector`] field will be ignored. /// /// [`flags`]: #structfield.flags /// [`selector`]: #structfield.selector pub selector: TcActionMessageFlags, } impl Nla for TcActionMessageFlagsWithSelector { fn value_len(&self) -> usize { 8 } fn kind(&self) -> u16 { TCA_ROOT_FLAGS } fn emit_value(&self, buffer: &mut [u8]) { buffer[..4].copy_from_slice(&self.flags.bits().to_ne_bytes()); buffer[4..8].copy_from_slice(&self.selector.bits().to_ne_bytes()); } } impl TcActionMessageFlagsWithSelector { /// Create a new [`TcActionMessageFlagsWithSelector`] with the given /// [`flags`]. /// The [`selector`] field is set to the same value as [`flags`] (i.e., none /// of the [`flags`] will be ignored). /// /// [`flags`]: #structfield.flags /// [`selector`]: #structfield.selector #[must_use] pub fn new(flags: TcActionMessageFlags) -> Self { Self { flags, selector: flags, } } /// Create a new [`TcActionMessageFlagsWithSelector`] with the given /// [`flags`] and [`selector`]. /// /// [`flags`]: #structfield.flags /// [`selector`]: #structfield.selector #[must_use] pub fn new_with_selector( flags: TcActionMessageFlags, selector: TcActionMessageFlags, ) -> Self { Self { flags, selector } } } impl<'a, T: AsRef<[u8]> + 'a + ?Sized> Parseable> for TcActionMessageFlagsWithSelector { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let value = buf.value(); if value.len() != 8 { return Err(DecodeError::from("invalid length")); } let flags = TcActionMessageFlags::from_bits(u32::from_ne_bytes( value[0..4].try_into().context("invalid length")?, )) .ok_or_else(|| DecodeError::from("invalid flags"))?; let selector = TcActionMessageFlags::from_bits(u32::from_ne_bytes( value[4..].try_into().context("invalid length")?, )) .ok_or_else(|| DecodeError::from("invalid flags selector"))?; Ok(Self::new_with_selector(flags, selector)) } } const TCA_ACT_TAB: u16 = 1; const TCA_ROOT_FLAGS: u16 = 2; const TCA_ROOT_COUNT: u16 = 3; const TCA_ROOT_TIME_DELTA: u16 = 4; const TCA_ROOT_EXT_WARN_MSG: u16 = 5; /// This enum is used to represent the different types of attributes that can be /// part of a [`TcActionMessage`]. /// /// This enum is non-exhaustive, additional variants may be added in the future. #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum TcActionMessageAttribute { /// Collection of `TcActions`. Actions(Vec), /// Flags to configure action dumps (list operations). Flags(TcActionMessageFlagsWithSelector), /// Number of actions being dumped. RootCount(u32), /// Time delta. RootTimeDelta(u32), /// Extended warning message. RootExtWarnMsg(String), /// Other attributes unknown at the time of writing. Other(DefaultNla), } impl<'a, T: AsRef<[u8]> + 'a + ?Sized> Parseable> for TcActionMessageAttribute { fn parse(buf: &NlaBuffer<&'a T>) -> Result { Ok(match buf.kind() { TCA_ACT_TAB => { let actions = NlasIterator::new(buf.value()) .map(|nla| TcAction::parse(&nla?)) .collect::, _>>()?; Self::Actions(actions) } TCA_ROOT_FLAGS => { Self::Flags(TcActionMessageFlagsWithSelector::parse(buf)?) } TCA_ROOT_COUNT => { let count = u32::from_ne_bytes( buf.value().try_into().context("invalid length")?, ); Self::RootCount(count) } TCA_ROOT_TIME_DELTA => { let delta = u32::from_be_bytes( buf.value().try_into().context("invalid length")?, ); Self::RootTimeDelta(delta) } TCA_ROOT_EXT_WARN_MSG => { let msg = String::from_utf8(buf.value().to_vec()) .context("invalid utf8")?; Self::RootExtWarnMsg(msg) } _ => Self::Other(DefaultNla::parse(buf)?), }) } } impl Nla for TcActionMessageAttribute { fn value_len(&self) -> usize { match self { Self::Actions(actions) => actions.as_slice().buffer_len(), Self::Flags(_) => 8, Self::RootCount(_) => 4, Self::RootTimeDelta(_) => 4, Self::RootExtWarnMsg(msg) => msg.len(), Self::Other(nla) => nla.value_len(), } } fn kind(&self) -> u16 { match self { Self::Actions(_) => TCA_ACT_TAB, Self::Flags(_) => TCA_ROOT_FLAGS, Self::RootCount(_) => TCA_ROOT_COUNT, Self::RootTimeDelta(_) => TCA_ROOT_TIME_DELTA, Self::RootExtWarnMsg(_) => TCA_ROOT_EXT_WARN_MSG, Self::Other(nla) => nla.kind(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Actions(actions) => actions.as_slice().emit(buffer), Self::Flags(flags) => { flags.emit_value(buffer); } Self::RootCount(count) => { buffer.copy_from_slice(&count.to_ne_bytes()); } Self::RootTimeDelta(delta) => { buffer.copy_from_slice(&delta.to_be_bytes()); } Self::RootExtWarnMsg(msg) => buffer.copy_from_slice(msg.as_bytes()), Self::Other(nla) => nla.emit_value(buffer), } } } impl<'a, T: AsRef<[u8]> + 'a + ?Sized> Parseable> for TcActionMessage { fn parse(buf: &TcActionMessageBuffer<&'a T>) -> Result { let attrs: Result, DecodeError> = buf .attributes() .map(|attr| TcActionMessageAttribute::parse(&attr?)) .collect::, _>>(); Ok(Self { header: TcActionMessageHeader::parse(buf) .context("failed to parse tc message header")?, attributes: attrs?, }) } } impl Emitable for TcActionMessage { fn buffer_len(&self) -> usize { self.header.buffer_len() + self.attributes.as_slice().buffer_len() } fn emit(&self, buffer: &mut [u8]) { self.header.emit(buffer); self.attributes .as_slice() .emit(&mut buffer[self.header.buffer_len()..]); } } netlink-packet-route-0.21.0/src/tc/actions/mirror.rs000064400000000000000000000120001046102023000204570ustar 00000000000000// SPDX-License-Identifier: MIT /// Mirror 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::{DefaultNla, Nla, NlaBuffer}, traits::{Emitable, Parseable}, DecodeError, }; use super::{TcActionGeneric, TcActionGenericBuffer}; /// Traffic control action used to mirror or redirect packets. #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct TcActionMirror {} impl TcActionMirror { /// The `TcActionAttribute::Kind` of this action. pub const KIND: &'static str = "mirred"; } const TCA_MIRRED_TM: u16 = 1; const TCA_MIRRED_PARMS: u16 = 2; /// Options for the `TcActionMirror` action. #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum TcActionMirrorOption { /// TODO: document this after we make it something better than `Vec` Tm(Vec), /// Parameters for the mirred action. Parms(TcMirror), /// Other attributes unknown at the time of writing. Other(DefaultNla), } impl Nla for TcActionMirrorOption { fn value_len(&self) -> usize { match self { Self::Tm(bytes) => bytes.len(), Self::Parms(_) => TC_MIRRED_BUF_LEN, Self::Other(attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Tm(bytes) => buffer.copy_from_slice(bytes.as_slice()), Self::Parms(p) => p.emit(buffer), Self::Other(attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Tm(_) => TCA_MIRRED_TM, Self::Parms(_) => TCA_MIRRED_PARMS, Self::Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for TcActionMirrorOption { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { TCA_MIRRED_TM => Self::Tm(payload.to_vec()), TCA_MIRRED_PARMS => Self::Parms(TcMirror::parse( &TcMirrorBuffer::new_checked(payload)?, )?), _ => Self::Other(DefaultNla::parse(buf)?), }) } } const TC_MIRRED_BUF_LEN: usize = TcActionGeneric::BUF_LEN + 8; /// Parameters for the mirred action. #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct TcMirror { /// Generic action parameters. pub generic: TcActionGeneric, /// Describes how the packet be mirrored or redirected. pub eaction: TcMirrorActionType, /// Interface index to mirror or redirect to. pub ifindex: u32, } // kernel struct `tc_mirred` buffer!(TcMirrorBuffer(TC_MIRRED_BUF_LEN) { generic: (slice, 0..20), eaction: (i32, 20..24), ifindex: (u32, 24..28), }); impl Emitable for TcMirror { fn buffer_len(&self) -> usize { TC_MIRRED_BUF_LEN } fn emit(&self, buffer: &mut [u8]) { let mut packet = TcMirrorBuffer::new(buffer); self.generic.emit(packet.generic_mut()); packet.set_eaction(self.eaction.into()); packet.set_ifindex(self.ifindex); } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for TcMirror { fn parse(buf: &TcMirrorBuffer<&T>) -> Result { Ok(Self { generic: TcActionGeneric::parse(&TcActionGenericBuffer::new( buf.generic(), ))?, eaction: buf.eaction().into(), ifindex: buf.ifindex(), }) } } const TCA_EGRESS_REDIR: i32 = 1; const TCA_EGRESS_MIRROR: i32 = 2; const TCA_INGRESS_REDIR: i32 = 3; const TCA_INGRESS_MIRROR: i32 = 4; /// Type of mirroring or redirecting action. #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] #[non_exhaustive] pub enum TcMirrorActionType { #[default] /// Redirect to the egress pipeline. EgressRedir, /// Mirror to the egress pipeline. EgressMirror, /// Redirect to the ingress pipeline. IngressRedir, /// Mirror to the ingress pipeline. IngressMirror, /// Other action type unknown at the time of writing. Other(i32), } impl From for TcMirrorActionType { fn from(d: i32) -> Self { match d { TCA_EGRESS_REDIR => Self::EgressRedir, TCA_EGRESS_MIRROR => Self::EgressMirror, TCA_INGRESS_REDIR => Self::IngressRedir, TCA_INGRESS_MIRROR => Self::IngressMirror, _ => Self::Other(d), } } } impl From for i32 { fn from(v: TcMirrorActionType) -> i32 { match v { TcMirrorActionType::EgressRedir => TCA_EGRESS_REDIR, TcMirrorActionType::EgressMirror => TCA_EGRESS_MIRROR, TcMirrorActionType::IngressRedir => TCA_INGRESS_REDIR, TcMirrorActionType::IngressMirror => TCA_INGRESS_MIRROR, TcMirrorActionType::Other(d) => d, } } } netlink-packet-route-0.21.0/src/tc/actions/mod.rs000064400000000000000000000012711046102023000177340ustar 00000000000000// SPDX-License-Identifier: MIT pub use nat_flag::TcNatFlags; pub use self::action::{ TcAction, TcActionAttribute, TcActionGeneric, TcActionGenericBuffer, TcActionOption, TcActionType, }; pub use self::header::{TcActionMessageBuffer, TcActionMessageHeader}; pub use self::message::{ TcActionMessage, TcActionMessageAttribute, TcActionMessageFlags, TcActionMessageFlagsWithSelector, }; pub use self::mirror::{ TcActionMirror, TcActionMirrorOption, TcMirror, TcMirrorActionType, TcMirrorBuffer, }; pub use self::nat::{TcActionNat, TcActionNatOption, TcNat, TcNatBuffer}; mod action; mod header; mod message; mod mirror; mod nat; mod nat_flag; #[cfg(test)] pub mod tests; netlink-packet-route-0.21.0/src/tc/actions/nat.rs000064400000000000000000000111071046102023000177360ustar 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::{DefaultNla, Nla, NlaBuffer}, traits::{Emitable, Parseable}, DecodeError, }; use super::{nat_flag::TcNatFlags, TcActionGeneric, TcActionGenericBuffer}; const TCA_NAT_PARMS: u16 = 1; const TCA_NAT_TM: u16 = 2; /// Network address translation action. #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct TcActionNat {} impl TcActionNat { pub(crate) const KIND: &'static str = "nat"; } /// Options for the [`TcActionNat`] action. #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum TcActionNatOption { /// TODO: document this after we make it something better than `Vec` Tm(Vec), /// Parameters for the nat action. Parms(TcNat), /// Other attributes unknown at the time of writing. Other(DefaultNla), } impl Nla for TcActionNatOption { fn value_len(&self) -> usize { match self { Self::Tm(bytes) => bytes.len(), Self::Parms(v) => v.buffer_len(), Self::Other(attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Tm(bytes) => buffer.copy_from_slice(bytes.as_slice()), Self::Parms(p) => p.emit(buffer), Self::Other(attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Tm(_) => TCA_NAT_TM, Self::Parms(_) => TCA_NAT_PARMS, Self::Other(nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for TcActionNatOption { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { TCA_NAT_TM => Self::Tm(payload.to_vec()), TCA_NAT_PARMS => { Self::Parms(TcNat::parse(&TcNatBuffer::new_checked(payload)?)?) } _ => Self::Other(DefaultNla::parse(buf)?), }) } } const TC_NAT_BUF_LEN: usize = TcActionGeneric::BUF_LEN + 16; /// Network address translation action. #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct TcNat { /// Common attributes for all actions. pub generic: TcActionGeneric, /// Original address. pub old_addr: Ipv4Addr, /// New address. pub new_addr: Ipv4Addr, /// Mask of the old address pub mask: Ipv4Addr, /// Flags for the NAT action. pub flags: TcNatFlags, } impl Default for TcNat { fn default() -> Self { Self { generic: TcActionGeneric::default(), old_addr: Ipv4Addr::UNSPECIFIED, new_addr: Ipv4Addr::UNSPECIFIED, mask: Ipv4Addr::UNSPECIFIED, flags: TcNatFlags::empty(), } } } buffer!(TcNatBuffer(TC_NAT_BUF_LEN) { generic: (slice, 0..TcActionGeneric::BUF_LEN), old_addr: (slice, TcActionGeneric::BUF_LEN..(TcActionGeneric::BUF_LEN+4)), new_addr: (slice, (TcActionGeneric::BUF_LEN+4)..(TcActionGeneric::BUF_LEN+8)), mask: (slice, (TcActionGeneric::BUF_LEN+8)..(TcActionGeneric::BUF_LEN+12)), flags: (u32, (TcActionGeneric::BUF_LEN+12)..TC_NAT_BUF_LEN), }); 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); self.generic.emit(packet.generic_mut()); packet .old_addr_mut() .copy_from_slice(&self.old_addr.octets()); packet .new_addr_mut() .copy_from_slice(&self.new_addr.octets()); packet.mask_mut().copy_from_slice(&self.mask.octets()); packet.set_flags(self.flags.bits()); } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for TcNat { fn parse(buf: &TcNatBuffer<&T>) -> Result { Ok(Self { generic: TcActionGeneric::parse(&TcActionGenericBuffer::new( buf.generic(), ))?, old_addr: parse_ipv4(buf.old_addr())?, new_addr: parse_ipv4(buf.new_addr())?, mask: parse_ipv4(buf.mask())?, flags: TcNatFlags::from_bits_retain(buf.flags()), }) } } fn parse_ipv4(data: &[u8]) -> Result { if data.len() != 4 { Err(DecodeError::from(format!( "Invalid length of IPv4 Address, expecting 4 bytes, but got {:?}", data ))) } else { Ok(Ipv4Addr::new(data[0], data[1], data[2], data[3])) } } netlink-packet-route-0.21.0/src/tc/actions/nat_flag.rs000064400000000000000000000016631046102023000207350ustar 00000000000000// SPDX-License-Identifier: MIT const TCA_NAT_FLAG_EGRESS: u32 = 1; const TCA_ACT_FLAGS_USER_BITS: u32 = 16; const TCA_ACT_FLAGS_POLICE: u32 = 1u32 << TCA_ACT_FLAGS_USER_BITS; const TCA_ACT_FLAGS_BIND: u32 = 1u32 << (TCA_ACT_FLAGS_USER_BITS + 1); const TCA_ACT_FLAGS_REPLACE: u32 = 1u32 << (TCA_ACT_FLAGS_USER_BITS + 2); const TCA_ACT_FLAGS_NO_RTNL: u32 = 1u32 << (TCA_ACT_FLAGS_USER_BITS + 3); const TCA_ACT_FLAGS_AT_INGRESS: u32 = 1u32 << (TCA_ACT_FLAGS_USER_BITS + 4); bitflags! { /// Network Address Translation flags. #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] #[non_exhaustive] pub struct TcNatFlags: u32 { const Egress = TCA_NAT_FLAG_EGRESS; const Police = TCA_ACT_FLAGS_POLICE; const Bind = TCA_ACT_FLAGS_BIND; const Replace = TCA_ACT_FLAGS_REPLACE; const NoRtnl = TCA_ACT_FLAGS_NO_RTNL; const AtIngress = TCA_ACT_FLAGS_AT_INGRESS; const _ = !0; } } netlink-packet-route-0.21.0/src/tc/actions/tests/action.rs000064400000000000000000000037421046102023000216010ustar 00000000000000// SPDX-License-Identifier: MIT use crate::tc::{ TcAction, TcActionAttribute, TcActionGeneric, TcActionGenericBuffer, TcActionType, TcStats2, TcStatsBasic, }; use netlink_packet_utils::nla::NlaBuffer; use netlink_packet_utils::{Emitable, Parseable}; #[test] fn tc_action_generic_parse_back() { let orig = TcActionGeneric { index: 1, capab: 2, action: TcActionType::Reclassify, refcnt: 3, bindcnt: 4, }; let mut buffer = vec![0; orig.buffer_len()]; orig.emit(&mut buffer); let parsed = TcActionGeneric::parse( &TcActionGenericBuffer::new_checked(buffer).unwrap(), ) .unwrap(); assert_eq!(orig, parsed); } #[test] fn tc_action_parse_back_minimal() { let orig = TcAction { tab: 1, attributes: vec![TcActionAttribute::Kind("example".into())], }; let mut buffer = vec![0; orig.buffer_len()]; orig.emit(&mut buffer); let parsed = TcAction::parse(&NlaBuffer::new_checked(buffer.as_slice()).unwrap()) .unwrap(); assert_eq!(orig, parsed); } #[test] fn tc_action_parse_back_example() { let orig = TcAction { tab: 1, attributes: vec![ TcActionAttribute::Kind("example".into()), TcActionAttribute::Index(1), TcActionAttribute::Cookie(vec![1, 2, 3, 4, 5, 6, 7, 8]), TcActionAttribute::InHwCount(99), TcActionAttribute::Stats(vec![ TcStats2::Basic(TcStatsBasic { bytes: 1, packets: 2, }), TcStats2::BasicHw(TcStatsBasic { bytes: 3, packets: 4, }), ]), TcActionAttribute::Options(vec![]), ], }; let mut buffer = vec![0; orig.buffer_len()]; orig.emit(&mut buffer); let parsed = TcAction::parse(&NlaBuffer::new_checked(buffer.as_slice()).unwrap()) .unwrap(); assert_eq!(orig, parsed); } netlink-packet-route-0.21.0/src/tc/actions/tests/header.rs000064400000000000000000000047001046102023000215470ustar 00000000000000// SPDX-License-Identifier: MIT use crate::tc::actions::{TcActionMessageBuffer, TcActionMessageHeader}; use crate::AddressFamily; use netlink_packet_utils::{Emitable, Parseable}; #[test] fn tc_action_message_header_parse_back_all_known_families() { for family in [ AddressFamily::Unspec, // AddressFamily::Local, // `Local` and `Unix` overlap! AddressFamily::Unix, AddressFamily::Inet, AddressFamily::Ax25, AddressFamily::Ipx, AddressFamily::Appletalk, AddressFamily::Netrom, AddressFamily::Bridge, AddressFamily::Atmpvc, AddressFamily::X25, AddressFamily::Inet6, AddressFamily::Rose, AddressFamily::Decnet, AddressFamily::Netbeui, AddressFamily::Security, AddressFamily::Key, // AddressFamily::Route, // `Route` and `Netlink` overlap! AddressFamily::Netlink, AddressFamily::Packet, AddressFamily::Ash, AddressFamily::Econet, AddressFamily::Atmsvc, AddressFamily::Rds, AddressFamily::Sna, AddressFamily::Irda, AddressFamily::Pppox, AddressFamily::Wanpipe, AddressFamily::Llc, AddressFamily::Ib, AddressFamily::Mpls, AddressFamily::Can, AddressFamily::Tipc, AddressFamily::Bluetooth, AddressFamily::Iucv, AddressFamily::Rxrpc, AddressFamily::Isdn, AddressFamily::Phonet, AddressFamily::Ieee802154, AddressFamily::Caif, AddressFamily::Alg, AddressFamily::Nfc, AddressFamily::Vsock, AddressFamily::Kcm, AddressFamily::Qipcrtr, AddressFamily::Smc, AddressFamily::Xdp, AddressFamily::Mctp, ] { let orig = TcActionMessageHeader { family }; let mut buffer = vec![0; orig.buffer_len()]; orig.emit(&mut buffer); let parsed = TcActionMessageHeader::parse( &TcActionMessageBuffer::new_checked(&buffer).unwrap(), ) .unwrap(); assert_eq!(orig, parsed); } } #[test] fn tc_action_message_header_parse_back_other() { let orig = TcActionMessageHeader { family: AddressFamily::Other(99), }; let mut buffer = vec![0; orig.buffer_len()]; orig.emit(&mut buffer); let parsed = TcActionMessageHeader::parse( &TcActionMessageBuffer::new_checked(&buffer).unwrap(), ) .unwrap(); assert_eq!(orig, parsed); } netlink-packet-route-0.21.0/src/tc/actions/tests/message.rs000064400000000000000000000374301046102023000217510ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::nla::{DefaultNla, NlaBuffer}; use netlink_packet_utils::{Emitable, Parseable}; use crate::tc::actions::message::TcActionMessageAttribute::{ Actions, Flags, RootCount, RootExtWarnMsg, RootTimeDelta, }; use crate::tc::actions::message::{ TcActionMessage, TcActionMessageAttribute, TcActionMessageFlags, TcActionMessageFlagsWithSelector, }; use crate::tc::actions::{TcActionMessageBuffer, TcActionMessageHeader}; use crate::tc::TcAction; use crate::tc::TcActionAttribute::{Cookie, Index, Kind}; use crate::AddressFamily; mod mirror { use netlink_packet_utils::nla::DefaultNla; use netlink_packet_utils::Parseable; use crate::tc::actions::message::TcActionMessage; use crate::tc::actions::message::TcActionMessageAttribute::{ Actions, RootCount, }; use crate::tc::actions::{TcActionMessageBuffer, TcActionMessageHeader}; use crate::tc::TcActionAttribute::{ InHwCount, Kind, Options, Other, Stats, }; use crate::tc::TcActionMirrorOption::{Parms, Tm}; use crate::tc::TcActionOption::Mirror; use crate::tc::TcActionType::{Pipe, Stolen}; use crate::tc::TcMirrorActionType::{EgressRedir, IngressMirror}; use crate::tc::TcStats2::{Basic, BasicHw, Queue}; use crate::tc::{ TcAction, TcActionGeneric, TcMirror, TcStatsBasic, TcStatsQueue, }; use crate::AddressFamily; /// Captured `TcActionMessage` examples used for testing. mod message { /// Capture of request message for /// /// ```bash /// tc actions add action mirred egress redirect dev lo index 1 /// ``` pub(super) const CREATE1: &[u8] = &[ 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, 0x34, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x6d, 0x69, 0x72, 0x72, 0x65, 0x64, 0x00, 0x00, 0x24, 0x00, 0x02, 0x80, 0x20, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, ]; /// Capture of request message for /// /// ```bash /// tc actions add action mirred ingress mirror dev lo index 2 /// ``` pub(super) const CREATE2: &[u8] = &[ 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, 0x34, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x6d, 0x69, 0x72, 0x72, 0x65, 0x64, 0x00, 0x00, 0x24, 0x00, 0x02, 0x80, 0x20, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, ]; /// Capture of request message for /// /// ```bash /// tc actions list action mirred /// ``` /// /// after the messages in [`CREATE1`] and [`CREATE2`] have been added. pub(super) const LIST: &[u8] = &[ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x64, 0x01, 0x01, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x6d, 0x69, 0x72, 0x72, 0x65, 0x64, 0x00, 0x00, 0x44, 0x00, 0x04, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x02, 0x00, 0x20, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x6d, 0x69, 0x72, 0x72, 0x65, 0x64, 0x00, 0x00, 0x44, 0x00, 0x04, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x02, 0x00, 0x20, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; } #[test] fn parse_message1_create() { let expected = TcActionMessage { header: TcActionMessageHeader { family: AddressFamily::Unspec, }, attributes: vec![Actions(vec![TcAction { tab: 1, attributes: vec![ Kind("mirred".into()), Options(vec![Mirror(Parms(TcMirror { generic: TcActionGeneric { index: 1, capab: 0, action: Stolen, refcnt: 0, bindcnt: 0, }, eaction: EgressRedir, ifindex: 1, }))]), ], }])], }; let parsed = TcActionMessage::parse( &TcActionMessageBuffer::new_checked(&message::CREATE1).unwrap(), ) .unwrap(); assert_eq!(parsed, expected); } #[test] fn parse_message2_create() { let expected = TcActionMessage { header: TcActionMessageHeader { family: AddressFamily::Unspec, }, attributes: vec![Actions(vec![TcAction { tab: 1, attributes: vec![ Kind("mirred".into()), Options(vec![Mirror(Parms(TcMirror { generic: TcActionGeneric { index: 2, capab: 0, action: Pipe, refcnt: 0, bindcnt: 0, }, eaction: IngressMirror, ifindex: 1, }))]), ], }])], }; let buf = message::CREATE2; let parsed = TcActionMessage::parse( &TcActionMessageBuffer::new_checked(&buf).unwrap(), ) .unwrap(); assert_eq!(parsed, expected); } #[test] #[allow(clippy::too_many_lines)] fn parse_message3_list() { let expected = TcActionMessage { header: TcActionMessageHeader { family: AddressFamily::Unspec, }, attributes: vec![ RootCount(2), Actions(vec![ TcAction { tab: 0, attributes: vec![ Kind("mirred".into()), Stats(vec![ Basic(TcStatsBasic { bytes: 0, packets: 0, }), BasicHw(TcStatsBasic { bytes: 0, packets: 0, }), Queue(TcStatsQueue { qlen: 0, backlog: 0, drops: 0, requeues: 0, overlimits: 0, }), ]), Other(DefaultNla::new( 9, vec![0, 0, 0, 0, 3, 0, 0, 0], )), InHwCount(0), Options(vec![ Mirror(Parms(TcMirror { generic: TcActionGeneric { index: 1, capab: 0, action: Stolen, refcnt: 1, bindcnt: 0, }, eaction: EgressRedir, ifindex: 1, })), Mirror(Tm(vec![ 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, ])), ]), ], }, TcAction { tab: 1, attributes: vec![ Kind("mirred".into()), Stats(vec![ Basic(TcStatsBasic { bytes: 0, packets: 0, }), BasicHw(TcStatsBasic { bytes: 0, packets: 0, }), Queue(TcStatsQueue { qlen: 0, backlog: 0, drops: 0, requeues: 0, overlimits: 0, }), ]), Other(DefaultNla::new( 9, vec![0, 0, 0, 0, 3, 0, 0, 0], )), InHwCount(0), Options(vec![ Mirror(Parms(TcMirror { generic: TcActionGeneric { index: 2, capab: 0, action: Pipe, refcnt: 1, bindcnt: 0, }, eaction: IngressMirror, ifindex: 1, })), Mirror(Tm(vec![ 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, ])), ]), ], }, ]), ], }; let parsed = TcActionMessage::parse( &TcActionMessageBuffer::new_checked(&message::LIST).unwrap(), ) .unwrap(); assert_eq!(parsed, expected); } } #[test] fn tc_action_message_attribute_parse_back_blank_actions() { let orig = Actions(vec![]); let mut buffer = vec![0; orig.buffer_len()]; orig.emit(&mut buffer); let parsed = TcActionMessageAttribute::parse( &NlaBuffer::new_checked(buffer.as_slice()).unwrap(), ) .unwrap(); assert_eq!(orig, parsed); } #[test] fn tc_action_message_attribute_parse_back_example_action() { let orig = Actions(vec![TcAction { tab: 9999, attributes: vec![Kind("example".into())], }]); let mut buffer = vec![0; orig.buffer_len()]; orig.emit(&mut buffer); let parsed = TcActionMessageAttribute::parse( &NlaBuffer::new_checked(buffer.as_slice()).unwrap(), ) .unwrap(); assert_eq!(orig, parsed); } #[test] fn tc_action_message_attribute_parse_back_multiple_example_action() { let orig = Actions(vec![ TcAction { tab: 1111, attributes: vec![Kind("example1".into())], }, TcAction { tab: 2222, attributes: vec![ Kind("example2".into()), Index(42), Cookie(vec![1, 2, 3, 4, 5, 6]), ], }, ]); let mut buffer = vec![0; orig.buffer_len()]; orig.emit(&mut buffer); let parsed = TcActionMessageAttribute::parse( &NlaBuffer::new_checked(buffer.as_slice()).unwrap(), ) .unwrap(); assert_eq!(orig, parsed); } #[test] fn tc_action_message_flags_parse_back_default() { let orig = TcActionMessageFlagsWithSelector::default(); let mut buffer = vec![0; orig.buffer_len()]; orig.emit(&mut buffer); let parsed = TcActionMessageFlagsWithSelector::parse( &NlaBuffer::new_checked(buffer.as_slice()).unwrap(), ) .unwrap(); assert_eq!(orig, parsed); } #[test] fn tc_action_message_flags_parse_back_example_value() { let orig = TcActionMessageFlagsWithSelector { flags: TcActionMessageFlags::LargeDump | TcActionMessageFlags::TerseDump, selector: TcActionMessageFlags::LargeDump, }; let mut buffer = vec![0; orig.buffer_len()]; orig.emit(&mut buffer); let parsed = TcActionMessageFlagsWithSelector::parse( &NlaBuffer::new_checked(buffer.as_slice()).unwrap(), ) .unwrap(); assert_eq!(orig, parsed); } #[test] fn tc_action_message_parse_back_default() { let orig = TcActionMessage::default(); let mut buffer = vec![0; orig.buffer_len()]; orig.emit(&mut buffer); let parsed = TcActionMessage::parse( &TcActionMessageBuffer::new_checked(buffer.as_slice()).unwrap(), ) .unwrap(); assert_eq!(orig, parsed); } #[test] fn tc_action_message_parse_back_example_value() { let orig = TcActionMessage { header: TcActionMessageHeader { family: AddressFamily::Alg, }, attributes: vec![ Flags(TcActionMessageFlagsWithSelector { flags: TcActionMessageFlags::LargeDump, selector: TcActionMessageFlags::LargeDump, }), RootCount(42), RootTimeDelta(43), RootExtWarnMsg("hello".to_string()), TcActionMessageAttribute::Other(DefaultNla::new( 99, vec![1, 2, 3, 4], )), ], }; let mut buffer = vec![0; orig.buffer_len()]; orig.emit(&mut buffer); let parsed = TcActionMessage::parse( &TcActionMessageBuffer::new_checked(buffer.as_slice()).unwrap(), ) .unwrap(); assert_eq!(orig, parsed); } netlink-packet-route-0.21.0/src/tc/actions/tests/mirror.rs000064400000000000000000000045531046102023000216370ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::nla::NlaBuffer; use netlink_packet_utils::{Emitable, Parseable}; use crate::tc::{ TcActionGeneric, TcActionGenericBuffer, TcActionMirrorOption, TcActionType, TcMirror, TcMirrorActionType, TcMirrorBuffer, }; #[test] fn tc_action_generic_parse_back() { let orig = TcActionGeneric { index: 1, capab: 2, action: TcActionType::Reclassify, refcnt: 3, bindcnt: 4, }; let mut buffer = vec![0; orig.buffer_len()]; orig.emit(&mut buffer); let parsed = TcActionGeneric::parse( &TcActionGenericBuffer::new_checked(buffer).unwrap(), ) .unwrap(); assert_eq!(orig, parsed); } #[test] fn tc_mirror_default_parse_back() { let orig = TcMirror { generic: Default::default(), eaction: Default::default(), ifindex: 111, }; let mut buffer = vec![0; orig.buffer_len()]; orig.emit(&mut buffer); let parsed = TcMirror::parse( &TcMirrorBuffer::new_checked(buffer.as_slice()).unwrap(), ) .unwrap(); assert_eq!(orig, parsed); } #[test] fn tc_mirror_example_parse_back() { let orig = TcMirror { generic: TcActionGeneric { index: 1, capab: 2, action: TcActionType::Ok, refcnt: 3, bindcnt: 4, }, eaction: TcMirrorActionType::IngressMirror, ifindex: 99, }; let mut buffer = vec![0; orig.buffer_len()]; orig.emit(&mut buffer); let parsed = TcMirror::parse( &TcMirrorBuffer::new_checked(buffer.as_slice()).unwrap(), ) .unwrap(); assert_eq!(orig, parsed); } #[test] fn tc_mirror_tm_default_parse_back() { let mirror_option = TcActionMirrorOption::Tm(vec![]); let mut buffer = vec![0; mirror_option.buffer_len()]; mirror_option.emit(&mut buffer); let nla_buf = NlaBuffer::new_checked(&buffer).unwrap(); let parsed = TcActionMirrorOption::parse(&nla_buf).unwrap(); assert_eq!(mirror_option, parsed); } #[test] fn tc_mirror_tm_example_parse_back() { let mirror_option = TcActionMirrorOption::Tm(vec![1, 2, 3]); let mut buffer = vec![0; mirror_option.buffer_len()]; mirror_option.emit(&mut buffer); let nla_buf = NlaBuffer::new_checked(&buffer).unwrap(); let parsed = TcActionMirrorOption::parse(&nla_buf).unwrap(); assert_eq!(mirror_option, parsed); } netlink-packet-route-0.21.0/src/tc/actions/tests/mod.rs000064400000000000000000000001571046102023000211000ustar 00000000000000// SPDX-License-Identifier: MIT pub mod action; pub mod header; pub mod message; pub mod mirror; pub mod nat; netlink-packet-route-0.21.0/src/tc/actions/tests/nat.rs000064400000000000000000000306151046102023000211050ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::Ipv4Addr; use netlink_packet_utils::nla::NlaBuffer; use netlink_packet_utils::{Emitable, Parseable}; use crate::tc::actions::message::TcActionMessage; use crate::tc::actions::message::TcActionMessageAttribute::Actions; use crate::tc::actions::{TcActionMessageBuffer, TcActionMessageHeader}; use crate::tc::TcActionAttribute::{InHwCount, Kind, Options, Stats}; use crate::tc::TcActionNatOption::{Parms, Tm}; use crate::tc::TcActionOption::Nat; use crate::tc::TcStats2::{Basic, BasicHw, Queue}; use crate::tc::{ TcAction, TcActionGeneric, TcActionNatOption, TcActionType, TcNat, TcNatFlags, TcStatsBasic, TcStatsQueue, }; use crate::AddressFamily; /// Capture of request for /// /// ```bash /// tc actions add action nat ingress 1.2.3.4/32 5.6.7.0 index 1 /// ``` const TC_ACTION_NAT_EXAMPLE1: &[u8] = &[ 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x08, 0x00, 0x01, 0x00, 0x6e, 0x61, 0x74, 0x00, 0x2c, 0x00, 0x02, 0x80, 0x28, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, ]; fn tc_action_message_nat_example1() -> TcActionMessage { TcActionMessage { header: TcActionMessageHeader { family: AddressFamily::Unspec, }, attributes: vec![Actions(vec![TcAction { tab: 1, attributes: vec![ Kind("nat".into()), Options(vec![Nat(Parms(TcNat { generic: TcActionGeneric { index: 1, capab: 0, action: TcActionType::Ok, refcnt: 0, bindcnt: 0, }, old_addr: Ipv4Addr::new(1, 2, 3, 4), new_addr: Ipv4Addr::new(5, 6, 7, 0), mask: Ipv4Addr::new(255, 255, 255, 255), flags: TcNatFlags::empty(), }))]), ], }])], } } #[test] fn parse_tc_action_nat_example1() { let buf = TC_ACTION_NAT_EXAMPLE1; let parsed = TcActionMessage::parse( &TcActionMessageBuffer::new_checked(&buf).unwrap(), ) .unwrap(); assert_eq!(parsed, tc_action_message_nat_example1()); } #[test] fn emit_tc_action_nat_example1() { let example = tc_action_message_nat_example1(); let mut buf = vec![0; example.buffer_len()]; example.emit(&mut buf); assert_eq!(buf.as_slice(), TC_ACTION_NAT_EXAMPLE1); } /// Capture of request for /// /// ```bash /// tc actions add action nat ingress 1.2.3.0/24 5.6.7.9 index 2 /// ``` const TC_ACTION_NAT_EXAMPLE2: &[u8] = &[ 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x08, 0x00, 0x01, 0x00, 0x6e, 0x61, 0x74, 0x00, 0x2c, 0x00, 0x02, 0x80, 0x28, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x00, 0x05, 0x06, 0x07, 0x09, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, ]; fn tc_action_message_nat_example2() -> TcActionMessage { TcActionMessage { header: TcActionMessageHeader { family: AddressFamily::Unspec, }, attributes: vec![Actions(vec![TcAction { tab: 1, attributes: vec![ Kind("nat".into()), Options(vec![Nat(Parms(TcNat { generic: TcActionGeneric { index: 2, capab: 0, action: TcActionType::Ok, refcnt: 0, bindcnt: 0, }, old_addr: Ipv4Addr::new(1, 2, 3, 0), new_addr: Ipv4Addr::new(5, 6, 7, 9), mask: Ipv4Addr::new(255, 255, 255, 0), flags: TcNatFlags::empty(), }))]), ], }])], } } #[test] fn parse_tc_action_nat_example2() { let buf = TC_ACTION_NAT_EXAMPLE2; let parsed = TcActionMessage::parse( &TcActionMessageBuffer::new_checked(&buf).unwrap(), ) .unwrap(); assert_eq!(parsed, tc_action_message_nat_example2()); } #[test] fn emit_tc_action_nat_example2() { let example = tc_action_message_nat_example2(); let mut buf = vec![0; example.buffer_len()]; example.emit(&mut buf); assert_eq!(buf.as_slice(), TC_ACTION_NAT_EXAMPLE2); } /// Capture of request for /// /// ```bash /// tc actions add action nat egress 2.3.4.0/24 5.6.7.9 index 3 /// ``` const TC_ACTION_NAT_EXAMPLE3: &[u8] = &[ 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x08, 0x00, 0x01, 0x00, 0x6e, 0x61, 0x74, 0x00, 0x2c, 0x00, 0x02, 0x80, 0x28, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x04, 0x00, 0x05, 0x06, 0x07, 0x09, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, ]; fn tc_action_message_nat_example3() -> TcActionMessage { TcActionMessage { header: TcActionMessageHeader { family: AddressFamily::Unspec, }, attributes: vec![Actions(vec![TcAction { tab: 1, attributes: vec![ Kind("nat".into()), Options(vec![Nat(Parms(TcNat { generic: TcActionGeneric { index: 3, capab: 0, action: TcActionType::Ok, refcnt: 0, bindcnt: 0, }, old_addr: Ipv4Addr::new(2, 3, 4, 0), new_addr: Ipv4Addr::new(5, 6, 7, 9), mask: Ipv4Addr::new(255, 255, 255, 0), flags: TcNatFlags::Egress, }))]), ], }])], } } #[test] fn parse_tc_action_nat_example3() { let buf = TC_ACTION_NAT_EXAMPLE3; let parsed = TcActionMessage::parse( &TcActionMessageBuffer::new_checked(&buf).unwrap(), ) .unwrap(); assert_eq!(parsed, tc_action_message_nat_example3()); } #[test] fn emit_tc_action_nat_example3() { let example = tc_action_message_nat_example3(); let mut buf = vec![0x00; example.buffer_len()]; example.emit(&mut buf); assert_eq!(buf.as_slice(), TC_ACTION_NAT_EXAMPLE3); } const TC_ACTION_NAT_OPTION_PARAMS_EXAMPLES: [TcActionNatOption; 2] = [ Parms(TcNat { flags: TcNatFlags::empty(), generic: TcActionGeneric { action: TcActionType::Reclassify, bindcnt: 1, capab: 2, index: 3, refcnt: 4, }, mask: Ipv4Addr::BROADCAST, new_addr: Ipv4Addr::new(1, 2, 3, 4), old_addr: Ipv4Addr::new(5, 6, 7, 8), }), Parms(TcNat { flags: TcNatFlags::empty(), generic: TcActionGeneric { action: TcActionType::Pipe, bindcnt: 5, capab: 6, index: 7, refcnt: 8, }, mask: Ipv4Addr::new(255, 255, 255, 254), new_addr: Ipv4Addr::new(2, 1, 255, 0), old_addr: Ipv4Addr::new(7, 2, 88, 44), }), ]; #[test] fn tc_action_nat_option_parse_back_example_params() { for example in TC_ACTION_NAT_OPTION_PARAMS_EXAMPLES { let mut buffer = vec![0; example.buffer_len()]; example.emit(&mut buffer); let parsed = TcActionNatOption::parse( &NlaBuffer::new_checked(buffer.as_slice()).unwrap(), ) .unwrap(); assert_eq!(example, parsed); } } #[test] fn tc_action_nat_option_emit_uses_whole_buffer() { for example in TC_ACTION_NAT_OPTION_PARAMS_EXAMPLES { let mut buffer1 = vec![0x00; example.buffer_len()]; let mut buffer2 = vec![0xff; example.buffer_len()]; example.emit(&mut buffer1); example.emit(&mut buffer2); assert_eq!(buffer1, buffer2); } } fn tc_action_nat_option_tm_examples() -> [TcActionNatOption; 4] { [ Tm(vec![]), Tm(vec![1]), Tm(vec![1, 2, 3, 4]), Tm(vec![99; 10]), ] } #[test] fn tc_action_nat_option_parse_back_example_tm() { for example in &tc_action_nat_option_tm_examples() { let mut buffer = vec![0; example.buffer_len()]; example.emit(&mut buffer); let parsed = TcActionNatOption::parse( &NlaBuffer::new_checked(buffer.as_slice()).unwrap(), ) .unwrap(); assert_eq!(example, &parsed); } } #[test] fn tc_action_nat_option_emit_tm_uses_whole_buffer() { for example in &tc_action_nat_option_tm_examples() { let mut buffer1 = vec![0x00; example.buffer_len()]; let mut buffer2 = vec![0xff; example.buffer_len()]; example.emit(&mut buffer1); example.emit(&mut buffer2); assert_eq!(buffer1, buffer2); } } /// Setup: /// /// ```bash /// tc actions flush action nat /// tc actions add action nat ingress 192.0.2.1/32 203.0.113.1 index 1 /// ``` /// /// Then capture netlink response message of this command: /// /// ```bash /// tc -statistics actions get action nat index 1 /// ``` /// /// Raw packet modification: /// * cooked header removed (16 bytes). /// * rtnetlink header removed (16 bytes). #[test] fn test_get_filter_nat() { const RAW: &[u8] = &[ 0x00, 0x00, 0x00, 0x00, 0xac, 0x00, 0x01, 0x00, 0xa8, 0x00, 0x01, 0x00, 0x08, 0x00, 0x01, 0x00, 0x6e, 0x61, 0x74, 0x00, 0x44, 0x00, 0x04, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x02, 0x80, 0x28, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x02, 0x01, 0xcb, 0x00, 0x71, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let expected = TcActionMessage { header: TcActionMessageHeader { family: AddressFamily::Unspec, }, attributes: vec![Actions(vec![TcAction { tab: 1, attributes: vec![ Kind("nat".into()), Stats(vec![ Basic(TcStatsBasic { bytes: 0, packets: 0, }), BasicHw(TcStatsBasic { bytes: 0, packets: 0, }), Queue(TcStatsQueue { qlen: 0, backlog: 0, drops: 0, requeues: 0, overlimits: 0, }), ]), InHwCount(0), Options(vec![ Nat(Parms(TcNat { generic: TcActionGeneric { index: 1, capab: 0, action: TcActionType::Ok, refcnt: 1, bindcnt: 0, }, old_addr: [192, 0, 2, 1].into(), new_addr: [203, 0, 113, 1].into(), mask: Ipv4Addr::BROADCAST, flags: TcNatFlags::empty(), })), Nat(Tm(vec![ 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, ])), ]), ], }])], }; assert_eq!( expected, TcActionMessage::parse(&TcActionMessageBuffer::new(&RAW)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, RAW); } netlink-packet-route-0.21.0/src/tc/attribute.rs000064400000000000000000000131211046102023000175150ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, parsers::{parse_string, parse_u32, parse_u8}, DecodeError, Emitable, Parseable, ParseableParametrized, }; use super::{ TcOption, TcStats, TcStats2, TcStatsBuffer, TcXstats, VecTcOption, }; const TCA_KIND: u16 = 1; const TCA_OPTIONS: u16 = 2; const TCA_STATS: u16 = 3; const TCA_XSTATS: u16 = 4; const TCA_RATE: u16 = 5; const TCA_FCNT: u16 = 6; const TCA_STATS2: u16 = 7; const TCA_STAB: u16 = 8; // const TCA_PAD: u16 = 9; const TCA_DUMP_INVISIBLE: u16 = 10; const TCA_CHAIN: u16 = 11; const TCA_HW_OFFLOAD: u16 = 12; // const TCA_INGRESS_BLOCK: u16 = 13; // TODO // const TCA_EGRESS_BLOCK: u16 = 14; // TODO #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum TcAttribute { /// Name of queueing discipline Kind(String), /// Options follow Options(Vec), /// Statistics Stats(TcStats), /// Module-specific statistics Xstats(TcXstats), /// Rate limit Rate(Vec), Fcnt(Vec), Stats2(Vec), Stab(Vec), Chain(u32), HwOffload(u8), DumpInvisible(bool), Other(DefaultNla), } impl Nla for TcAttribute { fn value_len(&self) -> usize { match *self { Self::Rate(ref bytes) | Self::Fcnt(ref bytes) | Self::Stab(ref bytes) => bytes.len(), Self::Chain(_) => 4, Self::Xstats(ref v) => v.buffer_len(), Self::HwOffload(_) => 1, Self::Stats2(ref v) => v.as_slice().buffer_len(), Self::Stats(ref v) => v.buffer_len(), Self::Kind(ref string) => string.as_bytes().len() + 1, Self::Options(ref opt) => opt.as_slice().buffer_len(), Self::DumpInvisible(_) => 0, // The existence of NLA means true Self::Other(ref attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match *self { Self::Rate(ref bytes) | Self::Fcnt(ref bytes) | Self::Stab(ref bytes) => buffer.copy_from_slice(bytes.as_slice()), Self::Chain(v) => NativeEndian::write_u32(buffer, v), Self::Xstats(ref v) => v.emit(buffer), Self::HwOffload(ref val) => buffer[0] = *val, Self::Stats2(ref stats) => stats.as_slice().emit(buffer), Self::Stats(ref stats) => stats.emit(buffer), Self::Kind(ref string) => { buffer[..string.as_bytes().len()] .copy_from_slice(string.as_bytes()); buffer[string.as_bytes().len()] = 0; } Self::Options(ref opt) => opt.as_slice().emit(buffer), Self::DumpInvisible(_) => (), Self::Other(ref attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { match *self { Self::Kind(_) => TCA_KIND, Self::Options(_) => TCA_OPTIONS, Self::Stats(_) => TCA_STATS, Self::Xstats(_) => TCA_XSTATS, Self::Rate(_) => TCA_RATE, Self::Fcnt(_) => TCA_FCNT, Self::Stats2(_) => TCA_STATS2, Self::Stab(_) => TCA_STAB, Self::Chain(_) => TCA_CHAIN, Self::HwOffload(_) => TCA_HW_OFFLOAD, Self::DumpInvisible(_) => TCA_DUMP_INVISIBLE, Self::Other(ref nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> ParseableParametrized, &str> for TcAttribute { fn parse_with_param( buf: &NlaBuffer<&'a T>, kind: &str, ) -> Result { let payload = buf.value(); Ok(match buf.kind() { TCA_KIND => TcAttribute::Kind( parse_string(payload).context("invalid TCA_KIND")?, ), TCA_OPTIONS => TcAttribute::Options( VecTcOption::parse_with_param(buf, kind) .context(format!("Invalid TCA_OPTIONS for kind: {kind}"))? .0, ), TCA_STATS => TcAttribute::Stats( TcStats::parse( &TcStatsBuffer::new_checked(payload) .context("invalid TCA_STATS")?, ) .context("failed to parse TCA_STATS")?, ), TCA_XSTATS => TcAttribute::Xstats( TcXstats::parse_with_param(buf, kind) .context("invalid TCA_XSTATS")?, ), TCA_RATE => TcAttribute::Rate(payload.to_vec()), TCA_FCNT => TcAttribute::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(TcStats2::parse_with_param(&nla, kind).context( format!("failed to parse TCA_STATS2 for kind {kind}"), )?); } TcAttribute::Stats2(nlas) } TCA_STAB => TcAttribute::Stab(payload.to_vec()), TCA_CHAIN => TcAttribute::Chain( parse_u32(payload).context("failed to parse TCA_CHAIN")?, ), TCA_HW_OFFLOAD => TcAttribute::HwOffload( parse_u8(payload).context("failed to parse TCA_HW_OFFLOAD")?, ), TCA_DUMP_INVISIBLE => TcAttribute::DumpInvisible(true), _ => TcAttribute::Other( DefaultNla::parse(buf).context("failed to parse tc nla")?, ), }) } } netlink-packet-route-0.21.0/src/tc/filters/cls_u32.rs000064400000000000000000000232221046102023000204370ustar 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::{DefaultNla, Nla, NlaBuffer, NlasIterator}, parsers::parse_u32, traits::{Emitable, Parseable}, DecodeError, }; use super::u32_flags::{TcU32OptionFlags, TcU32SelectorFlags}; use crate::tc::{TcAction, TcHandle}; const TC_U32_SEL_BUF_LEN: usize = 16; const TC_U32_KEY_BUF_LEN: usize = 16; const TCA_U32_CLASSID: u16 = 1; const TCA_U32_HASH: u16 = 2; const TCA_U32_LINK: u16 = 3; const TCA_U32_DIVISOR: u16 = 4; const TCA_U32_SEL: u16 = 5; const TCA_U32_POLICE: u16 = 6; const TCA_U32_ACT: u16 = 7; const TCA_U32_INDEV: u16 = 8; const TCA_U32_PCNT: u16 = 9; const TCA_U32_MARK: u16 = 10; const TCA_U32_FLAGS: u16 = 11; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct TcFilterU32 {} impl TcFilterU32 { pub const KIND: &'static str = "u32"; } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum TcFilterU32Option { ClassId(TcHandle), Hash(u32), Link(u32), Divisor(u32), Selector(TcU32Selector), Police(Vec), Action(Vec), Indev(Vec), Pnct(Vec), Mark(Vec), Flags(TcU32OptionFlags), Other(DefaultNla), } impl Nla for TcFilterU32Option { fn value_len(&self) -> usize { match self { Self::Police(b) | Self::Indev(b) | Self::Pnct(b) | Self::Mark(b) => b.len(), Self::Hash(_) | Self::Link(_) | Self::Divisor(_) | Self::Flags(_) => 4, Self::ClassId(_) => 4, Self::Selector(s) => s.buffer_len(), Self::Action(acts) => acts.as_slice().buffer_len(), Self::Other(attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Police(b) | Self::Indev(b) | Self::Pnct(b) | Self::Mark(b) => buffer.copy_from_slice(b.as_slice()), Self::Hash(i) | Self::Link(i) | Self::Divisor(i) => { NativeEndian::write_u32(buffer, *i) } Self::Flags(f) => NativeEndian::write_u32(buffer, f.bits()), Self::ClassId(i) => NativeEndian::write_u32(buffer, (*i).into()), Self::Selector(s) => s.emit(buffer), Self::Action(acts) => acts.as_slice().emit(buffer), Self::Other(attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::ClassId(_) => TCA_U32_CLASSID, Self::Hash(_) => TCA_U32_HASH, Self::Link(_) => TCA_U32_LINK, Self::Divisor(_) => TCA_U32_DIVISOR, Self::Selector(_) => TCA_U32_SEL, Self::Police(_) => TCA_U32_POLICE, Self::Action(_) => TCA_U32_ACT, Self::Indev(_) => TCA_U32_INDEV, Self::Pnct(_) => TCA_U32_PCNT, Self::Mark(_) => TCA_U32_MARK, Self::Flags(_) => TCA_U32_FLAGS, Self::Other(attr) => attr.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for TcFilterU32Option { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { TCA_U32_CLASSID => Self::ClassId(TcHandle::from( parse_u32(payload).context("failed to parse TCA_U32_UNSPEC")?, )), TCA_U32_HASH => Self::Hash( parse_u32(payload).context("failed to parse TCA_U32_HASH")?, ), TCA_U32_LINK => Self::Link( parse_u32(payload).context("failed to parse TCA_U32_LINK")?, ), TCA_U32_DIVISOR => Self::Divisor( parse_u32(payload) .context("failed to parse TCA_U32_DIVISOR")?, ), TCA_U32_SEL => Self::Selector( TcU32Selector::parse( &TcU32SelectorBuffer::new_checked(payload) .context("invalid TCA_U32_SEL")?, ) .context("failed to parse TCA_U32_SEL")?, ), TCA_U32_POLICE => Self::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( TcAction::parse(&act) .context("failed to parse TCA_U32_ACT")?, ); } Self::Action(acts) } TCA_U32_INDEV => Self::Indev(payload.to_vec()), TCA_U32_PCNT => Self::Pnct(payload.to_vec()), TCA_U32_MARK => Self::Mark(payload.to_vec()), TCA_U32_FLAGS => Self::Flags(TcU32OptionFlags::from_bits_retain( parse_u32(payload).context("failed to parse TCA_U32_FLAGS")?, )), _ => Self::Other( DefaultNla::parse(buf).context("failed to parse u32 nla")?, ), }) } } #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct TcU32Selector { pub flags: TcU32SelectorFlags, 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!(TcU32SelectorBuffer { 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..TC_U32_SEL_BUF_LEN), keys: (slice, TC_U32_SEL_BUF_LEN..), }); impl> TcU32SelectorBuffer { 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 < TC_U32_SEL_BUF_LEN { return Err(format!( "invalid TcU32SelectorBuffer: length {len} < {TC_U32_SEL_BUF_LEN}" ) .into()); } // Expect the buffer to be large enough to hold `nkeys`. let expected_len = ((self.nkeys() as usize) * TC_U32_KEY_BUF_LEN) + TC_U32_SEL_BUF_LEN; if len < expected_len { return Err(format!( "invalid RouteNextHopBuffer: length {} < {}", len, expected_len, ) .into()); } Ok(()) } } impl Emitable for TcU32Selector { fn buffer_len(&self) -> usize { TC_U32_SEL_BUF_LEN + (self.nkeys as usize * TC_U32_KEY_BUF_LEN) } fn emit(&self, buffer: &mut [u8]) { let mut packet = TcU32SelectorBuffer::new(buffer); packet.set_flags(self.flags.bits()); 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); let key_buf = packet.keys_mut(); for (i, k) in self.keys.iter().enumerate() { k.emit( &mut key_buf [(i * TC_U32_KEY_BUF_LEN)..((i + 1) * TC_U32_KEY_BUF_LEN)], ); } } } impl + ?Sized> Parseable> for TcU32Selector { fn parse(buf: &TcU32SelectorBuffer<&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 = TcU32KeyBuffer::new_checked( &key_payload [(i * TC_U32_KEY_BUF_LEN)..(i + 1) * TC_U32_KEY_BUF_LEN], ) .context("invalid u32 key")?; keys.push( TcU32Key::parse(&keybuf).context("failed to parse u32 key")?, ); } Ok(Self { flags: TcU32SelectorFlags::from_bits_retain(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 TcU32Key { pub mask: u32, pub val: u32, pub off: i32, pub offmask: i32, } buffer!(TcU32KeyBuffer(TC_U32_KEY_BUF_LEN) { mask: (u32, 0..4), val: (u32, 4..8), off: (i32, 8..12), offmask: (i32, 12..TC_U32_KEY_BUF_LEN), }); impl Emitable for TcU32Key { fn buffer_len(&self) -> usize { TC_U32_KEY_BUF_LEN } fn emit(&self, buffer: &mut [u8]) { let mut packet = TcU32KeyBuffer::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 TcU32Key { fn parse(buf: &TcU32KeyBuffer) -> Result { Ok(Self { mask: buf.mask(), val: buf.val(), off: buf.off(), offmask: buf.offmask(), }) } } netlink-packet-route-0.21.0/src/tc/filters/matchall.rs000064400000000000000000000061571046102023000207620ustar 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::{DefaultNla, Nla, NlaBuffer, NlasIterator}, parsers::parse_u32, traits::{Emitable, Parseable}, DecodeError, }; use crate::tc::{TcAction, TcHandle}; const TCA_MATCHALL_CLASSID: u16 = 1; const TCA_MATCHALL_ACT: u16 = 2; const TCA_MATCHALL_FLAGS: u16 = 3; const TCA_MATCHALL_PCNT: u16 = 4; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct TcFilterMatchAll {} impl TcFilterMatchAll { pub const KIND: &'static str = "matchall"; } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum TcFilterMatchAllOption { ClassId(TcHandle), Action(Vec), Pnct(Vec), Flags(u32), Other(DefaultNla), } impl Nla for TcFilterMatchAllOption { fn value_len(&self) -> usize { match self { Self::Pnct(b) => b.len(), Self::ClassId(_) => 4, Self::Flags(_) => 4, Self::Action(acts) => acts.as_slice().buffer_len(), Self::Other(attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Pnct(b) => buffer.copy_from_slice(b.as_slice()), Self::ClassId(i) => NativeEndian::write_u32(buffer, (*i).into()), Self::Flags(i) => NativeEndian::write_u32(buffer, *i), Self::Action(acts) => acts.as_slice().emit(buffer), Self::Other(attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::ClassId(_) => TCA_MATCHALL_CLASSID, Self::Action(_) => TCA_MATCHALL_ACT, Self::Pnct(_) => TCA_MATCHALL_PCNT, Self::Flags(_) => TCA_MATCHALL_FLAGS, Self::Other(attr) => attr.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for TcFilterMatchAllOption { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { TCA_MATCHALL_CLASSID => Self::ClassId( parse_u32(payload) .context("failed to parse TCA_MATCHALL_UNSPEC")? .into(), ), TCA_MATCHALL_ACT => { let mut acts = vec![]; for act in NlasIterator::new(payload) { let act = act.context("invalid TCA_MATCHALL_ACT")?; acts.push( TcAction::parse(&act) .context("failed to parse TCA_MATCHALL_ACT")?, ); } Self::Action(acts) } TCA_MATCHALL_PCNT => Self::Pnct(payload.to_vec()), TCA_MATCHALL_FLAGS => Self::Flags( parse_u32(payload) .context("failed to parse TCA_MATCHALL_FLAGS")?, ), _ => Self::Other( DefaultNla::parse(buf).context("failed to parse u32 nla")?, ), }) } } netlink-packet-route-0.21.0/src/tc/filters/mod.rs000064400000000000000000000004751046102023000177510ustar 00000000000000// SPDX-License-Identifier: MIT mod cls_u32; mod matchall; mod u32_flags; pub use self::cls_u32::{ TcFilterU32, TcFilterU32Option, TcU32Key, TcU32Selector, TcU32SelectorBuffer, }; pub use self::matchall::{TcFilterMatchAll, TcFilterMatchAllOption}; pub use u32_flags::{TcU32OptionFlags, TcU32SelectorFlags}; netlink-packet-route-0.21.0/src/tc/filters/u32_flags.rs000064400000000000000000000020551046102023000207530ustar 00000000000000// SPDX-License-Identifier: MIT const TC_U32_TERMINAL: u8 = 1; const TC_U32_OFFSET: u8 = 2; const TC_U32_VAROFFSET: u8 = 4; const TC_U32_EAT: u8 = 8; bitflags! { #[derive(Debug, Default, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub struct TcU32SelectorFlags: u8 { const Terminal = TC_U32_TERMINAL; const Offset = TC_U32_OFFSET; const VarOffset = TC_U32_VAROFFSET; const Eat = TC_U32_EAT; const _ = !0; } } const TCA_CLS_FLAGS_SKIP_HW: u32 = 1 << 0; const TCA_CLS_FLAGS_SKIP_SW: u32 = 1 << 1; const TCA_CLS_FLAGS_IN_HW: u32 = 1 << 2; const TCA_CLS_FLAGS_NOT_IN_HW: u32 = 1 << 3; const TCA_CLS_FLAGS_VERBOSE: u32 = 1 << 4; bitflags! { #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub struct TcU32OptionFlags: u32 { const SkipHw = TCA_CLS_FLAGS_SKIP_HW; const SkipSw = TCA_CLS_FLAGS_SKIP_SW; const InHw = TCA_CLS_FLAGS_IN_HW; const NotInHw = TCA_CLS_FLAGS_NOT_IN_HW; const Verbose = TCA_CLS_FLAGS_VERBOSE; const _ = !0; } } netlink-packet-route-0.21.0/src/tc/header.rs000064400000000000000000000060311046102023000167440ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::{NlaBuffer, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; use crate::AddressFamily; 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 attributes( &self, ) -> impl Iterator, DecodeError>> { NlasIterator::new(self.payload()) } } #[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct TcHeader { pub family: AddressFamily, // Interface index pub index: i32, // Qdisc handle pub handle: TcHandle, // Parent Qdisc pub parent: TcHandle, pub info: u32, } impl TcHeader { pub const TCM_IFINDEX_MAGIC_BLOCK: u32 = 0xFFFFFFFF; } 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.into()); packet.set_index(self.index); packet.set_handle(self.handle.into()); packet.set_parent(self.parent.into()); packet.set_info(self.info); } } impl> Parseable> for TcHeader { fn parse(buf: &TcMessageBuffer) -> Result { Ok(Self { family: buf.family().into(), index: buf.index(), handle: buf.handle().into(), parent: buf.parent().into(), info: buf.info(), }) } } #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] pub struct TcHandle { pub major: u16, pub minor: u16, } impl TcHandle { pub const UNSPEC: Self = Self { major: 0, minor: 0 }; pub const ROOT: Self = Self { major: u16::MAX, minor: u16::MAX, }; pub const INGRESS: Self = Self { major: u16::MAX, minor: 0xfff1, }; pub const CLSACT: Self = Self::INGRESS; pub const MIN_PRIORITY: u16 = 0xFFE0; pub const MIN_INGRESS: u16 = 0xFFF2; pub const MIN_EGRESS: u16 = 0xFFF3; } impl From for TcHandle { fn from(d: u32) -> Self { let bytes = d.to_be_bytes(); Self { major: u16::from_be_bytes([bytes[0], bytes[1]]), minor: u16::from_be_bytes([bytes[2], bytes[3]]), } } } impl From for u32 { fn from(v: TcHandle) -> u32 { let major_bytes = v.major.to_be_bytes(); let minor_bytes = v.minor.to_be_bytes(); u32::from_be_bytes([ major_bytes[0], major_bytes[1], minor_bytes[0], minor_bytes[1], ]) } } impl std::fmt::Display for TcHandle { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}:{}", self.major, self.minor) } } netlink-packet-route-0.21.0/src/tc/message.rs000064400000000000000000000042571046102023000171500ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ traits::{Emitable, Parseable, ParseableParametrized}, DecodeError, }; use super::{TcAttribute, TcHeader, TcMessageBuffer}; #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct TcMessage { pub header: TcHeader, pub attributes: Vec, } impl TcMessage { pub fn into_parts(self) -> (TcHeader, Vec) { (self.header, self.attributes) } pub fn from_parts(header: TcHeader, attributes: Vec) -> Self { TcMessage { header, attributes } } /// Create a new `TcMessage` with the given index pub fn with_index(index: i32) -> Self { Self { header: TcHeader { index, ..Default::default() }, attributes: Vec::new(), } } } 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")?, attributes: 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 attributes = vec![]; let mut kind = String::new(); for nla_buf in buf.attributes() { let attribute = TcAttribute::parse_with_param(&nla_buf?, kind.as_str())?; if let TcAttribute::Kind(s) = &attribute { kind = s.to_string(); } attributes.push(attribute) } Ok(attributes) } } impl Emitable for TcMessage { fn buffer_len(&self) -> usize { self.header.buffer_len() + self.attributes.as_slice().buffer_len() } fn emit(&self, buffer: &mut [u8]) { self.header.emit(buffer); self.attributes .as_slice() .emit(&mut buffer[self.header.buffer_len()..]); } } netlink-packet-route-0.21.0/src/tc/mod.rs000064400000000000000000000025101046102023000162710ustar 00000000000000// SPDX-License-Identifier: MIT mod actions; mod attribute; mod filters; mod header; mod message; mod options; mod qdiscs; mod stats; pub use self::actions::{ TcAction, TcActionAttribute, TcActionGeneric, TcActionGenericBuffer, TcActionMessage, TcActionMessageAttribute, TcActionMessageBuffer, TcActionMessageFlags, TcActionMessageFlagsWithSelector, TcActionMirror, TcActionMirrorOption, TcActionNat, TcActionNatOption, TcActionOption, TcActionType, TcMirror, TcMirrorActionType, TcMirrorBuffer, TcNat, TcNatBuffer, TcNatFlags, }; pub use self::attribute::TcAttribute; pub use self::filters::{ TcFilterMatchAll, TcFilterMatchAllOption, TcFilterU32, TcFilterU32Option, TcU32Key, TcU32OptionFlags, TcU32Selector, TcU32SelectorBuffer, TcU32SelectorFlags, }; pub use self::header::{TcHandle, TcHeader, TcMessageBuffer}; pub use self::message::TcMessage; pub use self::options::TcOption; pub use self::qdiscs::{ TcFqCodelClStats, TcFqCodelClStatsBuffer, TcFqCodelQdStats, TcFqCodelQdStatsBuffer, TcFqCodelXstats, TcQdiscFqCodel, TcQdiscFqCodelOption, TcQdiscIngress, TcQdiscIngressOption, }; pub use self::stats::{ TcStats, TcStats2, TcStatsBasic, TcStatsBasicBuffer, TcStatsBuffer, TcStatsQueue, TcStatsQueueBuffer, TcXstats, }; pub(crate) use self::options::VecTcOption; #[cfg(test)] mod tests; netlink-packet-route-0.21.0/src/tc/options.rs000064400000000000000000000102231046102023000172050ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, traits::{Parseable, ParseableParametrized}, DecodeError, }; use super::{ TcFilterMatchAll, TcFilterMatchAllOption, TcFilterU32, TcFilterU32Option, TcQdiscFqCodel, TcQdiscFqCodelOption, TcQdiscIngress, TcQdiscIngressOption, }; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum TcOption { FqCodel(TcQdiscFqCodelOption), // Qdisc specific options Ingress(TcQdiscIngressOption), // Filter specific options U32(TcFilterU32Option), // matchall options MatchAll(TcFilterMatchAllOption), // Other options Other(DefaultNla), } impl Nla for TcOption { fn value_len(&self) -> usize { match self { Self::FqCodel(u) => u.value_len(), Self::Ingress(u) => u.value_len(), 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::FqCodel(u) => u.emit_value(buffer), Self::Ingress(u) => u.emit_value(buffer), 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::FqCodel(u) => u.kind(), Self::Ingress(u) => u.kind(), Self::U32(u) => u.kind(), Self::MatchAll(m) => m.kind(), Self::Other(o) => o.kind(), } } } impl<'a, T> ParseableParametrized, &str> for TcOption where T: AsRef<[u8]> + ?Sized, { fn parse_with_param( buf: &NlaBuffer<&'a T>, kind: &str, ) -> Result { Ok(match kind { TcQdiscIngress::KIND => { Self::Ingress(TcQdiscIngressOption::parse(buf).context( "failed to parse ingress TCA_OPTIONS attributes", )?) } TcQdiscFqCodel::KIND => { Self::FqCodel(TcQdiscFqCodelOption::parse(buf).context( "failed to parse fq_codel TCA_OPTIONS attributes", )?) } TcFilterU32::KIND => Self::U32( TcFilterU32Option::parse(buf) .context("failed to parse u32 TCA_OPTIONS attributes")?, ), TcFilterMatchAll::KIND => { Self::MatchAll(TcFilterMatchAllOption::parse(buf).context( "failed to parse matchall TCA_OPTIONS attributes", )?) } _ => Self::Other(DefaultNla::parse(buf)?), }) } } pub(crate) struct VecTcOption(pub(crate) Vec); impl<'a, T> ParseableParametrized, &str> for VecTcOption where T: AsRef<[u8]> + ?Sized, { fn parse_with_param( buf: &NlaBuffer<&'a T>, kind: &str, ) -> Result { Ok(match kind { TcFilterU32::KIND | TcFilterMatchAll::KIND | TcQdiscIngress::KIND | TcQdiscFqCodel::KIND => { let mut nlas = vec![]; for nla in NlasIterator::new(buf.value()) { let nla = nla.context(format!( "Invalid TCA_OPTIONS for kind: {kind}", ))?; nlas.push( TcOption::parse_with_param(&nla, kind).context( format!( "Failed to parse TCA_OPTIONS for kind: {kind}", ), )?, ) } Self(nlas) } // Kernel has no guide line or code indicate the scheduler // should place a nla_nest here. The `sfq` qdisc kernel code is // using single NLA instead nested ones. Hence we are storing // unknown Nla as Vec with single item. _ => Self(vec![TcOption::Other(DefaultNla::parse(buf)?)]), }) } } netlink-packet-route-0.21.0/src/tc/qdiscs/fq_codel.rs000064400000000000000000000255171046102023000205700ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::{parse_u32, parse_u8}, traits::{Emitable, Parseable}, DecodeError, }; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct TcQdiscFqCodel {} impl TcQdiscFqCodel { pub(crate) const KIND: &'static str = "fq_codel"; } const TC_FQ_CODEL_QD_STATS_LEN: usize = 36; const TC_FQ_CODEL_CL_STATS_LEN: usize = 24; const TCA_FQ_CODEL_XSTATS_QDISC: u32 = 0; const TCA_FQ_CODEL_XSTATS_CLASS: u32 = 1; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum TcFqCodelXstats { Qdisc(TcFqCodelQdStats), Class(TcFqCodelClStats), Other(Vec), } impl + ?Sized> Parseable for TcFqCodelXstats { fn parse(buf: &T) -> Result { if buf.as_ref().len() < 4 { return Err(DecodeError::from(format!( "Invalid TcFqCodelXstats {:?}", buf.as_ref() ))); } let mut buf_type_bytes = [0; 4]; buf_type_bytes.copy_from_slice(&buf.as_ref()[0..4]); let buf_type = u32::from_ne_bytes(buf_type_bytes); match buf_type { TCA_FQ_CODEL_XSTATS_QDISC => { Ok(Self::Qdisc(TcFqCodelQdStats::parse( &TcFqCodelQdStatsBuffer::new(&buf.as_ref()[4..]), )?)) } TCA_FQ_CODEL_XSTATS_CLASS => { Ok(Self::Class(TcFqCodelClStats::parse( &TcFqCodelClStatsBuffer::new(&buf.as_ref()[4..]), )?)) } _ => Ok(Self::Other(buf.as_ref().to_vec())), } } } impl Emitable for TcFqCodelXstats { fn buffer_len(&self) -> usize { match self { Self::Qdisc(_) => { TC_FQ_CODEL_QD_STATS_LEN + std::mem::size_of::() } Self::Class(_) => { TC_FQ_CODEL_CL_STATS_LEN + std::mem::size_of::() } Self::Other(v) => v.len(), } } fn emit(&self, buffer: &mut [u8]) { match self { Self::Qdisc(v) => { buffer[0..4] .copy_from_slice(&TCA_FQ_CODEL_XSTATS_QDISC.to_ne_bytes()); v.emit(&mut buffer[4..]); } Self::Class(v) => { buffer[0..4] .copy_from_slice(&TCA_FQ_CODEL_XSTATS_CLASS.to_ne_bytes()); v.emit(&mut buffer[4..]); } Self::Other(v) => buffer.copy_from_slice(v.as_slice()), } } } #[derive(Default, Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub struct TcFqCodelQdStats { pub maxpacket: u32, pub drop_overlimit: u32, pub ecn_mark: u32, pub new_flow_count: u32, pub new_flows_len: u32, pub old_flows_len: u32, pub ce_mark: u32, pub memory_usage: u32, pub drop_overmemory: u32, } buffer!(TcFqCodelQdStatsBuffer(TC_FQ_CODEL_QD_STATS_LEN) { maxpacket: (u32, 0..4), drop_overlimit: (u32, 4..8), ecn_mark: (u32, 8..12), new_flow_count: (u32, 12..16), new_flows_len: (u32, 16..20), old_flows_len: (u32, 20..24), ce_mark: (u32, 24..28), memory_usage: (u32, 28..32), drop_overmemory: (u32,32..36), }); impl> Parseable> for TcFqCodelQdStats { fn parse(buf: &TcFqCodelQdStatsBuffer) -> Result { Ok(Self { maxpacket: buf.maxpacket(), drop_overlimit: buf.drop_overlimit(), ecn_mark: buf.ecn_mark(), new_flow_count: buf.new_flow_count(), new_flows_len: buf.new_flows_len(), old_flows_len: buf.old_flows_len(), ce_mark: buf.ce_mark(), memory_usage: buf.memory_usage(), drop_overmemory: buf.drop_overmemory(), }) } } impl Emitable for TcFqCodelQdStats { fn buffer_len(&self) -> usize { TC_FQ_CODEL_QD_STATS_LEN + std::mem::size_of::() } fn emit(&self, buffer: &mut [u8]) { let mut buffer = TcFqCodelQdStatsBuffer::new(buffer); buffer.set_maxpacket(self.maxpacket); buffer.set_drop_overlimit(self.drop_overlimit); buffer.set_ecn_mark(self.ecn_mark); buffer.set_new_flow_count(self.new_flow_count); buffer.set_new_flows_len(self.new_flows_len); buffer.set_old_flows_len(self.old_flows_len); buffer.set_ce_mark(self.ce_mark); buffer.set_memory_usage(self.memory_usage); buffer.set_drop_overmemory(self.drop_overmemory); } } #[derive(Default, Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub struct TcFqCodelClStats { deficit: i32, ldelay: u32, count: u32, lastcount: u32, dropping: u32, drop_next: i32, } buffer!(TcFqCodelClStatsBuffer(TC_FQ_CODEL_CL_STATS_LEN) { deficit: (i32, 0..4), ldelay: (u32,4..8), count: (u32, 8..12), lastcount: (u32, 12..16), dropping: (u32, 16..20), drop_next: (i32, 20..24), }); impl> Parseable> for TcFqCodelClStats { fn parse(buf: &TcFqCodelClStatsBuffer) -> Result { Ok(Self { deficit: buf.deficit(), ldelay: buf.ldelay(), count: buf.count(), lastcount: buf.lastcount(), dropping: buf.dropping(), drop_next: buf.drop_next(), }) } } impl Emitable for TcFqCodelClStats { fn buffer_len(&self) -> usize { TC_FQ_CODEL_CL_STATS_LEN + std::mem::size_of::() } fn emit(&self, buffer: &mut [u8]) { let mut buffer = TcFqCodelClStatsBuffer::new(buffer); buffer.set_deficit(self.deficit); buffer.set_ldelay(self.ldelay); buffer.set_count(self.count); buffer.set_lastcount(self.lastcount); buffer.set_dropping(self.dropping); buffer.set_drop_next(self.drop_next); } } const TCA_FQ_CODEL_TARGET: u16 = 1; const TCA_FQ_CODEL_LIMIT: u16 = 2; const TCA_FQ_CODEL_INTERVAL: u16 = 3; const TCA_FQ_CODEL_ECN: u16 = 4; const TCA_FQ_CODEL_FLOWS: u16 = 5; const TCA_FQ_CODEL_QUANTUM: u16 = 6; const TCA_FQ_CODEL_CE_THRESHOLD: u16 = 7; const TCA_FQ_CODEL_DROP_BATCH_SIZE: u16 = 8; const TCA_FQ_CODEL_MEMORY_LIMIT: u16 = 9; const TCA_FQ_CODEL_CE_THRESHOLD_SELECTOR: u16 = 10; const TCA_FQ_CODEL_CE_THRESHOLD_MASK: u16 = 11; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum TcQdiscFqCodelOption { Target(u32), Limit(u32), Interval(u32), Ecn(u32), Flows(u32), Quantum(u32), CeThreshold(u32), DropBatchSize(u32), MemoryLimit(u32), CeThresholdSelector(u8), CeThresholdMask(u8), Other(DefaultNla), } impl Nla for TcQdiscFqCodelOption { fn value_len(&self) -> usize { match self { Self::Target(_) | Self::Limit(_) | Self::Interval(_) | Self::Ecn(_) | Self::Flows(_) | Self::Quantum(_) | Self::CeThreshold(_) | Self::DropBatchSize(_) | Self::MemoryLimit(_) => 4, Self::CeThresholdSelector(_) | Self::CeThresholdMask(_) => 1, Self::Other(attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Target(d) | Self::Limit(d) | Self::Interval(d) | Self::Ecn(d) | Self::Flows(d) | Self::Quantum(d) | Self::CeThreshold(d) | Self::DropBatchSize(d) | Self::MemoryLimit(d) => NativeEndian::write_u32(buffer, *d), Self::CeThresholdSelector(d) | Self::CeThresholdMask(d) => { buffer[0] = *d } Self::Other(attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Target(_) => TCA_FQ_CODEL_TARGET, Self::Limit(_) => TCA_FQ_CODEL_LIMIT, Self::Interval(_) => TCA_FQ_CODEL_INTERVAL, Self::Ecn(_) => TCA_FQ_CODEL_ECN, Self::Flows(_) => TCA_FQ_CODEL_FLOWS, Self::Quantum(_) => TCA_FQ_CODEL_QUANTUM, Self::CeThreshold(_) => TCA_FQ_CODEL_CE_THRESHOLD, Self::DropBatchSize(_) => TCA_FQ_CODEL_DROP_BATCH_SIZE, Self::MemoryLimit(_) => TCA_FQ_CODEL_MEMORY_LIMIT, Self::CeThresholdSelector(_) => TCA_FQ_CODEL_CE_THRESHOLD_SELECTOR, Self::CeThresholdMask(_) => TCA_FQ_CODEL_CE_THRESHOLD_MASK, Self::Other(attr) => attr.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for TcQdiscFqCodelOption { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { TCA_FQ_CODEL_TARGET => Self::Target( parse_u32(payload) .context("failed to parse TCA_FQ_CODEL_TARGET")?, ), TCA_FQ_CODEL_LIMIT => Self::Limit( parse_u32(payload) .context("failed to parse TCA_FQ_CODEL_LIMIT")?, ), TCA_FQ_CODEL_INTERVAL => Self::Interval( parse_u32(payload) .context("failed to parse TCA_FQ_CODEL_INTERVAL")?, ), TCA_FQ_CODEL_ECN => Self::Ecn( parse_u32(payload) .context("failed to parse TCA_FQ_CODEL_ECN")?, ), TCA_FQ_CODEL_FLOWS => Self::Flows( parse_u32(payload) .context("failed to parse TCA_FQ_CODEL_FLOWS")?, ), TCA_FQ_CODEL_QUANTUM => Self::Quantum( parse_u32(payload) .context("failed to parse TCA_FQ_CODEL_QUANTUM")?, ), TCA_FQ_CODEL_CE_THRESHOLD => Self::CeThreshold( parse_u32(payload) .context("failed to parse TCA_FQ_CODEL_CETHRESHOLD")?, ), TCA_FQ_CODEL_DROP_BATCH_SIZE => Self::DropBatchSize( parse_u32(payload) .context("failed to parse TCA_FQ_CODEL_DROP_BATCH_SIZE")?, ), TCA_FQ_CODEL_MEMORY_LIMIT => Self::MemoryLimit( parse_u32(payload) .context("failed to parse TCA_FQ_CODEL_MEMORY_LIMIT")?, ), TCA_FQ_CODEL_CE_THRESHOLD_SELECTOR => { Self::CeThresholdSelector(parse_u8(payload).context( "failed to parse TCA_FQ_CODEL_CE_THRESHOLD_SELECTOR", )?) } TCA_FQ_CODEL_CE_THRESHOLD_MASK => { Self::CeThresholdMask(parse_u8(payload).context( "failed to parse TCA_FQ_CODEL_CE_THRESHOLD_MASK", )?) } _ => Self::Other( DefaultNla::parse(buf).context("failed to parse u32 nla")?, ), }) } } netlink-packet-route-0.21.0/src/tc/qdiscs/ingress.rs000064400000000000000000000023521046102023000204560ustar 00000000000000// SPDX-License-Identifier: MIT // Currently, the qdisc ingress does not have any attribute, kernel // just start a empty nla_nest. This is just a place holder use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, DecodeError, Parseable, }; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct TcQdiscIngress {} #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum TcQdiscIngressOption { Other(DefaultNla), } impl TcQdiscIngress { pub(crate) const KIND: &'static str = "ingress"; } impl Nla for TcQdiscIngressOption { fn value_len(&self) -> usize { match self { Self::Other(attr) => attr.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Other(attr) => attr.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::Other(attr) => attr.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for TcQdiscIngressOption { fn parse(buf: &NlaBuffer<&'a T>) -> Result { Ok(Self::Other( DefaultNla::parse(buf).context("failed to parse ingress nla")?, )) } } netlink-packet-route-0.21.0/src/tc/qdiscs/mod.rs000064400000000000000000000004601046102023000175610ustar 00000000000000// SPDX-License-Identifier: MIT mod fq_codel; mod ingress; pub use self::fq_codel::{ TcFqCodelClStats, TcFqCodelClStatsBuffer, TcFqCodelQdStats, TcFqCodelQdStatsBuffer, TcFqCodelXstats, TcQdiscFqCodel, TcQdiscFqCodelOption, }; pub use self::ingress::{TcQdiscIngress, TcQdiscIngressOption}; netlink-packet-route-0.21.0/src/tc/stats/basic.rs000064400000000000000000000021151046102023000177320ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; /// Byte/Packet throughput statistics #[derive(Default, Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub struct TcStatsBasic { /// number of seen bytes pub bytes: u64, /// number of seen packets pub packets: u32, } // real size is 12, but kernel is align to 64bits(8 bytes) const STATS_BASIC_LEN: usize = 16; buffer!(TcStatsBasicBuffer(STATS_BASIC_LEN) { bytes: (u64, 0..8), packets: (u32, 8..12), }); impl> Parseable> for TcStatsBasic { fn parse(buf: &TcStatsBasicBuffer) -> Result { Ok(TcStatsBasic { bytes: buf.bytes(), packets: buf.packets(), }) } } impl Emitable for TcStatsBasic { fn buffer_len(&self) -> usize { STATS_BASIC_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = TcStatsBasicBuffer::new(buffer); buffer.set_bytes(self.bytes); buffer.set_packets(self.packets); } } netlink-packet-route-0.21.0/src/tc/stats/compat.rs000064400000000000000000000036411046102023000201410ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; /// Generic queue statistics #[derive(Default, Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub struct TcStats { /// 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, } // real size is 36, but kernel is align to 64bits(8 bytes) const STATS_LEN: usize = 40; buffer!(TcStatsBuffer(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 TcStats { fn parse(buf: &TcStatsBuffer) -> 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 TcStats { fn buffer_len(&self) -> usize { STATS_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = TcStatsBuffer::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.21.0/src/tc/stats/mod.rs000064400000000000000000000004761046102023000174400ustar 00000000000000// SPDX-License-Identifier: MIT mod basic; mod compat; mod queue; mod stats2; mod xstats; pub use self::basic::{TcStatsBasic, TcStatsBasicBuffer}; pub use self::compat::{TcStats, TcStatsBuffer}; pub use self::queue::{TcStatsQueue, TcStatsQueueBuffer}; pub use self::stats2::TcStats2; pub use self::xstats::TcXstats; netlink-packet-route-0.21.0/src/tc/stats/queue.rs000064400000000000000000000027361046102023000200060ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; /// Queuing statistics #[derive(Default, Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub struct TcStatsQueue { /// 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, } const STATS_QUEUE_LEN: usize = 20; buffer!(TcStatsQueueBuffer( 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 TcStatsQueue { fn parse(buf: &TcStatsQueueBuffer) -> Result { Ok(Self { qlen: buf.qlen(), backlog: buf.backlog(), drops: buf.drops(), requeues: buf.requeues(), overlimits: buf.overlimits(), }) } } impl Emitable for TcStatsQueue { fn buffer_len(&self) -> usize { STATS_QUEUE_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = TcStatsQueueBuffer::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.21.0/src/tc/stats/stats2.rs000064400000000000000000000047631046102023000201040ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, traits::{Emitable, Parseable, ParseableParametrized}, DecodeError, }; use super::{ TcStatsBasic, TcStatsBasicBuffer, TcStatsQueue, TcStatsQueueBuffer, TcXstats, }; const TCA_STATS_BASIC: u16 = 1; // const TCA_STATS_RATE_EST: u16 = 2; // TODO const TCA_STATS_QUEUE: u16 = 3; const TCA_STATS_APP: u16 = 4; // const TCA_STATS_RATE_EST64: u16 = 5; // TODO // const TCA_STATS_PAD: u16 = 6; const TCA_STATS_BASIC_HW: u16 = 7; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum TcStats2 { App(TcXstats), Basic(TcStatsBasic), Queue(TcStatsQueue), BasicHw(TcStatsBasic), Other(DefaultNla), } impl Nla for TcStats2 { fn value_len(&self) -> usize { match self { Self::App(v) => v.buffer_len(), Self::Basic(v) => v.buffer_len(), Self::Queue(v) => v.buffer_len(), Self::BasicHw(v) => v.buffer_len(), Self::Other(ref nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::App(v) => v.emit(buffer), Self::Basic(v) => v.emit(buffer), Self::Queue(v) => v.emit(buffer), Self::BasicHw(v) => v.emit(buffer), Self::Other(ref nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { match self { Self::App(_) => TCA_STATS_APP, Self::Basic(_) => TCA_STATS_BASIC, Self::Queue(_) => TCA_STATS_QUEUE, Self::BasicHw(_) => TCA_STATS_BASIC_HW, Self::Other(ref nla) => nla.kind(), } } } impl<'a, T> ParseableParametrized, &str> for TcStats2 where T: AsRef<[u8]> + ?Sized, { fn parse_with_param( buf: &NlaBuffer<&'a T>, kind: &str, ) -> Result { let payload = buf.value(); Ok(match buf.kind() { TCA_STATS_APP => Self::App(TcXstats::parse_with_param(buf, kind)?), TCA_STATS_BASIC => Self::Basic(TcStatsBasic::parse( &TcStatsBasicBuffer::new(payload), )?), TCA_STATS_QUEUE => Self::Queue(TcStatsQueue::parse( &TcStatsQueueBuffer::new(payload), )?), TCA_STATS_BASIC_HW => Self::BasicHw(TcStatsBasic::parse( &TcStatsBasicBuffer::new(payload), )?), _ => Self::Other(DefaultNla::parse(buf)?), }) } } netlink-packet-route-0.21.0/src/tc/stats/xstats.rs000064400000000000000000000022301046102023000201750ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::NlaBuffer, traits::{Emitable, Parseable, ParseableParametrized}, DecodeError, }; use crate::tc::{TcFqCodelXstats, TcQdiscFqCodel}; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum TcXstats { FqCodel(TcFqCodelXstats), Other(Vec), } impl Emitable for TcXstats { fn buffer_len(&self) -> usize { match self { Self::FqCodel(v) => v.buffer_len(), Self::Other(v) => v.len(), } } fn emit(&self, buffer: &mut [u8]) { match self { Self::FqCodel(v) => v.emit(buffer), Self::Other(v) => buffer.copy_from_slice(v.as_slice()), } } } impl<'a, T> ParseableParametrized, &str> for TcXstats where T: AsRef<[u8]> + ?Sized, { fn parse_with_param( buf: &NlaBuffer<&'a T>, kind: &str, ) -> Result { Ok(match kind { TcQdiscFqCodel::KIND => { TcXstats::FqCodel(TcFqCodelXstats::parse(buf.value())?) } _ => TcXstats::Other(buf.value().to_vec()), }) } } netlink-packet-route-0.21.0/src/tc/tests/filter_matchall.rs000064400000000000000000000162721046102023000220200ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::{ tc::{ TcAction, TcActionAttribute, TcActionGeneric, TcActionMirrorOption, TcActionOption, TcActionType, TcAttribute, TcFilterMatchAllOption, TcHandle, TcHeader, TcMessage, TcMessageBuffer, TcMirror, TcMirrorActionType, TcOption, TcStats2, TcStatsBasic, TcStatsQueue, }, AddressFamily, }; // Setup: // ip link add dummy1 type dummy // ip link set dummy1 up // ip link add dummy2 type dummy // ip link set dummy2 up // tc qdisc add dev dummy1 handle ffff: ingress // tc filter add dev dummy1 parent ffff: matchall \ // action mirred egress mirror dev dummy2 // // Capture nlmon of this command: // // tc -s filter show dev dummy1 // // Raw packet modification: // * rtnetlink header removed. #[test] fn test_get_filter_matchall() { let raw = vec![ 0x00, // AF_UNSPEC 0x00, 0x00, 0x00, // padding 0x32, 0x00, 0x00, 0x00, // iface index 50 0x01, 0x00, 0x00, 0x00, // handle 0:1 0x00, 0x00, 0x01, 0x00, // parent 1:0 0x00, 0x03, 0x00, 0xc0, // info: 3221226240, TODO: no idea 0x0d, 0x00, // length 13 0x01, 0x00, // TCA_KIND 0x6d, 0x61, 0x74, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, // "matchall\0" and 3 bytes pad 0x08, 0x00, // length 8 0x0b, 0x00, // TCA_CHAIN 0x00, 0x00, 0x00, 0x00, // chain: 0 0xc0, 0x00, // length 192 0x02, 0x00, // TCA_OPTIONS for `matchall` 0x08, 0x00, // length 8 0x03, 0x00, // TCA_MATCHALL_FLAGS 0x08, 0x00, 0x00, 0x00, // flags: 8 0x0c, 0x00, // length 12 0x04, 0x00, // TCA_MATCHALL_PCNT 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // TODO 0xa8, 0x00, // length 168 0x02, 0x00, // TCA_MATCHALL_ACT 0xa4, 0x00, // length 164 0x01, 0x00, // TCA_ACT_TAB 0x0b, 0x00, // length 11 0x01, 0x00, // TCA_ACT_KIND 0x6d, 0x69, 0x72, 0x72, 0x65, 0x64, 0x00, 0x00, // "mirred\0" and 1 padding byte 0x44, 0x00, // length 68 0x04, 0x00, // TCA_ACT_STATS 0x14, 0x00, // length 14 0x01, 0x00, // TCA_STATS_BASIC 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // bytes: 70 0x01, 0x00, 0x00, 0x00, // packet 1 0x00, 0x00, 0x00, 0x00, // padding 0x14, 0x00, // length 14 0x07, 0x00, // TCA_STATS_BASIC_HW 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // bytes: 0 0x00, 0x00, 0x00, 0x00, // packet 0 0x00, 0x00, 0x00, 0x00, // padding 0x18, 0x00, // length 24 0x03, 0x00, // TCA_STATS_QUEUE 0x00, 0x00, 0x00, 0x00, // qlen: 0 0x00, 0x00, 0x00, 0x00, // backlog: 0 0x00, 0x00, 0x00, 0x00, // drops: 0 0x00, 0x00, 0x00, 0x00, // requeues: 0 0x00, 0x00, 0x00, 0x00, // overlimits: 0 0x08, 0x00, // length 8 0x0a, 0x00, // TCA_ACT_IN_HW_COUNT 0x00, 0x00, 0x00, 0x00, // 0 0x48, 0x00, // length 72 0x02, 0x80, // TCA_ACT_OPTIONS 0x20, 0x00, // length 32 0x02, 0x00, // TCA_MIRRED_PARMS 0x01, 0x00, 0x00, 0x00, // index 1 0x00, 0x00, 0x00, 0x00, // capab 0 0x03, 0x00, 0x00, 0x00, // action 3 0x01, 0x00, 0x00, 0x00, // refcount 1 0x01, 0x00, 0x00, 0x00, // bindcnt 1 0x02, 0x00, 0x00, 0x00, // eaction 2 0x33, 0x00, 0x00, 0x00, // ifindex 51 0x24, 0x00, // length 36 0x01, 0x00, // TCA_MIRRED_TM 0x90, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // TODO TCA_MIRRED_TM ]; let expected = TcMessage { header: TcHeader { family: AddressFamily::Unspec, index: 50, handle: TcHandle { major: 0, minor: 1 }, parent: TcHandle { major: 1, minor: 0 }, info: 3221226240, // TODO(Gris Ge): What's this }, attributes: vec![ TcAttribute::Kind("matchall".to_string()), TcAttribute::Chain(0), TcAttribute::Options(vec![ TcOption::MatchAll(TcFilterMatchAllOption::Flags(8)), TcOption::MatchAll(TcFilterMatchAllOption::Pnct(vec![ 1, 0, 0, 0, 0, 0, 0, 0, // TODO(Gris Ge) ])), TcOption::MatchAll(TcFilterMatchAllOption::Action(vec![ TcAction { tab: 1, attributes: vec![ TcActionAttribute::Kind("mirred".to_string()), TcActionAttribute::Stats(vec![ TcStats2::Basic(TcStatsBasic { bytes: 70, packets: 1, }), TcStats2::BasicHw(TcStatsBasic { bytes: 0, packets: 0, }), TcStats2::Queue(TcStatsQueue { qlen: 0, backlog: 0, drops: 0, requeues: 0, overlimits: 0, }), ]), TcActionAttribute::InHwCount(0), TcActionAttribute::Options(vec![ TcActionOption::Mirror( TcActionMirrorOption::Parms(TcMirror { generic: TcActionGeneric { index: 1, capab: 0, action: TcActionType::Pipe, refcnt: 1, bindcnt: 1, }, eaction: TcMirrorActionType::EgressMirror, ifindex: 51, }), ), TcActionOption::Mirror( // TODO(Gris Ge) TcActionMirrorOption::Tm(vec![ 144, 3, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, ]), ), ]), ], }, ])), ]), ], }; assert_eq!( expected, TcMessage::parse(&TcMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/tc/tests/filter_u32.rs000064400000000000000000000116171046102023000206420ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::Ipv4Addr; use netlink_packet_utils::{Emitable, Parseable}; use crate::{ tc::{ filters::{TcU32OptionFlags, TcU32SelectorFlags}, TcAttribute, TcFilterU32Option, TcHandle, TcHeader, TcMessage, TcMessageBuffer, TcOption, TcU32Key, TcU32Selector, TcU32SelectorBuffer, }, AddressFamily, }; // Setup: // ip link add veth1 type veth peer veth1.peer // ip link set veth1 up // ip link set veth1.peer up // tc qdisc add dev veth1 root handle 1: prio bands 4 // tc qdisc add dev veth1 parent 1:4 handle 40: netem loss 10% delay 40ms // tc filter add dev veth1 \ // protocol ip parent 1:0 prio 4 u32 match ip dst \ // 192.168.190.7 match ip dport 36000 0xffff flowid 1:4 // // Capture nlmon of this command: // // tc -s filter show dev veth1 // // Raw packet modification: // * rtnetlink header removed. #[test] fn test_get_filter_u32() { let raw = vec![ 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x80, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x01, 0x00, 0x75, 0x33, 0x32, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x02, 0x00, 0x34, 0x00, 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xa8, 0xbe, 0x07, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x8c, 0xa0, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x00, 0x00, 0x24, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let expected = TcMessage { header: TcHeader { family: AddressFamily::Unspec, index: 35, handle: TcHandle { major: 0x8000, minor: 0x800, }, parent: TcHandle { major: 1, minor: 0 }, info: 262152, }, attributes: vec![ TcAttribute::Kind("u32".to_string()), TcAttribute::Chain(0), TcAttribute::Options(vec![ TcOption::U32(TcFilterU32Option::Selector(TcU32Selector { flags: TcU32SelectorFlags::Terminal, offshift: 0, nkeys: 2, offmask: 0, off: 0, offoff: 0, hoff: 0, hmask: 0, keys: vec![ TcU32Key { mask: 0xffffffff, val: u32::from_ne_bytes( Ipv4Addr::new(192, 168, 190, 7).octets(), ), off: 16, offmask: 0, }, TcU32Key { mask: 0xffff0000, val: u32::from_be(36000), off: 20, offmask: 0, }, ], })), TcOption::U32(TcFilterU32Option::Hash(u32::from_be(0x80))), TcOption::U32(TcFilterU32Option::ClassId(TcHandle { major: 1, minor: 4, })), TcOption::U32(TcFilterU32Option::Flags( TcU32OptionFlags::NotInHw, )), TcOption::U32(TcFilterU32Option::Pnct(vec![0; 32])), // TODO ]), ], }; assert_eq!( expected, TcMessage::parse(&TcMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } // Verify that [`TcU32Selector`] fails to parse a buffer with an // invalid number of keys. #[test] fn test_tcu32_selector_invalid_nkeys() { // TC u32 selector buffer layout: // |byte0|byte1|byte2|byte3|byte4|byte5|byte6|byte7| // |-----|-----|-----|-----|-----|-----|-----|-----| // |flags|shift|nkeys|pad | offmask | off | // |-----|-----|-----|-----|-----|-----|-----|-----| // | offoff | hoff | hmask | // |-----|-----|-----|-----|-----|-----|-----|-----| // | keys | // | ... | let buffer = [ 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; assert!(TcU32SelectorBuffer::new_checked(buffer).is_err()); } netlink-packet-route-0.21.0/src/tc/tests/mod.rs000064400000000000000000000002411046102023000174320ustar 00000000000000// SPDX-License-Identifier: MIT #[cfg(test)] mod filter_matchall; #[cfg(test)] mod filter_u32; #[cfg(test)] mod qdisc_fq_codel; #[cfg(test)] mod qdisc_ingress; netlink-packet-route-0.21.0/src/tc/tests/qdisc_fq_codel.rs000064400000000000000000000163551046102023000216270ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::{ tc::{ TcAttribute, TcFqCodelQdStats, TcFqCodelXstats, TcHandle, TcHeader, TcMessage, TcMessageBuffer, TcOption, TcQdiscFqCodelOption, TcStats, TcStats2, TcStatsBasic, TcStatsQueue, TcXstats, }, AddressFamily, }; // Setup: // * Connect a OpenVPN // // Capture nlmon of this command: // // tc -s qdisc show dev openvpn0 // // Raw packet modification: // * rtnetlink header removed. #[test] fn test_get_qdisc_fq_codel() { let raw = vec![ 0x00, // AF_UNSPEC 0x00, 0x00, 0x00, // padding 0x1c, 0x00, 0x00, 0x00, // iface index: 28 0x00, 0x00, 0x00, 0x00, // handle 0:0 (TC_H_UNSPEC) 0xff, 0xff, 0xff, 0xff, // parent u32::MAX (TC_H_ROOT) 0x02, 0x00, 0x00, 0x00, // info(refcount): 2 0x0d, 0x00, // length 13 0x01, 0x00, // TCA_KIND 0x66, 0x71, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x6c, 0x00, 0x00, 0x00, 0x00, // "fq_codel\0" and 3 bytes pad 0x44, 0x00, // length 68 0x02, 0x00, // TCA_OPTIONS for `fq_codel` 0x08, 0x00, // length 8 0x01, 0x00, // TCA_FQ_CODEL_TARGET 0x87, 0x13, 0x00, 0x00, // 4999 0x08, 0x00, // length 8 0x02, 0x00, // TCA_FQ_CODEL_LIMIT 0x00, 0x28, 0x00, 0x00, // 10240 0x08, 0x00, // length 8 0x03, 0x00, // TCA_FQ_CODEL_INTERVAL 0x9f, 0x86, 0x01, 0x00, // 99999 0x08, 0x00, // length 8 0x04, 0x00, // TCA_FQ_CODEL_ECN 0x01, 0x00, 0x00, 0x00, // 1 0x08, 0x00, // length 8 0x06, 0x00, // TCA_FQ_CODEL_QUANTUM 0x3c, 0x05, 0x00, 0x00, // 1340 0x08, 0x00, // length 8 0x08, 0x00, // TCA_FQ_CODEL_DROP_BATCH_SIZE 0x40, 0x00, 0x00, 0x00, // 64 0x08, 0x00, // length 8 0x09, 0x00, // TCA_FQ_CODEL_MEMORY_LIMIT 0x00, 0x00, 0x00, 0x02, // 33554432 0x08, 0x00, // length 8 0x05, 0x00, // TCA_FQ_CODEL_FLOWS 0x00, 0x04, 0x00, 0x00, // 1024 0x05, 0x00, // length 5 0x0c, 0x00, // TCA_HW_OFFLOAD 0x00, 0x00, 0x00, 0x00, // 0 with padding 0x5c, 0x00, // length 92 0x07, 0x00, // TCA_STATS2 0x2c, 0x00, // length 44 0x04, 0x00, // TCA_STATS_APP 0x00, 0x00, 0x00, 0x00, // TCA_FQ_CODEL_XSTATS_QDISC 0x70, 0x01, 0x00, 0x00, // maxpacket: 368 0x00, 0x00, 0x00, 0x00, // drop_overlimit: 0 0x00, 0x00, 0x00, 0x00, // ecn_mark: 0 0x24, 0x00, 0x00, 0x00, // new_flow_count: 36 0x00, 0x00, 0x00, 0x00, // new_flows_len: 0 0x00, 0x00, 0x00, 0x00, // old_flows_len: 0 0x00, 0x00, 0x00, 0x00, // ce_mark: 0 0x00, 0x00, 0x00, 0x00, // memory_usage: 0 0x00, 0x00, 0x00, 0x00, // drop_overmemory: 0 0x14, 0x00, // length 20 0x01, 0x00, // TCA_STATS_BASIC 0xe2, 0x47, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, // bytes: 14698466 0xb9, 0x67, 0x01, 0x00, // packets: 92089 0x00, 0x00, 0x00, 0x00, // padding 0x18, 0x00, // length 24 0x03, 0x00, // TCA_STATS_QUEUE 0x00, 0x00, 0x00, 0x00, // qlen: 0 0x00, 0x00, 0x00, 0x00, // backlog: 0 0x00, 0x00, 0x00, 0x00, // drops: 0 0x00, 0x00, 0x00, 0x00, // requeues: 0 0x00, 0x00, 0x00, 0x00, // overlimits: 0 0x2c, 0x00, // length 44 0x03, 0x00, // TCA_STATS 0xe2, 0x47, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, // bytes: 14698466 0xb9, 0x67, 0x01, 0x00, // packets: 92089 0x00, 0x00, 0x00, 0x00, // drops: 0 0x00, 0x00, 0x00, 0x00, // overlimits: 0 0x00, 0x00, 0x00, 0x00, // bps: 0 0x00, 0x00, 0x00, 0x00, // pps: 0 0x00, 0x00, 0x00, 0x00, // qlen: 0 0x00, 0x00, 0x00, 0x00, // backlog: 0 0x00, 0x00, 0x00, 0x00, // padding 0x2c, 0x00, // length 44 0x04, 0x00, // TCA_XSTATS 0x00, 0x00, 0x00, 0x00, // TCA_FQ_CODEL_XSTATS_QDISC 0x70, 0x01, 0x00, 0x00, // maxpacket: 368 0x00, 0x00, 0x00, 0x00, // drop_overlimit: 0 0x00, 0x00, 0x00, 0x00, // ecn_mark: 0 0x24, 0x00, 0x00, 0x00, // new_flow_count: 36 0x00, 0x00, 0x00, 0x00, // new_flows_len: 0 0x00, 0x00, 0x00, 0x00, // old_flows_len: 0 0x00, 0x00, 0x00, 0x00, // ce_mark: 0 0x00, 0x00, 0x00, 0x00, // memory_usage: 0 0x00, 0x00, 0x00, 0x00, // drop_overmemory: 0 ]; let expected = TcMessage { header: TcHeader { family: AddressFamily::Unspec, index: 28, handle: TcHandle::UNSPEC, parent: TcHandle::ROOT, info: 2, }, attributes: vec![ TcAttribute::Kind("fq_codel".to_string()), TcAttribute::Options(vec![ TcOption::FqCodel(TcQdiscFqCodelOption::Target(4999)), TcOption::FqCodel(TcQdiscFqCodelOption::Limit(10240)), TcOption::FqCodel(TcQdiscFqCodelOption::Interval(99999)), TcOption::FqCodel(TcQdiscFqCodelOption::Ecn(1)), TcOption::FqCodel(TcQdiscFqCodelOption::Quantum(1340)), TcOption::FqCodel(TcQdiscFqCodelOption::DropBatchSize(64)), TcOption::FqCodel(TcQdiscFqCodelOption::MemoryLimit(33554432)), TcOption::FqCodel(TcQdiscFqCodelOption::Flows(1024)), ]), TcAttribute::HwOffload(0), TcAttribute::Stats2(vec![ TcStats2::App(TcXstats::FqCodel(TcFqCodelXstats::Qdisc( TcFqCodelQdStats { maxpacket: 368, drop_overlimit: 0, ecn_mark: 0, new_flow_count: 36, new_flows_len: 0, old_flows_len: 0, ce_mark: 0, memory_usage: 0, drop_overmemory: 0, }, ))), TcStats2::Basic(TcStatsBasic { bytes: 14698466, packets: 92089, }), TcStats2::Queue(TcStatsQueue { qlen: 0, backlog: 0, drops: 0, requeues: 0, overlimits: 0, }), ]), TcAttribute::Stats(TcStats { bytes: 14698466, packets: 92089, drops: 0, overlimits: 0, bps: 0, pps: 0, qlen: 0, backlog: 0, }), TcAttribute::Xstats(TcXstats::FqCodel(TcFqCodelXstats::Qdisc( TcFqCodelQdStats { maxpacket: 368, drop_overlimit: 0, ecn_mark: 0, new_flow_count: 36, new_flows_len: 0, old_flows_len: 0, ce_mark: 0, memory_usage: 0, drop_overmemory: 0, }, ))), ], }; assert_eq!( expected, TcMessage::parse(&TcMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/tc/tests/qdisc_ingress.rs000064400000000000000000000056611046102023000215230ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::{ tc::{ TcAttribute, TcHandle, TcHeader, TcMessage, TcMessageBuffer, TcStats, TcStats2, TcStatsBasic, TcStatsQueue, }, AddressFamily, }; // Setup: // ip link add veth1 type veth peer veth1.peer // ip link set veth1 up // ip link set veth1.peer up // tc qdisc add dev veth1 handle ffff: ingress // // Capture nlmon of this command: // // tc -s qdisc show dev veth1 // // Raw packet modification: // * rtnetlink header removed. #[test] fn test_get_qdisc_ingress() { let raw = vec![ 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf1, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x00, 0x04, 0x00, 0x02, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x07, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let expected = TcMessage { header: TcHeader { family: AddressFamily::Unspec, index: 31, handle: TcHandle { major: 0xffff, minor: 0, }, parent: TcHandle { major: 0xffff, minor: 0xfff1, }, info: 1, }, attributes: vec![ TcAttribute::Kind("ingress".to_string()), TcAttribute::Options(vec![]), TcAttribute::HwOffload(0), TcAttribute::Stats2(vec![ TcStats2::Basic(TcStatsBasic { bytes: 0, packets: 0, }), TcStats2::Queue(TcStatsQueue { qlen: 0, backlog: 0, drops: 0, requeues: 0, overlimits: 0, }), ]), TcAttribute::Stats(TcStats { bytes: 0, packets: 0, drops: 0, overlimits: 0, bps: 0, pps: 0, qlen: 0, backlog: 0, }), ], }; assert_eq!( expected, TcMessage::parse(&TcMessageBuffer::new(&raw)).unwrap() ); let mut buf = vec![0; expected.buffer_len()]; expected.emit(&mut buf); assert_eq!(buf, raw); } netlink-packet-route-0.21.0/src/tests.rs000064400000000000000000000031711046102023000162520ustar 00000000000000// SPDX-License-Identifier: MIT // This file only contains testing parsing RouteNetlinkMessage, not focusing on // detailed sub-component parsing. Each component has their own tests moduel. use netlink_packet_core::{NetlinkHeader, NetlinkMessage, NetlinkPayload}; use netlink_packet_utils::Emitable; use crate::{ link::{LinkAttribute, LinkExtentMask, LinkMessage}, RouteNetlinkMessage, }; // wireshark capture of nlmon against command: // ip link show dev lo #[test] fn test_get_link() { let raw: Vec = vec![ 0x30, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x00, 0xe6, 0x9c, 0x69, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x09, 0x00, 0x00, 0x00, 0x07, 0x00, 0x03, 0x00, 0x6c, 0x6f, 0x00, 0x00, ]; let mut header = NetlinkHeader::default(); header.length = 48; header.message_type = 18; header.flags = 1; header.sequence_number = 1701420262; let expected = NetlinkMessage::new( header, NetlinkPayload::from(RouteNetlinkMessage::GetLink(LinkMessage { attributes: vec![ LinkAttribute::ExtMask(vec![ LinkExtentMask::Vf, LinkExtentMask::SkipStats, ]), LinkAttribute::IfName("lo".to_string()), ], ..Default::default() })), ); assert_eq!(NetlinkMessage::deserialize(&raw).unwrap(), expected); let mut buffer = vec![0; expected.buffer_len()]; expected.emit(&mut buffer); assert_eq!(buffer.as_slice(), raw); } netlink-packet-route-0.21.0/tools/make_release.sh000075500000000000000000000043771046102023000201000ustar 00000000000000#!/bin/bash -ex # SPDX-License-Identifier: MIT MAIN_BRANCH_NAME="main" UPSTERAM_GIT="https://github.com/rust-netlink/netlink-packet-route.git" TMP_CHANGELOG_FILE=$(mktemp) EDITOR="${EDITOR:-vim}" if ! command -v jq &> /dev/null then echo "Please install jq to proceed" exit 1 fi if ! command -v cargo set-version &> /dev/null then echo 'Please install cargo-edit via `cargo install cargo-edit` to proceed' exit 1 fi CHANGLOG_FORMAT=" ### Breaking changes\n\ - N/A\n\ \n\ ### New features\n\ - N/A\n\ \n\ ### Bug fixes" function cleanup { rm -f $TMP_CHANGELOG_FILE } trap cleanup ERR EXIT CODE_BASE_DIR=$(readlink -f "$(dirname -- "$0")/.."); cd $CODE_BASE_DIR; CUR_VERSION=$(cargo metadata --no-deps --format-version 1 | \ jq '.packages[0].version' --raw-output) CUR_MAJOR_VERSION=$(echo $CUR_VERSION|cut -f1 -d.) CUR_MINOR_VERSION=$(echo $CUR_VERSION|cut -f2 -d.) CUR_MICRO_VERSION=$(echo $CUR_VERSION|cut -f3 -d.) # TODO: Be smart on bumping major, micro or minor version by checking API # stability NEXT_VERSION="${CUR_MAJOR_VERSION}.$((CUR_MINOR_VERSION + 1)).0"; git branch new_release || true git checkout new_release git fetch upstream || (git remote add upstream $UPSTERAM_GIT; \ git fetch upstream) git reset --hard upstream/$MAIN_BRANCH_NAME echo "Checking 'cargo publish --dry-run'" cargo set-version $NEXT_VERSION cargo publish --dry-run echo "# Changelog" > $TMP_CHANGELOG_FILE echo "## [$NEXT_VERSION] - $(date +%F)" >> $TMP_CHANGELOG_FILE echo -e $CHANGLOG_FORMAT >> $TMP_CHANGELOG_FILE git log --oneline --format=" - %s. (%h)" \ v${CUR_VERSION}..upstream/$MAIN_BRANCH_NAME -- | \ grep -v -E '^ - test:' | \ grep -v -E '^ - Bump version' | \ grep -v -E 'cargo clippy' >> $TMP_CHANGELOG_FILE echo "" >> $TMP_CHANGELOG_FILE $EDITOR $TMP_CHANGELOG_FILE if [ $(wc -l < $TMP_CHANGELOG_FILE) -lt 2 ];then echo "No CHANGELOG addition, exiting" git checkout CHANGELOG exit 1 fi CHANGELOG_STR=$(sed -n '3,$p' $TMP_CHANGELOG_FILE|tr '#' '=') sed -n '2,$p' CHANGELOG >> $TMP_CHANGELOG_FILE mv $TMP_CHANGELOG_FILE $CODE_BASE_DIR/CHANGELOG git commit --signoff -a -m "New release ${NEXT_VERSION}" \ -m "$CHANGELOG_STR" git push origin +new_release echo "Please visit github to create pull request for this breach" netlink-packet-route-0.21.0/tools/test_cross_build.sh000075500000000000000000000010111046102023000210100ustar 00000000000000#!/bin/bash -ex # SPDX-License-Identifier: MIT EXEC_PATH=$(dirname "$(realpath "$0")") PROJECT_PATH="$(dirname $EXEC_PATH)" CI_CONFIG_FOR_BUILD="$PROJECT_PATH/.github/workflows/build.yml" BUILD_TARGETS=$(sed -ne 's/^.*rust_target: "\(.*\)"/\1/p' $CI_CONFIG_FOR_BUILD) cd $PROJECT_PATH for BUILD_TARGET in $BUILD_TARGETS;do if [ "CHK$(rustup target list --installed \ | grep $BUILD_TARGET)" == "CHK" ];then rustup target add $BUILD_TARGET fi cargo build --target $BUILD_TARGET done