1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
// 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_audit::{
//!     AuditMessage,
//!     NetlinkMessage,
//!     NetlinkPayload,
//!     StatusMessage,
//!     NLM_F_ACK,
//!     NLM_F_REQUEST,
//! };
//! 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::{
//!     LinkMessage,
//!     NetlinkHeader,
//!     NetlinkMessage,
//!     RtnlMessage,
//!     NLM_F_DUMP,
//!     NLM_F_REQUEST,
//! };
//!
//! 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 msg = NetlinkMessage {
//!         header: NetlinkHeader {
//!             sequence_number: 1,
//!             flags: NLM_F_DUMP | NLM_F_REQUEST,
//!             ..Default::default()
//!         },
//!         payload: RtnlMessage::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 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,
    ))
}