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