libpnetlibpnet 使用 Rust 提供跨平台的低级网络 API。它包含四个关键组件:
packet 模块,支持数据包的安全构造和操作;
pnet_macros 库,为 packet 模块提供基础设施;
transport 模块,支持传输层协议的实现;
datalink 模块,支持直接发送和接收数据链路层数据包。
libpnet?使用低级网络编程的原因有很多,使用 Rust 进行低级网络编程的原因更多。这里列出一些。
着手开发新传输层协议通常有两种方式:
用脚本语言,比如 Python,编写;
用 C 编写。
前者非常适合尝试新想法和快速原型设计,但不太适合实际应用。虽然通常可以从这些实现中获得合理的性能,但通常比 C 实现慢得多,不适合任何“繁重的工作”。
另一个选择是用 C 编写 - 这将带来很好的性能,但随之而来的是许多其它问题:
缺乏内存安全性 - 这是基于 C 的网络栈中安全漏洞和其它错误的重要原因。很容易忘记进行边界检查或在释放后使用指针。
缺乏线程安全性 - 必须确保使用正确的锁,以及正确使用。
缺乏高层次抽象 - 脚本语言(比如 Python)的部分吸引力是更高层次的抽象能力,这使得 API 更简单,编程更容易。
使用 libpnet 和 Rust,可以兼具两者的优点。具备高级抽象、内存和线程安全性,以及 C 的性能。
许多网络工具(比如 ping 和 traceroute)依赖于能够操作网络层和传输层头部,但标准网络栈(比如 std::io::net 提供的)不支持。
直接在数据链路层操作数据包有诸多好处,查看数据包在网络上传输时的原始形式。这样做有很多用途,包括网络诊断、数据包捕获和流量整形。
最新构建的文档在 https://docs.rs/pnet/。
为使用 libpnet,向 Cargo.toml 添加如下内容:
[dependencies.pnet]version = "0.34.0"libpnet 应该与最新稳定版本的 Rust 兼容。
在运行测试套件时,一些网络测试可能失败 - 解决该问题的最简单方法是以 root 或管理员用户的身份运行 cargo test。通常可以避免这种情况,但是需要引入更多的操作。
在 Windows 上构建有三个要求:
必须使用使用 MSVC 工具链的 Rust 版本。
必须安装 WinPcap 或 npcap(测试使用的版本为 WinPcap 4.1.3)(如果使用 npcap,确保使用"在 WinPcap API 兼容模式中,安装 Npcap"安装)
必须将来自 WinPcap Developers pack 的 Packet.lib 放在该仓库的根下名为 lib 的目录中。另外,还可以使用 %LIB% 或 $Env:LIB 环境变量中列出的任何位置。对于 64 位工具链,该文件位于 WpdPack/Lib/x64/Packet.lib;对于 32 位工具链,位于 WpdPack/Lib/Packet.lib。
https://github.com/libpnet/libpnet/tree/main/examples
x
use pnet::packet::ip::IpNextHeaderProtocols;use pnet::packet::ipv4::{checksum, Ipv4Flags, Ipv4Packet, MutableIpv4Packet};use pnet::packet::tcp::{ipv4_checksum, MutableTcpPacket, TcpFlags};use pnet::transport::transport_channel;use pnet::transport::TransportChannelType::Layer3;use std::io;use std::net::{IpAddr, Ipv4Addr, SocketAddrV4};
pub fn send_reset( source: SocketAddrV4, destination: SocketAddrV4, sequence_number: u32, acknowledgement_number: u32,) -> io::Result<usize> { let protocol = Layer3(IpNextHeaderProtocols::Tcp); let (mut tx, mut _rx) = match transport_channel(4096, protocol) { Ok((tx, rx)) => (tx, rx), Err(e) => { return Err(e); } };
// IP Header 中不包含选项 const IPV4_HEADER_LEN: usize = 20; // TCP Header 中不包含选项,比如 TSVal 等 const TCP_HEADER_LEN: usize = 20; const TOTAL_LENGTH: u16 = (IPV4_HEADER_LEN + TCP_HEADER_LEN) as _;
let mut packet = [0u8; IPV4_HEADER_LEN + TCP_HEADER_LEN]; { let mut ip_header = MutableIpv4Packet::new(&mut packet[..]).unwrap(); ip_header.set_version(4); ip_header.set_header_length(5); ip_header.set_total_length(TOTAL_LENGTH); ip_header.set_identification(257u16.to_be()); ip_header.set_flags(Ipv4Flags::DontFragment); ip_header.set_fragment_offset(0); ip_header.set_ttl(64); ip_header.set_next_level_protocol(IpNextHeaderProtocols::Tcp); ip_header.set_source(source.ip().to_owned()); ip_header.set_destination(destination.ip().to_owned()); let imm_header = checksum(&ip_header.to_immutable()); ip_header.set_checksum(imm_header); } { let mut tcp_header = MutableTcpPacket::new(&mut packet[IPV4_HEADER_LEN..]).unwrap(); tcp_header.set_source(source.port()); tcp_header.set_destination(destination.port()); tcp_header.set_sequence(sequence_number); tcp_header.set_acknowledgement(acknowledgement_number); tcp_header.set_data_offset(5); // 没有任何选项,因此 TCP Header 长度为 5 * 4 字节 tcp_header.set_flags(TcpFlags::RST); tcp_header.set_window(29200); let checksum = ipv4_checksum(&tcp_header.to_immutable(), source.ip(), destination.ip()); tcp_header.set_checksum(checksum); }
match tx.send_to( Ipv4Packet::new(&packet[..]).unwrap(), IpAddr::V4(destination.ip().to_owned()), ) { Ok(n) => Ok(n), Err(e) => Err(e), }}
fn main() { let n = send_reset( SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 12345), SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 45678), 783786044, 2198547145, ) .unwrap(); println!("send ok[{:?}]", n);}