Ubuntu 22.04虚拟机中C程序无法捕获自定义MAC原始数据包问题
原始套接字无法捕获非本网段MAC地址数据包的解决方法
问题背景
VMware Workstation环境下,两台同网段Ubuntu 22.04虚拟机,其中一台发送带非本网段MAC地址的原始数据包,另一台可通过Wireshark捕获该数据包,但自行编写的C原始套接字程序无法捕获。已确认iptables无原始数据包拦截规则,libpcap可正常捕获但因架构限制无法替换,两台虚拟机可正常通信。
核心原因
网卡未开启混杂模式。默认状态下,网卡仅接收目标MAC为本机、广播或多播的数据包,非本网段MAC的数据包会被网卡硬件过滤丢弃。Wireshark启动时会自动将网卡切换为混杂模式,因此能捕获所有经过网卡的流量;而手动编写的原始套接字程序默认不开启该模式,无法获取这类数据包。
解决方案
- 开启网卡混杂模式
通过ioctl调用设置网卡的IFF_PROMISC标志位,让网卡接收所有经过的数据包。 - 修正main函数参数错误
原代码中main函数参数格式错误,需调整为标准格式。 - 处理非阻塞read的返回值
非阻塞模式下无数据可读时,read会返回-1并设置errno为EAGAIN,需添加判断避免无意义报错。
修正后的代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/ioctl.h> #include <net/if.h> #include <linux/if_packet.h> #include <net/ethernet.h> #include <arpa/inet.h> #include <linux/filter.h> #include <fcntl.h> #include <errno.h> int main(int argc, char** argv) { int s, stat, cc; unsigned char buf[ETH_FRAME_LEN]; struct sockaddr_ll saddr; struct ifreq ifr; char *interface = "ens33"; s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (s < 0) { perror("socket"); exit(EXIT_FAILURE); } // 获取网卡索引 strncpy(ifr.ifr_name, interface, IFNAMSIZ); if(ioctl(s, SIOGIFINDEX, &ifr)) { perror("ioctl get index"); close(s); exit(EXIT_FAILURE); } // 开启混杂模式 if(ioctl(s, SIOCGIFFLAGS, &ifr)) { perror("ioctl get flags"); close(s); exit(EXIT_FAILURE); } ifr.ifr_flags |= IFF_PROMISC; if(ioctl(s, SIOCSIFFLAGS, &ifr)) { perror("ioctl set promisc"); close(s); exit(EXIT_FAILURE); } saddr.sll_family = AF_PACKET; saddr.sll_ifindex = ifr.ifr_ifindex; saddr.sll_protocol = htons(ETH_P_ALL); if (bind(s, (struct sockaddr *)&saddr, sizeof(saddr))) { perror("bind"); exit(EXIT_FAILURE); } // 设置非阻塞模式 stat = fcntl (s, F_SETFL, O_NONBLOCK); if (stat < 0) perror ("fcntl non-block"); while(1) { cc = read (s, buf, sizeof(buf)); if(cc < 0) { if(errno != EAGAIN) { perror("read"); close(s); exit(EXIT_FAILURE); } // 无数据时短暂休眠,避免占用过高CPU usleep(1000); continue; } printf("Captured %d bytes\n", cc); // 处理数据包逻辑 } close(s); return 0; }
额外说明
- 开启混杂模式需要root权限,运行程序时需使用
sudo - 若不再需要混杂模式,程序退出前可恢复网卡原有标志位(移除
IFF_PROMISC),避免影响网卡默认行为
内容的提问来源于stack exchange,提问作者Antonio Garcia
相关产品推荐
相关产品推荐

