netlink-packet-generic-0.3.3/.cargo_vcs_info.json0000644000000001360000000000100153620ustar { "git": { "sha1": "865fe7cab27c9f1855e7cc577e03858f96dab344" }, "path_in_vcs": "" }netlink-packet-generic-0.3.3/.github/workflows/clippy-rustfmt.yml000064400000000000000000000011021046102023000232660ustar 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 netlink-packet-generic-0.3.3/.github/workflows/license.yml000064400000000000000000000005201046102023000217110ustar 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-generic-0.3.3/.github/workflows/main.yml000064400000000000000000000014751046102023000212250ustar 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: Build examples run: cargo build --examples - name: Test and Generate code coverage run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: files: lcov.info fail_ci_if_error: true netlink-packet-generic-0.3.3/.gitignore000064400000000000000000000000411046102023000161350ustar 00000000000000Cargo.lock target vendor/ *.swp netlink-packet-generic-0.3.3/.licenserc.yaml000064400000000000000000000003711046102023000170640ustar 00000000000000header: license: content: | SPDX-License-Identifier: MIT paths-ignore: - 'target' - '**/*.toml' - '**/*.lock' - '**/*.yml' - '**/*.md' - 'CHANGELOG' - 'LICENSE-MIT' - '.gitignore' comment: on-failure netlink-packet-generic-0.3.3/.rustfmt.toml000064400000000000000000000001141046102023000166250ustar 00000000000000max_width = 80 wrap_comments = true reorder_imports = true edition = "2021" netlink-packet-generic-0.3.3/CHANGELOG000064400000000000000000000005171046102023000153670ustar 00000000000000# Changelog ## [0.3.3] - 2023-07-09 ### Breaking changes - N/A ### New features - N/A ### Bug fixes - Removed depdency on libc. (13fc4cb) - Fix the incorrect emit of mcast and ops. (31fcccd) ## [0.3.2] - 2023-01-29 ### Breaking changes - N/A ### New features - N/A ### Bug fixes - Use latest rust-netlink crates. (fc3dc0c) netlink-packet-generic-0.3.3/Cargo.lock0000644000000070100000000000100133330ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "anyhow" version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "libc" version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "log" version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "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-generic" version = "0.3.3" dependencies = [ "anyhow", "byteorder", "netlink-packet-core", "netlink-packet-utils", "netlink-sys", ] [[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 = "paste" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4b27ab7be369122c218afc2079489cdcb4b517c0a3fc386ff11e1fedfcc2b35" [[package]] name = "proc-macro2" version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" dependencies = [ "proc-macro2", ] [[package]] name = "syn" version = "2.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "thiserror" version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "unicode-ident" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" netlink-packet-generic-0.3.3/Cargo.toml0000644000000021040000000000100133550ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "netlink-packet-generic" version = "0.3.3" authors = ["Leo "] description = "generic netlink packet types" homepage = "https://github.com/rust-netlink/netlink-packet-generic" readme = "README.md" keywords = [ "netlink", "linux", ] license = "MIT" repository = "https://github.com/rust-netlink/netlink-packet-generic" [dependencies.anyhow] version = "1.0.39" [dependencies.byteorder] version = "1.4.2" [dependencies.netlink-packet-core] version = "0.7.0" [dependencies.netlink-packet-utils] version = "0.5.2" [dev-dependencies.netlink-sys] version = "0.8.3" netlink-packet-generic-0.3.3/Cargo.toml.orig000064400000000000000000000010701046102023000170370ustar 00000000000000[package] name = "netlink-packet-generic" version = "0.3.3" authors = ["Leo "] edition = "2018" homepage = "https://github.com/rust-netlink/netlink-packet-generic" repository = "https://github.com/rust-netlink/netlink-packet-generic" keywords = ["netlink", "linux"] license = "MIT" readme = "README.md" description = "generic netlink packet types" [dependencies] anyhow = "1.0.39" byteorder = "1.4.2" netlink-packet-core = { version = "0.7.0" } netlink-packet-utils = { version = "0.5.2" } [dev-dependencies] netlink-sys = { version = "0.8.3" } netlink-packet-generic-0.3.3/LICENSE-MIT000064400000000000000000000027731046102023000156170ustar 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-generic-0.3.3/README.md000064400000000000000000000000561046102023000154320ustar 00000000000000# Rust crate for generic netlink packet types netlink-packet-generic-0.3.3/examples/list_generic_family.rs000064400000000000000000000062651046102023000223570ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_core::{ NetlinkMessage, NetlinkPayload, NLM_F_DUMP, NLM_F_REQUEST, }; use netlink_packet_generic::{ ctrl::{nlas::GenlCtrlAttrs, GenlCtrl, GenlCtrlCmd}, GenlMessage, }; use netlink_sys::{protocols::NETLINK_GENERIC, Socket, SocketAddr}; fn main() { let mut socket = Socket::new(NETLINK_GENERIC).unwrap(); socket.bind_auto().unwrap(); socket.connect(&SocketAddr::new(0, 0)).unwrap(); let mut genlmsg = GenlMessage::from_payload(GenlCtrl { cmd: GenlCtrlCmd::GetFamily, nlas: vec![], }); genlmsg.finalize(); let mut nlmsg = NetlinkMessage::from(genlmsg); nlmsg.header.flags = NLM_F_REQUEST | NLM_F_DUMP; nlmsg.finalize(); let mut txbuf = vec![0u8; nlmsg.buffer_len()]; nlmsg.serialize(&mut txbuf); socket.send(&txbuf, 0).unwrap(); let mut rxbuf = vec![0u8; 4096]; let mut offset = 0; 'outer: loop { let size = socket.recv(&mut rxbuf, 0).unwrap(); loop { let buf = &rxbuf[offset..]; // Parse the message let msg = >>::deserialize(buf) .unwrap(); match msg.payload { NetlinkPayload::Done(_) => break 'outer, NetlinkPayload::InnerMessage(genlmsg) => { if GenlCtrlCmd::NewFamily == genlmsg.payload.cmd { print_entry(genlmsg.payload.nlas); } } 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 print_entry(entry: Vec) { let family_id = entry .iter() .find_map(|nla| { if let GenlCtrlAttrs::FamilyId(id) = nla { Some(*id) } else { None } }) .expect("Cannot find FamilyId attribute"); let family_name = entry .iter() .find_map(|nla| { if let GenlCtrlAttrs::FamilyName(name) = nla { Some(name.as_str()) } else { None } }) .expect("Cannot find FamilyName attribute"); let version = entry .iter() .find_map(|nla| { if let GenlCtrlAttrs::Version(ver) = nla { Some(*ver) } else { None } }) .expect("Cannot find Version attribute"); let hdrsize = entry .iter() .find_map(|nla| { if let GenlCtrlAttrs::HdrSize(hdr) = nla { Some(*hdr) } else { None } }) .expect("Cannot find HdrSize attribute"); if hdrsize == 0 { println!("0x{family_id:04x} {family_name} [Version {version}]"); } else { println!("0x{family_id:04x} {family_name} [Version {version}] [Header {hdrsize} bytes]"); } } netlink-packet-generic-0.3.3/src/buffer.rs000064400000000000000000000023361046102023000165640ustar 00000000000000// SPDX-License-Identifier: MIT //! Buffer definition of generic netlink packet use crate::{constants::GENL_HDRLEN, header::GenlHeader, message::GenlMessage}; use netlink_packet_utils::{DecodeError, Parseable, ParseableParametrized}; use std::fmt::Debug; buffer!(GenlBuffer(GENL_HDRLEN) { cmd: (u8, 0), version: (u8, 1), payload: (slice, GENL_HDRLEN..), }); impl ParseableParametrized<[u8], u16> for GenlMessage where F: ParseableParametrized<[u8], GenlHeader> + Debug, { fn parse_with_param( buf: &[u8], message_type: u16, ) -> Result { let buf = GenlBuffer::new_checked(buf)?; Self::parse_with_param(&buf, message_type) } } impl<'a, F, T> ParseableParametrized, u16> for GenlMessage where F: ParseableParametrized<[u8], GenlHeader> + Debug, T: AsRef<[u8]> + ?Sized, { fn parse_with_param( buf: &GenlBuffer<&'a T>, message_type: u16, ) -> Result { let header = GenlHeader::parse(buf)?; let payload_buf = buf.payload(); Ok(GenlMessage::new( header, F::parse_with_param(payload_buf, header)?, message_type, )) } } netlink-packet-generic-0.3.3/src/constants.rs000064400000000000000000000051421046102023000173250ustar 00000000000000// SPDX-License-Identifier: MIT //! Define constants related to generic netlink pub const GENL_ID_CTRL: u16 = 16; pub const GENL_HDRLEN: usize = 4; pub const CTRL_CMD_UNSPEC: u8 = 0; pub const CTRL_CMD_NEWFAMILY: u8 = 1; pub const CTRL_CMD_DELFAMILY: u8 = 2; pub const CTRL_CMD_GETFAMILY: u8 = 3; pub const CTRL_CMD_NEWOPS: u8 = 4; pub const CTRL_CMD_DELOPS: u8 = 5; pub const CTRL_CMD_GETOPS: u8 = 6; pub const CTRL_CMD_NEWMCAST_GRP: u8 = 7; pub const CTRL_CMD_DELMCAST_GRP: u8 = 8; pub const CTRL_CMD_GETMCAST_GRP: u8 = 9; pub const CTRL_CMD_GETPOLICY: u8 = 10; pub const CTRL_ATTR_UNSPEC: u16 = 0; pub const CTRL_ATTR_FAMILY_ID: u16 = 1; pub const CTRL_ATTR_FAMILY_NAME: u16 = 2; pub const CTRL_ATTR_VERSION: u16 = 3; pub const CTRL_ATTR_HDRSIZE: u16 = 4; pub const CTRL_ATTR_MAXATTR: u16 = 5; pub const CTRL_ATTR_OPS: u16 = 6; pub const CTRL_ATTR_MCAST_GROUPS: u16 = 7; pub const CTRL_ATTR_POLICY: u16 = 8; pub const CTRL_ATTR_OP_POLICY: u16 = 9; pub const CTRL_ATTR_OP: u16 = 10; pub const CTRL_ATTR_OP_UNSPEC: u16 = 0; pub const CTRL_ATTR_OP_ID: u16 = 1; pub const CTRL_ATTR_OP_FLAGS: u16 = 2; pub const CTRL_ATTR_MCAST_GRP_UNSPEC: u16 = 0; pub const CTRL_ATTR_MCAST_GRP_NAME: u16 = 1; pub const CTRL_ATTR_MCAST_GRP_ID: u16 = 2; pub const CTRL_ATTR_POLICY_UNSPEC: u16 = 0; pub const CTRL_ATTR_POLICY_DO: u16 = 1; pub const CTRL_ATTR_POLICY_DUMP: u16 = 2; pub const NL_ATTR_TYPE_INVALID: u32 = 0; pub const NL_ATTR_TYPE_FLAG: u32 = 1; pub const NL_ATTR_TYPE_U8: u32 = 2; pub const NL_ATTR_TYPE_U16: u32 = 3; pub const NL_ATTR_TYPE_U32: u32 = 4; pub const NL_ATTR_TYPE_U64: u32 = 5; pub const NL_ATTR_TYPE_S8: u32 = 6; pub const NL_ATTR_TYPE_S16: u32 = 7; pub const NL_ATTR_TYPE_S32: u32 = 8; pub const NL_ATTR_TYPE_S64: u32 = 9; pub const NL_ATTR_TYPE_BINARY: u32 = 10; pub const NL_ATTR_TYPE_STRING: u32 = 11; pub const NL_ATTR_TYPE_NUL_STRING: u32 = 12; pub const NL_ATTR_TYPE_NESTED: u32 = 13; pub const NL_ATTR_TYPE_NESTED_ARRAY: u32 = 14; pub const NL_ATTR_TYPE_BITFIELD32: u32 = 15; pub const NL_POLICY_TYPE_ATTR_UNSPEC: u16 = 0; pub const NL_POLICY_TYPE_ATTR_TYPE: u16 = 1; pub const NL_POLICY_TYPE_ATTR_MIN_VALUE_S: u16 = 2; pub const NL_POLICY_TYPE_ATTR_MAX_VALUE_S: u16 = 3; pub const NL_POLICY_TYPE_ATTR_MIN_VALUE_U: u16 = 4; pub const NL_POLICY_TYPE_ATTR_MAX_VALUE_U: u16 = 5; pub const NL_POLICY_TYPE_ATTR_MIN_LENGTH: u16 = 6; pub const NL_POLICY_TYPE_ATTR_MAX_LENGTH: u16 = 7; pub const NL_POLICY_TYPE_ATTR_POLICY_IDX: u16 = 8; pub const NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE: u16 = 9; pub const NL_POLICY_TYPE_ATTR_BITFIELD32_MASK: u16 = 10; pub const NL_POLICY_TYPE_ATTR_PAD: u16 = 11; pub const NL_POLICY_TYPE_ATTR_MASK: u16 = 12; netlink-packet-generic-0.3.3/src/ctrl/mod.rs000064400000000000000000000072141046102023000170360ustar 00000000000000// SPDX-License-Identifier: MIT //! Generic netlink controller implementation //! //! This module provides the definition of the controller packet. //! It also serves as an example for creating a generic family. use self::nlas::*; use crate::{constants::*, traits::*, GenlHeader}; use anyhow::Context; use netlink_packet_utils::{nla::NlasIterator, traits::*, DecodeError}; use std::convert::{TryFrom, TryInto}; /// Netlink attributes for this family pub mod nlas; /// Command code definition of Netlink controller (nlctrl) family #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum GenlCtrlCmd { /// Notify from event NewFamily, /// Notify from event DelFamily, /// Request to get family info GetFamily, /// Currently unused NewOps, /// Currently unused DelOps, /// Currently unused GetOps, /// Notify from event NewMcastGrp, /// Notify from event DelMcastGrp, /// Currently unused GetMcastGrp, /// Request to get family policy GetPolicy, } impl From for u8 { fn from(cmd: GenlCtrlCmd) -> u8 { use GenlCtrlCmd::*; match cmd { NewFamily => CTRL_CMD_NEWFAMILY, DelFamily => CTRL_CMD_DELFAMILY, GetFamily => CTRL_CMD_GETFAMILY, NewOps => CTRL_CMD_NEWOPS, DelOps => CTRL_CMD_DELOPS, GetOps => CTRL_CMD_GETOPS, NewMcastGrp => CTRL_CMD_NEWMCAST_GRP, DelMcastGrp => CTRL_CMD_DELMCAST_GRP, GetMcastGrp => CTRL_CMD_GETMCAST_GRP, GetPolicy => CTRL_CMD_GETPOLICY, } } } impl TryFrom for GenlCtrlCmd { type Error = DecodeError; fn try_from(value: u8) -> Result { use GenlCtrlCmd::*; Ok(match value { CTRL_CMD_NEWFAMILY => NewFamily, CTRL_CMD_DELFAMILY => DelFamily, CTRL_CMD_GETFAMILY => GetFamily, CTRL_CMD_NEWOPS => NewOps, CTRL_CMD_DELOPS => DelOps, CTRL_CMD_GETOPS => GetOps, CTRL_CMD_NEWMCAST_GRP => NewMcastGrp, CTRL_CMD_DELMCAST_GRP => DelMcastGrp, CTRL_CMD_GETMCAST_GRP => GetMcastGrp, CTRL_CMD_GETPOLICY => GetPolicy, cmd => { return Err(DecodeError::from(format!( "Unknown control command: {cmd}" ))) } }) } } /// Payload of generic netlink controller #[derive(Clone, Debug, PartialEq, Eq)] pub struct GenlCtrl { /// Command code of this message pub cmd: GenlCtrlCmd, /// Netlink attributes in this message pub nlas: Vec, } impl GenlFamily for GenlCtrl { fn family_name() -> &'static str { "nlctrl" } fn family_id(&self) -> u16 { GENL_ID_CTRL } fn command(&self) -> u8 { self.cmd.into() } fn version(&self) -> u8 { 2 } } impl Emitable for GenlCtrl { fn emit(&self, buffer: &mut [u8]) { self.nlas.as_slice().emit(buffer) } fn buffer_len(&self) -> usize { self.nlas.as_slice().buffer_len() } } impl ParseableParametrized<[u8], GenlHeader> for GenlCtrl { fn parse_with_param( buf: &[u8], header: GenlHeader, ) -> Result { Ok(Self { cmd: header.cmd.try_into()?, nlas: parse_ctrlnlas(buf)?, }) } } fn parse_ctrlnlas(buf: &[u8]) -> Result, DecodeError> { let nlas = NlasIterator::new(buf) .map(|nla| nla.and_then(|nla| GenlCtrlAttrs::parse(&nla))) .collect::, _>>() .context("failed to parse control message attributes")?; Ok(nlas) } netlink-packet-generic-0.3.3/src/ctrl/nlas/mcast.rs000064400000000000000000000053701046102023000203240ustar 00000000000000// SPDX-License-Identifier: MIT use crate::constants::*; use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{Nla, NlaBuffer}, parsers::*, traits::*, DecodeError, }; use std::{mem::size_of_val, ops::Deref}; pub(crate) struct McastGroupList(Vec); impl Deref for McastGroupList { type Target = Vec; fn deref(&self) -> &Self::Target { &self.0 } } impl From<&Vec>> for McastGroupList { fn from(groups: &Vec>) -> Self { Self( groups .iter() .cloned() .enumerate() .map(|(index, nlas)| McastGroup { index: index as u16, nlas, }) .collect(), ) } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct McastGroup { pub index: u16, pub nlas: Vec, } impl Nla for McastGroup { fn value_len(&self) -> usize { self.nlas.as_slice().buffer_len() } fn kind(&self) -> u16 { self.index + 1 } fn emit_value(&self, buffer: &mut [u8]) { self.nlas.as_slice().emit(buffer); } } #[derive(Clone, Debug, PartialEq, Eq)] pub enum McastGrpAttrs { Name(String), Id(u32), } impl Nla for McastGrpAttrs { fn value_len(&self) -> usize { use McastGrpAttrs::*; match self { Name(s) => s.as_bytes().len() + 1, Id(v) => size_of_val(v), } } fn kind(&self) -> u16 { use McastGrpAttrs::*; match self { Name(_) => CTRL_ATTR_MCAST_GRP_NAME, Id(_) => CTRL_ATTR_MCAST_GRP_ID, } } fn emit_value(&self, buffer: &mut [u8]) { use McastGrpAttrs::*; match self { Name(s) => { buffer[..s.len()].copy_from_slice(s.as_bytes()); buffer[s.len()] = 0; } Id(v) => NativeEndian::write_u32(buffer, *v), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for McastGrpAttrs { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { CTRL_ATTR_MCAST_GRP_NAME => Self::Name( parse_string(payload) .context("invalid CTRL_ATTR_MCAST_GRP_NAME value")?, ), CTRL_ATTR_MCAST_GRP_ID => Self::Id( parse_u32(payload) .context("invalid CTRL_ATTR_MCAST_GRP_ID value")?, ), kind => { return Err(DecodeError::from(format!( "Unknown NLA type: {kind}" ))) } }) } } netlink-packet-generic-0.3.3/src/ctrl/nlas/mod.rs000064400000000000000000000235661046102023000200030ustar 00000000000000// SPDX-License-Identifier: MIT use crate::constants::*; use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{Nla, NlaBuffer, NlasIterator}, parsers::*, traits::*, DecodeError, }; use std::mem::size_of_val; mod mcast; mod oppolicy; mod ops; mod policy; pub use mcast::*; pub use oppolicy::*; pub use ops::*; pub use policy::*; #[derive(Clone, Debug, PartialEq, Eq)] pub enum GenlCtrlAttrs { FamilyId(u16), FamilyName(String), Version(u32), HdrSize(u32), MaxAttr(u32), Ops(Vec>), McastGroups(Vec>), Policy(PolicyAttr), OpPolicy(OppolicyAttr), Op(u32), } impl Nla for GenlCtrlAttrs { fn value_len(&self) -> usize { use GenlCtrlAttrs::*; match self { FamilyId(v) => size_of_val(v), FamilyName(s) => s.len() + 1, Version(v) => size_of_val(v), HdrSize(v) => size_of_val(v), MaxAttr(v) => size_of_val(v), Ops(nlas) => OpList::from(nlas).as_slice().buffer_len(), McastGroups(nlas) => { McastGroupList::from(nlas).as_slice().buffer_len() } Policy(nla) => nla.buffer_len(), OpPolicy(nla) => nla.buffer_len(), Op(v) => size_of_val(v), } } fn kind(&self) -> u16 { use GenlCtrlAttrs::*; match self { FamilyId(_) => CTRL_ATTR_FAMILY_ID, FamilyName(_) => CTRL_ATTR_FAMILY_NAME, Version(_) => CTRL_ATTR_VERSION, HdrSize(_) => CTRL_ATTR_HDRSIZE, MaxAttr(_) => CTRL_ATTR_MAXATTR, Ops(_) => CTRL_ATTR_OPS, McastGroups(_) => CTRL_ATTR_MCAST_GROUPS, Policy(_) => CTRL_ATTR_POLICY, OpPolicy(_) => CTRL_ATTR_OP_POLICY, Op(_) => CTRL_ATTR_OP, } } fn emit_value(&self, buffer: &mut [u8]) { use GenlCtrlAttrs::*; match self { FamilyId(v) => NativeEndian::write_u16(buffer, *v), FamilyName(s) => { buffer[..s.len()].copy_from_slice(s.as_bytes()); buffer[s.len()] = 0; } Version(v) => NativeEndian::write_u32(buffer, *v), HdrSize(v) => NativeEndian::write_u32(buffer, *v), MaxAttr(v) => NativeEndian::write_u32(buffer, *v), Ops(nlas) => { OpList::from(nlas).as_slice().emit(buffer); } McastGroups(nlas) => { McastGroupList::from(nlas).as_slice().emit(buffer); } Policy(nla) => nla.emit_value(buffer), OpPolicy(nla) => nla.emit_value(buffer), Op(v) => NativeEndian::write_u32(buffer, *v), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for GenlCtrlAttrs { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { CTRL_ATTR_FAMILY_ID => Self::FamilyId( parse_u16(payload) .context("invalid CTRL_ATTR_FAMILY_ID value")?, ), CTRL_ATTR_FAMILY_NAME => Self::FamilyName( parse_string(payload) .context("invalid CTRL_ATTR_FAMILY_NAME value")?, ), CTRL_ATTR_VERSION => Self::Version( parse_u32(payload) .context("invalid CTRL_ATTR_VERSION value")?, ), CTRL_ATTR_HDRSIZE => Self::HdrSize( parse_u32(payload) .context("invalid CTRL_ATTR_HDRSIZE value")?, ), CTRL_ATTR_MAXATTR => Self::MaxAttr( parse_u32(payload) .context("invalid CTRL_ATTR_MAXATTR value")?, ), CTRL_ATTR_OPS => { let ops = NlasIterator::new(payload) .map(|nlas| { nlas.and_then(|nlas| { NlasIterator::new(nlas.value()) .map(|nla| { nla.and_then(|nla| OpAttrs::parse(&nla)) }) .collect::, _>>() }) }) .collect::>, _>>() .context("failed to parse CTRL_ATTR_OPS")?; Self::Ops(ops) } CTRL_ATTR_MCAST_GROUPS => { let groups = NlasIterator::new(payload) .map(|nlas| { nlas.and_then(|nlas| { NlasIterator::new(nlas.value()) .map(|nla| { nla.and_then(|nla| { McastGrpAttrs::parse(&nla) }) }) .collect::, _>>() }) }) .collect::>, _>>() .context("failed to parse CTRL_ATTR_MCAST_GROUPS")?; Self::McastGroups(groups) } CTRL_ATTR_POLICY => Self::Policy( PolicyAttr::parse(&NlaBuffer::new(payload)) .context("failed to parse CTRL_ATTR_POLICY")?, ), CTRL_ATTR_OP_POLICY => Self::OpPolicy( OppolicyAttr::parse(&NlaBuffer::new(payload)) .context("failed to parse CTRL_ATTR_OP_POLICY")?, ), CTRL_ATTR_OP => Self::Op(parse_u32(payload)?), kind => { return Err(DecodeError::from(format!( "Unknown NLA type: {kind}" ))) } }) } } #[cfg(test)] mod tests { use super::*; #[test] fn mcast_groups_parse() { let mcast_bytes: [u8; 24] = [ 24, 0, // Netlink header length 7, 0, // Netlink header kind (Mcast groups) 20, 0, // Mcast group nested NLA length 1, 0, // Mcast group kind 8, 0, // Id length 2, 0, // Id kind 1, 0, 0, 0, // Id 8, 0, // Name length 1, 0, // Name kind b't', b'e', b's', b't', // Name ]; let nla_buffer = NlaBuffer::new_checked(&mcast_bytes[..]) .expect("Failed to create NlaBuffer"); let result_attr = GenlCtrlAttrs::parse(&nla_buffer) .expect("Failed to parse encoded McastGroups"); let expected_attr = GenlCtrlAttrs::McastGroups(vec![vec![ McastGrpAttrs::Id(1), McastGrpAttrs::Name("test".to_string()), ]]); assert_eq!(expected_attr, result_attr); } #[test] fn mcast_groups_emit() { let mcast_attr = GenlCtrlAttrs::McastGroups(vec![ vec![ McastGrpAttrs::Id(7), McastGrpAttrs::Name("group1".to_string()), ], vec![ McastGrpAttrs::Id(8), McastGrpAttrs::Name("group2".to_string()), ], ]); let expected_bytes: [u8; 52] = [ 52, 0, // Netlink header length 7, 0, // Netlink header kind (Mcast groups) 24, 0, // Mcast group nested NLA length 1, 0, // Mcast group kind (index 1) 8, 0, // Id length 2, 0, // Id kind 7, 0, 0, 0, // Id 11, 0, // Name length 1, 0, // Name kind b'g', b'r', b'o', b'u', b'p', b'1', 0, // Name 0, // mcast group padding 24, 0, // Mcast group nested NLA length 2, 0, // Mcast group kind (index 2) 8, 0, // Id length 2, 0, // Id kind 8, 0, 0, 0, // Id 11, 0, // Name length 1, 0, // Name kind b'g', b'r', b'o', b'u', b'p', b'2', 0, // Name 0, // padding ]; let mut buf = vec![0u8; 100]; mcast_attr.emit(&mut buf); assert_eq!(&expected_bytes[..], &buf[..expected_bytes.len()]); } #[test] fn ops_parse() { let ops_bytes: [u8; 24] = [ 24, 0, // Netlink header length 6, 0, // Netlink header kind (Ops) 20, 0, // Op nested NLA length 0, 0, // Op kind 8, 0, // Id length 1, 0, // Id kind 1, 0, 0, 0, // Id 8, 0, // Flags length 2, 0, // Flags kind 123, 0, 0, 0, // Flags ]; let nla_buffer = NlaBuffer::new_checked(&ops_bytes[..]) .expect("Failed to create NlaBuffer"); let result_attr = GenlCtrlAttrs::parse(&nla_buffer) .expect("Failed to parse encoded McastGroups"); let expected_attr = GenlCtrlAttrs::Ops(vec![vec![OpAttrs::Id(1), OpAttrs::Flags(123)]]); assert_eq!(expected_attr, result_attr); } #[test] fn ops_emit() { let ops = GenlCtrlAttrs::Ops(vec![ vec![OpAttrs::Id(1), OpAttrs::Flags(11)], vec![OpAttrs::Id(3), OpAttrs::Flags(33)], ]); let expected_bytes: [u8; 44] = [ 44, 0, // Netlink header length 6, 0, // Netlink header kind (Ops) 20, 0, // Op nested NLA length 1, 0, // Op kind 8, 0, // Id length 1, 0, // Id kind 1, 0, 0, 0, // Id 8, 0, // Flags length 2, 0, // Flags kind 11, 0, 0, 0, // Flags 20, 0, // Op nested NLA length 2, 0, // Op kind 8, 0, // Id length 1, 0, // Id kind 3, 0, 0, 0, // Id 8, 0, // Flags length 2, 0, // Flags kind 33, 0, 0, 0, // Flags ]; let mut buf = vec![0u8; 100]; ops.emit(&mut buf); assert_eq!(&expected_bytes[..], &buf[..expected_bytes.len()]); } } netlink-packet-generic-0.3.3/src/ctrl/nlas/oppolicy.rs000064400000000000000000000052311046102023000210470ustar 00000000000000// SPDX-License-Identifier: MIT use crate::constants::*; use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{Nla, NlaBuffer, NlasIterator}, parsers::*, traits::*, DecodeError, }; use std::mem::size_of_val; #[derive(Clone, Debug, PartialEq, Eq)] pub struct OppolicyAttr { pub cmd: u8, pub policy_idx: Vec, } impl Nla for OppolicyAttr { fn value_len(&self) -> usize { self.policy_idx.as_slice().buffer_len() } fn kind(&self) -> u16 { self.cmd as u16 } fn emit_value(&self, buffer: &mut [u8]) { self.policy_idx.as_slice().emit(buffer); } fn is_nested(&self) -> bool { true } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for OppolicyAttr { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); let policy_idx = NlasIterator::new(payload) .map(|nla| nla.and_then(|nla| OppolicyIndexAttr::parse(&nla))) .collect::, _>>() .context("failed to parse OppolicyAttr")?; Ok(Self { cmd: buf.kind() as u8, policy_idx, }) } } #[derive(Clone, Debug, PartialEq, Eq)] pub enum OppolicyIndexAttr { Do(u32), Dump(u32), } impl Nla for OppolicyIndexAttr { fn value_len(&self) -> usize { use OppolicyIndexAttr::*; match self { Do(v) => size_of_val(v), Dump(v) => size_of_val(v), } } fn kind(&self) -> u16 { use OppolicyIndexAttr::*; match self { Do(_) => CTRL_ATTR_POLICY_DO, Dump(_) => CTRL_ATTR_POLICY_DUMP, } } fn emit_value(&self, buffer: &mut [u8]) { use OppolicyIndexAttr::*; match self { Do(v) => NativeEndian::write_u32(buffer, *v), Dump(v) => NativeEndian::write_u32(buffer, *v), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for OppolicyIndexAttr { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { CTRL_ATTR_POLICY_DO => Self::Do( parse_u32(payload) .context("invalid CTRL_ATTR_POLICY_DO value")?, ), CTRL_ATTR_POLICY_DUMP => Self::Dump( parse_u32(payload) .context("invalid CTRL_ATTR_POLICY_DUMP value")?, ), kind => { return Err(DecodeError::from(format!( "Unknown NLA type: {kind}" ))) } }) } } netlink-packet-generic-0.3.3/src/ctrl/nlas/ops.rs000064400000000000000000000047121046102023000200150ustar 00000000000000// SPDX-License-Identifier: MIT use crate::constants::*; use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{Nla, NlaBuffer}, parsers::*, traits::*, DecodeError, }; use std::{mem::size_of_val, ops::Deref}; pub struct OpList(Vec); impl Deref for OpList { type Target = Vec; fn deref(&self) -> &Self::Target { &self.0 } } impl From<&Vec>> for OpList { fn from(ops: &Vec>) -> Self { Self( ops.iter() .cloned() .enumerate() .map(|(index, nlas)| Op { index: index as u16, nlas, }) .collect(), ) } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Op { pub index: u16, pub nlas: Vec, } impl Nla for Op { fn value_len(&self) -> usize { self.nlas.as_slice().buffer_len() } fn kind(&self) -> u16 { self.index + 1 } fn emit_value(&self, buffer: &mut [u8]) { self.nlas.as_slice().emit(buffer); } } #[derive(Clone, Debug, PartialEq, Eq)] pub enum OpAttrs { Id(u32), Flags(u32), } impl Nla for OpAttrs { fn value_len(&self) -> usize { use OpAttrs::*; match self { Id(v) => size_of_val(v), Flags(v) => size_of_val(v), } } fn kind(&self) -> u16 { use OpAttrs::*; match self { Id(_) => CTRL_ATTR_OP_ID, Flags(_) => CTRL_ATTR_OP_FLAGS, } } fn emit_value(&self, buffer: &mut [u8]) { use OpAttrs::*; match self { Id(v) => NativeEndian::write_u32(buffer, *v), Flags(v) => NativeEndian::write_u32(buffer, *v), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for OpAttrs { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { CTRL_ATTR_OP_ID => Self::Id( parse_u32(payload).context("invalid CTRL_ATTR_OP_ID value")?, ), CTRL_ATTR_OP_FLAGS => Self::Flags( parse_u32(payload) .context("invalid CTRL_ATTR_OP_FLAGS value")?, ), kind => { return Err(DecodeError::from(format!( "Unknown NLA type: {kind}" ))) } }) } } netlink-packet-generic-0.3.3/src/ctrl/nlas/policy.rs000064400000000000000000000232021046102023000205060ustar 00000000000000// SPDX-License-Identifier: MIT use crate::constants::*; use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{Nla, NlaBuffer, NlasIterator}, parsers::*, traits::*, DecodeError, }; use std::{ convert::TryFrom, mem::{size_of, size_of_val}, }; // PolicyAttr #[derive(Clone, Debug, PartialEq, Eq)] pub struct PolicyAttr { pub index: u16, pub attr_policy: AttributePolicyAttr, } impl Nla for PolicyAttr { fn value_len(&self) -> usize { self.attr_policy.buffer_len() } fn kind(&self) -> u16 { self.index } fn emit_value(&self, buffer: &mut [u8]) { self.attr_policy.emit(buffer); } fn is_nested(&self) -> bool { true } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for PolicyAttr { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(Self { index: buf.kind(), attr_policy: AttributePolicyAttr::parse(&NlaBuffer::new(payload)) .context("failed to parse PolicyAttr")?, }) } } // AttributePolicyAttr #[derive(Clone, Debug, PartialEq, Eq)] pub struct AttributePolicyAttr { pub index: u16, pub policies: Vec, } impl Nla for AttributePolicyAttr { fn value_len(&self) -> usize { self.policies.as_slice().buffer_len() } fn kind(&self) -> u16 { self.index } fn emit_value(&self, buffer: &mut [u8]) { self.policies.as_slice().emit(buffer); } fn is_nested(&self) -> bool { true } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for AttributePolicyAttr { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); let policies = NlasIterator::new(payload) .map(|nla| nla.and_then(|nla| NlPolicyTypeAttrs::parse(&nla))) .collect::, _>>() .context("failed to parse AttributePolicyAttr")?; Ok(Self { index: buf.kind(), policies, }) } } // PolicyTypeAttrs #[derive(Clone, Debug, PartialEq, Eq)] pub enum NlPolicyTypeAttrs { Type(NlaType), MinValueSigned(i64), MaxValueSigned(i64), MaxValueUnsigned(u64), MinValueUnsigned(u64), MinLength(u32), MaxLength(u32), PolicyIdx(u32), PolicyMaxType(u32), Bitfield32Mask(u32), Mask(u64), } impl Nla for NlPolicyTypeAttrs { fn value_len(&self) -> usize { use NlPolicyTypeAttrs::*; match self { Type(v) => size_of_val(v), MinValueSigned(v) => size_of_val(v), MaxValueSigned(v) => size_of_val(v), MaxValueUnsigned(v) => size_of_val(v), MinValueUnsigned(v) => size_of_val(v), MinLength(v) => size_of_val(v), MaxLength(v) => size_of_val(v), PolicyIdx(v) => size_of_val(v), PolicyMaxType(v) => size_of_val(v), Bitfield32Mask(v) => size_of_val(v), Mask(v) => size_of_val(v), } } fn kind(&self) -> u16 { use NlPolicyTypeAttrs::*; match self { Type(_) => NL_POLICY_TYPE_ATTR_TYPE, MinValueSigned(_) => NL_POLICY_TYPE_ATTR_MIN_VALUE_S, MaxValueSigned(_) => NL_POLICY_TYPE_ATTR_MAX_VALUE_S, MaxValueUnsigned(_) => NL_POLICY_TYPE_ATTR_MIN_VALUE_U, MinValueUnsigned(_) => NL_POLICY_TYPE_ATTR_MAX_VALUE_U, MinLength(_) => NL_POLICY_TYPE_ATTR_MIN_LENGTH, MaxLength(_) => NL_POLICY_TYPE_ATTR_MAX_LENGTH, PolicyIdx(_) => NL_POLICY_TYPE_ATTR_POLICY_IDX, PolicyMaxType(_) => NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE, Bitfield32Mask(_) => NL_POLICY_TYPE_ATTR_BITFIELD32_MASK, Mask(_) => NL_POLICY_TYPE_ATTR_MASK, } } fn emit_value(&self, buffer: &mut [u8]) { use NlPolicyTypeAttrs::*; match self { Type(v) => NativeEndian::write_u32(buffer, u32::from(*v)), MinValueSigned(v) => NativeEndian::write_i64(buffer, *v), MaxValueSigned(v) => NativeEndian::write_i64(buffer, *v), MaxValueUnsigned(v) => NativeEndian::write_u64(buffer, *v), MinValueUnsigned(v) => NativeEndian::write_u64(buffer, *v), MinLength(v) => NativeEndian::write_u32(buffer, *v), MaxLength(v) => NativeEndian::write_u32(buffer, *v), PolicyIdx(v) => NativeEndian::write_u32(buffer, *v), PolicyMaxType(v) => NativeEndian::write_u32(buffer, *v), Bitfield32Mask(v) => NativeEndian::write_u32(buffer, *v), Mask(v) => NativeEndian::write_u64(buffer, *v), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for NlPolicyTypeAttrs { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { NL_POLICY_TYPE_ATTR_TYPE => { let value = parse_u32(payload) .context("invalid NL_POLICY_TYPE_ATTR_TYPE value")?; Self::Type(NlaType::try_from(value)?) } NL_POLICY_TYPE_ATTR_MIN_VALUE_S => Self::MinValueSigned( parse_i64(payload) .context("invalid NL_POLICY_TYPE_ATTR_MIN_VALUE_S value")?, ), NL_POLICY_TYPE_ATTR_MAX_VALUE_S => Self::MaxValueSigned( parse_i64(payload) .context("invalid NL_POLICY_TYPE_ATTR_MAX_VALUE_S value")?, ), NL_POLICY_TYPE_ATTR_MIN_VALUE_U => Self::MinValueUnsigned( parse_u64(payload) .context("invalid NL_POLICY_TYPE_ATTR_MIN_VALUE_U value")?, ), NL_POLICY_TYPE_ATTR_MAX_VALUE_U => Self::MaxValueUnsigned( parse_u64(payload) .context("invalid NL_POLICY_TYPE_ATTR_MAX_VALUE_U value")?, ), NL_POLICY_TYPE_ATTR_MIN_LENGTH => Self::MinLength( parse_u32(payload) .context("invalid NL_POLICY_TYPE_ATTR_MIN_LENGTH value")?, ), NL_POLICY_TYPE_ATTR_MAX_LENGTH => Self::MaxLength( parse_u32(payload) .context("invalid NL_POLICY_TYPE_ATTR_MAX_LENGTH value")?, ), NL_POLICY_TYPE_ATTR_POLICY_IDX => Self::PolicyIdx( parse_u32(payload) .context("invalid NL_POLICY_TYPE_ATTR_POLICY_IDX value")?, ), NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE => { Self::PolicyMaxType(parse_u32(payload).context( "invalid NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE value", )?) } NL_POLICY_TYPE_ATTR_BITFIELD32_MASK => { Self::Bitfield32Mask(parse_u32(payload).context( "invalid NL_POLICY_TYPE_ATTR_BITFIELD32_MASK value", )?) } NL_POLICY_TYPE_ATTR_MASK => Self::Mask( parse_u64(payload) .context("invalid NL_POLICY_TYPE_ATTR_MASK value")?, ), kind => { return Err(DecodeError::from(format!( "Unknown NLA type: {kind}" ))) } }) } } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum NlaType { Flag, U8, U16, U32, U64, S8, S16, S32, S64, Binary, String, NulString, Nested, NestedArray, Bitfield32, } impl From for u32 { fn from(nlatype: NlaType) -> u32 { match nlatype { NlaType::Flag => NL_ATTR_TYPE_FLAG, NlaType::U8 => NL_ATTR_TYPE_U8, NlaType::U16 => NL_ATTR_TYPE_U16, NlaType::U32 => NL_ATTR_TYPE_U32, NlaType::U64 => NL_ATTR_TYPE_U64, NlaType::S8 => NL_ATTR_TYPE_S8, NlaType::S16 => NL_ATTR_TYPE_S16, NlaType::S32 => NL_ATTR_TYPE_S32, NlaType::S64 => NL_ATTR_TYPE_S64, NlaType::Binary => NL_ATTR_TYPE_BINARY, NlaType::String => NL_ATTR_TYPE_STRING, NlaType::NulString => NL_ATTR_TYPE_NUL_STRING, NlaType::Nested => NL_ATTR_TYPE_NESTED, NlaType::NestedArray => NL_ATTR_TYPE_NESTED_ARRAY, NlaType::Bitfield32 => NL_ATTR_TYPE_BITFIELD32, } } } impl TryFrom for NlaType { type Error = DecodeError; fn try_from(value: u32) -> Result { Ok(match value { NL_ATTR_TYPE_FLAG => NlaType::Flag, NL_ATTR_TYPE_U8 => NlaType::U8, NL_ATTR_TYPE_U16 => NlaType::U16, NL_ATTR_TYPE_U32 => NlaType::U32, NL_ATTR_TYPE_U64 => NlaType::U64, NL_ATTR_TYPE_S8 => NlaType::S8, NL_ATTR_TYPE_S16 => NlaType::S16, NL_ATTR_TYPE_S32 => NlaType::S32, NL_ATTR_TYPE_S64 => NlaType::S64, NL_ATTR_TYPE_BINARY => NlaType::Binary, NL_ATTR_TYPE_STRING => NlaType::String, NL_ATTR_TYPE_NUL_STRING => NlaType::NulString, NL_ATTR_TYPE_NESTED => NlaType::Nested, NL_ATTR_TYPE_NESTED_ARRAY => NlaType::NestedArray, NL_ATTR_TYPE_BITFIELD32 => NlaType::Bitfield32, _ => { return Err(DecodeError::from(format!( "invalid NLA type: {value}" ))) } }) } } // FIXME: Add this into netlink_packet_utils::parser fn parse_i64(payload: &[u8]) -> Result { if payload.len() != size_of::() { return Err(format!("invalid i64: {payload:?}").into()); } Ok(NativeEndian::read_i64(payload)) } netlink-packet-generic-0.3.3/src/header.rs000064400000000000000000000015041046102023000165370ustar 00000000000000// SPDX-License-Identifier: MIT //! header definition of generic netlink packet use crate::{buffer::GenlBuffer, constants::GENL_HDRLEN}; use netlink_packet_utils::{DecodeError, Emitable, Parseable}; /// Generic Netlink header #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct GenlHeader { pub cmd: u8, pub version: u8, } impl Emitable for GenlHeader { fn buffer_len(&self) -> usize { GENL_HDRLEN } fn emit(&self, buffer: &mut [u8]) { let mut packet = GenlBuffer::new(buffer); packet.set_cmd(self.cmd); packet.set_version(self.version); } } impl> Parseable> for GenlHeader { fn parse(buf: &GenlBuffer) -> Result { Ok(Self { cmd: buf.cmd(), version: buf.version(), }) } } netlink-packet-generic-0.3.3/src/lib.rs000064400000000000000000000064101046102023000160560ustar 00000000000000// SPDX-License-Identifier: MIT //! This crate provides the packet of generic netlink family and its controller. //! //! The `[GenlMessage]` provides a generic netlink family message which is //! sub-protocol independant. //! You can wrap your message into the type, then it can be used in //! `netlink-proto` crate. //! //! # Implementing a generic netlink family //! A generic netlink family contains several commands, and a version number in //! the header. //! //! The payload usually consists of netlink attributes, carrying the messages to //! the peer. In order to help you to make your payload into a valid netlink //! packet, this crate requires the informations about the family id, //! and the informations in the generic header. So, you need to implement some //! traits on your types. //! //! All the things in the payload including all netlink attributes used //! and the optional header should be handled by your implementation. //! //! ## Serializaion / Deserialization //! To implement your generic netlink family, you should handle the payload //! serialization process including its specific header (if any) and the netlink //! attributes. //! //! To achieve this, you should implement [`netlink_packet_utils::Emitable`] //! trait for the payload type. //! //! For deserialization, [`netlink_packet_utils::ParseableParametrized<[u8], //! GenlHeader>`](netlink_packet_utils::ParseableParametrized) trait should be //! implemented. As mention above, to provide more scalability, we use the //! simplest buffer type: `[u8]` here. You can turn it into other buffer type //! easily during deserializing. //! //! ## `GenlFamily` trait //! The trait is aim to provide some necessary informations in order to build //! the packet headers of netlink (nlmsghdr) and generic netlink (genlmsghdr). //! //! ### `family_name()` //! The method let the resolver to obtain the name registered in the kernel. //! //! ### `family_id()` //! Few netlink family has static family ID (e.g. controller). The method is //! mainly used to let those family to return their familt ID. //! //! If you don't know what is this, please **DO NOT** implement this method. //! Since the default implementation return `GENL_ID_GENERATE`, which means //! the family ID is allocated by the kernel dynamically. //! //! ### `command()` //! This method tells the generic netlink command id of the packet //! The return value is used to fill the `cmd` field in the generic netlink //! header. //! //! ### `version()` //! This method return the family version of the payload. //! The return value is used to fill the `version` field in the generic netlink //! header. //! //! ## Family Header //! Few family would use a family specific message header. For simplification //! and scalability, this crate treats it as a part of the payload, and make //! implementations to handle the header by themselves. //! //! If you are implementing such a generic family, note that you should define //! the header data structure in your payload type and handle the serialization. #[macro_use] extern crate netlink_packet_utils; pub mod buffer; pub use self::buffer::GenlBuffer; pub mod constants; pub mod ctrl; pub mod header; pub use self::header::GenlHeader; pub mod message; pub use self::message::GenlMessage; pub mod traits; pub use self::traits::GenlFamily; netlink-packet-generic-0.3.3/src/message.rs000064400000000000000000000130131046102023000167310ustar 00000000000000// SPDX-License-Identifier: MIT //! Message definition and method implementations use crate::{buffer::GenlBuffer, header::GenlHeader, traits::*}; use netlink_packet_core::{ NetlinkDeserializable, NetlinkHeader, NetlinkPayload, NetlinkSerializable, }; use netlink_packet_utils::{DecodeError, Emitable, ParseableParametrized}; use std::fmt::Debug; #[cfg(doc)] use netlink_packet_core::NetlinkMessage; /// Represent the generic netlink messages /// /// This type can wrap data types `F` which represents a generic family payload. /// The message can be serialize/deserialize if the type `F` implements /// [`GenlFamily`], [`Emitable`], and [`ParseableParametrized<[u8], /// GenlHeader>`](ParseableParametrized). #[derive(Clone, Debug, PartialEq, Eq)] pub struct GenlMessage { pub header: GenlHeader, pub payload: F, resolved_family_id: u16, } impl GenlMessage where F: Debug, { /// Construct the message pub fn new(header: GenlHeader, payload: F, family_id: u16) -> Self { Self { header, payload, resolved_family_id: family_id, } } /// Construct the message by the given header and payload pub fn from_parts(header: GenlHeader, payload: F) -> Self { Self { header, payload, resolved_family_id: 0, } } /// Consume this message and return its header and payload pub fn into_parts(self) -> (GenlHeader, F) { (self.header, self.payload) } /// Return the previously set resolved family ID in this message. /// /// This value would be used to serialize the message only if /// the ([`GenlFamily::family_id()`]) return 0 in the underlying type. pub fn resolved_family_id(&self) -> u16 { self.resolved_family_id } /// Set the resolved dynamic family ID of the message, if the generic family /// uses dynamic generated ID by kernel. /// /// This method is a interface to provide other high level library to /// set the resolved family ID before the message is serialized. /// /// # Usage /// Normally, you don't have to call this function directly if you are /// using library which helps you handle the dynamic family id. /// /// If you are the developer of some high level generic netlink library, /// you can call this method to set the family id resolved by your resolver. /// Without having to modify the `message_type` field of the serialized /// netlink packet header before sending it. pub fn set_resolved_family_id(&mut self, family_id: u16) { self.resolved_family_id = family_id; } } impl GenlMessage where F: GenlFamily + Debug, { /// Build the message from the payload /// /// This function would automatically fill the header for you. You can /// directly emit the message without having to call /// [`finalize()`](Self::finalize). pub fn from_payload(payload: F) -> Self { Self { header: GenlHeader { cmd: payload.command(), version: payload.version(), }, payload, resolved_family_id: 0, } } /// Ensure the header ([`GenlHeader`]) is consistent with the payload (`F: /// GenlFamily`): /// /// - Fill the command and version number into the header /// /// If you are not 100% sure the header is correct, this method should be /// called before calling [`Emitable::emit()`], as it could get error /// result if the header is inconsistent with the message. pub fn finalize(&mut self) { self.header.cmd = self.payload.command(); self.header.version = self.payload.version(); } /// Return the resolved family ID which should be filled into the /// `message_type` field in [`NetlinkHeader`]. /// /// The implementation of [`NetlinkSerializable::message_type()`] would use /// this function's result as its the return value. Thus, the family id can /// be automatically filled into the `message_type` during the call to /// [`NetlinkMessage::finalize()`]. pub fn family_id(&self) -> u16 { let static_id = self.payload.family_id(); if static_id == 0 { self.resolved_family_id } else { static_id } } } impl Emitable for GenlMessage where F: GenlFamily + Emitable + Debug, { fn buffer_len(&self) -> usize { self.header.buffer_len() + self.payload.buffer_len() } fn emit(&self, buffer: &mut [u8]) { self.header.emit(buffer); let buffer = &mut buffer[self.header.buffer_len()..]; self.payload.emit(buffer); } } impl NetlinkSerializable for GenlMessage where F: GenlFamily + Emitable + Debug, { fn message_type(&self) -> u16 { self.family_id() } fn buffer_len(&self) -> usize { ::buffer_len(self) } fn serialize(&self, buffer: &mut [u8]) { self.emit(buffer) } } impl NetlinkDeserializable for GenlMessage where F: ParseableParametrized<[u8], GenlHeader> + Debug, { type Error = DecodeError; fn deserialize( header: &NetlinkHeader, payload: &[u8], ) -> Result { let buffer = GenlBuffer::new_checked(payload)?; GenlMessage::parse_with_param(&buffer, header.message_type) } } impl From> for NetlinkPayload> where F: Debug, { fn from(message: GenlMessage) -> Self { NetlinkPayload::InnerMessage(message) } } netlink-packet-generic-0.3.3/src/traits.rs000064400000000000000000000022241046102023000166150ustar 00000000000000// SPDX-License-Identifier: MIT //! Traits for implementing generic netlink family /// Provide the definition for generic netlink family /// /// Family payload type should implement this trait to provide necessary /// informations in order to build the packet headers (`nlmsghdr` and /// `genlmsghdr`). /// /// If you are looking for an example implementation, you can refer to the /// [`crate::ctrl`] module. pub trait GenlFamily { /// Return the unique family name registered in the kernel /// /// Let the resolver lookup the dynamically assigned ID fn family_name() -> &'static str; /// Return the assigned family ID /// /// # Note /// The implementation of generic family should assign the ID to /// `GENL_ID_GENERATE` (0x0). So the controller can dynamically assign /// the family ID. /// /// Regarding to the reason above, you should not have to implement the /// function unless the family uses static ID. fn family_id(&self) -> u16 { 0 } /// Return the command type of the current message fn command(&self) -> u8; /// Indicate the protocol version fn version(&self) -> u8; } netlink-packet-generic-0.3.3/tests/query_family_id.rs000064400000000000000000000035271046102023000210530ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_core::{NetlinkMessage, NetlinkPayload, NLM_F_REQUEST}; use netlink_packet_generic::{ ctrl::{nlas::GenlCtrlAttrs, GenlCtrl, GenlCtrlCmd}, GenlMessage, }; use netlink_sys::{protocols::NETLINK_GENERIC, Socket, SocketAddr}; #[test] fn query_family_id() { let mut socket = Socket::new(NETLINK_GENERIC).unwrap(); socket.bind_auto().unwrap(); socket.connect(&SocketAddr::new(0, 0)).unwrap(); let mut genlmsg = GenlMessage::from_payload(GenlCtrl { cmd: GenlCtrlCmd::GetFamily, nlas: vec![GenlCtrlAttrs::FamilyName("nlctrl".to_owned())], }); genlmsg.finalize(); let mut nlmsg = NetlinkMessage::from(genlmsg); nlmsg.header.flags = NLM_F_REQUEST; nlmsg.finalize(); println!("Buffer length: {}", nlmsg.buffer_len()); let mut txbuf = vec![0u8; nlmsg.buffer_len()]; nlmsg.serialize(&mut txbuf); socket.send(&txbuf, 0).unwrap(); let (rxbuf, _addr) = socket.recv_from_full().unwrap(); let rx_packet = >>::deserialize(&rxbuf).unwrap(); if let NetlinkPayload::InnerMessage(genlmsg) = rx_packet.payload { if GenlCtrlCmd::NewFamily == genlmsg.payload.cmd { let family_id = genlmsg .payload .nlas .iter() .find_map(|nla| { if let GenlCtrlAttrs::FamilyId(id) = nla { Some(*id) } else { None } }) .expect("Cannot find FamilyId attribute"); // nlctrl's family must be 0x10 assert_eq!(0x10, family_id); } else { panic!("Invalid payload type: {:?}", genlmsg.payload.cmd); } } else { panic!("Failed to get family ID"); } }