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}