9 个版本
0.2.0 | 2020 年 7 月 2 日 |
---|---|
0.1.7 | 2020 年 4 月 21 日 |
0.1.6 | 2020 年 2 月 14 日 |
0.1.0 | 2019 年 1 月 24 日 |
#1857 in 网络编程
52KB
1K SLoC
packet_crafter
创建、解析和操作数据包。这个 crate 提供了使用直观的高级接口轻松处理数据包的工具。如果您希望请求任何功能或改进,请只需打开一个问题,同样地,当然可以报告任何错误。
请注意,此文档中的任何类型注解,例如 let data: Vec<u8> = packet.into_vec();
,仅添加以供参考,并不需要在您的代码中添加。
用法
创建新数据包
步骤 1
导入包的适当部分,并创建一个新的数据包结构以开始填充它。有两种创建新数据包的方法,方法 1 将设置内部缓冲区的容量为给定协议的适当长度,方法 2 将创建一个完全空的数据包,内部缓冲区容量设置为 0。对于大型数据包,推荐使用方法 1,因为它将更有效率,因为无需在添加数据时不断增加缓冲区的容量。
extern crate packet_crafter;
use packet_crafter::{
//import sub-modules
headers,
protocol_numbers,
ethertype_numbers,
//import data types
Packet,
Protocol
};
fn main() {
// method 1 (preferred)
let mut new_packet = Packet::new(
vec![
Protocol::ETH,
Protocol::IP,
Protocol::TCP
]
);
// method 2
let mut new_packet = Packet::new_empty();
步骤 2
创建标题并将它们添加到数据包中。这里我们将采用步骤 1 中的方法 1 的数据包结构,即以太网 > IP > TCP
new_packet.add_header(
headers::EthernetHeader::new(
[6,5,4,3,2,1], // source mac address
[1,2,3,4,5,6], // destination mac address
ethertype_numbers::ETHERTYPE_IPV4
)
);
new_packet.add_header(
headers::IpHeader::new(
[192, 168, 1, 128], // source IP
[192, 168, 1, 38], // destination IP
Protocol::TCP // next protocol
)
);
new_packet.add_header(
headers::TcpHeader::new(
3838, // source port
3838 // destination port
)
);
步骤 3
这一步可能并不总是必要的,例如在ICMP回显的情况下,因为只需要读取头部而不需要数据。然而,在许多情况下,您可能想要添加一些有效载荷数据。同样,有两种方法可以实现这一点,方法1将覆盖现有的有效载荷数据(如果这是一个新数据包,那么将没有数据,但如果这是一个从原始数据解析出来的数据包,那么可能有一些数据),方法2将附加到现有的有效载荷数据。注意方法2中没有出现 .collect()
,尽管代码仍然可以工作,但这并不是必需的。如果您查看源代码中的函数签名,您就会明白为什么不需要它。
// method 1
new_packet.set_payload("Hello, world!".bytes().collect());
// method 2
new_packet.extend_payload("Hello, world!".bytes());
步骤4
数据现在已经准备好了,我们只需要烘焙数据包。这是填充校验和字段和长度字段的地方,例如IP头部中的长度字段(有关为什么即使在操作系统可能会覆盖这些字段的情况下,我仍然决定计算这些字段的原因,请参见这个文档页面)
let data: Vec<u8> = new_packet.into_vec();
}
请始终记住,可以将 &Vec<u8>
传递给期望 &[u8]
的函数:)
解析数据包
解析数据包非常简单,只要数据包是以IP头部或以太网II头部开始
extern crate packet_crafter;
use packet_crafter::Packet;
fn main() {
let raw_data: &[u8] = your_function_to_read_a_packet_from_socket();
let parsed_packet: Result<Packet, packet_crafter::ParseError> = Packet::parse(raw_data);
}
注意:尚未实现IPv6数据包的解析
操作数据包
假设我们刚刚解析了在“创建新数据包”示例中创建的数据包,因此它是一个ETH > IP > TCP数据包,我们想要更新TCP字段的端口号以及目的IP地址
// imports elided
fn main() {
let raw_data: &[u8] = your_function_to_read_a_packet_from_socket();
let mut packet = Packet::parse(raw_data).unwrap();
let mut tcp_header = packet.get_tcp_header().unwrap();
tcp_header.set_dst_port(21);
let mut ip_header = packet.get_ip_header().unwrap();
ip_header.set_dst_ip([192, 168, 1, 84]);
packet.update_header(ip_header);
// packet is now good to go, so make it and then send it:
let data = packet.into_vec();
your_function_to_send_packet_down_socket(&data);
}
依赖项
~1.5MB
~35K SLoC