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

相关推荐
齐 飞19 分钟前
MongoDB笔记01-概念与安装
前端·数据库·笔记·后端·mongodb
LunarCod36 分钟前
WorkFlow源码剖析——Communicator之TCPServer(中)
后端·workflow·c/c++·网络框架·源码剖析·高性能高并发
码农派大星。1 小时前
Spring Boot 配置文件
java·spring boot·后端
杜杜的man2 小时前
【go从零单排】go中的结构体struct和method
开发语言·后端·golang
幼儿园老大*2 小时前
走进 Go 语言基础语法
开发语言·后端·学习·golang·go
llllinuuu2 小时前
Go语言结构体、方法与接口
开发语言·后端·golang
cookies_s_s2 小时前
Golang--协程和管道
开发语言·后端·golang
为什么这亚子2 小时前
九、Go语言快速入门之map
运维·开发语言·后端·算法·云原生·golang·云计算
想进大厂的小王2 小时前
项目架构介绍以及Spring cloud、redis、mq 等组件的基本认识
redis·分布式·后端·spring cloud·微服务·架构
阿尔帕兹2 小时前
构建 HTTP 服务端与 Docker 镜像:从开发到测试
网络协议·http·docker