20 releases (10 breaking)

0.11.3 Jan 29, 2024
0.11.2 Jul 9, 2023
0.11.1 Jan 28, 2023
0.10.0 Jun 24, 2022
0.1.1 Jan 2, 2019

#303 in Network programming

Download history 28765/week @ 2023-12-23 46849/week @ 2023-12-30 63174/week @ 2024-01-06 70319/week @ 2024-01-13 68808/week @ 2024-01-20 71816/week @ 2024-01-27 71379/week @ 2024-02-03 70953/week @ 2024-02-10 80038/week @ 2024-02-17 78992/week @ 2024-02-24 76136/week @ 2024-03-02 80366/week @ 2024-03-09 85702/week @ 2024-03-16 81879/week @ 2024-03-23 96146/week @ 2024-03-30 75972/week @ 2024-04-06

353,076 downloads per month
Used in 274 crates (12 directly)

MIT license

44KB
843 lines

Rust async netlink protocol

The netlink-proto crate is an asynchronous implementation of the netlink protocol. It only depends on netlink-packet-core for the NetlinkMessage type and netlink-sys for the socket.


lib.rs:

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"
use futures::stream::StreamExt;
use netlink_packet_core::{NetlinkMessage, NetlinkPayload, NLM_F_ACK,
    NLM_F_REQUEST};
use netlink_packet_audit::{
    AuditMessage,
    StatusMessage,
};
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.

use futures::StreamExt;

use netlink_packet_route::{link::LinkMessage, RouteNetlinkMessage};
use netlink_packet_core::{
    NetlinkHeader,
    NetlinkMessage,
    NLM_F_REQUEST, NLM_F_DUMP
};

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 mut nl_hdr = NetlinkHeader::default();
    nl_hdr.flags = NLM_F_DUMP | NLM_F_REQUEST;

    let msg = NetlinkMessage::new(
        nl_hdr,
        RouteNetlinkMessage::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(())
}

Dependencies

~3–16MB
~184K SLoC