netlink-packet-route-0.19.0/.cargo_vcs_info.json0000644000000001360000000000100151700ustar { "git": { "sha1": "aec3fc64b662447da2fbed5f9c9bd565cdea14ec" }, "path_in_vcs": "" }netlink-packet-route-0.19.0/.github/workflows/build.yml000064400000000000000000000014621046102023000212020ustar 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.19.0/.github/workflows/clippy-rustfmt.yml000064400000000000000000000011211046102023000230750ustar 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.19.0/.github/workflows/license.yml000064400000000000000000000005201046102023000215170ustar 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.19.0/.github/workflows/main.yml000064400000000000000000000015101046102023000210210ustar 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.19.0/.gitignore000064400000000000000000000000461046102023000157500ustar 00000000000000Cargo.lock target vendor/ tags *.swp netlink-packet-route-0.19.0/.licenserc.yaml000064400000000000000000000003711046102023000166720ustar 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.19.0/.rustfmt.toml000064400000000000000000000001141046102023000164330ustar 00000000000000max_width = 80 wrap_comments = true reorder_imports = true edition = "2021" netlink-packet-route-0.19.0/CHANGELOG000064400000000000000000000071641046102023000152020ustar 00000000000000# Changelog ## [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.19.0/Cargo.lock0000644000000157500000000000100131530ustar # 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.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "ctor" version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote 1.0.35", "syn 1.0.109", ] [[package]] name = "derive-into-owned" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "576fce04d31d592013a5887ba8d9c3830adff329e5096d7e1eb5e8e61262ca62" dependencies = [ "quote 0.3.15", "syn 0.11.11", ] [[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "libc" version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "netlink-packet-core" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4" dependencies = [ "anyhow", "byteorder", "netlink-packet-utils", ] [[package]] name = "netlink-packet-route" version = "0.19.0" dependencies = [ "anyhow", "byteorder", "libc", "log", "netlink-packet-core", "netlink-packet-utils", "netlink-sys", "pcap-file", "pretty_assertions", ] [[package]] name = "netlink-packet-utils" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" dependencies = [ "anyhow", "byteorder", "paste", "thiserror", ] [[package]] name = "netlink-sys" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" dependencies = [ "bytes", "libc", "log", ] [[package]] name = "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.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pcap-file" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ad13fed1a83120159aea81b265074f21d753d157dd16b10cc3790ecba40a341" dependencies = [ "byteorder", "derive-into-owned", "thiserror", ] [[package]] name = "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.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" [[package]] name = "quote" version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] name = "syn" version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" dependencies = [ "quote 0.3.15", "synom", "unicode-xid", ] [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote 1.0.35", "unicode-ident", ] [[package]] name = "syn" version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote 1.0.35", "unicode-ident", ] [[package]] name = "synom" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" dependencies = [ "unicode-xid", ] [[package]] name = "thiserror" version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote 1.0.35", "syn 2.0.48", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-xid" version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" [[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.19.0/Cargo.toml0000644000000025230000000000100131700ustar # 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.19.0" authors = ["Corentin Henry "] description = "netlink packet types" homepage = "https://github.com/rust-netlink/netlink-packet-route" readme = "README.md" keywords = [ "netlink", "linux", ] license = "MIT" repository = "https://github.com/rust-netlink/netlink-packet-route" [[example]] name = "dump_packet_links" [dependencies.anyhow] version = "1.0.31" [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.pcap-file] version = "1.1.1" [dev-dependencies.pretty_assertions] version = "0.7.2" [features] rich_nlas = [] netlink-packet-route-0.19.0/Cargo.toml.orig000064400000000000000000000013571046102023000166550ustar 00000000000000[package] authors = ["Corentin Henry "] name = "netlink-packet-route" version = "0.19.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" 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] pcap-file = "1.1.1" netlink-sys = { version = "0.8.5" } pretty_assertions = "0.7.2" netlink-packet-route-0.19.0/LICENSE-MIT000064400000000000000000000027731046102023000154250ustar 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.19.0/README.md000064400000000000000000000053661046102023000152510ustar 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.19.0/TODO.md000064400000000000000000000002471046102023000150520ustar 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.19.0/examples/dump_neighbour_tables.rs000064400000000000000000000042171046102023000225110ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_core::{ NetlinkHeader, NetlinkMessage, NetlinkPayload, NLM_F_DUMP, NLM_F_REQUEST, }; use netlink_packet_route::{ neighbour_table::NeighbourTableMessage, RouteNetlinkMessage, }; use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr}; fn main() { let mut socket = Socket::new(NETLINK_ROUTE).unwrap(); let _port_number = socket.bind_auto().unwrap().port_number(); socket.connect(&SocketAddr::new(0, 0)).unwrap(); let mut nl_hdr = NetlinkHeader::default(); nl_hdr.flags = NLM_F_DUMP | NLM_F_REQUEST; let mut req = NetlinkMessage::new( nl_hdr, NetlinkPayload::from(RouteNetlinkMessage::GetNeighbourTable( NeighbourTableMessage::default(), )), ); // IMPORTANT: call `finalize()` to automatically set the // `message_type` and `length` fields to the appropriate values in // the netlink header. req.finalize(); let mut buf = vec![0; req.header.length as usize]; req.serialize(&mut buf[..]); println!(">>> {req:?}"); socket.send(&buf[..], 0).unwrap(); let mut receive_buffer = vec![0; 4096]; let mut offset = 0; 'outer: loop { let size = socket.recv(&mut &mut receive_buffer[..], 0).unwrap(); loop { let bytes = &receive_buffer[offset..]; // Parse the message let msg: NetlinkMessage = NetlinkMessage::deserialize(bytes).unwrap(); match msg.payload { NetlinkPayload::Done(_) => break 'outer, NetlinkPayload::InnerMessage( RouteNetlinkMessage::NewNeighbourTable(entry), ) => { println!("HAHA {:?}", entry); } NetlinkPayload::Error(err) => { eprintln!("Received a netlink error message: {err:?}"); return; } _ => {} } offset += msg.header.length as usize; if offset == size || msg.header.length == 0 { offset = 0; break; } } } } netlink-packet-route-0.19.0/examples/dump_neighbours.rs000064400000000000000000000070731046102023000213450ustar 00000000000000// SPDX-License-Identifier: MIT use std::string::ToString; use netlink_packet_core::{ NetlinkHeader, NetlinkMessage, NetlinkPayload, NLM_F_DUMP, NLM_F_REQUEST, }; use netlink_packet_route::{ neighbour::{NeighbourAddress, NeighbourAttribute, NeighbourMessage}, AddressFamily, RouteNetlinkMessage, }; use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr}; fn main() { let mut socket = Socket::new(NETLINK_ROUTE).unwrap(); let _port_number = socket.bind_auto().unwrap().port_number(); socket.connect(&SocketAddr::new(0, 0)).unwrap(); let mut nl_hdr = NetlinkHeader::default(); nl_hdr.flags = NLM_F_DUMP | NLM_F_REQUEST; let mut req = NetlinkMessage::new( nl_hdr, NetlinkPayload::from(RouteNetlinkMessage::GetNeighbour( NeighbourMessage::default(), )), ); // IMPORTANT: call `finalize()` to automatically set the // `message_type` and `length` fields to the appropriate values in // the netlink header. req.finalize(); let mut buf = vec![0; req.header.length as usize]; req.serialize(&mut buf[..]); println!(">>> {req:?}"); socket.send(&buf[..], 0).unwrap(); let mut receive_buffer = vec![0; 4096]; let mut offset = 0; 'outer: loop { let size = socket.recv(&mut &mut receive_buffer[..], 0).unwrap(); loop { let bytes = &receive_buffer[offset..]; // Parse the message let msg: NetlinkMessage = NetlinkMessage::deserialize(bytes).unwrap(); match msg.payload { NetlinkPayload::Done(_) => break 'outer, NetlinkPayload::InnerMessage( RouteNetlinkMessage::NewNeighbour(entry), ) => { let address_family = entry.header.family; if address_family == AddressFamily::Inet || address_family == AddressFamily::Inet6 { print_entry(entry); } } NetlinkPayload::Error(err) => { eprintln!("Received a netlink error message: {err:?}"); return; } _ => {} } offset += msg.header.length as usize; if offset == size || msg.header.length == 0 { offset = 0; break; } } } } fn format_ip(addr: &NeighbourAddress) -> String { if let NeighbourAddress::Inet(ip) = addr { ip.to_string() } else if let NeighbourAddress::Inet6(ip) = addr { ip.to_string() } else { panic!("Invalid IP Address"); } } fn format_mac(buf: &[u8]) -> String { if buf.len() == 6 { format!( "{:<02x}:{:<02x}:{:<02x}:{:<02x}:{:<02x}:{:<02x}", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5] ) } else { "00:00:00:00:00:00".into() } } fn print_entry(entry: NeighbourMessage) { let state = entry.header.state; if let (Some(dest), Some(lladdr)) = ( entry.attributes.iter().find_map(|nla| { if let NeighbourAttribute::Destination(addr) = nla { Some(format_ip(addr)) } else { None } }), entry.attributes.iter().find_map(|nla| { if let NeighbourAttribute::LinkLocalAddress(addr) = nla { Some(format_mac(addr)) } else { None } }), ) { println!("{dest:<30} {lladdr:<20} ({state})"); } } netlink-packet-route-0.19.0/examples/dump_packet_link_bridge_vlan.rs000064400000000000000000000055421046102023000240170ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_core::{ NetlinkHeader, NetlinkMessage, NetlinkPayload, NLM_F_DUMP, NLM_F_REQUEST, }; use netlink_packet_route::{ link::{LinkAttribute, LinkExtentMask, LinkMessage}, AddressFamily, RouteNetlinkMessage, }; use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr}; fn main() { let mut socket = Socket::new(NETLINK_ROUTE).unwrap(); let _port_number = socket.bind_auto().unwrap().port_number(); socket.connect(&SocketAddr::new(0, 0)).unwrap(); let mut message = LinkMessage::default(); message.header.interface_family = AddressFamily::Bridge; message.attributes.push(LinkAttribute::ExtMask(vec![ LinkExtentMask::BrvlanCompressed, ])); let mut packet = NetlinkMessage::new( NetlinkHeader::default(), NetlinkPayload::from(RouteNetlinkMessage::GetLink(message)), ); packet.header.flags = NLM_F_DUMP | NLM_F_REQUEST; packet.header.sequence_number = 1; packet.finalize(); let mut buf = vec![0; packet.header.length as usize]; // Before calling serialize, it is important to check that the buffer in // which we're emitting is big enough for the packet, other // `serialize()` panics. assert!(buf.len() == packet.buffer_len()); packet.serialize(&mut buf[..]); println!(">>> {packet:?}"); socket.send(&buf[..], 0).unwrap(); let mut receive_buffer = vec![0; 4096]; let mut offset = 0; // we set the NLM_F_DUMP flag so we expect a multipart rx_packet in // response. loop { let size = socket.recv(&mut &mut receive_buffer[..], 0).unwrap(); loop { let bytes = &receive_buffer[offset..]; // Note that we're parsing a NetlinkBuffer<&&[u8]>, NOT a // NetlinkBuffer<&[u8]> here. This is important because // Parseable is only implemented for // NetlinkBuffer<&'a T>, where T implements AsRef<[u8] + 'a. This is // not particularly user friendly, but this is a low // level library anyway. // // Note also that the same could be written more explicitely with: // // let rx_packet = // as // Parseable>::parse(NetlinkBuffer::new(&bytes)) // .unwrap(); // let rx_packet: NetlinkMessage = NetlinkMessage::deserialize(bytes).unwrap(); println!("<<< {rx_packet:?}"); if matches!(rx_packet.payload, NetlinkPayload::Done(_)) { println!("Done!"); return; } offset += rx_packet.header.length as usize; if offset == size || rx_packet.header.length == 0 { offset = 0; break; } } } } netlink-packet-route-0.19.0/examples/dump_packet_links.rs000064400000000000000000000051651046102023000216470ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_core::{ NetlinkHeader, NetlinkMessage, NetlinkPayload, NLM_F_DUMP, NLM_F_REQUEST, }; use netlink_packet_route::{link::LinkMessage, RouteNetlinkMessage}; use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr}; fn main() { let mut socket = Socket::new(NETLINK_ROUTE).unwrap(); let _port_number = socket.bind_auto().unwrap().port_number(); socket.connect(&SocketAddr::new(0, 0)).unwrap(); let mut packet = NetlinkMessage::new( NetlinkHeader::default(), NetlinkPayload::from(RouteNetlinkMessage::GetLink( LinkMessage::default(), )), ); packet.header.flags = NLM_F_DUMP | NLM_F_REQUEST; packet.header.sequence_number = 1; packet.finalize(); let mut buf = vec![0; packet.header.length as usize]; // Before calling serialize, it is important to check that the buffer in // which we're emitting is big enough for the packet, other // `serialize()` panics. assert!(buf.len() == packet.buffer_len()); packet.serialize(&mut buf[..]); println!(">>> {packet:?}"); socket.send(&buf[..], 0).unwrap(); let mut receive_buffer = vec![0; 4096]; let mut offset = 0; // we set the NLM_F_DUMP flag so we expect a multipart rx_packet in // response. loop { let size = socket.recv(&mut &mut receive_buffer[..], 0).unwrap(); loop { let bytes = &receive_buffer[offset..]; // Note that we're parsing a NetlinkBuffer<&&[u8]>, NOT a // NetlinkBuffer<&[u8]> here. This is important because // Parseable is only implemented for // NetlinkBuffer<&'a T>, where T implements AsRef<[u8] + 'a. This is // not particularly user friendly, but this is a low // level library anyway. // // Note also that the same could be written more explicitely with: // // let rx_packet = // as // Parseable>::parse(NetlinkBuffer::new(&bytes)) // .unwrap(); // let rx_packet: NetlinkMessage = NetlinkMessage::deserialize(bytes).unwrap(); println!("<<< {rx_packet:?}"); if matches!(rx_packet.payload, NetlinkPayload::Done(_)) { println!("Done!"); return; } offset += rx_packet.header.length as usize; if offset == size || rx_packet.header.length == 0 { offset = 0; break; } } } } netlink-packet-route-0.19.0/examples/dump_rules.rs000064400000000000000000000037351046102023000203330ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_core::{ NetlinkHeader, NetlinkMessage, NetlinkPayload, NLM_F_DUMP, NLM_F_REQUEST, }; use netlink_packet_route::{rule::RuleMessage, RouteNetlinkMessage}; use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr}; fn main() { let mut socket = Socket::new(NETLINK_ROUTE).unwrap(); let _port_number = socket.bind_auto().unwrap().port_number(); socket.connect(&SocketAddr::new(0, 0)).unwrap(); let mut nl_hdr = NetlinkHeader::default(); nl_hdr.flags = NLM_F_REQUEST | NLM_F_DUMP; let mut packet = NetlinkMessage::new( nl_hdr, NetlinkPayload::from(RouteNetlinkMessage::GetRule( RuleMessage::default(), )), ); packet.finalize(); let mut buf = vec![0; packet.header.length as usize]; // Before calling serialize, it is important to check that the buffer in // which we're emitting is big enough for the packet, other // `serialize()` panics. assert!(buf.len() == packet.buffer_len()); packet.serialize(&mut buf[..]); println!(">>> {packet:?}"); if let Err(e) = socket.send(&buf[..], 0) { println!("SEND ERROR {e}"); } let mut receive_buffer = vec![0; 4096]; let mut offset = 0; // we set the NLM_F_DUMP flag so we expect a multipart rx_packet in // response. while let Ok(size) = socket.recv(&mut &mut receive_buffer[..], 0) { loop { let bytes = &receive_buffer[offset..]; let rx_packet = >::deserialize(bytes) .unwrap(); println!("<<< {rx_packet:?}"); if matches!(rx_packet.payload, NetlinkPayload::Done(_)) { println!("Done!"); return; } offset += rx_packet.header.length as usize; if offset == size || rx_packet.header.length == 0 { offset = 0; break; } } } } netlink-packet-route-0.19.0/examples/new_rule.rs000064400000000000000000000037751046102023000200000ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_core::{ NetlinkHeader, NetlinkMessage, NetlinkPayload, NLM_F_ACK, NLM_F_CREATE, NLM_F_EXCL, NLM_F_REQUEST, }; use netlink_packet_route::{ route::RouteProtocol, rule::{RuleAction, RuleAttribute, RuleHeader, RuleMessage}, AddressFamily, RouteNetlinkMessage, }; use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr}; fn main() { let mut socket = Socket::new(NETLINK_ROUTE).unwrap(); let _port_number = socket.bind_auto().unwrap().port_number(); socket.connect(&SocketAddr::new(0, 0)).unwrap(); let rule_msg_hdr = RuleHeader { family: AddressFamily::Inet, table: 254, action: RuleAction::ToTable, ..Default::default() }; let mut rule_msg = RuleMessage::default(); rule_msg.header = rule_msg_hdr; rule_msg.attributes = vec![ RuleAttribute::Table(254), RuleAttribute::SuppressPrefixLen(4294967295), RuleAttribute::Priority(1000), RuleAttribute::Protocol(RouteProtocol::Kernel), ]; let mut nl_hdr = NetlinkHeader::default(); nl_hdr.flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK; let mut msg = NetlinkMessage::new( nl_hdr, NetlinkPayload::from(RouteNetlinkMessage::NewRule(rule_msg)), ); msg.finalize(); let mut buf = vec![0; 1024 * 8]; msg.serialize(&mut buf[..msg.buffer_len()]); println!(">>> {msg:?}"); socket .send(&buf, 0) .expect("failed to send netlink message"); let mut receive_buffer = vec![0; 4096]; while let Ok(_size) = socket.recv(&mut receive_buffer, 0) { let bytes = &receive_buffer[..]; let rx_packet = >::deserialize(bytes); println!("<<< {rx_packet:?}"); if let Ok(rx_packet) = rx_packet { if let NetlinkPayload::Error(e) = rx_packet.payload { eprintln!("{e:?}"); } else { return; } } } } netlink-packet-route-0.19.0/src/address/addr_flags.rs000064400000000000000000000141421046102023000206320ustar 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; #[derive(Clone, Eq, PartialEq, Debug, Copy)] #[non_exhaustive] pub enum AddressFlag { Secondary, Nodad, Optimistic, Dadfailed, Homeaddress, Deprecated, Tentative, Permanent, Managetempaddr, Noprefixroute, Mcautojoin, StablePrivacy, Other(u32), } impl From for AddressFlag { fn from(d: u32) -> Self { match d { IFA_F_SECONDARY => Self::Secondary, IFA_F_NODAD => Self::Nodad, IFA_F_OPTIMISTIC => Self::Optimistic, IFA_F_DADFAILED => Self::Dadfailed, IFA_F_HOMEADDRESS => Self::Homeaddress, IFA_F_DEPRECATED => Self::Deprecated, IFA_F_TENTATIVE => Self::Tentative, IFA_F_PERMANENT => Self::Permanent, IFA_F_MANAGETEMPADDR => Self::Managetempaddr, IFA_F_NOPREFIXROUTE => Self::Noprefixroute, IFA_F_MCAUTOJOIN => Self::Mcautojoin, IFA_F_STABLE_PRIVACY => Self::StablePrivacy, _ => Self::Other(d), } } } impl From for u32 { fn from(v: AddressFlag) -> u32 { match v { AddressFlag::Secondary => IFA_F_SECONDARY, AddressFlag::Nodad => IFA_F_NODAD, AddressFlag::Optimistic => IFA_F_OPTIMISTIC, AddressFlag::Dadfailed => IFA_F_DADFAILED, AddressFlag::Homeaddress => IFA_F_HOMEADDRESS, AddressFlag::Deprecated => IFA_F_DEPRECATED, AddressFlag::Tentative => IFA_F_TENTATIVE, AddressFlag::Permanent => IFA_F_PERMANENT, AddressFlag::Managetempaddr => IFA_F_MANAGETEMPADDR, AddressFlag::Noprefixroute => IFA_F_NOPREFIXROUTE, AddressFlag::Mcautojoin => IFA_F_MCAUTOJOIN, AddressFlag::StablePrivacy => IFA_F_STABLE_PRIVACY, AddressFlag::Other(d) => d, } } } const ALL_ADDR_FLAGS: [AddressFlag; 12] = [ AddressFlag::Secondary, AddressFlag::Nodad, AddressFlag::Optimistic, AddressFlag::Dadfailed, AddressFlag::Homeaddress, AddressFlag::Deprecated, AddressFlag::Tentative, AddressFlag::Permanent, AddressFlag::Managetempaddr, AddressFlag::Noprefixroute, AddressFlag::Mcautojoin, AddressFlag::StablePrivacy, ]; #[derive(Clone, Eq, PartialEq, Debug)] pub struct AddressFlags(pub(crate) Vec); impl From for AddressFlags { fn from(d: u32) -> Self { let mut got: u32 = 0; let mut ret = Vec::new(); for flag in ALL_ADDR_FLAGS { if (d & u32::from(flag)) > 0 { ret.push(flag); got += u32::from(flag); } } if got != d { log::warn!("Discarded unsupported IFA_F_: {}", d - got); } Self(ret) } } impl From<&AddressFlags> for u32 { fn from(v: &AddressFlags) -> u32 { let mut d: u32 = 0; for flag in &v.0 { d += u32::from(*flag); } d } } #[derive(Clone, Eq, PartialEq, Debug, Copy)] #[non_exhaustive] #[repr(u8)] /// [AddressHeaderFlag] is only used for [super::AddressHeader] and holding /// subset(first byte) of [AddressFlag]. pub enum AddressHeaderFlag { Secondary, Nodad, Optimistic, Dadfailed, Homeaddress, Deprecated, Tentative, Permanent, Other(u8), } impl From for AddressHeaderFlag { fn from(d: u8) -> Self { match d { d if d == IFA_F_SECONDARY as u8 => Self::Secondary, d if d == IFA_F_NODAD as u8 => Self::Nodad, d if d == IFA_F_OPTIMISTIC as u8 => Self::Optimistic, d if d == IFA_F_DADFAILED as u8 => Self::Dadfailed, d if d == IFA_F_HOMEADDRESS as u8 => Self::Homeaddress, d if d == IFA_F_DEPRECATED as u8 => Self::Deprecated, d if d == IFA_F_TENTATIVE as u8 => Self::Tentative, d if d == IFA_F_PERMANENT as u8 => Self::Permanent, _ => Self::Other(d), } } } impl From for u8 { fn from(v: AddressHeaderFlag) -> u8 { match v { AddressHeaderFlag::Secondary => IFA_F_SECONDARY as u8, AddressHeaderFlag::Nodad => IFA_F_NODAD as u8, AddressHeaderFlag::Optimistic => IFA_F_OPTIMISTIC as u8, AddressHeaderFlag::Dadfailed => IFA_F_DADFAILED as u8, AddressHeaderFlag::Homeaddress => IFA_F_HOMEADDRESS as u8, AddressHeaderFlag::Deprecated => IFA_F_DEPRECATED as u8, AddressHeaderFlag::Tentative => IFA_F_TENTATIVE as u8, AddressHeaderFlag::Permanent => IFA_F_PERMANENT as u8, AddressHeaderFlag::Other(d) => d, } } } const ALL_HDR_ADDR_FLAGS: [AddressHeaderFlag; 8] = [ AddressHeaderFlag::Secondary, AddressHeaderFlag::Nodad, AddressHeaderFlag::Optimistic, AddressHeaderFlag::Dadfailed, AddressHeaderFlag::Homeaddress, AddressHeaderFlag::Deprecated, AddressHeaderFlag::Tentative, AddressHeaderFlag::Permanent, ]; #[derive(Clone, Eq, PartialEq, Debug)] pub struct AddressHeaderFlags(pub(crate) Vec); impl From for AddressHeaderFlags { fn from(d: u8) -> Self { let mut got: u8 = 0; let mut ret = Vec::new(); for flag in ALL_HDR_ADDR_FLAGS { if (d & u8::from(flag)) > 0 { ret.push(flag); got += u8::from(flag); } } if got != d { log::warn!("Discarded unsupported IFA_F_: {}", d - got); } Self(ret) } } impl From<&AddressHeaderFlags> for u8 { fn from(v: &AddressHeaderFlags) -> u8 { let mut d: u8 = 0; for flag in &v.0 { d += u8::from(*flag); } d } } netlink-packet-route-0.19.0/src/address/addr_scope.rs000064400000000000000000000022411046102023000206440ustar 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.19.0/src/address/attribute.rs000064400000000000000000000165611046102023000205560ustar 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::{AddressFlag, 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(Vec), 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, u32::from(&AddressFlags(value.to_vec())), ), 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( parse_u32(payload).context("invalid IFA_FLAGS value")?, ) .0, ), kind => Self::Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, ), }) } } netlink-packet-route-0.19.0/src/address/cache_info.rs000064400000000000000000000023361046102023000206240ustar 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.19.0/src/address/message.rs000064400000000000000000000062501046102023000201710ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{NlaBuffer, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; use crate::{ address::{ AddressAttribute, AddressHeaderFlag, 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: Vec, 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(u8::from(&AddressHeaderFlags(self.flags.to_vec()))); 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(buf.flags()).0, 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.19.0/src/address/mod.rs000064400000000000000000000007051046102023000173230ustar 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::{ AddressFlag, AddressFlags, AddressHeaderFlag, 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.19.0/src/address/tests/ipv4.rs000064400000000000000000000034631046102023000205740ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::{IpAddr, Ipv4Addr}; use netlink_packet_utils::{Emitable, Parseable}; use crate::address::{ AddressAttribute, AddressFlag, AddressHeader, AddressHeaderFlag, 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: vec![AddressHeaderFlag::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(vec![AddressFlag::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.19.0/src/address/tests/ipv6.rs000064400000000000000000000045731046102023000206010ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::{IpAddr, Ipv6Addr}; use netlink_packet_utils::{nla::NlaBuffer, Emitable, Parseable}; use crate::address::{ AddressAttribute, AddressFlag, AddressHeader, AddressHeaderFlag, 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(vec![ AddressFlag::Permanent, AddressFlag::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: vec![AddressHeaderFlag::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(vec![ AddressFlag::Permanent, AddressFlag::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.19.0/src/address/tests/mod.rs000064400000000000000000000001171046102023000204620ustar 00000000000000// SPDX-License-Identifier: MIT #[cfg(test)] mod ipv4; #[cfg(test)] mod ipv6; netlink-packet-route-0.19.0/src/address_family_fallback.rs000064400000000000000000000022361046102023000217250ustar 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.19.0/src/address_family_freebsd.rs000064400000000000000000000104021046102023000215720ustar 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.19.0/src/address_family_linux.rs000064400000000000000000000146341046102023000213320ustar 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.19.0/src/ip.rs000064400000000000000000000141041046102023000155250ustar 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.19.0/src/lib.rs000064400000000000000000000040321046102023000156620ustar 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; netlink-packet-route-0.19.0/src/link/af_spec/bridge.rs000064400000000000000000000074331046102023000207150ustar 00000000000000// SPDX-License-Identifier: MIT use std::convert::TryFrom; use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{self, DefaultNla, NlaBuffer}, parsers::parse_u16, traits::Parseable, DecodeError, }; const IFLA_BRIDGE_FLAGS: u16 = 0; const IFLA_BRIDGE_VLAN_INFO: u16 = 2; #[derive(Clone, Eq, PartialEq, Debug)] #[non_exhaustive] pub enum AfSpecBridge { Flags(u16), VlanInfo(BridgeVlanInfo), Other(DefaultNla), } impl nla::Nla for AfSpecBridge { fn value_len(&self) -> usize { use self::AfSpecBridge::*; match *self { VlanInfo(_) => 4, Flags(_) => 2, Other(ref nla) => nla.value_len(), } } fn emit_value(&self, buffer: &mut [u8]) { use self::AfSpecBridge::*; match *self { Flags(value) => NativeEndian::write_u16(buffer, value), VlanInfo(ref info) => { buffer[..4].copy_from_slice(<[u8; 4]>::from(info).as_slice()) } Other(ref nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::AfSpecBridge::*; match *self { Flags(_) => IFLA_BRIDGE_FLAGS, VlanInfo(_) => IFLA_BRIDGE_VLAN_INFO, Other(ref nla) => nla.kind(), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for AfSpecBridge { fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::AfSpecBridge::*; let payload = buf.value(); Ok(match buf.kind() { IFLA_BRIDGE_VLAN_INFO => VlanInfo( BridgeVlanInfo::try_from(payload) .context("Invalid IFLA_BRIDGE_VLAN_INFO value")?, ), IFLA_BRIDGE_FLAGS => Flags( parse_u16(payload) .context("invalid IFLA_BRIDGE_FLAGS value")?, ), kind => Other( DefaultNla::parse(buf) .context(format!("Unknown NLA type {kind}"))?, ), }) } } #[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 netlink_packet_utils::nla::NlasIterator::new(buf.into_inner()) { let nla = nla.context(err)?; nlas.push(AfSpecBridge::parse(&nla).context(err)?); } Ok(Self(nlas)) } } #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] #[non_exhaustive] pub struct BridgeVlanInfo { pub flags: u16, pub vid: u16, } impl From<&BridgeVlanInfo> for [u8; 4] { fn from(d: &BridgeVlanInfo) -> Self { let mut ret = [0u8; 4]; NativeEndian::write_u16(&mut ret[0..2], d.flags); NativeEndian::write_u16(&mut ret[2..4], d.vid); ret } } impl TryFrom<&[u8]> for BridgeVlanInfo { type Error = DecodeError; fn try_from(raw: &[u8]) -> Result { if raw.len() == 4 { Ok(Self { flags: parse_u16(&raw[0..2]).context(format!( "Invalid IFLA_BRIDGE_VLAN_INFO value: {raw:?}" ))?, vid: parse_u16(&raw[2..4]).context(format!( "Invalid IFLA_BRIDGE_VLAN_INFO value: {raw:?}" ))?, }) } else { Err(DecodeError::from(format!( "Invalid IFLA_BRIDGE_VLAN_INFO value, expecting [u8;4], \ but got {raw:?}" ))) } } } netlink-packet-route-0.19.0/src/link/af_spec/inet.rs000064400000000000000000000212601046102023000204120ustar 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)] #[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.19.0/src/link/af_spec/inet6.rs000064400000000000000000000135261046102023000205060ustar 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; 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, u32::from(value)) } 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( 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(payload)).context( format!("invalid IFLA_INET6_STATS value {:?}", payload), )?, ), IFLA_INET6_ICMP6STATS => Icmp6Stats( super::super::Icmp6Stats::parse(&Icmp6StatsBuffer::new( payload, )) .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.19.0/src/link/af_spec/inet6_cache.rs000064400000000000000000000024731046102023000216300ustar 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.19.0/src/link/af_spec/inet6_devconf.rs000064400000000000000000000270251046102023000222110ustar 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)] #[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.19.0/src/link/af_spec/inet6_icmp.rs000064400000000000000000000027011046102023000215070ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; const ICMP6_STATS_LEN: usize = 48; #[derive(Clone, Copy, Eq, PartialEq, Debug)] #[non_exhaustive] pub struct Icmp6Stats { pub num: i64, pub in_msgs: i64, pub in_errors: i64, pub out_msgs: i64, pub out_errors: i64, pub csum_errors: i64, } 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.19.0/src/link/af_spec/inet6_iface_flag.rs000064400000000000000000000052031046102023000226170ustar 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; #[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct Inet6IfaceFlags(pub Vec); #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] #[repr(u32)] pub enum Inet6IfaceFlag { Otherconf = IF_RA_OTHERCONF, RaManaged = IF_RA_MANAGED, RaRcvd = IF_RA_RCVD, RsSent = IF_RS_SENT, Ready = IF_READY, Other(u32), } impl From for Inet6IfaceFlag { fn from(d: u32) -> Self { match d { d if (d & IF_RA_OTHERCONF) > 0 => Self::Otherconf, d if (d & IF_RA_MANAGED) > 0 => Self::RaManaged, d if (d & IF_RA_RCVD) > 0 => Self::RaRcvd, d if (d & IF_RS_SENT) > 0 => Self::RsSent, d if (d & IF_READY) > 0 => Self::Ready, _ => Self::Other(d), } } } impl From for u32 { fn from(v: Inet6IfaceFlag) -> u32 { match v { Inet6IfaceFlag::Otherconf => IF_RA_OTHERCONF, Inet6IfaceFlag::RaManaged => IF_RA_MANAGED, Inet6IfaceFlag::RaRcvd => IF_RA_RCVD, Inet6IfaceFlag::RsSent => IF_RS_SENT, Inet6IfaceFlag::Ready => IF_READY, Inet6IfaceFlag::Other(i) => i, } } } impl std::fmt::Display for Inet6IfaceFlag { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Otherconf => write!(f, "OTHERCONF"), Self::RaManaged => write!(f, "RA_MANAGED"), Self::RaRcvd => write!(f, "RA_RCVD"), Self::RsSent => write!(f, "RS_SENT"), Self::Ready => write!(f, "READY"), Self::Other(i) => write!(f, "Other({})", i), } } } const ALL_INET_IFACE_FLAGS: [Inet6IfaceFlag; 5] = [ Inet6IfaceFlag::Otherconf, Inet6IfaceFlag::RaManaged, Inet6IfaceFlag::RaRcvd, Inet6IfaceFlag::RsSent, Inet6IfaceFlag::Ready, ]; impl From for Inet6IfaceFlags { fn from(d: u32) -> Self { let mut got: u32 = 0; let mut ret = Vec::new(); for flag in ALL_INET_IFACE_FLAGS { if (d & u32::from(flag)) > 0 { ret.push(flag); got += u32::from(flag); } } if got != d { ret.push(Inet6IfaceFlag::Other(d - got)); } Self(ret) } } impl From<&Inet6IfaceFlags> for u32 { fn from(v: &Inet6IfaceFlags) -> u32 { let mut d: u32 = 0; for flag in &v.0 { d += u32::from(*flag); } d } } netlink-packet-route-0.19.0/src/link/af_spec/inet6_stats.rs000064400000000000000000000145151046102023000217230ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; const INET6_STATS_LEN: usize = 288; buffer!(Inet6StatsBuffer(INET6_STATS_LEN) { num: (i64, 0..8), in_pkts: (i64, 8..16), in_octets: (i64, 16..24), in_delivers: (i64, 24..32), out_forw_datagrams: (i64, 32..40), out_pkts: (i64, 40..48), out_octets: (i64, 48..56), in_hdr_errors: (i64, 56..64), in_too_big_errors: (i64, 64..72), in_no_routes: (i64, 72..80), in_addr_errors: (i64, 80..88), in_unknown_protos: (i64, 88..96), in_truncated_pkts: (i64, 96..104), in_discards: (i64, 104..112), out_discards: (i64, 112..120), out_no_routes: (i64, 120..128), reasm_timeout: (i64, 128..136), reasm_reqds: (i64, 136..144), reasm_oks: (i64, 144..152), reasm_fails: (i64, 152..160), frag_oks: (i64, 160..168), frag_fails: (i64, 168..176), frag_creates: (i64, 176..184), in_mcast_pkts: (i64, 184..192), out_mcast_pkts: (i64, 192..200), in_bcast_pkts: (i64, 200..208), out_bcast_pkts: (i64, 208..216), in_mcast_octets: (i64, 216..224), out_mcast_octets: (i64, 224..232), in_bcast_octets: (i64, 232..240), out_bcast_octets: (i64, 240..248), in_csum_errors: (i64, 248..256), in_no_ect_pkts: (i64, 256..264), in_ect1_pkts: (i64, 264..272), in_ect0_pkts: (i64, 272..280), in_ce_pkts: (i64, 280..288), }); #[derive(Clone, Copy, Eq, PartialEq, Debug)] #[non_exhaustive] pub struct Inet6Stats { pub num: i64, pub in_pkts: i64, pub in_octets: i64, pub in_delivers: i64, pub out_forw_datagrams: i64, pub out_pkts: i64, pub out_octets: i64, pub in_hdr_errors: i64, pub in_too_big_errors: i64, pub in_no_routes: i64, pub in_addr_errors: i64, pub in_unknown_protos: i64, pub in_truncated_pkts: i64, pub in_discards: i64, pub out_discards: i64, pub out_no_routes: i64, pub reasm_timeout: i64, pub reasm_reqds: i64, pub reasm_oks: i64, pub reasm_fails: i64, pub frag_oks: i64, pub frag_fails: i64, pub frag_creates: i64, pub in_mcast_pkts: i64, pub out_mcast_pkts: i64, pub in_bcast_pkts: i64, pub out_bcast_pkts: i64, pub in_mcast_octets: i64, pub out_mcast_octets: i64, pub in_bcast_octets: i64, pub out_bcast_octets: i64, pub in_csum_errors: i64, pub in_no_ect_pkts: i64, pub in_ect1_pkts: i64, pub in_ect0_pkts: i64, pub in_ce_pkts: i64, } impl> Parseable> for Inet6Stats { fn parse(buf: &Inet6StatsBuffer) -> Result { Ok(Self { num: buf.num(), in_pkts: buf.in_pkts(), in_octets: buf.in_octets(), in_delivers: buf.in_delivers(), out_forw_datagrams: buf.out_forw_datagrams(), out_pkts: buf.out_pkts(), out_octets: buf.out_octets(), in_hdr_errors: buf.in_hdr_errors(), in_too_big_errors: buf.in_too_big_errors(), in_no_routes: buf.in_no_routes(), in_addr_errors: buf.in_addr_errors(), in_unknown_protos: buf.in_unknown_protos(), in_truncated_pkts: buf.in_truncated_pkts(), in_discards: buf.in_discards(), out_discards: buf.out_discards(), out_no_routes: buf.out_no_routes(), reasm_timeout: buf.reasm_timeout(), reasm_reqds: buf.reasm_reqds(), reasm_oks: buf.reasm_oks(), reasm_fails: buf.reasm_fails(), frag_oks: buf.frag_oks(), frag_fails: buf.frag_fails(), frag_creates: buf.frag_creates(), in_mcast_pkts: buf.in_mcast_pkts(), out_mcast_pkts: buf.out_mcast_pkts(), in_bcast_pkts: buf.in_bcast_pkts(), out_bcast_pkts: buf.out_bcast_pkts(), in_mcast_octets: buf.in_mcast_octets(), out_mcast_octets: buf.out_mcast_octets(), in_bcast_octets: buf.in_bcast_octets(), out_bcast_octets: buf.out_bcast_octets(), in_csum_errors: buf.in_csum_errors(), in_no_ect_pkts: buf.in_no_ect_pkts(), in_ect1_pkts: buf.in_ect1_pkts(), in_ect0_pkts: buf.in_ect0_pkts(), in_ce_pkts: buf.in_ce_pkts(), }) } } impl Emitable for Inet6Stats { fn buffer_len(&self) -> usize { INET6_STATS_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = Inet6StatsBuffer::new(buffer); buffer.set_num(self.num); buffer.set_in_pkts(self.in_pkts); buffer.set_in_octets(self.in_octets); buffer.set_in_delivers(self.in_delivers); buffer.set_out_forw_datagrams(self.out_forw_datagrams); buffer.set_out_pkts(self.out_pkts); buffer.set_out_octets(self.out_octets); buffer.set_in_hdr_errors(self.in_hdr_errors); buffer.set_in_too_big_errors(self.in_too_big_errors); buffer.set_in_no_routes(self.in_no_routes); buffer.set_in_addr_errors(self.in_addr_errors); buffer.set_in_unknown_protos(self.in_unknown_protos); buffer.set_in_truncated_pkts(self.in_truncated_pkts); buffer.set_in_discards(self.in_discards); buffer.set_out_discards(self.out_discards); buffer.set_out_no_routes(self.out_no_routes); buffer.set_reasm_timeout(self.reasm_timeout); buffer.set_reasm_reqds(self.reasm_reqds); buffer.set_reasm_oks(self.reasm_oks); buffer.set_reasm_fails(self.reasm_fails); buffer.set_frag_oks(self.frag_oks); buffer.set_frag_fails(self.frag_fails); buffer.set_frag_creates(self.frag_creates); buffer.set_in_mcast_pkts(self.in_mcast_pkts); buffer.set_out_mcast_pkts(self.out_mcast_pkts); buffer.set_in_bcast_pkts(self.in_bcast_pkts); buffer.set_out_bcast_pkts(self.out_bcast_pkts); buffer.set_in_mcast_octets(self.in_mcast_octets); buffer.set_out_mcast_octets(self.out_mcast_octets); buffer.set_in_bcast_octets(self.in_bcast_octets); buffer.set_out_bcast_octets(self.out_bcast_octets); buffer.set_in_csum_errors(self.in_csum_errors); buffer.set_in_no_ect_pkts(self.in_no_ect_pkts); buffer.set_in_ect1_pkts(self.in_ect1_pkts); buffer.set_in_ect0_pkts(self.in_ect0_pkts); buffer.set_in_ce_pkts(self.in_ce_pkts); } } netlink-packet-route-0.19.0/src/link/af_spec/mod.rs000064400000000000000000000015741046102023000202400ustar 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, BridgeVlanInfo}; 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::{Inet6IfaceFlag, 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.19.0/src/link/af_spec/unspec.rs000064400000000000000000000067501046102023000207570ustar 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(&nla.value())) .context(err)? .0, ) } k if k == u8::from(AddressFamily::Inet6) as u16 => { AfSpecUnspec::Inet6( VecAfSpecInet6::parse(&NlaBuffer::new(&nla.value())) .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.19.0/src/link/attribute.rs000064400000000000000000000573241046102023000200700ustar 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), NetnsId(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::NetnsId(_) | 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::NetnsId(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::NetnsId(_) => 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 => Self::VfInfoList( VecLinkVfInfo::parse(&NlaBuffer::new(payload)) .context(format!("invalid IFLA_VFINFO_LIST {payload:?}"))? .0, ), IFLA_VF_PORTS => Self::VfPorts( VecLinkVfPort::parse(&NlaBuffer::new(payload)) .context(format!("invalid IFLA_VF_PORTS {payload:?}"))? .0, ), IFLA_PORT_SELF => Self::PortSelf( LinkVfPort::parse(&NlaBuffer::new(payload)) .context(format!("invalid IFLA_PORT_SELF {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 => match interface_family { AddressFamily::Inet6 => Self::ProtoInfoInet6( VecLinkProtoInfoInet6::parse(&NlaBuffer::new(payload)) .context(format!( "invalid IFLA_PROTINFO for AF_INET6 {payload:?}" ))? .0, ), #[cfg(any(target_os = "linux", target_os = "fuchsia",))] AddressFamily::Bridge => Self::ProtoInfoBridge( VecLinkProtoInfoBridge::parse(&NlaBuffer::new(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::NetnsId( parse_i32(payload) .context("invalid IFLA_LINK_NETNSID value")?, ), IFLA_OPERSTATE => Self::OperState( parse_u8(payload) .context("invalid IFLA_OPERSTATE value")? .into(), ), IFLA_MAP => Self::Map( super::Map::parse(&MapBuffer::new(payload)) .context(format!("Invalid IFLA_MAP value {:?}", payload))?, ), IFLA_STATS => Self::Stats( super::Stats::parse(&StatsBuffer::new(payload)).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 => Self::AfSpecUnspec( VecAfSpecUnspec::parse(&NlaBuffer::new(&buf.value())) .context("invalid IFLA_AF_SPEC value for AF_UNSPEC")? .0, ), #[cfg(any(target_os = "linux", target_os = "fuchsia",))] AddressFamily::Bridge => Self::AfSpecBridge( VecAfSpecBridge::parse(&NlaBuffer::new(&buf.value())) .context("invalid IFLA_AF_SPEC value for AF_BRIDGE")? .0, ), _ => Self::AfSpecUnknown(payload.to_vec()), }, IFLA_LINKINFO => Self::LinkInfo( VecLinkInfo::parse(&NlaBuffer::new(&buf.value())) .context("invalid IFLA_LINKINFO value")? .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.19.0/src/link/buffer_tool.rs000064400000000000000000000012721046102023000203620ustar 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.19.0/src/link/down_reason.rs000064400000000000000000000037231046102023000203750ustar 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.19.0/src/link/event.rs000064400000000000000000000042211046102023000171720ustar 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.19.0/src/link/ext_mask.rs000064400000000000000000000054121046102023000176670ustar 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.19.0/src/link/header.rs000064400000000000000000000074221046102023000173070ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::{NlaBuffer, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; use crate::{ link::{link_flag::VecLinkFlag, LinkFlag, LinkLayerType}, AddressFamily, }; 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, for instance `vec![LinkFlag::Up, LinkFlag::LowerUp]`. /// To convert `Vec` into `u32`, you may: /// `u32::from(&VecLinkFlag(Vec)` /// To convert `u32` to `Vec`, you may: /// `VecLinkFlag::from(u32).0` pub flags: Vec, /// Change mask for the `flags` field. pub change_mask: Vec, } 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(u32::from(&VecLinkFlag( self.change_mask.to_vec(), ))); packet.set_link_layer_type(u16::from(self.link_layer_type)); packet.set_flags(u32::from(&VecLinkFlag(self.flags.to_vec()))); } } 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: VecLinkFlag::from(buf.change_mask()).0, flags: VecLinkFlag::from(buf.flags()).0, }) } } netlink-packet-route-0.19.0/src/link/link_flag.rs000064400000000000000000000125111046102023000200000ustar 00000000000000// SPDX-License-Identifier: MIT 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; #[derive(Debug, PartialEq, Eq, Clone, Default)] pub(crate) struct VecLinkFlag(pub Vec); #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub enum LinkFlag { Up, Broadcast, Debug, Loopback, Pointopoint, Notrailers, Running, Noarp, Promisc, Allmulti, Controller, Port, Multicast, Portsel, Automedia, Dynamic, LowerUp, Dormant, Echo, Other(u32), } impl From for LinkFlag { fn from(d: u32) -> Self { match d { IFF_UP => Self::Up, IFF_BROADCAST => Self::Broadcast, IFF_DEBUG => Self::Debug, IFF_LOOPBACK => Self::Loopback, IFF_POINTOPOINT => Self::Pointopoint, IFF_NOTRAILERS => Self::Notrailers, IFF_RUNNING => Self::Running, IFF_NOARP => Self::Noarp, IFF_PROMISC => Self::Promisc, IFF_ALLMULTI => Self::Allmulti, IFF_CONTROLLER => Self::Controller, IFF_PORT => Self::Port, IFF_MULTICAST => Self::Multicast, IFF_PORTSEL => Self::Portsel, IFF_AUTOMEDIA => Self::Automedia, IFF_DYNAMIC => Self::Dynamic, IFF_LOWER_UP => Self::LowerUp, IFF_DORMANT => Self::Dormant, IFF_ECHO => Self::Echo, _ => Self::Other(d), } } } impl From for u32 { fn from(v: LinkFlag) -> u32 { match v { LinkFlag::Up => IFF_UP, LinkFlag::Broadcast => IFF_BROADCAST, LinkFlag::Debug => IFF_DEBUG, LinkFlag::Loopback => IFF_LOOPBACK, LinkFlag::Pointopoint => IFF_POINTOPOINT, LinkFlag::Notrailers => IFF_NOTRAILERS, LinkFlag::Running => IFF_RUNNING, LinkFlag::Noarp => IFF_NOARP, LinkFlag::Promisc => IFF_PROMISC, LinkFlag::Allmulti => IFF_ALLMULTI, LinkFlag::Controller => IFF_CONTROLLER, LinkFlag::Port => IFF_PORT, LinkFlag::Multicast => IFF_MULTICAST, LinkFlag::Portsel => IFF_PORTSEL, LinkFlag::Automedia => IFF_AUTOMEDIA, LinkFlag::Dynamic => IFF_DYNAMIC, LinkFlag::LowerUp => IFF_LOWER_UP, LinkFlag::Dormant => IFF_DORMANT, LinkFlag::Echo => IFF_ECHO, LinkFlag::Other(i) => i, } } } impl std::fmt::Display for LinkFlag { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Up => write!(f, "UP"), Self::Broadcast => write!(f, "BROADCAST"), Self::Debug => write!(f, "DEBUG"), Self::Loopback => write!(f, "LOOPBACK"), Self::Pointopoint => write!(f, "POINTOPOINT"), Self::Notrailers => write!(f, "NOTRAILERS"), Self::Running => write!(f, "RUNNING"), Self::Noarp => write!(f, "NOARP"), Self::Promisc => write!(f, "PROMISC"), Self::Allmulti => write!(f, "ALLMULTI"), Self::Controller => write!(f, "CONTROLLER"), Self::Port => write!(f, "PORT"), Self::Multicast => write!(f, "MULTICAST"), Self::Portsel => write!(f, "PORTSEL"), Self::Automedia => write!(f, "AUTOMEDIA"), Self::Dynamic => write!(f, "DYNAMIC"), Self::LowerUp => write!(f, "LOWER_UP"), Self::Dormant => write!(f, "DORMANT"), Self::Echo => write!(f, "ECHO"), Self::Other(i) => write!(f, "Other({})", i), } } } // Please sort this list. const ALL_LINK_FLAGS: [LinkFlag; 19] = [ LinkFlag::Allmulti, LinkFlag::Automedia, LinkFlag::Broadcast, LinkFlag::Controller, LinkFlag::Debug, LinkFlag::Dormant, LinkFlag::Dynamic, LinkFlag::Echo, LinkFlag::Loopback, LinkFlag::LowerUp, LinkFlag::Multicast, LinkFlag::Noarp, LinkFlag::Notrailers, LinkFlag::Pointopoint, LinkFlag::Port, LinkFlag::Portsel, LinkFlag::Promisc, LinkFlag::Running, LinkFlag::Up, ]; impl From for VecLinkFlag { 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(LinkFlag::Other(d - got)); } Self(ret) } } impl From<&VecLinkFlag> for u32 { fn from(v: &VecLinkFlag) -> u32 { let mut d: u32 = 0; for flag in &v.0 { d += u32::from(*flag); } d } } netlink-packet-route-0.19.0/src/link/link_info/bond.rs000064400000000000000000000446301046102023000207530ustar 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; #[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() ))?), }) } } // Some attributes (ARP_IP_TARGET, NS_IP6_TARGET) contain a nested // list of IP addresses, where each element uses the index as NLA kind // and the address as value. InfoBond exposes vectors of IP addresses, // and we use this struct for serialization. struct BondIpAddrNla { index: u16, addr: IpAddr, } struct BondIpAddrNlaList(Vec); impl Deref for BondIpAddrNlaList { type Target = Vec; fn deref(&self) -> &Self::Target { &self.0 } } impl From<&Vec> for BondIpAddrNlaList { fn from(addrs: &Vec) -> Self { let mut nlas = Vec::new(); for (i, addr) in addrs.iter().enumerate() { let nla = BondIpAddrNla { index: i as u16, addr: IpAddr::V4(*addr), }; nlas.push(nla); } BondIpAddrNlaList(nlas) } } impl From<&Vec> for BondIpAddrNlaList { fn from(addrs: &Vec) -> Self { let mut nlas = Vec::new(); for (i, addr) in addrs.iter().enumerate() { let nla = BondIpAddrNla { index: i as u16, addr: IpAddr::V6(*addr), }; nlas.push(nla); } BondIpAddrNlaList(nlas) } } impl Nla for BondIpAddrNla { fn value_len(&self) -> usize { if self.addr.is_ipv4() { 4 } else { 16 } } fn emit_value(&self, buffer: &mut [u8]) { match self.addr { IpAddr::V4(addr) => buffer.copy_from_slice(&addr.octets()), IpAddr::V6(addr) => buffer.copy_from_slice(&addr.octets()), } } fn kind(&self) -> u16 { self.index } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoBond { Mode(u8), ActivePort(u32), MiiMon(u32), UpDelay(u32), DownDelay(u32), UseCarrier(u8), ArpInterval(u32), ArpIpTarget(Vec), ArpValidate(u32), ArpAllTargets(u32), Primary(u32), PrimaryReselect(u8), FailOverMac(u8), XmitHashPolicy(u8), ResendIgmp(u32), NumPeerNotif(u8), AllPortsActive(u8), MinLinks(u32), LpInterval(u32), PacketsPerPort(u32), AdLacpRate(u8), AdSelect(u8), AdInfo(Vec), AdActorSysPrio(u16), AdUserPortKey(u16), AdActorSystem([u8; 6]), TlbDynamicLb(u8), PeerNotifDelay(u32), AdLacpActive(u8), MissedMax(u8), NsIp6Target(Vec), 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) | 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::ActivePort(value) | Self::MiiMon(value) | Self::UpDelay(value) | Self::DownDelay(value) | Self::ArpInterval(value) | Self::ArpValidate(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")?, ), 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")?, ), 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.19.0/src/link/link_info/bond_port.rs000064400000000000000000000134431046102023000220150ustar 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.19.0/src/link/link_info/bridge.rs000064400000000000000000000634661046102023000212750ustar 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(u8), TopologyChange(u8), TopologyChangeDetected(u8), MulticastRouter(u8), MulticastSnooping(u8), MulticastQueryUseIfaddr(u8), MulticastQuerier(u8), NfCallIpTables(u8), NfCallIp6Tables(u8), NfCallArpTables(u8), VlanStatsEnabled(u8), MulticastStatsEnabled(u8), MulticastIgmpVersion(u8), MulticastMldVersion(u8), VlanStatsPerHost(u8), MultiBoolOpt(u64), MulticastQuerierState(Vec), Other(DefaultNla), } impl Nla for InfoBridge { 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(_) | 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) | 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")?, ), 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.19.0/src/link/link_info/bridge_port.rs000064400000000000000000000602131046102023000223240ustar 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.19.0/src/link/link_info/gre.rs000064400000000000000000000020471046102023000206020ustar 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.19.0/src/link/link_info/gre6.rs000064400000000000000000000020551046102023000206670ustar 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.19.0/src/link/link_info/gre_tap.rs000064400000000000000000000020521046102023000214420ustar 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.19.0/src/link/link_info/gre_tap6.rs000064400000000000000000000020561046102023000215340ustar 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.19.0/src/link/link_info/gtp.rs000064400000000000000000000020361046102023000206150ustar 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.19.0/src/link/link_info/hsr.rs000064400000000000000000000110761046102023000206230ustar 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.19.0/src/link/link_info/info_data.rs000064400000000000000000000321661046102023000217560ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{Nla, NlaBuffer, NlasIterator}, DecodeError, Emitable, Parseable, }; use super::super::{ InfoBond, InfoBridge, InfoGreTap, InfoGreTap6, InfoGreTun, InfoGreTun6, InfoGtp, InfoHsr, InfoIpVlan, 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), 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), 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::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::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::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::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::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) } _ => InfoData::Other(payload.to_vec()), }) } } netlink-packet-route-0.19.0/src/link/link_info/info_port.rs000064400000000000000000000072741046102023000220330ustar 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}; const BOND: &str = "bond"; const BRIDGE: &str = "bridge"; 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, 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::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::Other(s) => s.len(), }; len + 1 } fn emit_value(&self, buffer: &mut [u8]) { let s = match self { Self::Bond => BOND, Self::Bridge => BRIDGE, 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, _ => Self::Other(s), }) } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoPortData { BondPort(Vec), BridgePort(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::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::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::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.19.0/src/link/link_info/infos.rs000064400000000000000000000245701046102023000211500ustar 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 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"; #[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, MacVlan, MacVtap, GreTap, GreTap6, IpTun, SitTun, GreTun, GreTun6, Vti, Vrf, Gtp, Ipoib, Wireguard, Xfrm, MacSec, Hsr, 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::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::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::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::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, 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, _ => Self::Other(s), }) } } netlink-packet-route-0.19.0/src/link/link_info/ipoib.rs000064400000000000000000000041671046102023000211340ustar 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.19.0/src/link/link_info/ipvlan.rs000064400000000000000000000035271046102023000213220ustar 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(u16), Flags(u16), 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), Flags(value) => NativeEndian::write_u16(buffer, *value), 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")?, ), IFLA_IPVLAN_FLAGS => Flags( parse_u16(payload) .context("invalid IFLA_IPVLAN_FLAGS value")?, ), kind => Other(DefaultNla::parse(buf).context(format!( "unknown NLA type {kind} for IFLA_INFO_DATA(ipvlan)" ))?), }) } } netlink-packet-route-0.19.0/src/link/link_info/mac_vlan.rs000064400000000000000000000226361046102023000216130ustar 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(u32), 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 { use self::InfoMacVlan::*; 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::InfoMacVlan::*; match self { Mode(value) => NativeEndian::write_u32(buffer, *value), Flags(value) => NativeEndian::write_u16(buffer, *value), MacAddrMode(value) => NativeEndian::write_u32(buffer, *value), MacAddr(bytes) => buffer.copy_from_slice(bytes), MacAddrData(ref nlas) => nlas.as_slice().emit(buffer), MacAddrCount(value) => NativeEndian::write_u32(buffer, *value), BcQueueLen(value) => NativeEndian::write_u32(buffer, *value), BcQueueLenUsed(value) => NativeEndian::write_u32(buffer, *value), BcCutoff(value) => NativeEndian::write_i32(buffer, *value), Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::InfoMacVlan::*; match self { 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")?, ), 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(u32), Flags(u16), MacAddrMode(u32), MacAddr([u8; 6]), MacAddrData(Vec), MacAddrCount(u32), BcQueueLen(u32), BcQueueLenUsed(u32), BcCutoff(i32), Other(DefaultNla), } impl Nla for InfoMacVtap { fn value_len(&self) -> usize { use self::InfoMacVtap::*; match self { 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), 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")?, ), 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)" ))?), }) } } netlink-packet-route-0.19.0/src/link/link_info/macsec.rs000064400000000000000000000220531046102023000212570ustar 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.19.0/src/link/link_info/mod.rs000064400000000000000000000027421046102023000206060ustar 00000000000000// SPDX-License-Identifier: MIT mod bond; mod bond_port; mod bridge; mod bridge_port; 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, 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::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}; pub use self::infos::{InfoKind, LinkInfo}; pub use self::ipoib::InfoIpoib; pub use self::ipvlan::InfoIpVlan; pub use self::mac_vlan::{InfoMacVlan, InfoMacVtap}; 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.19.0/src/link/link_info/sit.rs000064400000000000000000000020471046102023000206240ustar 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.19.0/src/link/link_info/tun.rs000064400000000000000000000020301046102023000206230ustar 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.19.0/src/link/link_info/veth.rs000064400000000000000000000035261046102023000207760ustar 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.19.0/src/link/link_info/vlan.rs000064400000000000000000000125001046102023000207600ustar 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 => 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.19.0/src/link/link_info/vrf.rs000064400000000000000000000027751046102023000206320ustar 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.19.0/src/link/link_info/vti.rs000064400000000000000000000020361046102023000206250ustar 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.19.0/src/link/link_info/vxlan.rs000064400000000000000000000263361046102023000211640ustar 00000000000000// SPDX-License-Identifier: MIT 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(Vec), Group6(Vec), Link(u32), Local(Vec), Local6(Vec), 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(_) => 4, Self::Local(bytes) | Self::Local6(bytes) | Self::Group(bytes) | Self::Group6(bytes) => bytes.len(), 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::Local(value) | Self::Group(value) | Self::Group6(value) | Self::Local6(value) => buffer.copy_from_slice(value.as_slice()), 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 => Self::Group(payload.to_vec()), IFLA_VXLAN_GROUP6 => Self::Group6(payload.to_vec()), IFLA_VXLAN_LINK => Self::Link( parse_u32(payload).context("invalid IFLA_VXLAN_LINK value")?, ), IFLA_VXLAN_LOCAL => Self::Local(payload.to_vec()), IFLA_VXLAN_LOCAL6 => Self::Local6(payload.to_vec()), 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.19.0/src/link/link_info/xfrm.rs000064400000000000000000000034551046102023000210050ustar 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.19.0/src/link/link_info/xstats.rs000064400000000000000000000017131046102023000213520ustar 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.19.0/src/link/link_layer_type.rs000064400000000000000000000262311046102023000212500ustar 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.19.0/src/link/link_state.rs000064400000000000000000000032041046102023000202060ustar 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.19.0/src/link/map.rs000064400000000000000000000025431046102023000166330ustar 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)] #[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.19.0/src/link/message.rs000064400000000000000000000033631046102023000175030ustar 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.19.0/src/link/mod.rs000064400000000000000000000047311046102023000166360ustar 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; pub(crate) 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, BridgeVlanInfo, Icmp6Stats, Icmp6StatsBuffer, Inet6CacheInfo, Inet6CacheInfoBuffer, Inet6DevConf, Inet6DevConfBuffer, Inet6IfaceFlag, 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::LinkFlag; pub use self::link_info::{ BondAdInfo, BondPortState, BridgeId, BridgeIdBuffer, BridgePortMulticastRouter, BridgePortState, BridgeQuerierState, HsrProtocol, InfoBond, InfoBondPort, InfoBridge, InfoBridgePort, InfoData, InfoGreTap, InfoGreTap6, InfoGreTun, InfoGreTun6, InfoGtp, InfoHsr, InfoIpVlan, InfoIpoib, InfoKind, InfoMacSec, InfoMacVlan, InfoMacVtap, InfoPortData, InfoPortKind, InfoSitTun, InfoTun, InfoVeth, InfoVlan, InfoVrf, InfoVti, InfoVxlan, InfoXfrm, LinkInfo, LinkXstats, MacSecCipherId, MacSecOffload, MacSecValidate, 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.19.0/src/link/phys_id.rs000064400000000000000000000014301046102023000175070ustar 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.19.0/src/link/prop_list.rs000064400000000000000000000032071046102023000200670ustar 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.19.0/src/link/proto_info/bridge.rs000064400000000000000000000031071046102023000214650ustar 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.19.0/src/link/proto_info/inet6.rs000064400000000000000000000030761046102023000212630ustar 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.19.0/src/link/proto_info/mod.rs000064400000000000000000000003661046102023000210140ustar 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.19.0/src/link/sriov/broadcast.rs000064400000000000000000000022651046102023000211630ustar 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.19.0/src/link/sriov/guid.rs000064400000000000000000000017101046102023000201430ustar 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.19.0/src/link/sriov/link_state.rs000064400000000000000000000036721046102023000213610ustar 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.19.0/src/link/sriov/mac.rs000064400000000000000000000024301046102023000177530ustar 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.19.0/src/link/sriov/mod.rs000064400000000000000000000020621046102023000177730ustar 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.19.0/src/link/sriov/rate.rs000064400000000000000000000024151046102023000201510ustar 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.19.0/src/link/sriov/rss_query.rs000064400000000000000000000021601046102023000212470ustar 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.19.0/src/link/sriov/spoofchk.rs000064400000000000000000000021441046102023000210310ustar 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.19.0/src/link/sriov/stats.rs000064400000000000000000000100421046102023000203470ustar 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.19.0/src/link/sriov/trust.rs000064400000000000000000000020631046102023000203760ustar 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.19.0/src/link/sriov/tx_rate.rs000064400000000000000000000020131046102023000206560ustar 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.19.0/src/link/sriov/vf_list.rs000064400000000000000000000221301046102023000206600ustar 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(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.19.0/src/link/sriov/vf_port.rs000064400000000000000000000063341046102023000207010ustar 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(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.19.0/src/link/sriov/vf_vlan.rs000064400000000000000000000055071046102023000206560ustar 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.19.0/src/link/sriov/vlan.rs000064400000000000000000000022551046102023000201600ustar 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.19.0/src/link/stats.rs000064400000000000000000000117471046102023000172220ustar 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)] #[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.19.0/src/link/stats64.rs000064400000000000000000000123461046102023000173700ustar 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)] #[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.19.0/src/link/tests/bond.rs000064400000000000000000000135211046102023000201400ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::link::{ BondPortState, InfoBond, InfoBondPort, InfoData, InfoKind, InfoPortData, InfoPortKind, LinkAttribute, LinkFlag, LinkHeader, LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, MiiStatus, }; use crate::AddressFamily; #[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: vec![ LinkFlag::Broadcast, LinkFlag::Controller, LinkFlag::LowerUp, LinkFlag::Multicast, LinkFlag::Running, LinkFlag::Up, ], change_mask: vec![], }, attributes: vec![LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::Bond), LinkInfo::Data(InfoData::Bond(vec![ InfoBond::Mode(0), InfoBond::MiiMon(0), InfoBond::UpDelay(0), InfoBond::DownDelay(0), InfoBond::PeerNotifDelay(0), InfoBond::UseCarrier(1), InfoBond::ArpInterval(0), InfoBond::ArpValidate(0), 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: vec![ LinkFlag::Broadcast, LinkFlag::LowerUp, LinkFlag::Multicast, LinkFlag::Port, LinkFlag::Running, LinkFlag::Up, ], change_mask: vec![], }, 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); } netlink-packet-route-0.19.0/src/link/tests/bridge.rs000064400000000000000000000700401046102023000204510ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::{DefaultNla, NlaBuffer}, Emitable, Parseable, }; use crate::link::{ af_spec::VecAfSpecBridge, AfSpecBridge, AfSpecInet, AfSpecInet6, AfSpecUnspec, BridgeId, BridgePortMulticastRouter, BridgePortState, BridgeVlanInfo, Inet6CacheInfo, Inet6DevConf, Inet6IfaceFlag, Inet6IfaceFlags, InetDevConf, InfoBridge, InfoBridgePort, InfoData, InfoKind, InfoPortData, InfoPortKind, LinkAttribute, LinkFlag, 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: vec![ LinkFlag::Broadcast, LinkFlag::LowerUp, LinkFlag::Multicast, LinkFlag::Running, LinkFlag::Up, ], change_mask: vec![], }, 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(0), 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(vec![ Inet6IfaceFlag::RsSent, Inet6IfaceFlag::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: 6, vid: 1 }), AfSpecBridge::VlanInfo(BridgeVlanInfo { flags: 8, vid: 2 }), AfSpecBridge::VlanInfo(BridgeVlanInfo { flags: 16, 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: vec![LinkFlag::Broadcast, LinkFlag::Multicast], change_mask: vec![], }, 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); } netlink-packet-route-0.19.0/src/link/tests/hsr.rs000064400000000000000000000036311046102023000200130ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::link::{ HsrProtocol, InfoData, InfoHsr, InfoKind, LinkAttribute, LinkFlag, 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: vec![ LinkFlag::Broadcast, LinkFlag::LowerUp, LinkFlag::Multicast, LinkFlag::Running, LinkFlag::Up, ], change_mask: vec![], }, 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.19.0/src/link/tests/ipvlan.rs000064400000000000000000000027021046102023000205060ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::link::{ InfoData, InfoIpVlan, InfoKind, LinkAttribute, LinkFlag, 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: vec![LinkFlag::Broadcast, LinkFlag::Multicast], change_mask: vec![], }, attributes: vec![LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::IpVlan), LinkInfo::Data(InfoData::IpVlan(vec![ InfoIpVlan::Mode(0), InfoIpVlan::Flags(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); } netlink-packet-route-0.19.0/src/link/tests/loopback.rs000064400000000000000000000174571046102023000210240ustar 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, Inet6IfaceFlag, 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(vec![Inet6IfaceFlag::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.19.0/src/link/tests/macsec.rs000064400000000000000000000053401046102023000204510ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::link::{ InfoData, InfoKind, InfoMacSec, LinkAttribute, LinkFlag, 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: vec![ LinkFlag::Broadcast, LinkFlag::LowerUp, LinkFlag::Multicast, LinkFlag::Running, LinkFlag::Up, ], change_mask: vec![], }, 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.19.0/src/link/tests/macvlan.rs000064400000000000000000000044211046102023000206360ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::link::{ InfoData, InfoKind, InfoMacVlan, LinkAttribute, LinkFlag, LinkHeader, LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, }; 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: vec![ LinkFlag::Broadcast, LinkFlag::LowerUp, LinkFlag::Multicast, LinkFlag::Running, LinkFlag::Up, ], change_mask: vec![], }, attributes: vec![LinkAttribute::LinkInfo(vec![ LinkInfo::Kind(InfoKind::MacVlan), LinkInfo::Data(InfoData::MacVlan(vec![ InfoMacVlan::Mode(16), 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.19.0/src/link/tests/message.rs000064400000000000000000000157361046102023000206540ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::traits::{Emitable, ParseableParametrized}; use crate::link::{ LinkAttribute, LinkFlag, 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(), u32::from(LinkFlag::Loopback) | u32::from(LinkFlag::LowerUp) | u32::from(LinkFlag::Running) | u32::from(LinkFlag::Up) ); 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( u32::from(LinkFlag::Loopback) | u32::from(LinkFlag::LowerUp) | u32::from(LinkFlag::Running) | u32::from(LinkFlag::Up), ); 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: vec![ LinkFlag::Loopback, LinkFlag::LowerUp, LinkFlag::Running, LinkFlag::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.19.0/src/link/tests/mod.rs000064400000000000000000000010221046102023000177660ustar 00000000000000// SPDX-License-Identifier: MIT #[cfg(test)] mod bond; #[cfg(test)] mod bridge; #[cfg(test)] mod hsr; #[cfg(test)] mod ipvlan; #[cfg(test)] mod loopback; #[cfg(test)] mod macsec; #[cfg(test)] mod macvlan; #[cfg(test)] mod message; #[cfg(test)] mod prop_list; #[cfg(test)] mod sriov; #[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 //TODO(Gris Ge): need test for Icmp6Stats and Inet6Stats netlink-packet-route-0.19.0/src/link/tests/prop_list.rs000064400000000000000000000030371046102023000212320ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::link::{ LinkAttribute, LinkFlag, 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: vec![ LinkFlag::Broadcast, LinkFlag::LowerUp, LinkFlag::Multicast, LinkFlag::Running, LinkFlag::Up, ], change_mask: vec![], }, 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.19.0/src/link/tests/sriov.rs000064400000000000000000000177251046102023000203720ustar 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.19.0/src/link/tests/veth.rs000064400000000000000000000075641046102023000201760ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::link::{ InfoData, InfoKind, InfoVeth, LinkAttribute, LinkFlag, 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: vec![ LinkFlag::Broadcast, LinkFlag::LowerUp, LinkFlag::Multicast, LinkFlag::Running, LinkFlag::Up, ], change_mask: vec![], }, 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.19.0/src/link/tests/vlan.rs000064400000000000000000000055271046102023000201650ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::link::{ InfoData, InfoKind, InfoVlan, LinkAttribute, LinkFlag, 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: vec![ LinkFlag::Broadcast, LinkFlag::LowerUp, LinkFlag::Multicast, LinkFlag::Running, LinkFlag::Up, ], change_mask: vec![], }, 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.19.0/src/link/tests/vrf.rs000064400000000000000000000026541046102023000200200ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::link::{ InfoData, InfoKind, InfoVrf, LinkAttribute, LinkFlag, LinkHeader, LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, }; 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: vec![ LinkFlag::Controller, LinkFlag::LowerUp, LinkFlag::Noarp, LinkFlag::Running, LinkFlag::Up, ], change_mask: vec![], }, 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); } netlink-packet-route-0.19.0/src/link/tests/vxlan.rs000064400000000000000000000453701046102023000203550ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{nla::DefaultNla, Emitable, Parseable}; use crate::link::{ AfSpecInet, AfSpecInet6, AfSpecUnspec, Inet6CacheInfo, Inet6DevConf, Inet6IfaceFlag, Inet6IfaceFlags, InetDevConf, InfoData, InfoKind, InfoVxlan, LinkAttribute, LinkFlag, 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: vec![ LinkFlag::Broadcast, LinkFlag::LowerUp, LinkFlag::Multicast, LinkFlag::Running, LinkFlag::Up, ], change_mask: vec![], }, 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(vec![8, 8, 8, 8]), InfoVxlan::Link(13), InfoVxlan::Local(vec![1, 1, 1, 1]), 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(vec![ Inet6IfaceFlag::RsSent, Inet6IfaceFlag::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); } netlink-packet-route-0.19.0/src/link/tests/xdp.rs000064400000000000000000000127401046102023000200130ustar 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.19.0/src/link/tests/xfrm.rs000064400000000000000000000030311046102023000201650ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::link::{ InfoData, InfoKind, InfoXfrm, LinkAttribute, LinkFlag, 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: vec![ LinkFlag::LowerUp, LinkFlag::Noarp, LinkFlag::Running, LinkFlag::Up, ], change_mask: vec![], }, 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.19.0/src/link/vlan_protocol.rs000064400000000000000000000024571046102023000207430ustar 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.19.0/src/link/wireless.rs000064400000000000000000000011751046102023000177130ustar 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.19.0/src/link/xdp.rs000064400000000000000000000146621046102023000166560ustar 00000000000000// SPDX-License-Identifier: MIT use std::{convert::TryFrom, mem::size_of, os::fd::RawFd}; use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, parsers::{parse_i32, parse_u32}, DecodeError, Parseable, }; 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 => Self::Attached( XdpAttached::try_from(payload[0]) .context("invalid IFLA_XDP_ATTACHED value")?, ), 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.19.0/src/message.rs000064400000000000000000000573271046102023000165570ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ DecodeError, Emitable, Parseable, ParseableParametrized, }; use netlink_packet_core::{ NetlinkDeserializable, NetlinkHeader, NetlinkPayload, NetlinkSerializable, }; 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!(), } } // 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), 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_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, 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() } } #[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) } } } 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.19.0/src/neighbour/address.rs000064400000000000000000000030611046102023000205240ustar 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.19.0/src/neighbour/attribute.rs000064400000000000000000000130731046102023000211060ustar 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.19.0/src/neighbour/cache_info.rs000064400000000000000000000023461046102023000211620ustar 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.19.0/src/neighbour/flags.rs000064400000000000000000000036751046102023000202060ustar 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; #[derive(Clone, Eq, PartialEq, Debug, Copy)] #[non_exhaustive] pub enum NeighbourFlag { Use, // Hold NTF_SELF as Self is not rust reserved keyword Own, Controller, Proxy, ExtLearned, Offloaded, Sticky, Router, // No Other required as these are all 8 bits. } const ALL_RULE_FLAGS: [NeighbourFlag; 8] = [ NeighbourFlag::Use, NeighbourFlag::Own, NeighbourFlag::Controller, NeighbourFlag::Proxy, NeighbourFlag::ExtLearned, NeighbourFlag::Offloaded, NeighbourFlag::Sticky, NeighbourFlag::Router, ]; impl From for u8 { fn from(v: NeighbourFlag) -> u8 { match v { NeighbourFlag::Use => NTF_USE, NeighbourFlag::Own => NTF_SELF, NeighbourFlag::Controller => NTF_CONTROLLER, NeighbourFlag::Proxy => NTF_PROXY, NeighbourFlag::ExtLearned => NTF_EXT_LEARNED, NeighbourFlag::Offloaded => NTF_OFFLOADED, NeighbourFlag::Sticky => NTF_STICKY, NeighbourFlag::Router => NTF_ROUTER, } } } #[derive(Clone, Eq, PartialEq, Debug, Default)] pub(crate) struct VecNeighbourFlag(pub(crate) Vec); impl From for VecNeighbourFlag { fn from(d: u8) -> Self { let mut ret = Vec::new(); for flag in ALL_RULE_FLAGS { if (d & (u8::from(flag))) > 0 { ret.push(flag); } } Self(ret) } } impl From<&VecNeighbourFlag> for u8 { fn from(v: &VecNeighbourFlag) -> u8 { let mut d: u8 = 0; for flag in &v.0 { d += u8::from(*flag); } d } } netlink-packet-route-0.19.0/src/neighbour/header.rs000064400000000000000000000054461046102023000203400ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::{NlaBuffer, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; use super::{flags::VecNeighbourFlag, NeighbourFlag, 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: Vec, /// 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: VecNeighbourFlag::from(buf.flags()).0, 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(u8::from(&VecNeighbourFlag(self.flags.to_vec()))); packet.set_kind(self.kind.into()); } } netlink-packet-route-0.19.0/src/neighbour/kind.rs000064400000000000000000000033261046102023000200300ustar 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.19.0/src/neighbour/message.rs000064400000000000000000000036321046102023000205270ustar 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.19.0/src/neighbour/mod.rs000064400000000000000000000007641046102023000176650ustar 00000000000000// SPDX-License-Identifier: MIT mod address; mod attribute; mod cache_info; pub(crate) 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::NeighbourFlag; pub use self::header::{NeighbourHeader, NeighbourMessageBuffer}; pub use self::message::NeighbourMessage; pub use self::state::NeighbourState; netlink-packet-route-0.19.0/src/neighbour/state.rs000064400000000000000000000044311046102023000202210ustar 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.19.0/src/neighbour/tests/bridge.rs000064400000000000000000000023531046102023000215000ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::{ neighbour::{ NeighbourAttribute, NeighbourFlag, 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: vec![NeighbourFlag::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.19.0/src/neighbour/tests/ip.rs000064400000000000000000000126521046102023000206570ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::{Ipv4Addr, Ipv6Addr}; use std::str::FromStr; use netlink_packet_utils::{Emitable, Parseable}; use crate::{ neighbour::{ NeighbourAttribute, NeighbourCacheInfo, NeighbourFlag, 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: vec![], 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: vec![NeighbourFlag::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: vec![], 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.19.0/src/neighbour/tests/mod.rs000064400000000000000000000001171046102023000210170ustar 00000000000000// SPDX-License-Identifier: MIT #[cfg(test)] mod bridge; #[cfg(test)] mod ip; netlink-packet-route-0.19.0/src/neighbour_table/attribute.rs000064400000000000000000000112251046102023000222520ustar 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 => Self::Parms( VecNeighbourTableParameter::parse(&NlaBuffer::new(payload)) .context(format!("invalid NDTA_PARMS {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.19.0/src/neighbour_table/config.rs000064400000000000000000000037261046102023000215230ustar 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.19.0/src/neighbour_table/header.rs000064400000000000000000000024411046102023000214770ustar 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.19.0/src/neighbour_table/message.rs000064400000000000000000000032731046102023000216770ustar 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.19.0/src/neighbour_table/mod.rs000064400000000000000000000007711046102023000210320ustar 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.19.0/src/neighbour_table/param.rs000064400000000000000000000212261046102023000213510ustar 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.19.0/src/neighbour_table/stats.rs000064400000000000000000000046471046102023000214170ustar 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.19.0/src/neighbour_table/tests.rs000064400000000000000000000210701046102023000214100ustar 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.19.0/src/nsid/attribute.rs000064400000000000000000000051471046102023000200640ustar 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.19.0/src/nsid/header.rs000064400000000000000000000021401046102023000172770ustar 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.19.0/src/nsid/message.rs000064400000000000000000000027251046102023000175040ustar 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.19.0/src/nsid/mod.rs000064400000000000000000000003451046102023000166330ustar 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.19.0/src/nsid/tests.rs000064400000000000000000000043311046102023000172150ustar 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.19.0/src/prefix/attribute.rs000064400000000000000000000041201046102023000204120ustar 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.19.0/src/prefix/cache_info.rs000064400000000000000000000017161046102023000204750ustar 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.19.0/src/prefix/header.rs000064400000000000000000000024321046102023000176430ustar 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.19.0/src/prefix/message.rs000064400000000000000000000035641046102023000200460ustar 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.19.0/src/prefix/mod.rs000064400000000000000000000002671046102023000171760ustar 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.19.0/src/prefix/tests.rs000064400000000000000000000031751046102023000175620ustar 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.19.0/src/route/address.rs000064400000000000000000000036211046102023000177020ustar 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, }, route::MplsLabel, AddressFamily, }; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum RouteAddress { Inet(Ipv4Addr), Inet6(Ipv6Addr), Mpls(MplsLabel), Other(Vec), } 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.19.0/src/route/attribute.rs000064400000000000000000000276671046102023000203000ustar 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.19.0/src/route/cache_info.rs000064400000000000000000000031321046102023000203300ustar 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.19.0/src/route/flags.rs000064400000000000000000000057551046102023000173630ustar 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; #[derive(Clone, Eq, PartialEq, Debug, Copy)] #[non_exhaustive] pub enum RouteFlag { // Kernel also store next hope flags here Dead, Pervasive, Onlink, Offload, Linkdown, Unresolved, Trap, // Next hope flags ends Notify, Cloned, Equalize, Prefix, LookupTable, FibMatch, RtOffload, RtTrap, OffloadFailed, Other(u32), } const ALL_ROUTE_FLAGS: [RouteFlag; 16] = [ RouteFlag::Dead, RouteFlag::Pervasive, RouteFlag::Onlink, RouteFlag::Offload, RouteFlag::Linkdown, RouteFlag::Unresolved, RouteFlag::Trap, RouteFlag::Notify, RouteFlag::Cloned, RouteFlag::Equalize, RouteFlag::Prefix, RouteFlag::LookupTable, RouteFlag::FibMatch, RouteFlag::RtOffload, RouteFlag::RtTrap, RouteFlag::OffloadFailed, ]; impl From for u32 { fn from(v: RouteFlag) -> u32 { match v { RouteFlag::Dead => RTNH_F_DEAD.into(), RouteFlag::Pervasive => RTNH_F_PERVASIVE.into(), RouteFlag::Onlink => RTNH_F_ONLINK.into(), RouteFlag::Offload => RTNH_F_OFFLOAD.into(), RouteFlag::Linkdown => RTNH_F_LINKDOWN.into(), RouteFlag::Unresolved => RTNH_F_UNRESOLVED.into(), RouteFlag::Trap => RTNH_F_TRAP.into(), RouteFlag::Notify => RTM_F_NOTIFY, RouteFlag::Cloned => RTM_F_CLONED, RouteFlag::Equalize => RTM_F_EQUALIZE, RouteFlag::Prefix => RTM_F_PREFIX, RouteFlag::LookupTable => RTM_F_LOOKUP_TABLE, RouteFlag::FibMatch => RTM_F_FIB_MATCH, RouteFlag::RtOffload => RTM_F_OFFLOAD, RouteFlag::RtTrap => RTM_F_TRAP, RouteFlag::OffloadFailed => RTM_F_OFFLOAD_FAILED, RouteFlag::Other(i) => i, } } } #[derive(Clone, Eq, PartialEq, Debug, Default)] pub(crate) struct VecRouteFlag(pub(crate) Vec); impl From for VecRouteFlag { fn from(d: u32) -> Self { let mut got: u32 = 0; let mut ret = Vec::new(); for flag in ALL_ROUTE_FLAGS { if (d & (u32::from(flag))) > 0 { ret.push(flag); got += u32::from(flag); } } if got != d { ret.push(RouteFlag::Other(d - got)); } Self(ret) } } impl From<&VecRouteFlag> for u32 { fn from(v: &VecRouteFlag) -> u32 { let mut d: u32 = 0; for flag in &v.0 { d += u32::from(*flag); } d } } netlink-packet-route-0.19.0/src/route/header.rs000064400000000000000000000304051046102023000175050ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::{NlaBuffer, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; use super::{super::AddressFamily, flags::VecRouteFlag, RouteFlag}; 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: Vec, } 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: VecRouteFlag::from(buf.flags()).0, }) } } 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(u32::from(&VecRouteFlag(self.flags.to_vec()))); } } #[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.19.0/src/route/lwtunnel.rs000064400000000000000000000121251046102023000201240ustar 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.19.0/src/route/message.rs000064400000000000000000000053261046102023000177050ustar 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.19.0/src/route/metrics.rs000064400000000000000000000152351046102023000177270ustar 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.19.0/src/route/mfc_stats.rs000064400000000000000000000021071046102023000202360ustar 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.19.0/src/route/mod.rs000064400000000000000000000020101046102023000170230ustar 00000000000000// SPDX-License-Identifier: MIT mod address; mod attribute; mod cache_info; pub(crate) mod flags; mod header; mod lwtunnel; mod message; pub(crate) mod metrics; mod mfc_stats; mod mpls; pub(crate) 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::flags::RouteFlag; 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, RouteNextHopFlag}; pub use self::preference::RoutePreference; pub use self::realm::RouteRealm; pub use self::via::{RouteVia, RouteViaBuffer}; netlink-packet-route-0.19.0/src/route/mpls.rs000064400000000000000000000132571046102023000172360ustar 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.19.0/src/route/next_hops.rs000064400000000000000000000140311046102023000202610ustar 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; #[derive(Clone, Eq, PartialEq, Debug, Copy)] #[non_exhaustive] pub enum RouteNextHopFlag { Dead, Pervasive, Onlink, Offload, Linkdown, Unresolved, Trap, Other(u8), } impl From for u8 { fn from(v: RouteNextHopFlag) -> u8 { match v { RouteNextHopFlag::Dead => RTNH_F_DEAD, RouteNextHopFlag::Pervasive => RTNH_F_PERVASIVE, RouteNextHopFlag::Onlink => RTNH_F_ONLINK, RouteNextHopFlag::Offload => RTNH_F_OFFLOAD, RouteNextHopFlag::Linkdown => RTNH_F_LINKDOWN, RouteNextHopFlag::Unresolved => RTNH_F_UNRESOLVED, RouteNextHopFlag::Trap => RTNH_F_TRAP, RouteNextHopFlag::Other(i) => i, } } } const ALL_NH_FLAGS: [RouteNextHopFlag; 7] = [ RouteNextHopFlag::Dead, RouteNextHopFlag::Pervasive, RouteNextHopFlag::Onlink, RouteNextHopFlag::Offload, RouteNextHopFlag::Linkdown, RouteNextHopFlag::Unresolved, RouteNextHopFlag::Trap, ]; #[derive(Clone, Eq, PartialEq, Debug)] struct VecRouteNextHopFlag(Vec); impl From for VecRouteNextHopFlag { fn from(d: u8) -> Self { let mut got: u8 = 0; let mut ret = Vec::new(); for flag in ALL_NH_FLAGS { if (d & (u8::from(flag))) > 0 { ret.push(flag); got += u8::from(flag); } } if got != d { ret.push(RouteNextHopFlag::Other(d - got)); } Self(ret) } } impl From<&VecRouteNextHopFlag> for u8 { fn from(v: &VecRouteNextHopFlag) -> u8 { let mut d: u8 = 0; for flag in &v.0 { d += u8::from(*flag); } d } } 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, 8 + 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: Vec, /// 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: VecRouteNextHopFlag::from(buf.flags()).0, 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(u8::from(&VecRouteNextHopFlag(self.flags.to_vec()))); 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.19.0/src/route/preference.rs000064400000000000000000000022701046102023000203720ustar 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.19.0/src/route/realm.rs000064400000000000000000000020561046102023000173560ustar 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.19.0/src/route/tests/cache_info.rs000064400000000000000000000072041046102023000214760ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::Ipv6Addr; use std::str::FromStr; use netlink_packet_utils::traits::{Emitable, Parseable}; 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: vec![], }, 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.19.0/src/route/tests/expires.rs000064400000000000000000000032271046102023000211000ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::Ipv6Addr; use std::str::FromStr; use netlink_packet_utils::traits::{Emitable, Parseable}; 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: vec![], }, 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.19.0/src/route/tests/loopback.rs000064400000000000000000000121431046102023000212100ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::traits::{Emitable, Parseable}; use std::net::{Ipv4Addr, Ipv6Addr}; 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: vec![], }, 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: vec![], }, 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: vec![], }, 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.19.0/src/route/tests/mod.rs000064400000000000000000000004101046102023000201670ustar 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.19.0/src/route/tests/mpls.rs000064400000000000000000000277731046102023000204100ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::{Ipv4Addr, Ipv6Addr}; use std::str::FromStr; use netlink_packet_utils::traits::{Emitable, Parseable}; 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: vec![], }, 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: vec![], }, 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: vec![], }, 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: vec![], }, 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: vec![], }, 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.19.0/src/route/tests/multipath.rs000064400000000000000000000000401046102023000214160ustar 00000000000000// SPDX-License-Identifier: MIT netlink-packet-route-0.19.0/src/route/tests/realm.rs000064400000000000000000000051721046102023000205220ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::Ipv4Addr; use std::str::FromStr; use netlink_packet_utils::traits::{Emitable, Parseable}; use crate::route::{ RouteAttribute, RouteCacheInfo, RouteFlag, 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: vec![RouteFlag::Cloned, RouteFlag::Other(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.19.0/src/route/tests/route_flags.rs000064400000000000000000000031221046102023000217250ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::Ipv6Addr; use std::str::FromStr; use netlink_packet_utils::traits::{Emitable, Parseable}; use crate::route::{ RouteAttribute, RouteFlag, RouteHeader, RouteMessage, RouteMessageBuffer, 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: vec![RouteFlag::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); } netlink-packet-route-0.19.0/src/route/tests/uid.rs000064400000000000000000000050321046102023000201760ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::Ipv4Addr; use std::str::FromStr; use netlink_packet_utils::traits::{Emitable, Parseable}; use crate::route::{ RouteAttribute, RouteCacheInfo, RouteFlag, 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: vec![ RouteFlag::Cloned, // TODO(Gris Ge): Waiting reply from kernel team for this // value. RouteFlag::Other(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.19.0/src/route/tests/via.rs000064400000000000000000000036331046102023000202010ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::{Ipv4Addr, Ipv6Addr}; use std::str::FromStr; use netlink_packet_utils::traits::{Emitable, Parseable}; 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: vec![], }, 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.19.0/src/route/via.rs000064400000000000000000000053001046102023000170300ustar 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.19.0/src/rule/action.rs000064400000000000000000000026601046102023000173450ustar 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.19.0/src/rule/attribute.rs000064400000000000000000000177331046102023000201020ustar 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.19.0/src/rule/flags.rs000064400000000000000000000035161046102023000171650ustar 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; #[derive(Clone, Eq, PartialEq, Debug, Copy)] #[non_exhaustive] pub enum RuleFlag { Permanent, Invert, Unresolved, IifDetached, DevDetached, OifDetached, Other(u32), } const ALL_RULE_FLAGS: [RuleFlag; 5] = [ RuleFlag::Permanent, RuleFlag::Invert, RuleFlag::Unresolved, RuleFlag::IifDetached, RuleFlag::OifDetached, ]; impl From for u32 { fn from(v: RuleFlag) -> u32 { match v { RuleFlag::Permanent => FIB_RULE_PERMANENT, RuleFlag::Invert => FIB_RULE_INVERT, RuleFlag::Unresolved => FIB_RULE_UNRESOLVED, RuleFlag::IifDetached => FIB_RULE_IIF_DETACHED, RuleFlag::DevDetached => FIB_RULE_DEV_DETACHED, RuleFlag::OifDetached => FIB_RULE_OIF_DETACHED, RuleFlag::Other(i) => i, } } } #[derive(Clone, Eq, PartialEq, Debug, Default)] pub(crate) struct VecRuleFlag(pub(crate) Vec); impl From for VecRuleFlag { fn from(d: u32) -> Self { let mut got: u32 = 0; let mut ret = Vec::new(); for flag in ALL_RULE_FLAGS { if (d & (u32::from(flag))) > 0 { ret.push(flag); got += u32::from(flag); } } if got != d { ret.push(RuleFlag::Other(d - got)); } Self(ret) } } impl From<&VecRuleFlag> for u32 { fn from(v: &VecRuleFlag) -> u32 { let mut d: u32 = 0; for flag in &v.0 { d += u32::from(*flag); } d } } netlink-packet-route-0.19.0/src/rule/header.rs000064400000000000000000000040401046102023000173120ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{ nla::{NlaBuffer, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; use super::{super::AddressFamily, flags::VecRuleFlag, RuleAction, RuleFlag}; 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: Vec, } 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(u32::from(&VecRuleFlag(self.flags.to_vec()))); } } 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: VecRuleFlag::from(buf.flags()).0, }) } } netlink-packet-route-0.19.0/src/rule/message.rs000064400000000000000000000027331046102023000175150ustar 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.19.0/src/rule/mod.rs000064400000000000000000000006671046102023000166540ustar 00000000000000// SPDX-License-Identifier: MIT mod action; mod attribute; pub(crate) 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::RuleFlag; 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.19.0/src/rule/port_range.rs000064400000000000000000000020261046102023000202240ustar 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.19.0/src/rule/tests/fw_mark.rs000064400000000000000000000064141046102023000206610ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::{ route::RouteProtocol, rule::{ 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: vec![], }, 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: vec![], }, 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.19.0/src/rule/tests/iif_oif.rs000064400000000000000000000074221046102023000206370ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::Ipv4Addr; use std::str::FromStr; use netlink_packet_utils::{Emitable, Parseable}; use crate::{ route::RouteProtocol, rule::{ 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: vec![], }, 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: vec![], }, 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.19.0/src/rule/tests/l3mdev.rs000064400000000000000000000062021046102023000204200ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::{ route::RouteProtocol, rule::{ 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: vec![], }, 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: vec![], }, 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.19.0/src/rule/tests/mod.rs000064400000000000000000000003061046102023000200040ustar 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.19.0/src/rule/tests/on_boot_rules.rs000064400000000000000000000051031046102023000220760ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::{ route::RouteProtocol, rule::{ 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: vec![], }, 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: vec![], }, 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.19.0/src/rule/tests/sport_dport.rs000064400000000000000000000102761046102023000216130ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::{Emitable, Parseable}; use crate::{ route::{RouteProtocol, RouteRealm}, rule::{ 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: vec![], }, 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: vec![], }, 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.19.0/src/rule/tests/src_dst.rs000064400000000000000000000074441046102023000207000ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::{Ipv4Addr, Ipv6Addr}; use std::str::FromStr; use netlink_packet_utils::{Emitable, Parseable}; 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: vec![], }, 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: vec![], }, 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.19.0/src/rule/uid_range.rs000064400000000000000000000020571046102023000200250ustar 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.19.0/src/tc/actions/action.rs000064400000000000000000000244331046102023000204460ustar 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}, traits::{Emitable, Parseable, ParseableParametrized}, DecodeError, }; use super::{ TcActionMirror, TcActionMirrorOption, TcActionNat, TcActionNatOption, }; use crate::tc::TcStats2; const TCA_ACT_TAB: u16 = 1; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct TcAction { pub tab: u16, 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 { let mut attributes = vec![]; let mut kind = String::new(); for iter in NlasIterator::new(buf.value()) { let buf = iter.context("invalid action nla")?; let payload = buf.value(); attributes.push(match buf.kind() { TCA_ACT_KIND => { kind = parse_string(payload) .context("failed to parse TCA_ACT_KIND")?; TcActionAttribute::Kind(kind.clone()) } TCA_ACT_OPTIONS => { let mut nlas = vec![]; for nla in NlasIterator::new(payload) { let nla = nla.context("invalid TCA_ACT_OPTIONS")?; nlas.push( TcActionOption::parse_with_param(&nla, &kind) .context(format!( "failed to parse TCA_ACT_OPTIONS \ for kind {kind}" ))?, ) } TcActionAttribute::Options(nlas) } TCA_ACT_INDEX => TcActionAttribute::Index( parse_u32(payload) .context("failed to parse TCA_ACT_INDEX")?, ), TCA_ACT_STATS => { let mut nlas = vec![]; for nla in NlasIterator::new(payload) { let nla = nla.context("invalid TCA_ACT_STATS")?; nlas.push( TcStats2::parse_with_param(&nla, &kind).context( format!( "failed to parse TCA_ACT_STATS for \ kind {kind}", ), )?, ); } TcActionAttribute::Stats(nlas) } TCA_ACT_COOKIE => TcActionAttribute::Cookie(payload.to_vec()), TCA_ACT_IN_HW_COUNT => TcActionAttribute::InHwCount( parse_u32(payload) .context("failed to parse TCA_ACT_IN_HW_COUNT")?, ), _ => TcActionAttribute::Other( DefaultNla::parse(&buf) .context("failed to parse action nla")?, ), }); } 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; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum TcActionAttribute { Kind(String), Options(Vec), Index(u32), Stats(Vec), Cookie(Vec), InHwCount(u32), 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, 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(), } } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum TcActionOption { Mirror(TcActionMirrorOption), Nat(TcActionNatOption), 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")?, ), }) } } // `define tc_gen` in `linux/pkt_cls.h` #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] #[non_exhaustive] pub struct TcActionGeneric { pub index: u32, pub capab: u32, pub action: TcActionType, pub refcnt: i32, 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; #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] #[non_exhaustive] pub enum TcActionType { #[default] Unspec, Ok, Reclassify, Shot, Pipe, Stolen, Queued, Repeat, Redirect, Trap, 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.19.0/src/tc/actions/mirror.rs000064400000000000000000000103561046102023000205020ustar 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}; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct TcActionMirror {} impl TcActionMirror { pub const KIND: &'static str = "mirred"; } const TCA_MIRRED_TM: u16 = 1; const TCA_MIRRED_PARMS: u16 = 2; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum TcActionMirrorOption { Tm(Vec), Parms(TcMirror), 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; #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct TcMirror { pub generic: TcActionGeneric, pub eaction: TcMirrorActionType, 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; #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] #[non_exhaustive] pub enum TcMirrorActionType { #[default] EgressRedir, EgressMirror, IngressRedir, IngressMirror, 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.19.0/src/tc/actions/mod.rs000064400000000000000000000007051046102023000177440ustar 00000000000000// SPDX-License-Identifier: MIT mod action; mod mirror; mod nat; pub(crate) mod nat_flag; pub use self::action::{ TcAction, TcActionAttribute, TcActionGeneric, TcActionGenericBuffer, TcActionOption, TcActionType, }; pub use self::mirror::{ TcActionMirror, TcActionMirrorOption, TcMirror, TcMirrorActionType, TcMirrorBuffer, }; pub use self::nat::{TcActionNat, TcActionNatOption, TcNat, TcNatBuffer}; pub use self::nat_flag::TcNatFlag; netlink-packet-route-0.19.0/src/tc/actions/nat.rs000064400000000000000000000102411046102023000177430ustar 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::VecTcNatFlag, TcActionGeneric, TcActionGenericBuffer, TcNatFlag, }; const TCA_NAT_PARMS: u16 = 1; const TCA_NAT_TM: u16 = 2; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct TcActionNat {} impl TcActionNat { pub(crate) const KIND: &'static str = "nat"; } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum TcActionNatOption { Tm(Vec), Parms(TcNat), 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; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct TcNat { pub generic: TcActionGeneric, pub old_addr: Ipv4Addr, pub new_addr: Ipv4Addr, pub mask: Ipv4Addr, pub flags: Vec, } impl Default for TcNat { fn default() -> Self { Self { generic: TcActionGeneric::default(), old_addr: Ipv4Addr::UNSPECIFIED, new_addr: Ipv4Addr::UNSPECIFIED, mask: Ipv4Addr::UNSPECIFIED, flags: vec![], } } } 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(u32::from(&VecTcNatFlag(self.flags.to_vec()))); } } 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: VecTcNatFlag::from(buf.flags()).0, }) } } 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.19.0/src/tc/actions/nat_flag.rs000064400000000000000000000037371046102023000207500ustar 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); #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub enum TcNatFlag { Egress, Police, Bind, Replace, NoRtnl, AtIngress, Other(u32), } impl From for u32 { fn from(v: TcNatFlag) -> u32 { match v { TcNatFlag::Egress => TCA_NAT_FLAG_EGRESS, TcNatFlag::Police => TCA_ACT_FLAGS_POLICE, TcNatFlag::Bind => TCA_ACT_FLAGS_BIND, TcNatFlag::Replace => TCA_ACT_FLAGS_REPLACE, TcNatFlag::NoRtnl => TCA_ACT_FLAGS_NO_RTNL, TcNatFlag::AtIngress => TCA_ACT_FLAGS_AT_INGRESS, TcNatFlag::Other(i) => i, } } } const ALL_NAT_FLAGS: [TcNatFlag; 6] = [ TcNatFlag::Egress, TcNatFlag::Police, TcNatFlag::Bind, TcNatFlag::Replace, TcNatFlag::NoRtnl, TcNatFlag::AtIngress, ]; #[derive(Clone, Eq, PartialEq, Debug)] pub(crate) struct VecTcNatFlag(pub(crate) Vec); impl From for VecTcNatFlag { fn from(d: u32) -> Self { let mut got: u32 = 0; let mut ret = Vec::new(); for flag in ALL_NAT_FLAGS { if (d & (u32::from(flag))) > 0 { ret.push(flag); got += u32::from(flag); } } if got != d { ret.push(TcNatFlag::Other(d - got)); } Self(ret) } } impl From<&VecTcNatFlag> for u32 { fn from(v: &VecTcNatFlag) -> u32 { let mut d: u32 = 0; for flag in &v.0 { d += u32::from(*flag); } d } } netlink-packet-route-0.19.0/src/tc/attribute.rs000064400000000000000000000131211046102023000175240ustar 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.19.0/src/tc/filters/cls_u32.rs000064400000000000000000000217531046102023000204550ustar 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::{VecTcU32OptionFlag, VecTcU32SelectorFlag}; use crate::tc::{TcAction, TcHandle, TcU32OptionFlag, TcU32SelectorFlag}; 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(Vec), 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(v) => NativeEndian::write_u32( buffer, u32::from(&VecTcU32OptionFlag(v.to_vec())), ), 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( VecTcU32OptionFlag::from( parse_u32(payload) .context("failed to parse TCA_U32_FLAGS")?, ) .0, ), _ => Self::Other( DefaultNla::parse(buf).context("failed to parse u32 nla")?, ), }) } } #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct TcU32Selector { pub flags: Vec, 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(TC_U32_SEL_BUF_LEN) { flags: (u8, 0), offshift: (u8, 1), nkeys: (u8, 2), //pad: (u8, 3), offmask: (u16, 4..6), off: (u16, 6..8), offoff: (u16, 8..10), hoff: (u16, 10..12), hmask: (u32, 12..TC_U32_SEL_BUF_LEN), keys: (slice, TC_U32_SEL_BUF_LEN..), }); 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(u8::from(&VecTcU32SelectorFlag(self.flags.to_vec()))); 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: VecTcU32SelectorFlag::from(buf.flags()).0, 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.19.0/src/tc/filters/matchall.rs000064400000000000000000000061571046102023000207710ustar 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.19.0/src/tc/filters/mod.rs000064400000000000000000000004631046102023000177550ustar 00000000000000// SPDX-License-Identifier: MIT mod cls_u32; mod matchall; pub(crate) mod u32_flags; pub use self::cls_u32::{ TcFilterU32, TcFilterU32Option, TcU32Key, TcU32Selector, }; pub use self::matchall::{TcFilterMatchAll, TcFilterMatchAllOption}; pub use self::u32_flags::{TcU32OptionFlag, TcU32SelectorFlag}; netlink-packet-route-0.19.0/src/tc/filters/u32_flags.rs000064400000000000000000000065461046102023000207730ustar 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; #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub enum TcU32SelectorFlag { Terminal, Offset, VarOffset, Eat, Other(u8), } impl From for u8 { fn from(v: TcU32SelectorFlag) -> u8 { match v { TcU32SelectorFlag::Terminal => TC_U32_TERMINAL, TcU32SelectorFlag::Offset => TC_U32_OFFSET, TcU32SelectorFlag::VarOffset => TC_U32_VAROFFSET, TcU32SelectorFlag::Eat => TC_U32_EAT, TcU32SelectorFlag::Other(i) => i, } } } const ALL_SELECTOR_FLAGS: [TcU32SelectorFlag; 4] = [ TcU32SelectorFlag::Terminal, TcU32SelectorFlag::Offset, TcU32SelectorFlag::VarOffset, TcU32SelectorFlag::Eat, ]; #[derive(Clone, Eq, PartialEq, Debug)] pub(crate) struct VecTcU32SelectorFlag(pub(crate) Vec); impl From for VecTcU32SelectorFlag { fn from(d: u8) -> Self { let mut got: u8 = 0; let mut ret = Vec::new(); for flag in ALL_SELECTOR_FLAGS { if (d & (u8::from(flag))) > 0 { ret.push(flag); got += u8::from(flag); } } if got != d { ret.push(TcU32SelectorFlag::Other(d - got)); } Self(ret) } } impl From<&VecTcU32SelectorFlag> for u8 { fn from(v: &VecTcU32SelectorFlag) -> u8 { let mut d: u8 = 0; for flag in &v.0 { d += u8::from(*flag); } d } } 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; #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub enum TcU32OptionFlag { SkipHw, SkipSw, InHw, NotInHw, Verbose, Other(u32), } impl From for u32 { fn from(v: TcU32OptionFlag) -> u32 { match v { TcU32OptionFlag::SkipHw => TCA_CLS_FLAGS_SKIP_HW, TcU32OptionFlag::SkipSw => TCA_CLS_FLAGS_SKIP_SW, TcU32OptionFlag::InHw => TCA_CLS_FLAGS_IN_HW, TcU32OptionFlag::NotInHw => TCA_CLS_FLAGS_NOT_IN_HW, TcU32OptionFlag::Verbose => TCA_CLS_FLAGS_VERBOSE, TcU32OptionFlag::Other(i) => i, } } } const ALL_OPTION_FLAGS: [TcU32OptionFlag; 5] = [ TcU32OptionFlag::SkipHw, TcU32OptionFlag::SkipSw, TcU32OptionFlag::InHw, TcU32OptionFlag::NotInHw, TcU32OptionFlag::Verbose, ]; #[derive(Clone, Eq, PartialEq, Debug)] pub(crate) struct VecTcU32OptionFlag(pub(crate) Vec); impl From for VecTcU32OptionFlag { fn from(d: u32) -> Self { let mut got: u32 = 0; let mut ret = Vec::new(); for flag in ALL_OPTION_FLAGS { if (d & (u32::from(flag))) > 0 { ret.push(flag); got += u32::from(flag); } } if got != d { ret.push(TcU32OptionFlag::Other(d - got)); } Self(ret) } } impl From<&VecTcU32OptionFlag> for u32 { fn from(v: &VecTcU32OptionFlag) -> u32 { let mut d: u32 = 0; for flag in &v.0 { d += u32::from(*flag); } d } } netlink-packet-route-0.19.0/src/tc/header.rs000064400000000000000000000060311046102023000167530ustar 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.19.0/src/tc/message.rs000064400000000000000000000042571046102023000171570ustar 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.19.0/src/tc/mod.rs000064400000000000000000000022521046102023000163030ustar 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, TcActionMirror, TcActionMirrorOption, TcActionNat, TcActionNatOption, TcActionOption, TcActionType, TcMirror, TcMirrorActionType, TcMirrorBuffer, TcNat, TcNatBuffer, TcNatFlag, }; pub use self::attribute::TcAttribute; pub use self::filters::{ TcFilterMatchAll, TcFilterMatchAllOption, TcFilterU32, TcFilterU32Option, TcU32Key, TcU32OptionFlag, TcU32Selector, TcU32SelectorFlag, }; 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.19.0/src/tc/options.rs000064400000000000000000000102231046102023000172140ustar 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.19.0/src/tc/qdiscs/fq_codel.rs000064400000000000000000000255171046102023000205770ustar 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.19.0/src/tc/qdiscs/ingress.rs000064400000000000000000000023521046102023000204650ustar 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.19.0/src/tc/qdiscs/mod.rs000064400000000000000000000004601046102023000175700ustar 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.19.0/src/tc/stats/basic.rs000064400000000000000000000021151046102023000177410ustar 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.19.0/src/tc/stats/compat.rs000064400000000000000000000036411046102023000201500ustar 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.19.0/src/tc/stats/mod.rs000064400000000000000000000004761046102023000174470ustar 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.19.0/src/tc/stats/queue.rs000064400000000000000000000027361046102023000200150ustar 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.19.0/src/tc/stats/stats2.rs000064400000000000000000000047631046102023000201130ustar 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.19.0/src/tc/stats/xstats.rs000064400000000000000000000022301046102023000202040ustar 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.19.0/src/tc/tests/action_nat.rs000064400000000000000000000156521046102023000210150ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::Ipv4Addr; use netlink_packet_utils::{Emitable, Parseable}; use crate::{ tc::{ TcAction, TcActionAttribute, TcActionGeneric, TcActionNatOption, TcActionOption, TcActionType, TcAttribute, TcFilterU32Option, TcHandle, TcHeader, TcMessage, TcMessageBuffer, TcNat, TcOption, TcStats2, TcStatsBasic, TcStatsQueue, TcU32Key, TcU32OptionFlag, TcU32Selector, TcU32SelectorFlag, }, AddressFamily, }; // Setup: // ip link add dummy1 type dummy // ip link set dummy1 up // tc qdisc add dev dummy1 root handle 1: prio bands 4 // tc filter add dev dummy1 parent ffff: \ // protocol ip prio 10 u32 \ // match ip dst 192.0.2.1/32 \ // action nat ingress 192.0.2.1/32 203.0.113.1 // // Capture nlmon of this command: // // tc -s filter show dev dummy1 // // Raw packet modification: // * rtnetlink header removed. #[test] fn test_get_filter_nat() { let raw = vec![ 0x00, 0x00, 0x00, 0x00, 0x35, 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, 0x00, 0x01, 0x02, 0x00, 0x24, 0x00, 0x05, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x02, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x00, 0x00, 0xac, 0x00, 0x07, 0x00, 0xa8, 0x00, 0x01, 0x00, 0x08, 0x00, 0x01, 0x00, 0x6e, 0x61, 0x74, 0x00, 0x44, 0x00, 0x04, 0x00, 0x14, 0x00, 0x01, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 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, 0x00, 0x28, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x02, 0x02, 0xcb, 0x00, 0x71, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x02, 0x00, 0x87, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let expected = TcMessage { header: TcHeader { family: AddressFamily::Unspec, index: 53, handle: TcHandle { major: 0x8000, minor: 0x800, }, parent: TcHandle { major: 1, minor: 0 }, info: 262152, // TODO(Gris Ge) }, attributes: vec![ TcAttribute::Kind("u32".to_string()), TcAttribute::Chain(0), TcAttribute::Options(vec![ TcOption::U32(TcFilterU32Option::Selector(TcU32Selector { flags: vec![TcU32SelectorFlag::Terminal], offshift: 0, nkeys: 1, offmask: 0, off: 0, offoff: 0, hoff: 0, hmask: 0, keys: vec![TcU32Key { mask: 0xffffffff, val: u32::from_ne_bytes( Ipv4Addr::new(192, 0, 2, 2).octets(), ), off: 16, offmask: 0, }], })), TcOption::U32(TcFilterU32Option::Hash(u32::from_be(0x80))), TcOption::U32(TcFilterU32Option::Flags(vec![ TcU32OptionFlag::NotInHw, ])), TcOption::U32(TcFilterU32Option::Action(vec![TcAction { tab: 1, attributes: vec![ TcActionAttribute::Kind("nat".to_string()), TcActionAttribute::Stats(vec![ TcStats2::Basic(TcStatsBasic { bytes: 98, 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::Nat(TcActionNatOption::Parms( TcNat { generic: TcActionGeneric { index: 1, capab: 0, action: TcActionType::Ok, refcnt: 1, bindcnt: 1, }, old_addr: Ipv4Addr::new(192, 0, 2, 2), new_addr: Ipv4Addr::new(203, 0, 113, 1), mask: Ipv4Addr::new(255, 255, 255, 255), flags: vec![], }, )), TcActionOption::Nat(TcActionNatOption::Tm(vec![ 135, 20, 0, 0, 0, 0, 0, 0, 120, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 7, 0, 0, 0, 0, 0, 0, ])), ]), ], }])), TcOption::U32(TcFilterU32Option::Pnct(vec![ 4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 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.19.0/src/tc/tests/filter_matchall.rs000064400000000000000000000162721046102023000220270ustar 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, 0x00, // 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.19.0/src/tc/tests/filter_u32.rs000064400000000000000000000100471046102023000206450ustar 00000000000000// SPDX-License-Identifier: MIT use std::net::Ipv4Addr; use netlink_packet_utils::{Emitable, Parseable}; use crate::{ tc::{ TcAttribute, TcFilterU32Option, TcHandle, TcHeader, TcMessage, TcMessageBuffer, TcOption, TcU32Key, TcU32OptionFlag, TcU32Selector, TcU32SelectorFlag, }, 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: vec![TcU32SelectorFlag::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(vec![ TcU32OptionFlag::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); } netlink-packet-route-0.19.0/src/tc/tests/mod.rs000064400000000000000000000002761046102023000174510ustar 00000000000000// SPDX-License-Identifier: MIT #[cfg(test)] mod action_nat; #[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.19.0/src/tc/tests/qdisc_fq_codel.rs000064400000000000000000000163551046102023000216360ustar 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.19.0/src/tc/tests/qdisc_ingress.rs000064400000000000000000000056611046102023000215320ustar 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.19.0/src/tests.rs000064400000000000000000000031711046102023000162610ustar 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.19.0/tools/test_cross_build.sh000075500000000000000000000010111046102023000210170ustar 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