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

相关推荐
JavaGuide4 分钟前
一条 SQL 语句在 MySQL 中是如何执行的?
java·数据库·后端·mysql
ABin-阿斌24 分钟前
SpringBoot 整合 Easy_Trans 实现翻译的具体介绍
java·spring boot·后端
终末圆38 分钟前
MyBatis XML映射文件编写【后端 18】
xml·java·开发语言·后端·算法·spring·mybatis
_.Switch44 分钟前
Python Web 架构设计与性能优化
开发语言·前端·数据库·后端·python·架构·log4j
2401_857600951 小时前
心理教育辅导系统的设计与Spring Boot实现
java·spring boot·后端
北飞的山羊1 小时前
【计算机网络】详解UDP套接字&网络字节序&IP地址&端口号
linux·服务器·网络·后端·计算机网络·udp·信息与通信
☼YJLH☾1 小时前
第十章,XML
xml·java·后端·intellij-idea
拜见老天師1 小时前
SpringBoot中对数据库连接配置信息进行加密处理
数据库·spring boot·后端
記億揺晃着的那天2 小时前
SpringCloud从零开始简单搭建 - JDK17
java·spring boot·后端·spring cloud·nacos
憨憨憨憨憨到不行的程序员2 小时前
Spring框架基础知识
java·后端·spring