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

Linux下recvfrom()工作机制及ICMP数据包拦截问题咨询

Linux原始套接字与recvfrom()机制问题解答

核心误解与问题根源

  • 你对recvfrom()和PF_PACKET原始套接字的工作逻辑存在错误认知:当你通过这种原始套接字抓包时,内核会将数据包复制一份交付给应用程序,而非将内核缓冲区中的原数据包移除。PC2的内核协议栈仍会正常处理这些数据包——包括转发PC1发往PC3的ICMP请求,以及接收PC3返回的ICMP回复,你的程序仅能获取数据包的副本,无法干预内核的处理流程。
  • 代码中memset(buf, 0x0, 1522)仅清空了应用层自身的缓冲区,完全不影响内核中的数据包队列或协议栈运行,这就是你仍能收到ICMP回复的直接原因。

实现中断ICMP交互的可行方案

若要中断PC1与PC3间的ICMP交互,必须从内核层面拦截数据包,仅靠抓包程序无法实现:

  1. 使用iptables/netfilter:直接在PC2上配置规则丢弃目标ICMP包,示例命令:
    iptables -A FORWARD -i eth1 -o eth1 -p icmp -j DROP
    
    该规则会让内核直接丢弃eth1接口上转发的ICMP包,既不完成转发,也不会向应用层传递数据包副本。
  2. 使用AF_INET类型原始套接字接管处理:若需通过代码实现,可创建AF_INET类型的原始套接字并设置IP_HDRINCL选项,手动处理IP头部。此时内核默认不会将ICMP包交给协议栈处理,若你不手动转发数据包,即可中断交互。但这种方式复杂度较高,远不如iptables简便。

代码中的问题修正

你的代码存在几处明显错误:

  • printf("%s : received bytes = %d\n", bytes);格式符不匹配:%s对应字符串类型,但传入的是整数bytes,应修改为:
    printf("%02d:%02d:%02d : received bytes = %d\n", info->tm_hour, info->tm_min, info->tm_sec, bytes);
    
  • exit;应为exit(1);,否则编译会报错。
  • 通过固定长度(98字节)判断ICMP包极不可靠,不同环境、ICMP包类型的长度可能存在差异。正确做法是解析以太网帧头部,取出网络层协议类型,再解析IP头部,通过IP头部的protocol字段(值为1时表示ICMP)判断是否为目标数据包。

实验拓扑

+-------+       +-------+       +-------+                
    |       |       |       |       |       |
    | PC 1  | ----- | PC 2  | ----- | PC 3  |
    |       |       |       |       |       |  
    +-------+       +-------+       +-------+

原始测试代码

int main(int argc, char *argv[])
{
   int32_t sock;
   int8_t buf[1522];
   int32_t ifindex;
   int32_t ret;
   int32_t bytes;
   struct sockaddr_ll sll;
   struct ifreq ifr;
   time_t rawtime;
   struct tm *info;

   time(&rawtime);
   info = localtime(&rawtime);

   sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
   if (sock == -1)
   {
      printf("socket open failed\n");
      return 1;
   }

   strncpy(ifr.ifr_name, "eth1", sizeof(ifr.ifr_name));
   ret = ioctl(sock, SIOCGIFINDEX, &ifr);
   if (ret < 0)
   {
      printf("failed to get index\n");
      return -1;
   }

   sll.sll_family    = AF_PACKET;
   sll.sll_ifindex   = ifr.ifr_ifindex;
   sll.sll_protocol  = htons(ETH_P_ALL);

   ret = bind(sock, (struct sockaddr *)&sll, sizeof(struct sockaddr_ll));
   if (ret < 0)
   {
      close(sock);
      printf("interface binding error\n");
      return 1;
   }

   while(1)
   {
      bytes = recvfrom(sock, buf, 1522, 0, NULL, NULL);
      if (bytes < 0)
      {
         printf("error in recvfrom\n");
         exit;
      }
      if (bytes == 98)
      {
         memset(buf, 0x0, 1522);
      }
      printf("%s : received bytes = %d\n", bytes);
   }

   close(sock);
   return 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 22:22:53