在 Rust 中实现 TCP : 2.解析原始字节

解析原始字节

现在已经设置了虚拟网络接口并且它接收了数据位,实现 TCP 之旅的下一步是从接收到的数据字节中解析出数据包。默认情况下,除了从虚拟网络接口发送和接收的数据包之外,还会在数据包前面附加 4 个字节 的数据。 Tun/TAP documentation section 3.2 告诉我们收到的数据的结构。前 2 个字节是 flags ,可以提供有关收到的数据包的更多信息,例如,内核设置的"TUN_PKT_STRIP",用于向用户空间程序发出信号,表明数据包因缓冲区太小而被截断。接下来的两个字节是 proto 字段,它指定协议的 IP 版本。在前 4 个字节之后,其余数据是 原始协议。因此,我们将修改代码以解析标flags 和 proto字节。为此,可以使用 u16::from_be_bytes 方法读取数据包的前 2 个字节,并获取人类可读的flags 值。但要注意,网络顺序是大端字节序,因此我们使用 from big endian bytes 方法。将 循环体内容 替换为以下内容

rust 复制代码
loop {  
    let nbytes = nic.recv(&mut buf[..])?;
    let flags = u16::from_be_bytes([buf[0], buf[1]]);     
    let proto = u16::from_be_bytes([buf[2], buf[3]]);     
    eprintln!("read {} bytes: {:x?}", nbytes - 4, &buf[4..nbytes]);       
    }     

现在,当运行程序时,可以注意到 flags 和proto 字段被打印出来。在例子中,flags打印出值 0 , proto 打印出"86dd"。为了理解 proto 字段的含义,可以查看这张将 ether 类型 映射 到协议的表,可以查到我们解析的 proto 字段的值对应于互联网协议版本 6 (IPv6)。

由于我们在此实现中将重点关注 ipv4,因此可以通过添加此行在代码中添加过滤条件,以忽略 ether 类型不是 ipv4 的任何数据包

rust 复制代码
if proto != 0x0800{
//This is Not IPV4 skip it.
continue;
}

现在已经解析了内核在以太网帧前添加的前 4 个字节,剩下的就是 TCP 数据,需要解析它。由于我们主要关心的是协议的实现,因此使用一个第三方 crate 来帮助解析 IP 数据包和 TCP 信息。尽管使用 第三方 crate 来进行解析,但了解正在发生的情况仍然很重要。我们本质上是在代码中 解码 IPV4 头 .

一旦解码 IP 数据包,将能够获得一些重要信息,例如目标地址、源地址和协议。我们将用etherparse 包解析 IP 数据包头 。要将其添加到项目中,请将以下内容添加到cargo.toml中

ini 复制代码
etherparse = "0.13.0"
rust 复制代码
// Attempt to parse an IPv4 header from the provided buffer slice.
match etherparse::Ipv4HeaderSlice::from_slice(&buf[4..nbytes]) {
    // If the parsing was successful, proceed with the parsed packet.
    Ok(iph) => {
        // Extract the source IP address from the parsed packet.
        let src = iph.source_addr();
                // Extract the destination IP address from the parsed packet.
        let dst = iph.destination_addr();
                // Extract the protocol number from the parsed packet.
        // For TCP, this number is typically 6 (0x06).
        let proto = iph.protocol();

        // Check if the protocol number is not TCP (0x06).
        if proto != 0x06 {
            // If the packet is not a TCP packet, skip further processing.
            continue;
        }

        // Attempt to parse the TCP header from the buffer slice.
        // Here, we adjust the starting slice based on the length of the IPv4 header.
        match etherparse::TcpHeaderSlice::from_slice(&buf[4 + p.slice().len()..]) {
            // If TCP header parsing was successful, proceed.
            Ok(tcph) => {
                // Print the details: Source IP, Destination IP, and the Destination Port.
                eprintln!("{} -> {}: TCP to port {}", src, dst, tcph.destination_port());
            }
            // Handle potential errors while parsing the TCP header.
            Err(e) => {
                eprintln!("An error occurred while parsing TCP packet: {:?}", e);
            }
        }
    }
    // Handle potential errors while parsing the IPv4 header.
    Err(e) => {
        eprintln!("An error occurred while parsing IP packet: {:?}", e);
    }
}

运行上面的代码并使用 TCP 客户端 ping 我们的应用程序时, nc 192.168.0.2 80 应该看到 TCP 数据包与目标地址、源地址和协议一起被接收。

References 参考

相关推荐
傻啦嘿哟16 分钟前
代理IP在后端开发中的应用与后端工程师的角色
网络·网络协议·tcp/ip
向阳12181 小时前
Dubbo HTTP接入之triple协议
网络协议·http·dubbo
2401_857610031 小时前
SpringBoot社团管理:安全与维护
spring boot·后端·安全
凌冰_2 小时前
IDEA2023 SpringBoot整合MyBatis(三)
spring boot·后端·mybatis
码农飞飞2 小时前
深入理解Rust的模式匹配
开发语言·后端·rust·模式匹配·解构·结构体和枚举
一个小坑货2 小时前
Rust 的简介
开发语言·后端·rust
qq_172805592 小时前
RUST学习教程-安装教程
开发语言·学习·rust·安装
monkey_meng2 小时前
【遵守孤儿规则的External trait pattern】
开发语言·后端·rust
Estar.Lee2 小时前
时间操作[计算时间差]免费API接口教程
android·网络·后端·网络协议·tcp/ip
新知图书3 小时前
Rust编程与项目实战-模块std::thread(之一)
开发语言·后端·rust