netlink-packet-core-0.7.0/.cargo_vcs_info.json0000644000000001360000000000100146770ustar { "git": { "sha1": "91e71b69fe8d94a8ae7e2748b0443272c6c3307c" }, "path_in_vcs": "" }netlink-packet-core-0.7.0/.github/workflows/clippy-rustfmt.yml000064400000000000000000000011021046102023000226030ustar 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-core-0.7.0/.github/workflows/license.yml000064400000000000000000000005201046102023000212260ustar 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-core-0.7.0/.github/workflows/main.yml000064400000000000000000000013721046102023000205360ustar 00000000000000name: CI on: pull_request: types: [opened, synchronize, reopened] push: branches: - main jobs: ci: name: CI (stable) runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install Rust Stable run: | rustup override set stable rustup update stable rustup component add llvm-tools-preview - name: Install cargo-llvm-cov uses: taiki-e/install-action@cargo-llvm-cov - name: Test and Generate code coverage run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: files: lcov.info fail_ci_if_error: true netlink-packet-core-0.7.0/.gitignore000064400000000000000000000000411046102023000154520ustar 00000000000000Cargo.lock target vendor/ *.swp netlink-packet-core-0.7.0/.licenserc.yaml000064400000000000000000000003711046102023000164010ustar 00000000000000header: license: content: | SPDX-License-Identifier: MIT paths-ignore: - 'target' - '**/*.toml' - '**/*.lock' - '**/*.yml' - '**/*.md' - 'CHANGELOG' - 'LICENSE-MIT' - '.gitignore' comment: on-failure netlink-packet-core-0.7.0/.rustfmt.toml000064400000000000000000000001141046102023000161420ustar 00000000000000max_width = 80 wrap_comments = true reorder_imports = true edition = "2021" netlink-packet-core-0.7.0/CHANGELOG000064400000000000000000000015421046102023000147030ustar 00000000000000# Changelog ## [0.7.0] - 2023-07-09 ### Breaking changes - `NetlinkPayload::Ack` removed and replaced by `NetlinkPayload::Error` where `ErrorMessage.code` is set to None. (52732b3) ### New features - Derive `Default` for `ErrorMessage`. (3514766) ### Bug fixes - N/A ## [0.6.0] - 2023-06-26 ### Breaking changes - `NetlinkPayload::Done` changed to `NetlinkPayload::Done(DoneMessage)`. (0c75fb5) ### New features - Support full done message. (0c75fb5) ### Bug fixes - N/A ## [0.5.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. (53a4c4e) - Removed the reexport `netlink-packet-core::utils`, please use `netlink_packet_utils` directly. (a76010a) ### New features - N/A ### Bug fixes - N/A netlink-packet-core-0.7.0/Cargo.lock0000644000000066320000000000100126610ustar # 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 = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "libc" version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "netlink-packet-core" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "345b8ab5bd4e71a2986663e88c56856699d060e78e152e6e9d7966fcd5491297" dependencies = [ "anyhow", "byteorder", "libc", "netlink-packet-utils", ] [[package]] name = "netlink-packet-core" version = "0.7.0" dependencies = [ "anyhow", "byteorder", "netlink-packet-route", "netlink-packet-utils", ] [[package]] name = "netlink-packet-route" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5dee5ed749373c298237fe694eb0a51887f4cc1a27370c8464bac4382348f1a" dependencies = [ "anyhow", "bitflags", "byteorder", "libc", "netlink-packet-core 0.4.2", "netlink-packet-utils", ] [[package]] name = "netlink-packet-utils" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" dependencies = [ "anyhow", "byteorder", "paste", "thiserror", ] [[package]] name = "paste" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" [[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.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] [[package]] name = "syn" version = "2.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "thiserror" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "unicode-ident" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" netlink-packet-core-0.7.0/Cargo.toml0000644000000020260000000000100126750ustar # 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-core" version = "0.7.0" authors = ["Corentin Henry "] description = "netlink packet types" homepage = "https://github.com/rust-netlink/netlink-packet-core" readme = "README.md" keywords = [ "netlink", "linux", ] license = "MIT" repository = "https://github.com/rust-netlink/netlink-packet-core" [dependencies.anyhow] version = "1.0.31" [dependencies.byteorder] version = "1.3.2" [dependencies.netlink-packet-utils] version = "0.5.2" [dev-dependencies.netlink-packet-route] version = "0.13.0" netlink-packet-core-0.7.0/Cargo.toml.orig000064400000000000000000000007711046102023000163630ustar 00000000000000[package] authors = ["Corentin Henry "] name = "netlink-packet-core" version = "0.7.0" edition = "2018" homepage = "https://github.com/rust-netlink/netlink-packet-core" keywords = ["netlink", "linux"] license = "MIT" readme = "README.md" repository = "https://github.com/rust-netlink/netlink-packet-core" description = "netlink packet types" [dependencies] anyhow = "1.0.31" byteorder = "1.3.2" netlink-packet-utils = "0.5.2" [dev-dependencies] netlink-packet-route = "0.13.0" netlink-packet-core-0.7.0/LICENSE-MIT000064400000000000000000000027731046102023000151340ustar 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-core-0.7.0/README.md000064400000000000000000000003411046102023000147440ustar 00000000000000# Rust crate for common netlink packet parsing The `netlink-packet-core` is the glue for all the other netlink-packet-* crates. It provides a `NetlinkMessage` type that represent any netlink message for any sub-protocol. netlink-packet-core-0.7.0/examples/protocol.rs000064400000000000000000000101421046102023000175120ustar 00000000000000// SPDX-License-Identifier: MIT use std::{error::Error, fmt}; use netlink_packet_core::{ NetlinkDeserializable, NetlinkHeader, NetlinkMessage, NetlinkPayload, NetlinkSerializable, }; // PingPongMessage represent the messages for the "ping-pong" netlink // protocol. There are only two types of messages. #[derive(Debug, Clone, Eq, PartialEq)] pub enum PingPongMessage { Ping(Vec), Pong(Vec), } // The netlink header contains a "message type" field that identifies // the message it carries. Some values are reserved, and we // arbitrarily decided that "ping" type is 18 and "pong" type is 20. pub const PING_MESSAGE: u16 = 18; pub const PONG_MESSAGE: u16 = 20; // A custom error type for when deserialization fails. This is // required because `NetlinkDeserializable::Error` must implement // `std::error::Error`, so a simple `String` won't cut it. #[derive(Debug, Clone, Eq, PartialEq)] pub struct DeserializeError(&'static str); impl Error for DeserializeError { fn description(&self) -> &str { self.0 } fn source(&self) -> Option<&(dyn Error + 'static)> { None } } impl fmt::Display for DeserializeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } // NetlinkDeserializable implementation impl NetlinkDeserializable for PingPongMessage { type Error = DeserializeError; fn deserialize( header: &NetlinkHeader, payload: &[u8], ) -> Result { match header.message_type { PING_MESSAGE => Ok(PingPongMessage::Ping(payload.to_vec())), PONG_MESSAGE => Ok(PingPongMessage::Pong(payload.to_vec())), _ => Err(DeserializeError( "invalid ping-pong message: invalid message type", )), } } } // NetlinkSerializable implementation impl NetlinkSerializable for PingPongMessage { fn message_type(&self) -> u16 { match self { PingPongMessage::Ping(_) => PING_MESSAGE, PingPongMessage::Pong(_) => PONG_MESSAGE, } } fn buffer_len(&self) -> usize { match self { PingPongMessage::Ping(vec) | PingPongMessage::Pong(vec) => { vec.len() } } } fn serialize(&self, buffer: &mut [u8]) { match self { PingPongMessage::Ping(vec) | PingPongMessage::Pong(vec) => { buffer.copy_from_slice(&vec[..]) } } } } // It can be convenient to be able to create a NetlinkMessage directly // from a PingPongMessage. Since NetlinkMessage already implements // From>, we just need to implement // From> for this to work. impl From for NetlinkPayload { fn from(message: PingPongMessage) -> Self { NetlinkPayload::InnerMessage(message) } } fn main() { let ping_pong_message = PingPongMessage::Ping(vec![0, 1, 2, 3]); let mut packet = NetlinkMessage::from(ping_pong_message); // Before serializing the packet, it is very important to call // finalize() to ensure the header of the message is consistent // with its payload. Otherwise, a panic may occur when calling // `serialize()` packet.finalize(); // Prepare a buffer to serialize the packet. Note that we never // set explicitely `packet.header.length` above. This was done // automatically when we called `finalize()` let mut buf = vec![0; packet.header.length as usize]; // Serialize the packet packet.serialize(&mut buf[..]); // Deserialize the packet let deserialized_packet = NetlinkMessage::::deserialize(&buf) .expect("Failed to deserialize message"); // Normally, the deserialized packet should be exactly the same // than the serialized one. assert_eq!(deserialized_packet, packet); // This should print: // NetlinkMessage { header: NetlinkHeader { length: 20, message_type: 18, // flags: 0, sequence_number: 0, port_number: 0 }, payload: // InnerMessage(Ping([0, 1, 2, 3])) } println!("{packet:?}"); } netlink-packet-core-0.7.0/examples/rtnetlink.rs000064400000000000000000000025641046102023000176740ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_core::{NLM_F_DUMP, NLM_F_REQUEST}; use netlink_packet_route::{ rtnl::{LinkMessage, RtnlMessage}, NetlinkMessage, }; fn main() { // Create the netlink message, that contains the rtnetlink // message let mut packet = NetlinkMessage::from(RtnlMessage::GetLink(LinkMessage::default())); // Set a few fields in the packet's header packet.header.flags = NLM_F_DUMP | NLM_F_REQUEST; packet.header.sequence_number = 1; // Before serializing the packet, it is very important to call // finalize() to ensure the header of the message is consistent // with its payload. Otherwise, a panic may occur when calling // `serialize()` packet.finalize(); // Prepare a buffer to serialize the packet. Note that we never // set explicitely `packet.header.length` above. This was done // automatically when we called `finalize()` let mut buf = vec![0; packet.header.length as usize]; // Serialize the packet packet.serialize(&mut buf[..]); // Deserialize the packet let deserialized_packet = NetlinkMessage::::deserialize(&buf) .expect("Failed to deserialize message"); // Normally, the deserialized packet should be exactly the same // than the serialized one. assert_eq!(deserialized_packet, packet); println!("{packet:?}"); } netlink-packet-core-0.7.0/src/buffer.rs000064400000000000000000000350411046102023000161000ustar 00000000000000// SPDX-License-Identifier: MIT use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::DecodeError; use crate::{Field, Rest}; const LENGTH: Field = 0..4; const MESSAGE_TYPE: Field = 4..6; const FLAGS: Field = 6..8; const SEQUENCE_NUMBER: Field = 8..12; const PORT_NUMBER: Field = 12..16; const PAYLOAD: Rest = 16..; /// Length of a Netlink packet header pub const NETLINK_HEADER_LEN: usize = PAYLOAD.start; // Prevent some doctest snippers to be formatted, since we cannot add // the attribute directly in the doctest #[rustfmt::skip] #[derive(Debug, PartialEq, Eq, Clone)] /// A raw Netlink buffer that provides getters and setter for the various header fields, and to /// retrieve the payloads. /// /// # Example: reading a packet /// /// ```rust /// use netlink_packet_core::{NetlinkBuffer, NLM_F_MATCH, NLM_F_REQUEST, NLM_F_ROOT}; /// /// const RTM_GETLINK: u16 = 18; /// /// fn main() { /// // Artificially create an array of bytes that represents a netlink packet. /// // Normally, we would read it from a socket. /// let buffer = vec![ /// 0x28, 0x00, 0x00, 0x00, // length = 40 /// 0x12, 0x00, // message type = 18 (RTM_GETLINK) /// 0x01, 0x03, // flags = Request + Specify Tree Root + Return All Matching /// 0x34, 0x0e, 0xf9, 0x5a, // sequence number = 1526271540 /// 0x00, 0x00, 0x00, 0x00, // port id = 0 /// // payload /// 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /// 0x08, 0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x00]; /// /// // Wrap the storage into a NetlinkBuffer /// let packet = NetlinkBuffer::new_checked(&buffer[..]).unwrap(); /// /// // Check that the different accessor return the expected values /// assert_eq!(packet.length(), 40); /// assert_eq!(packet.message_type(), RTM_GETLINK); /// assert_eq!(packet.sequence_number(), 1526271540); /// assert_eq!(packet.port_number(), 0); /// assert_eq!(packet.payload_length(), 24); /// assert_eq!(packet.payload(), &buffer[16..]); /// assert_eq!( /// Into::::into(packet.flags()), /// NLM_F_ROOT | NLM_F_REQUEST | NLM_F_MATCH /// ); /// } /// ``` /// /// # Example: writing a packet /// /// ```rust /// use netlink_packet_core::{NetlinkBuffer, NLM_F_MATCH, NLM_F_REQUEST, NLM_F_ROOT}; /// /// const RTM_GETLINK: u16 = 18; /// /// fn main() { /// // The packet we want to write. /// let expected_buffer = vec![ /// 0x28, 0x00, 0x00, 0x00, // length = 40 /// 0x12, 0x00, // message type = 18 (RTM_GETLINK) /// 0x01, 0x03, // flags = Request + Specify Tree Root + Return All Matching /// 0x34, 0x0e, 0xf9, 0x5a, // sequence number = 1526271540 /// 0x00, 0x00, 0x00, 0x00, // port id = 0 /// // payload /// 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /// 0x08, 0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x00]; /// /// // Create a storage that is big enough for our packet /// let mut buf = vec![0; 40]; /// // the extra scope is to restrict the scope of the borrow /// { /// // Create a NetlinkBuffer. /// let mut packet = NetlinkBuffer::new(&mut buf); /// // Set the various fields /// packet.set_length(40); /// packet.set_message_type(RTM_GETLINK); /// packet.set_sequence_number(1526271540); /// packet.set_port_number(0); /// packet.set_flags(From::from(NLM_F_ROOT | NLM_F_REQUEST | NLM_F_MATCH)); /// // we kind of cheat here to keep the example short /// packet.payload_mut().copy_from_slice(&expected_buffer[16..]); /// } /// // Check that the storage contains the expected values /// assert_eq!(&buf[..], &expected_buffer[..]); /// } /// ``` /// /// Note that in this second example we don't call /// [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked) because the length field is /// initialized to 0, so `new_checked()` would return an error. #[non_exhaustive] pub struct NetlinkBuffer { pub buffer: T, } // Prevent some doc strings to be formatted, since we cannot add the // attribute directly in the doctest #[rustfmt::skip] impl> NetlinkBuffer { /// Create a new `NetlinkBuffer` that uses the given buffer as storage. Note that when calling /// this method no check is performed, so trying to access fields may panic. If you're not sure /// the given buffer contains a valid netlink packet, use /// [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked) instead. pub fn new(buffer: T) -> NetlinkBuffer { NetlinkBuffer { buffer } } // Prevent some doc strings to be formatted, since we cannot add // the attribute directly in the doctest #[rustfmt::skip] /// Check the length of the given buffer and make sure it's big enough so that trying to access /// packet fields won't panic. If the buffer is big enough, create a new `NewlinkBuffer` that /// uses this buffer as storage. /// /// # Example /// /// With a buffer that does not even contain a full header: /// /// ```rust /// use netlink_packet_core::NetlinkBuffer; /// static BYTES: [u8; 4] = [0x28, 0x00, 0x00, 0x00]; /// assert!(NetlinkBuffer::new_checked(&BYTES[..]).is_err()); /// ``` /// /// Here is a slightly more tricky error, where technically, the buffer is big enough to /// contains a valid packet. Here, accessing the packet header fields would not panic but /// accessing the payload would, so `new_checked` also checks the length field in the packet /// header: /// /// ```rust /// use netlink_packet_core::NetlinkBuffer; /// // The buffer is 24 bytes long. It contains a valid header but a truncated payload /// static BYTES: [u8; 24] = [ /// // The length field says the buffer is 40 bytes long /// 0x28, 0x00, 0x00, 0x00, /// 0x12, 0x00, // message type /// 0x01, 0x03, // flags /// 0x34, 0x0e, 0xf9, 0x5a, // sequence number /// 0x00, 0x00, 0x00, 0x00, // port id /// // payload /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; /// assert!(NetlinkBuffer::new_checked(&BYTES[..]).is_err()); /// ``` pub fn new_checked(buffer: T) -> Result, DecodeError> { 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 < PORT_NUMBER.end { Err(format!( "invalid netlink buffer: length is {} but netlink packets are at least {} bytes", len, PORT_NUMBER.end ) .into()) } else if len < self.length() as usize { Err(format!( "invalid netlink buffer: length field says {} the buffer is {} bytes long", self.length(), len ) .into()) } else if (self.length() as usize) < PORT_NUMBER.end { Err(format!( "invalid netlink buffer: length field says {} but netlink packets are at least {} bytes", self.length(), len ).into()) } else { Ok(()) } } /// Return the payload length. /// /// # Panic /// /// This panic is the underlying storage is too small or if the `length` field in the header is /// set to a value that exceeds the storage length (see /// [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked)) pub fn payload_length(&self) -> usize { let total_length = self.length() as usize; let payload_offset = PAYLOAD.start; // This may panic! total_length - payload_offset } /// Consume the packet, returning the underlying buffer. pub fn into_inner(self) -> T { self.buffer } /// Return the `length` field /// /// # Panic /// /// This panic is the underlying storage is too small (see [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked)) pub fn length(&self) -> u32 { let data = self.buffer.as_ref(); NativeEndian::read_u32(&data[LENGTH]) } /// Return the `type` field /// /// # Panic /// /// This panic is the underlying storage is too small (see [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked)) pub fn message_type(&self) -> u16 { let data = self.buffer.as_ref(); NativeEndian::read_u16(&data[MESSAGE_TYPE]) } /// Return the `flags` field /// /// # Panic /// /// This panic is the underlying storage is too small (see [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked)) pub fn flags(&self) -> u16 { let data = self.buffer.as_ref(); NativeEndian::read_u16(&data[FLAGS]) } /// Return the `sequence_number` field /// /// # Panic /// /// This panic is the underlying storage is too small (see [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked)) pub fn sequence_number(&self) -> u32 { let data = self.buffer.as_ref(); NativeEndian::read_u32(&data[SEQUENCE_NUMBER]) } /// Return the `port_number` field /// /// # Panic /// /// This panic is the underlying storage is too small (see [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked)) pub fn port_number(&self) -> u32 { let data = self.buffer.as_ref(); NativeEndian::read_u32(&data[PORT_NUMBER]) } } impl + AsMut<[u8]>> NetlinkBuffer { /// Set the packet header `length` field /// /// # Panic /// /// This panic is the underlying storage is too small (see /// [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked)) pub fn set_length(&mut self, value: u32) { let data = self.buffer.as_mut(); NativeEndian::write_u32(&mut data[LENGTH], value) } /// Set the packet header `message_type` field /// /// # Panic /// /// This panic is the underlying storage is too small (see /// [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked)) pub fn set_message_type(&mut self, value: u16) { let data = self.buffer.as_mut(); NativeEndian::write_u16(&mut data[MESSAGE_TYPE], value) } /// Set the packet header `flags` field /// /// # Panic /// /// This panic is the underlying storage is too small (see /// [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked)) pub fn set_flags(&mut self, value: u16) { let data = self.buffer.as_mut(); NativeEndian::write_u16(&mut data[FLAGS], value) } /// Set the packet header `sequence_number` field /// /// # Panic /// /// This panic is the underlying storage is too small (see /// [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked)) pub fn set_sequence_number(&mut self, value: u32) { let data = self.buffer.as_mut(); NativeEndian::write_u32(&mut data[SEQUENCE_NUMBER], value) } /// Set the packet header `port_number` field /// /// # Panic /// /// This panic is the underlying storage is too small (see /// [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked)) pub fn set_port_number(&mut self, value: u32) { let data = self.buffer.as_mut(); NativeEndian::write_u32(&mut data[PORT_NUMBER], value) } } impl<'a, T: AsRef<[u8]> + ?Sized> NetlinkBuffer<&'a T> { /// Return a pointer to the packet payload. /// /// # Panic /// /// This panic is the underlying storage is too small or if the `length` /// field in the header is set to a value that exceeds the storage /// length (see [`new_checked()`](struct.NetlinkBuffer.html#method. /// new_checked)) pub fn payload(&self) -> &'a [u8] { let range = PAYLOAD.start..self.length() as usize; let data = self.buffer.as_ref(); &data[range] } } impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> NetlinkBuffer<&'a mut T> { /// Return a mutable pointer to the payload. /// /// # Panic /// /// This panic is the underlying storage is too small or if the `length` /// field in the header is set to a value that exceeds the storage /// length (see [`new_checked()`](struct.NetlinkBuffer.html#method. /// new_checked)) pub fn payload_mut(&mut self) -> &mut [u8] { let range = PAYLOAD.start..self.length() as usize; let data = self.buffer.as_mut(); &mut data[range] } } #[cfg(test)] mod tests { use crate::{ constants::{NLM_F_MATCH, NLM_F_REQUEST, NLM_F_ROOT}, NetlinkBuffer, }; const RTM_GETLINK: u16 = 18; // a packet captured with tcpdump that was sent when running `ip link show` #[rustfmt::skip] static IP_LINK_SHOW_PKT: [u8; 40] = [ 0x28, 0x00, 0x00, 0x00, // length = 40 0x12, 0x00, // message type = 18 (RTM_GETLINK) 0x01, 0x03, // flags = Request + Specify Tree Root + Return All Matching 0x34, 0x0e, 0xf9, 0x5a, // sequence number = 1526271540 0x00, 0x00, 0x00, 0x00, // port id = 0 // payload 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x00]; #[test] fn packet_read() { let packet = NetlinkBuffer::new(&IP_LINK_SHOW_PKT[..]); assert_eq!(packet.length(), 40); assert_eq!(packet.message_type(), RTM_GETLINK); assert_eq!(packet.sequence_number(), 1526271540); assert_eq!(packet.port_number(), 0); let flags = packet.flags(); assert!(flags & NLM_F_ROOT == NLM_F_ROOT); assert!(flags & NLM_F_REQUEST == NLM_F_REQUEST); assert!(flags & NLM_F_MATCH == NLM_F_MATCH); assert_eq!(flags, NLM_F_ROOT | NLM_F_REQUEST | NLM_F_MATCH); assert_eq!(packet.payload_length(), 24); assert_eq!(packet.payload(), &IP_LINK_SHOW_PKT[16..]); } #[test] fn packet_build() { let mut buf = vec![0; 40]; { let mut packet = NetlinkBuffer::new(&mut buf); packet.set_length(40); packet.set_message_type(RTM_GETLINK); packet.set_sequence_number(1526271540); packet.set_port_number(0); packet.set_flags(NLM_F_ROOT | NLM_F_REQUEST | NLM_F_MATCH); packet .payload_mut() .copy_from_slice(&IP_LINK_SHOW_PKT[16..]); } assert_eq!(&buf[..], &IP_LINK_SHOW_PKT[..]); } } netlink-packet-core-0.7.0/src/constants.rs000064400000000000000000000031151046102023000166400ustar 00000000000000// SPDX-License-Identifier: MIT /// Must be set on all request messages (typically from user space to kernel /// space) pub const NLM_F_REQUEST: u16 = 1; /// Indicates the message is part of a multipart message terminated by /// NLMSG_DONE pub const NLM_F_MULTIPART: u16 = 2; /// Request for an acknowledgment on success. Typical direction of request is /// from user space (CPC) to kernel space (FEC). pub const NLM_F_ACK: u16 = 4; /// Echo this request. Typical direction of request is from user space (CPC) to /// kernel space (FEC). pub const NLM_F_ECHO: u16 = 8; /// Dump was inconsistent due to sequence change pub const NLM_F_DUMP_INTR: u16 = 16; /// Dump was filtered as requested pub const NLM_F_DUMP_FILTERED: u16 = 32; /// Return the complete table instead of a single entry. pub const NLM_F_ROOT: u16 = 256; /// Return all entries matching criteria passed in message content. pub const NLM_F_MATCH: u16 = 512; /// Return an atomic snapshot of the table. Requires `CAP_NET_ADMIN` capability /// or a effective UID of 0. pub const NLM_F_ATOMIC: u16 = 1024; pub const NLM_F_DUMP: u16 = 768; /// Replace existing matching object. pub const NLM_F_REPLACE: u16 = 256; /// Don't replace if the object already exists. pub const NLM_F_EXCL: u16 = 512; /// Create object if it doesn't already exist. pub const NLM_F_CREATE: u16 = 1024; /// Add to the end of the object list. pub const NLM_F_APPEND: u16 = 2048; /// Do not delete recursively pub const NLM_F_NONREC: u16 = 256; /// request was capped pub const NLM_F_CAPPED: u16 = 256; /// extended ACK TVLs were included pub const NLM_F_ACK_TLVS: u16 = 512; netlink-packet-core-0.7.0/src/done.rs000064400000000000000000000067361046102023000155650ustar 00000000000000// SPDX-License-Identifier: MIT use std::mem::size_of; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::DecodeError; use crate::{Emitable, Field, Parseable, Rest}; const CODE: Field = 0..4; const EXTENDED_ACK: Rest = 4..; const DONE_HEADER_LEN: usize = EXTENDED_ACK.start; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct DoneBuffer { buffer: T, } impl> DoneBuffer { pub fn new(buffer: T) -> DoneBuffer { DoneBuffer { buffer } } /// Consume the packet, returning the underlying buffer. pub fn into_inner(self) -> T { self.buffer } 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 < DONE_HEADER_LEN { Err(format!( "invalid DoneBuffer: length is {len} but DoneBuffer are \ at least {DONE_HEADER_LEN} bytes" ) .into()) } else { Ok(()) } } /// Return the error code pub fn code(&self) -> i32 { let data = self.buffer.as_ref(); NativeEndian::read_i32(&data[CODE]) } } impl<'a, T: AsRef<[u8]> + ?Sized> DoneBuffer<&'a T> { /// Return a pointer to the extended ack attributes. pub fn extended_ack(&self) -> &'a [u8] { let data = self.buffer.as_ref(); &data[EXTENDED_ACK] } } impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> DoneBuffer<&'a mut T> { /// Return a mutable pointer to the extended ack attributes. pub fn extended_ack_mut(&mut self) -> &mut [u8] { let data = self.buffer.as_mut(); &mut data[EXTENDED_ACK] } } impl + AsMut<[u8]>> DoneBuffer { /// set the error code field pub fn set_code(&mut self, value: i32) { let data = self.buffer.as_mut(); NativeEndian::write_i32(&mut data[CODE], value) } } #[derive(Debug, Default, Clone, PartialEq, Eq)] #[non_exhaustive] pub struct DoneMessage { pub code: i32, pub extended_ack: Vec, } impl Emitable for DoneMessage { fn buffer_len(&self) -> usize { size_of::() + self.extended_ack.len() } fn emit(&self, buffer: &mut [u8]) { let mut buffer = DoneBuffer::new(buffer); buffer.set_code(self.code); buffer .extended_ack_mut() .copy_from_slice(&self.extended_ack); } } impl<'buffer, T: AsRef<[u8]> + 'buffer> Parseable> for DoneMessage { fn parse(buf: &DoneBuffer<&'buffer T>) -> Result { Ok(DoneMessage { code: buf.code(), extended_ack: buf.extended_ack().to_vec(), }) } } #[cfg(test)] mod tests { use super::*; #[test] fn serialize_and_parse() { let expected = DoneMessage { code: 5, extended_ack: vec![1, 2, 3], }; let len = expected.buffer_len(); assert_eq!(len, size_of::() + expected.extended_ack.len()); let mut buf = vec![0; len]; expected.emit(&mut buf); let done_buf = DoneBuffer::new(&buf); assert_eq!(done_buf.code(), expected.code); assert_eq!(done_buf.extended_ack(), &expected.extended_ack); let got = DoneMessage::parse(&done_buf).unwrap(); assert_eq!(got, expected); } } netlink-packet-core-0.7.0/src/error.rs000064400000000000000000000146701046102023000157650ustar 00000000000000// SPDX-License-Identifier: MIT use std::{fmt, io, mem::size_of, num::NonZeroI32}; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::DecodeError; use crate::{Emitable, Field, Parseable, Rest}; const CODE: Field = 0..4; const PAYLOAD: Rest = 4..; const ERROR_HEADER_LEN: usize = PAYLOAD.start; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct ErrorBuffer { buffer: T, } impl> ErrorBuffer { pub fn new(buffer: T) -> ErrorBuffer { ErrorBuffer { buffer } } /// Consume the packet, returning the underlying buffer. pub fn into_inner(self) -> T { self.buffer } 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 < ERROR_HEADER_LEN { Err(format!( "invalid ErrorBuffer: length is {len} but ErrorBuffer are \ at least {ERROR_HEADER_LEN} bytes" ) .into()) } else { Ok(()) } } /// Return the error code. /// /// Returns `None` when there is no error to report (the message is an ACK), /// or a `Some(e)` if there is a non-zero error code `e` to report (the /// message is a NACK). pub fn code(&self) -> Option { let data = self.buffer.as_ref(); NonZeroI32::new(NativeEndian::read_i32(&data[CODE])) } } impl<'a, T: AsRef<[u8]> + ?Sized> ErrorBuffer<&'a T> { /// Return a pointer to the payload. pub fn payload(&self) -> &'a [u8] { let data = self.buffer.as_ref(); &data[PAYLOAD] } } impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> ErrorBuffer<&'a mut T> { /// Return a mutable pointer to the payload. pub fn payload_mut(&mut self) -> &mut [u8] { let data = self.buffer.as_mut(); &mut data[PAYLOAD] } } impl + AsMut<[u8]>> ErrorBuffer { /// set the error code field pub fn set_code(&mut self, value: i32) { let data = self.buffer.as_mut(); NativeEndian::write_i32(&mut data[CODE], value) } } /// An `NLMSG_ERROR` message. /// /// Per [RFC 3549 section 2.3.2.2], this message carries the return code for a /// request which will indicate either success (an ACK) or failure (a NACK). /// /// [RFC 3549 section 2.3.2.2]: https://datatracker.ietf.org/doc/html/rfc3549#section-2.3.2.2 #[derive(Debug, Default, Clone, PartialEq, Eq)] #[non_exhaustive] pub struct ErrorMessage { /// The error code. /// /// Holds `None` when there is no error to report (the message is an ACK), /// or a `Some(e)` if there is a non-zero error code `e` to report (the /// message is a NACK). /// /// See [Netlink message types] for details. /// /// [Netlink message types]: https://kernel.org/doc/html/next/userspace-api/netlink/intro.html#netlink-message-types pub code: Option, /// The original request's header. pub header: Vec, } impl Emitable for ErrorMessage { fn buffer_len(&self) -> usize { size_of::() + self.header.len() } fn emit(&self, buffer: &mut [u8]) { let mut buffer = ErrorBuffer::new(buffer); buffer.set_code(self.raw_code()); buffer.payload_mut().copy_from_slice(&self.header) } } impl<'buffer, T: AsRef<[u8]> + 'buffer> Parseable> for ErrorMessage { fn parse( buf: &ErrorBuffer<&'buffer T>, ) -> Result { // FIXME: The payload of an error is basically a truncated packet, which // requires custom logic to parse correctly. For now we just // return it as a Vec let header: NetlinkHeader = { // NetlinkBuffer::new_checked(self.payload()) // .context("failed to parse netlink header")? // .parse() // .context("failed to parse nelink header")? // }; Ok(ErrorMessage { code: buf.code(), header: buf.payload().to_vec(), }) } } impl ErrorMessage { /// Returns the raw error code. pub fn raw_code(&self) -> i32 { self.code.map_or(0, NonZeroI32::get) } /// According to [`netlink(7)`](https://linux.die.net/man/7/netlink) /// the `NLMSG_ERROR` return Negative errno or 0 for acknowledgements. /// /// convert into [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) /// using the absolute value from errno code pub fn to_io(&self) -> io::Error { io::Error::from_raw_os_error(self.raw_code().abs()) } } impl fmt::Display for ErrorMessage { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.to_io(), f) } } impl From for io::Error { fn from(e: ErrorMessage) -> io::Error { e.to_io() } } #[cfg(test)] mod tests { use super::*; #[test] fn into_io_error() { let io_err = io::Error::from_raw_os_error(95); let err_msg = ErrorMessage { code: NonZeroI32::new(-95), header: vec![], }; let to_io: io::Error = err_msg.to_io(); assert_eq!(err_msg.to_string(), io_err.to_string()); assert_eq!(to_io.raw_os_error(), io_err.raw_os_error()); } #[test] fn parse_ack() { let bytes = vec![0, 0, 0, 0]; let msg = ErrorBuffer::new_checked(&bytes) .and_then(|buf| ErrorMessage::parse(&buf)) .expect("failed to parse NLMSG_ERROR"); assert_eq!( ErrorMessage { code: None, header: Vec::new() }, msg ); assert_eq!(msg.raw_code(), 0); } #[test] fn parse_nack() { // SAFETY: value is non-zero. const ERROR_CODE: NonZeroI32 = unsafe { NonZeroI32::new_unchecked(-1234) }; let mut bytes = vec![0, 0, 0, 0]; NativeEndian::write_i32(&mut bytes, ERROR_CODE.get()); let msg = ErrorBuffer::new_checked(&bytes) .and_then(|buf| ErrorMessage::parse(&buf)) .expect("failed to parse NLMSG_ERROR"); assert_eq!( ErrorMessage { code: Some(ERROR_CODE), header: Vec::new() }, msg ); assert_eq!(msg.raw_code(), ERROR_CODE.get()); } } netlink-packet-core-0.7.0/src/header.rs000064400000000000000000000077641046102023000160720ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::DecodeError; use crate::{buffer::NETLINK_HEADER_LEN, Emitable, NetlinkBuffer, Parseable}; /// A Netlink header representation. A netlink header has the following /// structure: /// /// ```no_rust /// 0 8 16 24 32 /// +----------------+----------------+----------------+----------------+ /// | packet length (including header) | /// +----------------+----------------+----------------+----------------+ /// | message type | flags | /// +----------------+----------------+----------------+----------------+ /// | sequence number | /// +----------------+----------------+----------------+----------------+ /// | port number (formerly known as PID) | /// +----------------+----------------+----------------+----------------+ /// ``` #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Default)] #[non_exhaustive] pub struct NetlinkHeader { /// Length of the netlink packet, including the header and the payload pub length: u32, /// NetlinkMessage type. The meaning of this field depends on the netlink /// protocol family in use. pub message_type: u16, /// Flags. It should be set to one of the `NLM_F_*` constants. pub flags: u16, /// Sequence number of the packet pub sequence_number: u32, /// Port number (usually set to the the process ID) pub port_number: u32, } impl Emitable for NetlinkHeader { fn buffer_len(&self) -> usize { NETLINK_HEADER_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = NetlinkBuffer::new(buffer); buffer.set_message_type(self.message_type); buffer.set_length(self.length); buffer.set_flags(self.flags); buffer.set_sequence_number(self.sequence_number); buffer.set_port_number(self.port_number); } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for NetlinkHeader { fn parse(buf: &NetlinkBuffer<&'a T>) -> Result { Ok(NetlinkHeader { length: buf.length(), message_type: buf.message_type(), flags: buf.flags(), sequence_number: buf.sequence_number(), port_number: buf.port_number(), }) } } #[cfg(test)] mod tests { use super::*; use crate::constants::*; // a packet captured with tcpdump that was sent when running `ip link show` #[rustfmt::skip] static IP_LINK_SHOW_PKT: [u8; 40] = [ 0x28, 0x00, 0x00, 0x00, // length = 40 0x12, 0x00, // message type = 18 (RTM_GETLINK) 0x01, 0x03, // flags = Request + Specify Tree Root + Return All Matching 0x34, 0x0e, 0xf9, 0x5a, // sequence number = 1526271540 0x00, 0x00, 0x00, 0x00, // port id = 0 // payload 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x00]; const RTM_GETLINK: u16 = 18; #[test] fn repr_parse() { let repr = NetlinkHeader::parse( &NetlinkBuffer::new_checked(&IP_LINK_SHOW_PKT[..]).unwrap(), ) .unwrap(); assert_eq!(repr.length, 40); assert_eq!(repr.message_type, RTM_GETLINK); assert_eq!(repr.sequence_number, 1_526_271_540); assert_eq!(repr.port_number, 0); assert_eq!(repr.flags, NLM_F_ROOT | NLM_F_REQUEST | NLM_F_MATCH); } #[test] fn repr_emit() { let repr = NetlinkHeader { length: 40, message_type: RTM_GETLINK, sequence_number: 1_526_271_540, flags: NLM_F_ROOT | NLM_F_REQUEST | NLM_F_MATCH, port_number: 0, }; assert_eq!(repr.buffer_len(), 16); let mut buf = vec![0; 16]; repr.emit(&mut buf[..]); assert_eq!(&buf[..], &IP_LINK_SHOW_PKT[..16]); } } netlink-packet-core-0.7.0/src/lib.rs000064400000000000000000000247041046102023000154010ustar 00000000000000// SPDX-License-Identifier: MIT //! `netlink-packet-core` provides a generic netlink message //! `NetlinkMessage` that is independant of the sub-protocol. Such //! messages are not very useful by themselves, since they are just //! used to carry protocol-dependant messages. That is what the `T` //! represent: `T` is the `NetlinkMessage`'s protocol-dependant //! message. This can be any type that implements //! `NetlinkSerializable` and `NetlinkDeserializable`. //! //! For instance, the `netlink-packet-route` crate provides rtnetlink //! messages via `netlink_packet_route::RtnlMessage`, and //! `netlink-packet-audit` provides audit messages via //! `netlink_packet_audit::AuditMessage`. //! //! By itself, the `netlink-packet-core` crate is not very //! useful. However, it is used in `netlink-proto` to provide an //! asynchronous implementation of the netlink protocol for any //! sub-protocol. Thus, a crate that defines messages for a given //! netlink sub-protocol could integrate with `netlink-packet-core` //! and would get an asynchronous implementation for free. See the //! second example below for such an integration, via the //! `NetlinkSerializable` and `NetlinkDeserializable` traits. //! //! # Example: usage with `netlink-packet-route` //! //! This example shows how to serialize and deserialize netlink packet //! for the rtnetlink sub-protocol. It requires //! `netlink-packet-route`. //! //! ```rust //! use netlink_packet_core::{NLM_F_DUMP, NLM_F_REQUEST}; //! use netlink_packet_route::{LinkMessage, RtnlMessage, NetlinkMessage, //! NetlinkHeader}; //! //! // Create the netlink message, that contains the rtnetlink //! // message //! let mut packet = NetlinkMessage { //! header: NetlinkHeader { //! sequence_number: 1, //! flags: NLM_F_DUMP | NLM_F_REQUEST, //! ..Default::default() //! }, //! payload: RtnlMessage::GetLink(LinkMessage::default()).into(), //! }; //! //! // Before serializing the packet, it is important to call //! // finalize() to ensure the header of the message is consistent //! // with its payload. Otherwise, a panic may occur when calling //! // serialize() //! packet.finalize(); //! //! // Prepare a buffer to serialize the packet. Note that we never //! // set explicitely `packet.header.length` above. This was done //! // automatically when we called `finalize()` //! let mut buf = vec![0; packet.header.length as usize]; //! // Serialize the packet //! packet.serialize(&mut buf[..]); //! //! // Deserialize the packet //! let deserialized_packet = //! NetlinkMessage::::deserialize(&buf).expect("Failed to deserialize message"); //! //! // Normally, the deserialized packet should be exactly the same //! // than the serialized one. //! assert_eq!(deserialized_packet, packet); //! //! println!("{:?}", packet); //! ``` //! //! # Example: adding messages for new netlink sub-protocol //! //! Let's assume we have a netlink protocol called "ping pong" that //! defines two types of messages: "ping" messages, which payload can //! be any sequence of bytes, and "pong" message, which payload is //! also a sequence of bytes. The protocol works as follow: when an //! enpoint receives a "ping" message, it answers with a "pong", with //! the payload of the "ping" it's answering to. //! //! "ping" messages have type 18 and "pong" have type "20". Here is //! what a "ping" message that would look like if its payload is `[0, //! 1, 2, 3]`: //! //! ```no_rust //! 0 8 16 24 32 //! +----------------+----------------+----------------+----------------+ //! | packet length (including header) = 16 + 4 = 20 | //! +----------------+----------------+----------------+----------------+ //! | message type = 18 (ping) | flags | //! +----------------+----------------+----------------+----------------+ //! | sequence number | //! +----------------+----------------+----------------+----------------+ //! | port number | //! +----------------+----------------+----------------+----------------+ //! | 0 | 1 | 2 | 3 | //! +----------------+----------------+----------------+----------------+ //! ``` //! //! And the "pong" response would be: //! //! ```no_rust //! 0 8 16 24 32 //! +----------------+----------------+----------------+----------------+ //! | packet length (including header) = 16 + 4 = 20 | //! +----------------+----------------+----------------+----------------+ //! | message type = 20 (pong) | flags | //! +----------------+----------------+----------------+----------------+ //! | sequence number | //! +----------------+----------------+----------------+----------------+ //! | port number | //! +----------------+----------------+----------------+----------------+ //! | 0 | 1 | 2 | 3 | //! +----------------+----------------+----------------+----------------+ //! ``` //! //! Here is how we could implement the messages for such a protocol //! and integrate this implementation with `netlink-packet-core`: //! //! ```rust //! use netlink_packet_core::{ //! NetlinkDeserializable, NetlinkHeader, NetlinkMessage, NetlinkPayload, NetlinkSerializable, //! }; //! use std::error::Error; //! use std::fmt; //! //! // PingPongMessage represent the messages for the "ping-pong" netlink //! // protocol. There are only two types of messages. //! #[derive(Debug, Clone, Eq, PartialEq)] //! pub enum PingPongMessage { //! Ping(Vec), //! Pong(Vec), //! } //! //! // The netlink header contains a "message type" field that identifies //! // the message it carries. Some values are reserved, and we //! // arbitrarily decided that "ping" type is 18 and "pong" type is 20. //! pub const PING_MESSAGE: u16 = 18; //! pub const PONG_MESSAGE: u16 = 20; //! //! // A custom error type for when deserialization fails. This is //! // required because `NetlinkDeserializable::Error` must implement //! // `std::error::Error`, so a simple `String` won't cut it. //! #[derive(Debug, Clone, Eq, PartialEq)] //! pub struct DeserializeError(&'static str); //! //! impl Error for DeserializeError { //! fn description(&self) -> &str { //! self.0 //! } //! fn source(&self) -> Option<&(dyn Error + 'static)> { //! None //! } //! } //! //! impl fmt::Display for DeserializeError { //! fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { //! write!(f, "{}", self.0) //! } //! } //! //! // NetlinkDeserializable implementation //! impl NetlinkDeserializable for PingPongMessage { //! type Error = DeserializeError; //! //! fn deserialize(header: &NetlinkHeader, payload: &[u8]) -> Result { //! match header.message_type { //! PING_MESSAGE => Ok(PingPongMessage::Ping(payload.to_vec())), //! PONG_MESSAGE => Ok(PingPongMessage::Pong(payload.to_vec())), //! _ => Err(DeserializeError( //! "invalid ping-pong message: invalid message type", //! )), //! } //! } //! } //! //! // NetlinkSerializable implementation //! impl NetlinkSerializable for PingPongMessage { //! fn message_type(&self) -> u16 { //! match self { //! PingPongMessage::Ping(_) => PING_MESSAGE, //! PingPongMessage::Pong(_) => PONG_MESSAGE, //! } //! } //! //! fn buffer_len(&self) -> usize { //! match self { //! PingPongMessage::Ping(vec) | PingPongMessage::Pong(vec) => vec.len(), //! } //! } //! //! fn serialize(&self, buffer: &mut [u8]) { //! match self { //! PingPongMessage::Ping(vec) | PingPongMessage::Pong(vec) => { //! buffer.copy_from_slice(&vec[..]) //! } //! } //! } //! } //! //! // It can be convenient to be able to create a NetlinkMessage directly //! // from a PingPongMessage. Since NetlinkMessage already implements //! // From>, we just need to implement //! // From> for this to work. //! impl From for NetlinkPayload { //! fn from(message: PingPongMessage) -> Self { //! NetlinkPayload::InnerMessage(message) //! } //! } //! //! fn main() { //! let ping_pong_message = PingPongMessage::Ping(vec![0, 1, 2, 3]); //! let mut packet = NetlinkMessage::from(ping_pong_message); //! //! // Before serializing the packet, it is very important to call //! // finalize() to ensure the header of the message is consistent //! // with its payload. Otherwise, a panic may occur when calling //! // `serialize()` //! packet.finalize(); //! //! // Prepare a buffer to serialize the packet. Note that we never //! // set explicitely `packet.header.length` above. This was done //! // automatically when we called `finalize()` //! let mut buf = vec![0; packet.header.length as usize]; //! // Serialize the packet //! packet.serialize(&mut buf[..]); //! //! // Deserialize the packet //! let deserialized_packet = NetlinkMessage::::deserialize(&buf) //! .expect("Failed to deserialize message"); //! //! // Normally, the deserialized packet should be exactly the same //! // than the serialized one. //! assert_eq!(deserialized_packet, packet); //! //! // This should print: //! // NetlinkMessage { header: NetlinkHeader { length: 20, message_type: 18, flags: 0, sequence_number: 0, port_number: 0 }, payload: InnerMessage(Ping([0, 1, 2, 3])) } //! println!("{:?}", packet); //! } //! ``` use core::ops::{Range, RangeFrom}; /// Represent a multi-bytes field with a fixed size in a packet pub(crate) type Field = Range; /// Represent a field that starts at a given index in a packet pub(crate) type Rest = RangeFrom; pub mod done; pub use self::done::*; pub mod error; pub use self::error::*; pub mod buffer; pub use self::buffer::*; pub mod header; pub use self::header::*; mod traits; pub use self::traits::*; mod payload; pub use self::payload::*; mod message; pub use self::message::*; pub mod constants; pub use self::constants::*; pub(crate) use self::utils::traits::*; pub(crate) use netlink_packet_utils as utils; netlink-packet-core-0.7.0/src/message.rs000064400000000000000000000177701046102023000162640ustar 00000000000000// SPDX-License-Identifier: MIT use std::fmt::Debug; use anyhow::Context; use netlink_packet_utils::DecodeError; use crate::{ payload::{NLMSG_DONE, NLMSG_ERROR, NLMSG_NOOP, NLMSG_OVERRUN}, DoneBuffer, DoneMessage, Emitable, ErrorBuffer, ErrorMessage, NetlinkBuffer, NetlinkDeserializable, NetlinkHeader, NetlinkPayload, NetlinkSerializable, Parseable, }; /// Represent a netlink message. #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct NetlinkMessage { /// Message header (this is common to all the netlink protocols) pub header: NetlinkHeader, /// Inner message, which depends on the netlink protocol being used. pub payload: NetlinkPayload, } impl NetlinkMessage { /// Create a new netlink message from the given header and payload pub fn new(header: NetlinkHeader, payload: NetlinkPayload) -> Self { NetlinkMessage { header, payload } } /// Consume this message and return its header and payload pub fn into_parts(self) -> (NetlinkHeader, NetlinkPayload) { (self.header, self.payload) } } impl NetlinkMessage where I: NetlinkDeserializable, { /// Parse the given buffer as a netlink message pub fn deserialize(buffer: &[u8]) -> Result { let netlink_buffer = NetlinkBuffer::new_checked(&buffer)?; >>::parse(&netlink_buffer) } } impl NetlinkMessage where I: NetlinkSerializable, { /// Return the length of this message in bytes pub fn buffer_len(&self) -> usize { ::buffer_len(self) } /// Serialize this message and write the serialized data into the /// given buffer. `buffer` must big large enough for the whole /// message to fit, otherwise, this method will panic. To know how /// big the serialized message is, call `buffer_len()`. /// /// # Panic /// /// This method panics if the buffer is not big enough. pub fn serialize(&self, buffer: &mut [u8]) { self.emit(buffer) } /// Ensure the header (`NetlinkHeader`) is consistent with the payload /// (`NetlinkPayload`): /// /// - compute the payload length and set the header's length field /// - check the payload type and set the header's message type field /// accordingly /// /// If you are not 100% sure the header is correct, this method should be /// called before calling [`Emitable::emit()`](trait.Emitable.html# /// tymethod.emit), as it could panic if the header is inconsistent with /// the rest of the message. pub fn finalize(&mut self) { self.header.length = self.buffer_len() as u32; self.header.message_type = self.payload.message_type(); } } impl<'buffer, B, I> Parseable> for NetlinkMessage where B: AsRef<[u8]> + 'buffer, I: NetlinkDeserializable, { fn parse(buf: &NetlinkBuffer<&'buffer B>) -> Result { use self::NetlinkPayload::*; let header = >>::parse(buf) .context("failed to parse netlink header")?; let bytes = buf.payload(); let payload = match header.message_type { NLMSG_ERROR => { let msg = ErrorBuffer::new_checked(&bytes) .and_then(|buf| ErrorMessage::parse(&buf)) .context("failed to parse NLMSG_ERROR")?; Error(msg) } NLMSG_NOOP => Noop, NLMSG_DONE => { let msg = DoneBuffer::new_checked(&bytes) .and_then(|buf| DoneMessage::parse(&buf)) .context("failed to parse NLMSG_DONE")?; Done(msg) } NLMSG_OVERRUN => Overrun(bytes.to_vec()), message_type => { let inner_msg = I::deserialize(&header, bytes).context( format!("Failed to parse message with type {message_type}"), )?; InnerMessage(inner_msg) } }; Ok(NetlinkMessage { header, payload }) } } impl Emitable for NetlinkMessage where I: NetlinkSerializable, { fn buffer_len(&self) -> usize { use self::NetlinkPayload::*; let payload_len = match self.payload { Noop => 0, Done(ref msg) => msg.buffer_len(), Overrun(ref bytes) => bytes.len(), Error(ref msg) => msg.buffer_len(), InnerMessage(ref msg) => msg.buffer_len(), }; self.header.buffer_len() + payload_len } fn emit(&self, buffer: &mut [u8]) { use self::NetlinkPayload::*; self.header.emit(buffer); let buffer = &mut buffer[self.header.buffer_len()..self.header.length as usize]; match self.payload { Noop => {} Done(ref msg) => msg.emit(buffer), Overrun(ref bytes) => buffer.copy_from_slice(bytes), Error(ref msg) => msg.emit(buffer), InnerMessage(ref msg) => msg.serialize(buffer), } } } impl From for NetlinkMessage where T: Into>, { fn from(inner_message: T) -> Self { NetlinkMessage { header: NetlinkHeader::default(), payload: inner_message.into(), } } } #[cfg(test)] mod tests { use super::*; use std::{convert::Infallible, mem::size_of, num::NonZeroI32}; #[derive(Clone, Debug, Default, PartialEq)] struct FakeNetlinkInnerMessage; impl NetlinkSerializable for FakeNetlinkInnerMessage { fn message_type(&self) -> u16 { unimplemented!("unused by tests") } fn buffer_len(&self) -> usize { unimplemented!("unused by tests") } fn serialize(&self, _buffer: &mut [u8]) { unimplemented!("unused by tests") } } impl NetlinkDeserializable for FakeNetlinkInnerMessage { type Error = Infallible; fn deserialize( _header: &NetlinkHeader, _payload: &[u8], ) -> Result { unimplemented!("unused by tests") } } #[test] fn test_done() { let header = NetlinkHeader::default(); let done_msg = DoneMessage { code: 0, extended_ack: vec![6, 7, 8, 9], }; let mut want = NetlinkMessage::new( header, NetlinkPayload::::Done(done_msg.clone()), ); want.finalize(); let len = want.buffer_len(); assert_eq!( len, header.buffer_len() + size_of::() + done_msg.extended_ack.len() ); let mut buf = vec![1; len]; want.emit(&mut buf); let done_buf = DoneBuffer::new(&buf[header.buffer_len()..]); assert_eq!(done_buf.code(), done_msg.code); assert_eq!(done_buf.extended_ack(), &done_msg.extended_ack); let got = NetlinkMessage::parse(&NetlinkBuffer::new(&buf)).unwrap(); assert_eq!(got, want); } #[test] fn test_error() { // SAFETY: value is non-zero. const ERROR_CODE: NonZeroI32 = unsafe { NonZeroI32::new_unchecked(-8765) }; let header = NetlinkHeader::default(); let error_msg = ErrorMessage { code: Some(ERROR_CODE), header: vec![], }; let mut want = NetlinkMessage::new( header, NetlinkPayload::::Error(error_msg.clone()), ); want.finalize(); let len = want.buffer_len(); assert_eq!(len, header.buffer_len() + error_msg.buffer_len()); let mut buf = vec![1; len]; want.emit(&mut buf); let error_buf = ErrorBuffer::new(&buf[header.buffer_len()..]); assert_eq!(error_buf.code(), error_msg.code); let got = NetlinkMessage::parse(&NetlinkBuffer::new(&buf)).unwrap(); assert_eq!(got, want); } } netlink-packet-core-0.7.0/src/payload.rs000064400000000000000000000021641046102023000162600ustar 00000000000000// SPDX-License-Identifier: MIT use std::fmt::Debug; use crate::{DoneMessage, ErrorMessage, NetlinkSerializable}; /// The message is ignored. pub const NLMSG_NOOP: u16 = 1; /// The message signals an error and the payload contains a nlmsgerr structure. /// This can be looked at as a NACK and typically it is from FEC to CPC. pub const NLMSG_ERROR: u16 = 2; /// The message terminates a multipart message. /// Data lost pub const NLMSG_DONE: u16 = 3; pub const NLMSG_OVERRUN: u16 = 4; pub const NLMSG_ALIGNTO: u16 = 4; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum NetlinkPayload { Done(DoneMessage), Error(ErrorMessage), Noop, Overrun(Vec), InnerMessage(I), } impl NetlinkPayload where I: NetlinkSerializable, { pub fn message_type(&self) -> u16 { match self { NetlinkPayload::Done(_) => NLMSG_DONE, NetlinkPayload::Error(_) => NLMSG_ERROR, NetlinkPayload::Noop => NLMSG_NOOP, NetlinkPayload::Overrun(_) => NLMSG_OVERRUN, NetlinkPayload::InnerMessage(message) => message.message_type(), } } } netlink-packet-core-0.7.0/src/traits.rs000064400000000000000000000027671046102023000161460ustar 00000000000000// SPDX-License-Identifier: MIT use crate::NetlinkHeader; use std::error::Error; /// A `NetlinkDeserializable` type can be deserialized from a buffer pub trait NetlinkDeserializable: Sized { type Error: Error + Send + Sync + 'static; /// Deserialize the given buffer into `Self`. fn deserialize( header: &NetlinkHeader, payload: &[u8], ) -> Result; } pub trait NetlinkSerializable { fn message_type(&self) -> u16; /// Return the length of the serialized data. /// /// Most netlink messages are encoded following a /// [TLV](https://en.wikipedia.org/wiki/Type-length-value) scheme /// and this library takes advantage of this by pre-allocating /// buffers of the appropriate size when serializing messages, /// which is why `buffer_len` is needed. fn buffer_len(&self) -> usize; /// Serialize this types and write the serialized data into the given /// buffer. `buffer`'s length is exactly `InnerMessage::buffer_len()`. /// It means that if `InnerMessage::buffer_len()` is buggy and does not /// return the appropriate length, bad things can happen: /// /// - if `buffer_len()` returns a value _smaller than the actual data_, /// `emit()` may panics /// - if `buffer_len()` returns a value _bigger than the actual data_, the /// buffer will contain garbage /// /// # Panic /// /// This method panics if the buffer is not big enough. fn serialize(&self, buffer: &mut [u8]); }