2 个版本
0.1.1 | 2021 年 5 月 20 日 |
---|---|
0.1.0 | 2021 年 5 月 18 日 |
#89 在 #位域
每月下载量 78 次
在 7 个 crate 中使用(通过 libpacket)
87KB
2K SLoC
libpacket
用于各种协议的包解析器。
从未维护的 libpnet 中提取。
lib.rs
:
The pnet_macros crate provides the #[packet]
macro and compiler plugin, which is used to specify the format of on-the-wire packets, and automatically generate zero-copy accessors and mutators for the fields. It is used as follows
/// Import the `Packet` custom derive attribute
use libpacket_derive::Packet;
/// This module contains a list of type aliases which may be used
use libpacket_core::types::{u4, u12be};
/// Packets are specified in the same way as normal Rust structs, but with a `#[packet]`
/// attribute.
#[derive(Packet)]
pub struct Example {
// This is a simple field which contains a 4-bit, unsigned integer.
// Note that `u4` is simply an alias for `u8` - the name is a hint
// to the compiler plugin, it is NOT a usable 4 bit type!
simple_field1: u4,
// This specifies that `simple_field2` should be a 12-bit field,
// with bits stored in big endian
simple_field2: u12be,
// All packets must specify a `#[payload]`, which should be a
// `Vec<u8>`. This represents the packet's payload, for example in
// an IPv4 packet, the payload could be a UDP packet, or in a UDP
// packet the payload could be the application data. All the
// remaining space in the packet is considered to be the payload
// (this doesn't have to be the case, see the documentation for
// `#[payload]` below.
#[payload]
payload: Vec<u8>
}
A number of things will then be generated. You can see this in action in the documentation and source of each of the packet types in the pnet::packet
module. Things generated include (assuming the Example
struct from above)
- An
ExamplePacket<'p>
structure, which is used for receiving packets on the network. This structure contains- A method,
pub fn new<'p>(packet: &'p [u8]) -> ExamplePacket<'p>
, used for the construction of anExamplePacket
, given a buffer to store it. The buffer should be long enough to contain all the fields in the packet. - 一种方法,
pub fn to_immutable<'p>(&'p self) -> ExamplePacket<'p>
,它只是一个恒等函数。它的存在是为了与MutableExamplePacket
保持一致。 - 一些访问器方法,形式为
pub get_{field_name}(&self) -> {field_type}
,用于检索在线值的宿主表示。
- A method,
- 一个
MutableExamplePacket<'p>
结构体,在网络发送数据包时使用。该结构体包含- 一种方法,
pub fn new<'p>(packet: &'p mut [u8]) -> MutableExamplePacket<'p>
,用于根据存储它的缓冲区构建一个MutableExamplePacket
。该缓冲区应足够长,可以包含数据包中的所有字段。 - 一种方法,
pub fn to_immutable<'p>(&'p self) -> ExamplePacket<'p>
,它将MutableExamplePacket
转换为ExamplePacket
- 一种方法,
pub fn populate(&mut self, packet: Example)
,给定一个Example
结构体,将使用Example
结构体中的值填充MutableExamplePacket
。 - 一些访问器方法,形式为
pub get_{field_name}(&self) -> {field_type}
,用于检索在线值的宿主表示。 - 存在多种修改器方法,其形式为
pub set_{字段名}(&mut self, val: {字段类型})
,该方法将接受一个主机值,将其转换为所需的在线格式,并将其存储在支持MutableExamplePacket
的缓冲区中。
- 一种方法,
- 为每个
MutableExamplePacket
和ExamplePacket
结构实现了一系列特性和方法。这些包括pnet::packet::Packet
(ExamplePacket
和MutableExamplePacket
)pnet::packet::MutablePacket
(仅MutableExamplePacket
)std::fmt::Debug
(ExamplePacket
和MutableExamplePacket
)pnet::packet::FromPacket
(ExamplePacket
和MutableExamplePacket
)pnet::packet::PacketSize
(ExamplePacket
和MutableExamplePacket
)
- 一个
ExampleIterator
结构,它实现了std::iter::Iterator
,允许遍历另一个数据包中包含的ExamplePacket
向量。内部使用。
属性
字段可能具有一些属性,这些包括
-
#[length_fn = "function_name"]
此属性用于启用可变长度字段。要指定可变长度字段,它应该具有类型
Vec<T>
。它必须具有#[length_fn]
(或 #[length])属性,该属性指定用于计算字段长度的函数名称。长度函数的签名应为fn {function_name}<'a>(example_packet: &ExamplePacket<'a>) -> usize
,用您结构中适当命名的数据包类型替换&ExamplePacket<'a>
。您可以访问计算字段长度所需的任何字段。返回值应该是字段使用的字节数。向量中包含的类型可以是
pnet_macros::types
中指定的基本类型之一,或者是一个标记了 #[derive(Packet)] 的结构,例如Vec<Example>
。 -
#[length = "算术表达式"]
此属性用于启用可变长度字段。要指定可变长度字段,它应该具有
Vec<T>
类型。它必须具有#[length]
(或 #[length_fn])属性,该属性指定一个算术表达式来计算字段的长度。表达式中只包含字段名、常量、整数、基本算术表达式(+ - * / %)和括号。例如:#[length = "field_name + CONSTANT - 4]
。向量中包含的类型可以是
pnet_macros::types
中指定的基本类型之一,或者是一个标记了 #[derive(Packet)] 的结构,例如Vec<Example>
。 -
#[payload]
此属性指定与数据包关联的有效负载。这应指定与数据包关联的数据。它可以在两个地方使用
- 数据包中的最后一个字段,在这种情况下,它假定使用包含数据包的缓冲区的剩余长度
- 数据包中的另一个位置,在这种情况下,必须还指定
#[length_fn]
属性以给出有效负载的长度。如果数据包没有有效负载,则必须仍然指定此属性,但可以提供一个返回零的#[length_fn]
属性。
-
#[construct_with(, ...)]
不幸的是,编译器插件当前在装饰阶段(生成所有上述内容的阶段)无法访问类型信息,因此需要此属性。对于所有既不是原始类型也不是原始类型向量的字段,必须使用此属性。
- 字段类型必须有一个
new
方法,该方法接受一个或多个原始类型的参数。 - 字段必须带有
#[construct_with(...)]
属性,指定与new
方法相同的类型列表。 - 必须为字段类型实现
pnet::packet::ToPrimitiveValues
特性,该特性必须返回一个元组,包含在#[construct_with(...)]
属性和new
方法中指定的参数所指定的原始类型。
- 字段类型必须有一个
依赖项
~3–4.5MB
~88K SLoC