基于libpcap开发嗅探器时pcap_set_rfmon()调用始终失败如何解决
libpcap嗅探器
pcap_set_rfmon()调用失败解决方案 问题现象
- 基于libpcap开发底层网络嗅探器时,仅
pcap_set_rfmon()相关断言始终触发失败,其余功能运行正常 - 自查未发现代码编写错误,更换多款网卡、无线适配器后,所有设备均无法成功配置监听模式
- 原核心代码片段:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <pcap.h> #define JUMBO_FRAMES_MTU 9000 #define BIGGER_THAN_ALL_MTUS (64*1024) #define ERR(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0) int main(int argc, char **argv) { char errbuf[PCAP_ERRBUF_SIZE]; pcap_t *handle; if (argc < 2){ ERR("usage : ./prog itf-name\n"); } /* exiting if strlen of itf > 14 */ if (strlen(argv[1]) > IFNAMSIZ){ ERR("Interface name too long"); } strncpy(opt_args->device, argv[1], strlen(argv[1]) + 1); handle = pcap_create(opt_args->device, errbuf); if (handle == NULL){ (void)fprintf(stderr, "FATAL ERROR : couldn't create sock handle : %s\n", errbuf); goto fatal_error; } /* setting snaplen to 1500 */ assert(pcap_set_snaplen(handle, ETHERNET_MTU) == 0); assert(pcap_setnonblock(handle, -1, errbuf) != -1); /* if we can't put the device in monitor mode, so we display a warning but keep doing the capture */ /* I have a warning here */ if (pcap_can_set_rfmon(handle) != 1){ (void)fprintf(stderr, "WARNING : device can't be set up in monitor mode : %s\n", pcap_geterr(handle)); } else{ assert(pcap_set_rfmon(handle, 1) == 0); } /* we need now to launch the session capture */ if (pcap_activate(handle) < 0){ (void)fprintf(stderr, "FATAL ERROR : couldn't activate PCAP sock : %s\n", pcap_geterr(handle)); goto fatal_error; } /* pcap loop ... */ pcap_close(handle); return 0; fatal_error; pcap_close(handle); return 1; }
根因分析
问题由代码错误、调用时序错误、环境配置问题三类原因共同导致:
- 代码本身存在硬错误
- 引用未定义变量:代码中未定义
opt_args结构体,直接对opt_args->device执行拷贝属于野指针内存访问,会破坏进程内存空间,干扰后续libpcap句柄的配置逻辑 - 头文件缺失:使用了
IFNAMSIZ、ETHERNET_MTU、assert但未引入<net/if.h>、<net/ethernet.h>、<assert.h>头文件,宏值、函数声明异常会直接导致参数错误 - 接口参数非法:
pcap_setnonblock()第二个参数为布尔类型,仅接受0/1两个合法值,传入-1会直接调用失败 - 错误处理存在空指针风险:如果
pcap_create()返回NULL,goto到错误分支后会对空指针执行pcap_close(),直接触发段错误
- 引用未定义变量:代码中未定义
- libpcap接口调用时序错误
pcap_can_set_rfmon()必须在pcap_activate()执行成功后调用,才能返回准确的设备能力值。原代码在句柄未激活状态下调用该接口,返回值完全不可靠,后续pcap_set_rfmon()的执行分支判断本身就是错的。 - 环境与适配问题
- 权限不足:配置射频监听模式需要
CAP_NET_ADMIN权限,普通用户运行程序时,所有模式切换请求都会被内核直接拒绝 - 设备/驱动不兼容:有线网卡本身不支持无线专属的rfmon监听模式;部分消费级无线网卡的驱动不支持libpcap自动切换模式的接口,无法通过
pcap_set_rfmon()完成配置 - 版本兼容问题:1.9以前的旧版本libpcap在Linux 4.x+内核上存在rfmon配置逻辑的已知bug,无法正常下发模式切换请求
- 权限不足:配置射频监听模式需要
注意:不要在release版本中使用assert做核心逻辑校验,编译时添加
-DNDEBUG参数会直接移除所有assert代码,导致参数校验逻辑完全失效。
修复步骤
- 修复代码逻辑错误,调整接口调用时序
修正后的可运行核心代码参考:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <pcap.h> #include <net/if.h> #include <net/ethernet.h> #include <assert.h> #define ERR(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0) int main(int argc, char **argv) { char errbuf[PCAP_ERRBUF_SIZE]; pcap_t *handle; if (argc < 2){ ERR("usage : ./prog itf-name\n"); } if (strlen(argv[1]) >= IFNAMSIZ){ ERR("Interface name too long"); } // 移除不存在的opt_args逻辑,直接使用命令行传入的设备名 handle = pcap_create(argv[1], errbuf); if (handle == NULL){ fprintf(stderr, "FATAL ERROR : couldn't create sock handle : %s\n", errbuf); return 1; } // 所有set类配置必须在activate之前调用 int ret = pcap_set_snaplen(handle, ETHERNET_MTU); if (ret != 0) { fprintf(stderr, "set snaplen failed: %s\n", pcap_geterr(handle)); pcap_close(handle); return 1; } // 修正pcap_setnonblock参数,需要非阻塞传1,阻塞传0 ret = pcap_setnonblock(handle, 1, errbuf); if (ret != 0) { fprintf(stderr, "set nonblock failed: %s\n", errbuf); pcap_close(handle); return 1; } // 提前下发rfmon配置请求,激活时生效 pcap_set_rfmon(handle, 1); // 激活抓包句柄 ret = pcap_activate(handle); if (ret < 0){ fprintf(stderr, "FATAL ERROR : couldn't activate PCAP sock : %s\n", pcap_geterr(handle)); pcap_close(handle); return 1; } // 激活后再检查监听模式是否生效 if (pcap_can_set_rfmon(handle) != 1){ fprintf(stderr, "WARNING : device not running in monitor mode, will capture in managed mode : %s\n", pcap_geterr(handle)); } // 后续pcap_loop等抓包逻辑... pcap_close(handle); return 0; }
- 排查运行环境问题
- 必须使用root权限运行程序,例如执行
sudo ./sniffer wlan0 - 确认测试设备为支持监听模式的无线网卡,不要在有线网卡上尝试开启rfmon模式
- 如果网卡驱动不支持libpcap自动切模式,先手动通过系统命令将网卡切换为监听模式再启动程序:
# 关闭网卡 sudo ip link set wlan0 down # 切换为监听模式 sudo iw dev wlan0 set type monitor # 启动网卡 sudo ip link set wlan0 up
手动切换完成后,即使不在代码中调用pcap_set_rfmon(),抓包也会工作在监听模式下。
3. 版本兼容修复
如果以上步骤完成后仍无法正常配置,将libpcap升级到1.10及以上的稳定版本,修复旧版本对新内核的兼容问题。
内容的提问来源于stack exchange,提问作者Etienne Armangau
相关产品推荐
相关产品推荐

