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

为何设置IP首部flags字段时1<<14无效而2<<6有效?

问题解答:IP首部Flags字段设置无效的原因

你遇到的问题核心是主机字节序与网络字节序的位位置差异,加上构造IP首部时漏掉了字节序转换步骤导致的,咱们一步步拆解:

1. 先搞懂IP首部frag_off字段的结构

IP首部里的frag_off是一个16位的字段,必须严格遵循网络字节序(大端序),它的结构是:

  • 高3位是标志位(Flags):
    • 第15位(最高位):保留位R,必须为0
    • 第14位:DF位(Don't Fragment,禁止分片)—— 这应该是你想设置的位
    • 第13位:MF位(More Fragments,还有后续分片)
  • 低13位:分片偏移,单位是8字节

2. 原代码为什么无效?

你写的iph->frag_off |= (1 << 14);有两个关键问题:

  • 首先,你直接对主机字节序的frag_off字段做位操作,但IP首部要求这个字段是网络字节序。
  • 假设你的主机是小端序(绝大多数x86/x86_64设备都是),主机字节序的1<<14(即十六进制0x4000)在内存中会以0x00 0x40的顺序存储(低字节在前,高字节在后)。
  • 当你通过raw socket发送这个IP首部时,系统会直接把内存里的字节发出去,也就是0x00 0x40——这对应网络字节序的0x0040。
  • 网络字节序的0x0040高3位是000,所以Wireshark看到flags全为0,完全不符合你的预期。
  • 另外,你注意到其他字段比如tot_len、id都用了htons()转换为网络字节序,但frag_off漏掉了这一步——这是最核心的错误!

3. 为什么2 <<6能“正常”工作?

这其实是个巧合:

  • 2<<6等于128,也就是主机字节序的0x80。在小端主机内存中,这个16位值会被存储为0x80 0x00。
  • 发送出去的字节就是0x80 0x00,对应网络字节序的0x8000。
  • 网络字节序的0x8000高3位是100——这里你错误地把保留位R设成了1(本来应该为0),但有些网络设备对这个保留位的检查不严格,所以你的报文还是能发送成功。但这绝对不是正确的设置方式,换个严格的网络环境可能就失败了。

4. 正确的做法

要设置DF位(禁止分片),你需要先在主机字节序中标记对应位,再用htons()转换为网络字节序:

// 正确设置DF位(网络字节序的第14位对应0x4000)
iph->frag_off |= htons(0x4000);

或者更清晰的写法:

#define IP_DF_FLAG 0x4000 // 禁止分片标志
iph->frag_off |= htons(IP_DF_FLAG);

如果要设置MF位(还有后续分片),则用htons(0x2000)(对应1<<13)。

记住:所有IP首部的16位字段都需要用htons()转换为网络字节序,32位字段用htonl(),这样才能保证在任何主机架构上都生成符合规范的IP首部。

内容的提问来源于stack exchange,提问作者S. Sharma

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 03:28:04