12个版本 (6个重大更新)

0.7.0 2023年7月9日
0.5.0 2023年1月28日
0.4.2 2022年2月20日
0.4.1 2021年12月29日
0.1.0 2019年12月20日

#532Unix API

Download history 94584/week @ 2024-03-14 103119/week @ 2024-03-21 96767/week @ 2024-03-28 106286/week @ 2024-04-04 100246/week @ 2024-04-11 95663/week @ 2024-04-18 88023/week @ 2024-04-25 90374/week @ 2024-05-02 88879/week @ 2024-05-09 93419/week @ 2024-05-16 97897/week @ 2024-05-23 91755/week @ 2024-05-30 80476/week @ 2024-06-06 88307/week @ 2024-06-13 83187/week @ 2024-06-20 66999/week @ 2024-06-27

每月334,733次下载
用于 378 个crate(直接使用32个)

MIT许可证

55KB
787

Rust crate用于常见的netlink数据包解析

netlink-packet-core是所有其他netlink-packet-*crate的粘合剂。它提供了一个表示任何子协议的任何netlink消息的NetlinkMessage<T>类型。


lib.rs:

netlink-packet-core提供了一种通用的netlink消息NetlinkMessage<T>,它独立于子协议。这样的消息本身并不很有用,因为它们只是用来携带协议相关的消息。这就是T所代表的内容:TNetlinkMessage的协议相关消息。这可以是实现NetlinkSerializableNetlinkDeserializable的任何类型。

例如,netlink-packet-routecrate通过netlink_packet_route::RtnlMessage提供rtnetlink消息,而netlink-packet-audit通过netlink_packet_audit::AuditMessage提供审计消息。

单独来看,netlink-packet-core 库并不是非常有用。然而,它被用于 netlink-proto 中,为任何子协议提供了异步的 netlink 协议实现。因此,定义特定 netlink 子协议消息的库可以与 netlink-packet-core 集成,并免费获得异步实现。请参考下面的第二个示例,了解通过 NetlinkSerializableNetlinkDeserializable 特性进行这样的集成。

示例:使用 netlink-packet-route

此示例展示了如何序列化和反序列化 rtnetlink 子协议的 netlink 数据包。它需要 netlink-packet-route

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

// Create the netlink message, that contains the rtnetlink
// message
let mut packet = NetlinkMessage {
    header: NetlinkHeader {
        sequence_number: 1,
        flags: NLM_F_DUMP | NLM_F_REQUEST,
        ..Default::default()
    },
    payload: RtnlMessage::GetLink(LinkMessage::default()).into(),
};

// Before serializing the packet, it is important to call
// finalize() to ensure the header of the message is consistent
// with its payload. Otherwise, a panic may occur when calling
// serialize()
packet.finalize();

// Prepare a buffer to serialize the packet. Note that we never
// set explicitely `packet.header.length` above. This was done
// automatically when we called `finalize()`
let mut buf = vec![0; packet.header.length as usize];
// Serialize the packet
packet.serialize(&mut buf[..]);

// Deserialize the packet
let deserialized_packet =
    NetlinkMessage::<RtnlMessage>::deserialize(&buf).expect("Failed to deserialize message");

// Normally, the deserialized packet should be exactly the same
// than the serialized one.
assert_eq!(deserialized_packet, packet);

println!("{:?}", packet);

示例:为新的 netlink 子协议添加消息

假设我们有一个名为 "ping pong" 的 netlink 协议,它定义了两种类型的消息:"ping" 消息,其有效载荷可以是任意字节序列,以及 "pong" 消息,其有效载荷也是一个字节序列。该协议的工作原理如下:当一个端点收到一个 "ping" 消息时,它将用一个 "pong" 消息回应,其有效载荷是它所回应的 "ping" 的有效载荷。

"ping" 消息的类型为 18,"pong" 消息的类型为 "20"。下面是一个 "ping" 消息的示例,其有效载荷为 [0, 1, 2, 3]

0                8                16              24               32
+----------------+----------------+----------------+----------------+
|                 packet length (including header) = 16 + 4 = 20    |
+----------------+----------------+----------------+----------------+
|     message type = 18 (ping)    |              flags              |
+----------------+----------------+----------------+----------------+
|                           sequence number                         |
+----------------+----------------+----------------+----------------+
|                            port number                            |
+----------------+----------------+----------------+----------------+
|       0        |         1      |         2      |        3       |
+----------------+----------------+----------------+----------------+

相应的 "pong" 响应将是

0                8                16              24               32
+----------------+----------------+----------------+----------------+
|                 packet length (including header) = 16 + 4 = 20    |
+----------------+----------------+----------------+----------------+
|     message type = 20 (pong)    |              flags              |
+----------------+----------------+----------------+----------------+
|                           sequence number                         |
+----------------+----------------+----------------+----------------+
|                            port number                            |
+----------------+----------------+----------------+----------------+
|       0        |         1      |         2      |        3       |
+----------------+----------------+----------------+----------------+

以下是实现此类协议的消息以及将此实现与 netlink-packet-core 集成的示例

use netlink_packet_core::{
    NetlinkDeserializable, NetlinkHeader, NetlinkMessage, NetlinkPayload, NetlinkSerializable,
};
use std::error::Error;
use std::fmt;

// PingPongMessage represent the messages for the "ping-pong" netlink
// protocol. There are only two types of messages.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum PingPongMessage {
    Ping(Vec<u8>),
    Pong(Vec<u8>),
}

// The netlink header contains a "message type" field that identifies
// the message it carries. Some values are reserved, and we
// arbitrarily decided that "ping" type is 18 and "pong" type is 20.
pub const PING_MESSAGE: u16 = 18;
pub const PONG_MESSAGE: u16 = 20;

// A custom error type for when deserialization fails. This is
// required because `NetlinkDeserializable::Error` must implement
// `std::error::Error`, so a simple `String` won't cut it.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct DeserializeError(&'static str);

impl Error for DeserializeError {
    fn description(&self) -> &str {
        self.0
    }
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        None
    }
}

impl fmt::Display for DeserializeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

// NetlinkDeserializable implementation
impl NetlinkDeserializable for PingPongMessage {
    type Error = DeserializeError;

    fn deserialize(header: &NetlinkHeader, payload: &[u8]) -> Result<Self, Self::Error> {
        match header.message_type {
            PING_MESSAGE => Ok(PingPongMessage::Ping(payload.to_vec())),
            PONG_MESSAGE => Ok(PingPongMessage::Pong(payload.to_vec())),
            _ => Err(DeserializeError(
                "invalid ping-pong message: invalid message type",
            )),
        }
    }
}

// NetlinkSerializable implementation
impl NetlinkSerializable for PingPongMessage {
    fn message_type(&self) -> u16 {
        match self {
            PingPongMessage::Ping(_) => PING_MESSAGE,
            PingPongMessage::Pong(_) => PONG_MESSAGE,
        }
    }

    fn buffer_len(&self) -> usize {
        match self {
            PingPongMessage::Ping(vec) | PingPongMessage::Pong(vec) => vec.len(),
        }
    }

    fn serialize(&self, buffer: &mut [u8]) {
        match self {
            PingPongMessage::Ping(vec) | PingPongMessage::Pong(vec) => {
                buffer.copy_from_slice(&vec[..])
            }
        }
    }
}

// It can be convenient to be able to create a NetlinkMessage directly
// from a PingPongMessage. Since NetlinkMessage<T> already implements
// From<NetlinkPayload<T>>, we just need to implement
// From<NetlinkPayload<PingPongMessage>> for this to work.
impl From<PingPongMessage> for NetlinkPayload<PingPongMessage> {
    fn from(message: PingPongMessage) -> Self {
        NetlinkPayload::InnerMessage(message)
    }
}

fn main() {
    let ping_pong_message = PingPongMessage::Ping(vec![0, 1, 2, 3]);
    let mut packet = NetlinkMessage::from(ping_pong_message);

    // Before serializing the packet, it is very important to call
    // finalize() to ensure the header of the message is consistent
    // with its payload. Otherwise, a panic may occur when calling
    // `serialize()`
    packet.finalize();

    // Prepare a buffer to serialize the packet. Note that we never
    // set explicitely `packet.header.length` above. This was done
    // automatically when we called `finalize()`
    let mut buf = vec![0; packet.header.length as usize];
    // Serialize the packet
    packet.serialize(&mut buf[..]);

    // Deserialize the packet
    let deserialized_packet = NetlinkMessage::<PingPongMessage>::deserialize(&buf)
        .expect("Failed to deserialize message");

    // Normally, the deserialized packet should be exactly the same
    // than the serialized one.
    assert_eq!(deserialized_packet, packet);

    // This should print:
    // NetlinkMessage { header: NetlinkHeader { length: 20, message_type: 18, flags: 0, sequence_number: 0, port_number: 0 }, payload: InnerMessage(Ping([0, 1, 2, 3])) }
    println!("{:?}", packet);
}

依赖关系

~0.5–1MB
~23K SLoC