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