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

原始Socket缓冲区中IP与UDP头访问异常:指针偏移问题排查

问题:AF_PACKET原始Socket解析UDP头时指针偏移错误

我通过AF_PACKET类型的原始Socket接收数据包,接收代码如下:

struct msghdr msg;
struct iovec iov;
unsigned char buf[BUFSIZE] = { '\0', };
ssize n;
int fd;
...

fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));

...

msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_flags = 0;

iov.iov_base = buf;
iov.iov_len = sizeof(buf);

...   
n = recvmsg(sockfd, &msg, 0);
...
parse_pkt(buf, BUFSIZE);

数据包接收正常,但解析时遇到问题:

static int parse_packet(unsigned char *pkt, int len)
{
    struct iphdr *ip = (struct iphdr *)(pkt + 14);
    struct udphdr *udp;

    /* ip指针有效,可访问IP头字段,未被修改 */
    ...

    udp = (struct udphdr *)(ip + (ip->ihl << 2));
    /* 预期udp指针指向IP头之后的位置,但实际并非如此 */
    ...

}

上述代码中udp指针并未指向预期位置。数据包包含完整的无VLAN以太网头,ip指针已跳过以太网头,理论上ip + (ip->ihl << 2)应跳过IP头,但实际无效。而以下写法却能正常工作:

struct iphdr *ip = (struct iphdr *)(pkt + 14);
...
udp = (struct udphdr *)(pkt + 14 + (ip->ihl << 2));

请问这是什么原因?我哪里出错了?


解答

核心问题是混淆了C语言中结构体指针与字节指针的算术运算规则:

  • 当你使用struct iphdr*类型的指针ip做加法时,ip + N的计算逻辑是:(unsigned char*)ip + N * sizeof(struct iphdr),也就是偏移N个struct iphdr结构体的大小,而不是偏移N个字节。
  • 你的代码中ip->ihl << 2得到的是IP头的实际字节长度(因为ihl是32位字的个数,乘以4就是字节数),但用ip + 这个值时,相当于跳过了(ihl*4)个IP头的大小,这显然远远超过了实际需要偏移的字节数,导致udp指针指向错误的位置。

正确的做法是先将结构体指针转换为字节级指针(比如unsigned char*),再进行字节偏移:

udp = (struct udphdr *)((unsigned char *)ip + (ip->ihl << 2));

这样转换后,加法运算就会按字节数来偏移,加上IP头的实际字节长度后,就能准确指向UDP头的起始位置。

你后来的写法pkt + 14 + (ip->ihl << 2)能正常工作,是因为pkt本身就是unsigned char*类型,所有偏移都是按字节计算的,相当于直接从原始数据包起始位置跳过以太网头(14字节)和IP头(ihl*4字节),自然能定位到UDP头。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 18:20:56