netlink_proto/
lib.rs

1// SPDX-License-Identifier: MIT
2
3//! `netlink-proto` is an asynchronous implementation of the Netlink
4//! protocol.
5//!
6//! # Example: listening for audit events
7//!
8//! This example shows how to use `netlink-proto` with the `tokio`
9//! runtime to print audit events. It requires extra external
10//! dependencies:
11//!
12//! - `futures = "^0.3"`
13//! - `tokio = "^1.0"`
14//! - `netlink-packet-audit = "^0.1"`
15//!
16//! ```rust,no_run
17//! use futures::stream::StreamExt;
18//! use netlink_packet_audit::{
19//!     AuditMessage,
20//!     NetlinkMessage,
21//!     NetlinkPayload,
22//!     StatusMessage,
23//!     NLM_F_ACK,
24//!     NLM_F_REQUEST,
25//! };
26//! use std::process;
27//!
28//! use netlink_proto::{
29//!     new_connection,
30//!     sys::{protocols::NETLINK_AUDIT, SocketAddr},
31//! };
32//!
33//! const AUDIT_STATUS_ENABLED: u32 = 1;
34//! const AUDIT_STATUS_PID: u32 = 4;
35//!
36//! #[tokio::main]
37//! async fn main() -> Result<(), String> {
38//!     // Create a netlink socket. Here:
39//!     //
40//!     // - `conn` is a `Connection` that has the netlink socket. It's a
41//!     //   `Future` that keeps polling the socket and must be spawned an
42//!     //   the event loop.
43//!     //
44//!     // - `handle` is a `Handle` to the `Connection`. We use it to send
45//!     //   netlink messages and receive responses to these messages.
46//!     //
47//!     // - `messages` is a channel receiver through which we receive
48//!     //   messages that we have not solicited, ie that are not
49//!     //   response to a request we made. In this example, we'll receive
50//!     //   the audit event through that channel.
51//!     let (conn, mut handle, mut messages) = new_connection(NETLINK_AUDIT)
52//!         .map_err(|e| format!("Failed to create a new netlink connection: {}", e))?;
53//!
54//!     // Spawn the `Connection` so that it starts polling the netlink
55//!     // socket in the background.
56//!     tokio::spawn(conn);
57//!
58//!     // Use the `ConnectionHandle` to send a request to the kernel
59//!     // asking it to start multicasting audit event messages.
60//!     tokio::spawn(async move {
61//!         // Craft the packet to enable audit events
62//!         let mut status = StatusMessage::new();
63//!         status.enabled = 1;
64//!         status.pid = process::id();
65//!         status.mask = AUDIT_STATUS_ENABLED | AUDIT_STATUS_PID;
66//!         let payload = AuditMessage::SetStatus(status);
67//!         let mut nl_msg = NetlinkMessage::from(payload);
68//!         nl_msg.header.flags = NLM_F_REQUEST | NLM_F_ACK;
69//!
70//!         // We'll send unicast messages to the kernel.
71//!         let kernel_unicast: SocketAddr = SocketAddr::new(0, 0);
72//!         let mut response = match handle.request(nl_msg, kernel_unicast) {
73//!             Ok(response) => response,
74//!             Err(e) => {
75//!                 eprintln!("{}", e);
76//!                 return;
77//!             }
78//!         };
79//!
80//!         while let Some(message) = response.next().await {
81//!             if let NetlinkPayload::Error(err_message) = message.payload {
82//!                 eprintln!("Received an error message: {:?}", err_message);
83//!                 return;
84//!             }
85//!         }
86//!     });
87//!
88//!     // Finally, start receiving event through the `messages` channel.
89//!     println!("Starting to print audit events... press ^C to interrupt");
90//!     while let Some((message, _addr)) = messages.next().await {
91//!         if let NetlinkPayload::Error(err_message) = message.payload {
92//!             eprintln!("received an error message: {:?}", err_message);
93//!         } else {
94//!             println!("{:?}", message);
95//!         }
96//!     }
97//!
98//!     Ok(())
99//! }
100//! ```
101//!
102//! # Example: dumping all the machine's links
103//!
104//! This example shows how to use `netlink-proto` with the ROUTE
105//! protocol.
106//!
107//! Here we do not use `netlink_proto::new_connection()`, and instead
108//! create the socket manually and use call `send()` and `receive()`
109//! directly. In the previous example, the `NetlinkFramed` was wrapped
110//! in a `Connection` which was polled automatically by the runtime.
111//!
112//! ```rust,no_run
113//! use futures::StreamExt;
114//!
115//! use netlink_packet_route::{
116//!     LinkMessage,
117//!     NetlinkHeader,
118//!     NetlinkMessage,
119//!     RtnlMessage,
120//!     NLM_F_DUMP,
121//!     NLM_F_REQUEST,
122//! };
123//!
124//! use netlink_proto::{
125//!     new_connection,
126//!     sys::{protocols::NETLINK_ROUTE, SocketAddr},
127//! };
128//!
129//! #[tokio::main]
130//! async fn main() -> Result<(), String> {
131//!     // Create the netlink socket. Here, we won't use the channel that
132//!     // receives unsolicited messages.
133//!     let (conn, mut handle, _) = new_connection(NETLINK_ROUTE)
134//!         .map_err(|e| format!("Failed to create a new netlink connection: {}", e))?;
135//!
136//!     // Spawn the `Connection` in the background
137//!     tokio::spawn(conn);
138//!
139//!     // Create the netlink message that requests the links to be dumped
140//!     let msg = NetlinkMessage {
141//!         header: NetlinkHeader {
142//!             sequence_number: 1,
143//!             flags: NLM_F_DUMP | NLM_F_REQUEST,
144//!             ..Default::default()
145//!         },
146//!         payload: RtnlMessage::GetLink(LinkMessage::default()).into(),
147//!     };
148//!
149//!     // Send the request
150//!     let mut response = handle
151//!         .request(msg, SocketAddr::new(0, 0))
152//!         .map_err(|e| format!("Failed to send request: {}", e))?;
153//!
154//!     // Print all the messages received in response
155//!     loop {
156//!         if let Some(packet) = response.next().await {
157//!             println!("<<< {:?}", packet);
158//!         } else {
159//!             break;
160//!         }
161//!     }
162//!
163//!     Ok(())
164//! }
165//! ```
166#[macro_use]
167extern crate futures;
168#[macro_use]
169extern crate log;
170
171mod codecs;
172pub use crate::codecs::*;
173
174mod framed;
175pub use crate::framed::*;
176
177mod protocol;
178pub(crate) use self::protocol::{Protocol, Response};
179pub(crate) type Request<T> =
180    self::protocol::Request<T, UnboundedSender<crate::packet::NetlinkMessage<T>>>;
181
182mod connection;
183pub use crate::connection::*;
184
185mod errors;
186pub use crate::errors::*;
187
188mod handle;
189pub use crate::handle::*;
190
191use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
192use std::{fmt::Debug, io};
193
194pub use netlink_packet_core as packet;
195
196pub mod sys {
197    pub use netlink_sys::{protocols, AsyncSocket, AsyncSocketExt, SocketAddr};
198
199    #[cfg(feature = "tokio_socket")]
200    pub use netlink_sys::TokioSocket;
201
202    #[cfg(feature = "smol_socket")]
203    pub use netlink_sys::SmolSocket;
204}
205
206/// Create a new Netlink connection for the given Netlink protocol, and returns a handle to that
207/// connection as well as a stream of unsolicited messages received by that connection (unsolicited
208/// here means messages that are not a response to a request made by the `Connection`).
209/// `Connection<T>` wraps a Netlink socket and implements the Netlink protocol.
210///
211/// `protocol` must be one of the [`crate::sys::protocols`][protos] constants.
212///
213/// `T` is the type of netlink messages used for this protocol. For instance, if you're using the
214/// `NETLINK_AUDIT` protocol with the `netlink-packet-audit` crate, `T` will be
215/// `netlink_packet_audit::AuditMessage`. More generally, `T` is anything that can be serialized
216/// and deserialized into a Netlink message. See the `netlink_packet_core` documentation for
217/// details about the `NetlinkSerializable` and `NetlinkDeserializable` traits.
218///
219/// Most of the time, users will want to spawn the `Connection` on an async runtime, and use the
220/// handle to send messages.
221///
222/// [protos]: crate::sys::protocols
223#[cfg(feature = "tokio_socket")]
224#[allow(clippy::type_complexity)]
225pub fn new_connection<T>(
226    protocol: isize,
227) -> io::Result<(
228    Connection<T>,
229    ConnectionHandle<T>,
230    UnboundedReceiver<(packet::NetlinkMessage<T>, sys::SocketAddr)>,
231)>
232where
233    T: Debug + packet::NetlinkSerializable + packet::NetlinkDeserializable + Unpin,
234{
235    new_connection_with_codec(protocol)
236}
237
238/// Variant of [`new_connection`] that allows specifying a socket type to use for async handling
239#[allow(clippy::type_complexity)]
240pub fn new_connection_with_socket<T, S>(
241    protocol: isize,
242) -> io::Result<(
243    Connection<T, S>,
244    ConnectionHandle<T>,
245    UnboundedReceiver<(packet::NetlinkMessage<T>, sys::SocketAddr)>,
246)>
247where
248    T: Debug + packet::NetlinkSerializable + packet::NetlinkDeserializable + Unpin,
249    S: sys::AsyncSocket,
250{
251    new_connection_with_codec(protocol)
252}
253
254/// Variant of [`new_connection`] that allows specifying a socket type to use for async handling and a special codec
255#[allow(clippy::type_complexity)]
256pub fn new_connection_with_codec<T, S, C>(
257    protocol: isize,
258) -> io::Result<(
259    Connection<T, S, C>,
260    ConnectionHandle<T>,
261    UnboundedReceiver<(packet::NetlinkMessage<T>, sys::SocketAddr)>,
262)>
263where
264    T: Debug + packet::NetlinkSerializable + packet::NetlinkDeserializable + Unpin,
265    S: sys::AsyncSocket,
266    C: NetlinkMessageCodec,
267{
268    let (requests_tx, requests_rx) = unbounded::<Request<T>>();
269    let (messages_tx, messages_rx) = unbounded::<(packet::NetlinkMessage<T>, sys::SocketAddr)>();
270    Ok((
271        Connection::new(requests_rx, messages_tx, protocol)?,
272        ConnectionHandle::new(requests_tx),
273        messages_rx,
274    ))
275}