#tcp-udp #packet #udp-packet #packet-parser #ip #tcp #udp

packet_crafter

简单的创建/解析/操作数据包的工具。尽可能计算校验和和长度字段。

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 网络编程

MIT 许可证

52KB
1K SLoC

crates.io

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