如何通过原始套接字修改 IP 数据包头

背景:我们的设备上有个链路探测的功能,会定时请求公网的某个 IP 地址,以探测网络是不是连通的。具体的做法是会使用 icmp 或 dns 探测远端服务器,看请求能否正常响应,如果有响应,则认为链路正常,否则则认为不正常,需要采取对应的措施。但是问题的现象是每隔一段时间后,探测包就收不到回复了,导致我们认为线路异常。而实际上网络还是通的,使用系统自带的 ping 和 nslookup 工具也是没问题的。

最后抓包分析,怀疑是 IP 数据包中的 identify 字段为 0 导致的,因为不回复的都是为 0 的 id:

因此,我们就打算先把这 id 改掉试试。本身的实现上,我们使用的是原始套接字来构造 icmp 和 dns 请求,没办法控制 ip.id 。要想修改 ip.id,必须让内核放弃自动填充 ip 头的操作。要想做到这一点,需要用到 socket 选项中的 IP_HDRINCL 选项,它的作用就是告诉内核不要填充头部:

注意事项:

  1. IP 报文中,如果校验和设置为 0,内核会帮我们自动填充。但在 ICMP 报文中,内核不会自动填充。
  2. 如果不填写源地址,内核也会自动帮我们填充。

以下是一个自己修改的 PING 包示例代码:

 

发表评论