在 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 参考

相关推荐
.生产的驴几秒前
SpringBoot 封装统一API返回格式对象 标准化开发 请求封装 统一格式处理
java·数据库·spring boot·后端·spring·eclipse·maven
景天科技苑9 分钟前
【Rust】Rust中的枚举与模式匹配,原理解析与应用实战
开发语言·后端·rust·match·enum·枚举与模式匹配·rust枚举与模式匹配
红尘散仙43 分钟前
七、WebGPU 基础入门——Texture 纹理
前端·rust·gpu
红尘散仙44 分钟前
八、WebGPU 基础入门——加载图像纹理
前端·rust·gpu
w4ngzhen1 小时前
关于Bevy中的原型Archetypes
rust·游戏开发
Gazer_S1 小时前
【HTTP/2:信息高速公路的革命】
网络·网络协议·http
lLinkl1 小时前
项目笔记2:post请求是什么,还有什么请求
服务器·网络协议·http
追逐时光者1 小时前
MongoDB从入门到实战之Docker快速安装MongoDB
后端·mongodb
李匠20241 小时前
C++ RPC以及cmake
网络·c++·网络协议·rpc
方圆想当图灵1 小时前
深入理解 AOP:使用 AspectJ 实现对 Maven 依赖中 Jar 包类的织入
后端·maven