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}