You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

IP报头中数据报长度字段不一致问题排查

为什么Go原始套接字捕获的IP报头总长度字段与Wireshark不一致?

问题描述

我尝试从IP报头开始解析ICMP报文,使用的Go代码如下:

func main() {
    fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_ICMP)
    if err != nil {
        fmt.Println(err)
        return
    }

    for {
        buf := make([]byte, 1024)
        numRead, err := syscall.Read(fd, buf)
        if err != nil {
            fmt.Println(err)
        }

        fmt.Println(hex.EncodeToString(buf[:numRead]))

    }
}

操作步骤:

  • 运行该应用;
  • 使用Wireshark监听ICMP报文,过滤规则为ip.src==192.168.1.109||ip.dst==192.168.1.109;
  • ping google.com以捕获回显应答。

预期Wireshark与应用捕获的报文一致,但实际两者仅IP报头前20字节中的**数据报长度字段(第3-4字节)**不同:Go捕获的是4000,Wireshark捕获的是0054,请求解答该差异的原因。


可能的原因分析

1. 字段位置混淆(最可能)

IP报头的**总长度字段(Total Length)位于偏移2-3字节(从0开始计数,对应你说的第3-4字节),而标识字段(Identification)**位于偏移4-5字节。如果你误将标识字段当成了总长度字段,就会出现数值差异——标识字段通常是随机生成的,而总长度字段是固定的(对应ping报文的84字节,即0054)。

IP报头前20字节的关键字段位置参考:

偏移(字节)字段
0-1版本 + 报头长度
2-3总长度(Total Length)
4-5标识(Identification)
......

2. 字节序解读错误

IP报头的所有数值字段均采用网络字节序(大端),Wireshark会自动将其解析为十进制值展示,但你直接打印原始十六进制字节时,可能误将字节顺序按主机字节序(小端)解读。不过你描述的4000与0054数值差距过大,这个可能性较低。

可以在代码中添加字段解析逻辑验证:

import "encoding/binary"

// 在syscall.Read之后添加:
if numRead >= 4 {
    totalLength := binary.BigEndian.Uint16(buf[2:4])
    fmt.Printf("解析后的总长度(十进制):%d,十六进制:0x%04x\n", totalLength, totalLength)
}

如果解析后的十进制值为84(与Wireshark一致),说明你只是误读了原始字节的顺序。

3. 内核对原始套接字的字段修改

在Linux系统中,当通过SOCK_RAW接收ICMP报文时,极少数情况下内核会修改IP报头的总长度字段(比如特殊的内核参数或套接字配置)。但这种情况非常罕见,且通常会伴随其他字段的变化,与你描述的“仅该字段不同”不符。

4. Windows系统的特性差异

如果你的代码运行在Windows系统下,Windows的原始套接字行为与Linux不同:内核可能会将IP报头的数值字段转换为主机字节序返回给用户空间,导致原始字节与Wireshark展示的网络字节序不一致。


内容的提问来源于stack exchange,提问作者Commander_Rex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 04:14:55