| // SPDX-License-Identifier: MIT |
| |
| //! `netlink-proto` is an asynchronous implementation of the Netlink |
| //! protocol. |
| //! |
| //! # Example: listening for audit events |
| //! |
| //! This example shows how to use `netlink-proto` with the `tokio` |
| //! runtime to print audit events. It requires extra external |
| //! dependencies: |
| //! |
| //! - `futures = "^0.3"` |
| //! - `tokio = "^1.0"` |
| //! - `netlink-packet-audit = "^0.1"` |
| //! |
| //! ```rust,no_run |
| //! use futures::stream::StreamExt; |
| //! use netlink_packet_core::{NetlinkMessage, NetlinkPayload, NLM_F_ACK, |
| //! NLM_F_REQUEST}; |
| //! use netlink_packet_audit::{ |
| //! AuditMessage, |
| //! StatusMessage, |
| //! }; |
| //! use std::process; |
| //! |
| //! use netlink_proto::{ |
| //! new_connection, |
| //! sys::{protocols::NETLINK_AUDIT, SocketAddr}, |
| //! }; |
| //! |
| //! const AUDIT_STATUS_ENABLED: u32 = 1; |
| //! const AUDIT_STATUS_PID: u32 = 4; |
| //! |
| //! #[tokio::main] |
| //! async fn main() -> Result<(), String> { |
| //! // Create a netlink socket. Here: |
| //! // |
| //! // - `conn` is a `Connection` that has the netlink socket. It's a |
| //! // `Future` that keeps polling the socket and must be spawned an |
| //! // the event loop. |
| //! // |
| //! // - `handle` is a `Handle` to the `Connection`. We use it to send |
| //! // netlink messages and receive responses to these messages. |
| //! // |
| //! // - `messages` is a channel receiver through which we receive |
| //! // messages that we have not solicited, ie that are not |
| //! // response to a request we made. In this example, we'll receive |
| //! // the audit event through that channel. |
| //! let (conn, mut handle, mut messages) = new_connection(NETLINK_AUDIT) |
| //! .map_err(|e| format!("Failed to create a new netlink connection: {}", e))?; |
| //! |
| //! // Spawn the `Connection` so that it starts polling the netlink |
| //! // socket in the background. |
| //! tokio::spawn(conn); |
| //! |
| //! // Use the `ConnectionHandle` to send a request to the kernel |
| //! // asking it to start multicasting audit event messages. |
| //! tokio::spawn(async move { |
| //! // Craft the packet to enable audit events |
| //! let mut status = StatusMessage::new(); |
| //! status.enabled = 1; |
| //! status.pid = process::id(); |
| //! status.mask = AUDIT_STATUS_ENABLED | AUDIT_STATUS_PID; |
| //! let payload = AuditMessage::SetStatus(status); |
| //! let mut nl_msg = NetlinkMessage::from(payload); |
| //! nl_msg.header.flags = NLM_F_REQUEST | NLM_F_ACK; |
| //! |
| //! // We'll send unicast messages to the kernel. |
| //! let kernel_unicast: SocketAddr = SocketAddr::new(0, 0); |
| //! let mut response = match handle.request(nl_msg, kernel_unicast) { |
| //! Ok(response) => response, |
| //! Err(e) => { |
| //! eprintln!("{}", e); |
| //! return; |
| //! } |
| //! }; |
| //! |
| //! while let Some(message) = response.next().await { |
| //! if let NetlinkPayload::Error(err_message) = message.payload { |
| //! eprintln!("Received an error message: {:?}", err_message); |
| //! return; |
| //! } |
| //! } |
| //! }); |
| //! |
| //! // Finally, start receiving event through the `messages` channel. |
| //! println!("Starting to print audit events... press ^C to interrupt"); |
| //! while let Some((message, _addr)) = messages.next().await { |
| //! if let NetlinkPayload::Error(err_message) = message.payload { |
| //! eprintln!("received an error message: {:?}", err_message); |
| //! } else { |
| //! println!("{:?}", message); |
| //! } |
| //! } |
| //! |
| //! Ok(()) |
| //! } |
| //! ``` |
| //! |
| //! # Example: dumping all the machine's links |
| //! |
| //! This example shows how to use `netlink-proto` with the ROUTE |
| //! protocol. |
| //! |
| //! Here we do not use `netlink_proto::new_connection()`, and instead |
| //! create the socket manually and use call `send()` and `receive()` |
| //! directly. In the previous example, the `NetlinkFramed` was wrapped |
| //! in a `Connection` which was polled automatically by the runtime. |
| //! |
| //! ```rust,no_run |
| //! use futures::StreamExt; |
| //! |
| //! use netlink_packet_route::{link::LinkMessage, RouteNetlinkMessage}; |
| //! use netlink_packet_core::{ |
| //! NetlinkHeader, |
| //! NetlinkMessage, |
| //! NLM_F_REQUEST, NLM_F_DUMP |
| //! }; |
| //! |
| //! use netlink_proto::{ |
| //! new_connection, |
| //! sys::{protocols::NETLINK_ROUTE, SocketAddr}, |
| //! }; |
| //! |
| //! #[tokio::main] |
| //! async fn main() -> Result<(), String> { |
| //! // Create the netlink socket. Here, we won't use the channel that |
| //! // receives unsolicited messages. |
| //! let (conn, mut handle, _) = new_connection(NETLINK_ROUTE) |
| //! .map_err(|e| format!("Failed to create a new netlink connection: {}", e))?; |
| //! |
| //! // Spawn the `Connection` in the background |
| //! tokio::spawn(conn); |
| //! |
| //! // Create the netlink message that requests the links to be dumped |
| //! let mut nl_hdr = NetlinkHeader::default(); |
| //! nl_hdr.flags = NLM_F_DUMP | NLM_F_REQUEST; |
| //! |
| //! let msg = NetlinkMessage::new( |
| //! nl_hdr, |
| //! RouteNetlinkMessage::GetLink(LinkMessage::default()).into(), |
| //! ); |
| //! |
| //! // Send the request |
| //! let mut response = handle |
| //! .request(msg, SocketAddr::new(0, 0)) |
| //! .map_err(|e| format!("Failed to send request: {}", e))?; |
| //! |
| //! // Print all the messages received in response |
| //! loop { |
| //! if let Some(packet) = response.next().await { |
| //! println!("<<< {:?}", packet); |
| //! } else { |
| //! break; |
| //! } |
| //! } |
| //! |
| //! Ok(()) |
| //! } |
| //! ``` |
| #[macro_use] |
| extern crate futures; |
| #[macro_use] |
| extern crate log; |
| |
| mod codecs; |
| pub use crate::codecs::*; |
| |
| mod framed; |
| pub use crate::framed::*; |
| |
| mod protocol; |
| pub(crate) use self::protocol::{Protocol, Response}; |
| pub(crate) type Request<T> = self::protocol::Request< |
| T, |
| UnboundedSender<crate::packet::NetlinkMessage<T>>, |
| >; |
| |
| mod connection; |
| pub use crate::connection::*; |
| |
| mod errors; |
| pub use crate::errors::*; |
| |
| mod handle; |
| pub use crate::handle::*; |
| |
| use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; |
| use std::{fmt::Debug, io}; |
| |
| pub(crate) use netlink_packet_core as packet; |
| |
| pub mod sys { |
| pub use netlink_sys::{protocols, AsyncSocket, AsyncSocketExt, SocketAddr}; |
| |
| #[cfg(feature = "tokio_socket")] |
| pub use netlink_sys::TokioSocket; |
| |
| #[cfg(feature = "smol_socket")] |
| pub use netlink_sys::SmolSocket; |
| } |
| |
| /// Create a new Netlink connection for the given Netlink protocol, and returns |
| /// a handle to that connection as well as a stream of unsolicited messages |
| /// received by that connection (unsolicited here means messages that are not a |
| /// response to a request made by the `Connection`). `Connection<T>` wraps a |
| /// Netlink socket and implements the Netlink protocol. |
| /// |
| /// `protocol` must be one of the [`crate::sys::protocols`][protos] constants. |
| /// |
| /// `T` is the type of netlink messages used for this protocol. For instance, if |
| /// you're using the `NETLINK_AUDIT` protocol with the `netlink-packet-audit` |
| /// crate, `T` will be `netlink_packet_audit::AuditMessage`. More generally, `T` |
| /// is anything that can be serialized and deserialized into a Netlink message. |
| /// See the `netlink_packet_core` documentation for details about the |
| /// `NetlinkSerializable` and `NetlinkDeserializable` traits. |
| /// |
| /// Most of the time, users will want to spawn the `Connection` on an async |
| /// runtime, and use the handle to send messages. |
| /// |
| /// [protos]: crate::sys::protocols |
| #[cfg(feature = "tokio_socket")] |
| #[allow(clippy::type_complexity)] |
| pub fn new_connection<T>( |
| protocol: isize, |
| ) -> io::Result<( |
| Connection<T>, |
| ConnectionHandle<T>, |
| UnboundedReceiver<(packet::NetlinkMessage<T>, sys::SocketAddr)>, |
| )> |
| where |
| T: Debug |
| + packet::NetlinkSerializable |
| + packet::NetlinkDeserializable |
| + Unpin, |
| { |
| new_connection_with_codec(protocol) |
| } |
| |
| /// Variant of [`new_connection`] that allows specifying a socket type to use |
| /// for async handling |
| #[allow(clippy::type_complexity)] |
| pub fn new_connection_with_socket<T, S>( |
| protocol: isize, |
| ) -> io::Result<( |
| Connection<T, S>, |
| ConnectionHandle<T>, |
| UnboundedReceiver<(packet::NetlinkMessage<T>, sys::SocketAddr)>, |
| )> |
| where |
| T: Debug |
| + packet::NetlinkSerializable |
| + packet::NetlinkDeserializable |
| + Unpin, |
| S: sys::AsyncSocket, |
| { |
| new_connection_with_codec(protocol) |
| } |
| |
| /// Variant of [`new_connection`] that allows specifying a socket type to use |
| /// for async handling and a special codec |
| #[allow(clippy::type_complexity)] |
| pub fn new_connection_with_codec<T, S, C>( |
| protocol: isize, |
| ) -> io::Result<( |
| Connection<T, S, C>, |
| ConnectionHandle<T>, |
| UnboundedReceiver<(packet::NetlinkMessage<T>, sys::SocketAddr)>, |
| )> |
| where |
| T: Debug |
| + packet::NetlinkSerializable |
| + packet::NetlinkDeserializable |
| + Unpin, |
| S: sys::AsyncSocket, |
| C: NetlinkMessageCodec, |
| { |
| let (requests_tx, requests_rx) = unbounded::<Request<T>>(); |
| let (messages_tx, messages_rx) = |
| unbounded::<(packet::NetlinkMessage<T>, sys::SocketAddr)>(); |
| Ok(( |
| Connection::new(requests_rx, messages_tx, protocol)?, |
| ConnectionHandle::new(requests_tx), |
| messages_rx, |
| )) |
| } |
| |
| /// Variant of [`new_connection`] that allows specifying a socket type to use |
| /// for async handling, a special codec and a socket |
| #[allow(clippy::type_complexity)] |
| pub fn from_socket_with_codec<T, S, C>( |
| socket: S, |
| ) -> ( |
| Connection<T, S, C>, |
| ConnectionHandle<T>, |
| UnboundedReceiver<(packet::NetlinkMessage<T>, sys::SocketAddr)>, |
| ) |
| where |
| T: Debug |
| + packet::NetlinkSerializable |
| + packet::NetlinkDeserializable |
| + Unpin, |
| S: sys::AsyncSocket, |
| C: NetlinkMessageCodec, |
| { |
| let (requests_tx, requests_rx) = unbounded::<Request<T>>(); |
| let (messages_tx, messages_rx) = |
| unbounded::<(packet::NetlinkMessage<T>, sys::SocketAddr)>(); |
| ( |
| Connection::from_socket(requests_rx, messages_tx, socket), |
| ConnectionHandle::new(requests_tx), |
| messages_rx, |
| ) |
| } |