Linux下recvfrom()工作机制及ICMP数据包拦截问题咨询
Linux原始套接字与recvfrom()机制问题解答
核心误解与问题根源
- 你对
recvfrom()和PF_PACKET原始套接字的工作逻辑存在错误认知:当你通过这种原始套接字抓包时,内核会将数据包复制一份交付给应用程序,而非将内核缓冲区中的原数据包移除。PC2的内核协议栈仍会正常处理这些数据包——包括转发PC1发往PC3的ICMP请求,以及接收PC3返回的ICMP回复,你的程序仅能获取数据包的副本,无法干预内核的处理流程。 - 代码中
memset(buf, 0x0, 1522)仅清空了应用层自身的缓冲区,完全不影响内核中的数据包队列或协议栈运行,这就是你仍能收到ICMP回复的直接原因。
实现中断ICMP交互的可行方案
若要中断PC1与PC3间的ICMP交互,必须从内核层面拦截数据包,仅靠抓包程序无法实现:
- 使用iptables/netfilter:直接在PC2上配置规则丢弃目标ICMP包,示例命令:
该规则会让内核直接丢弃eth1接口上转发的ICMP包,既不完成转发,也不会向应用层传递数据包副本。iptables -A FORWARD -i eth1 -o eth1 -p icmp -j DROP - 使用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
相关产品推荐
相关产品推荐

