如何借助tcpdump与libpcap获取关联请求中的设备RSSI值
获取关联请求帧的RSSI值:tcpdump与libpcap实现方案
Got it, let's work through how to capture RSSI values for association requests using tcpdump and libpcap—since your current hex-printing approach isn't practical for ongoing use.
一、先用tcpdump快速验证与抓取
首先,你需要确保网卡处于监控模式(因为要捕获802.11管理帧,比如关联请求)。先开启监控模式:
iw dev wlan0 set type monitor ifconfig wlan0mon up
然后用tcpdump捕获关联请求帧并输出RSSI:
tcpdump -i wlan0mon -e -s 256 -vv type mgt subtype assoc-req
-e:显示链路层信息(包括MAC地址和信号强度)-vv:开启详细输出,会包含RSSI相关字段type mgt subtype assoc-req:过滤仅抓取管理帧中的关联请求
输出示例里,你会看到类似这样的字段:
signal -52 dBm noise -92 dBm
这就是你要的RSSI值(这里是-52 dBm)。不同网卡/驱动的输出格式可能略有差异,但核心是找“signal”或“RSSI”相关的数值。
二、用libpcap集成到你的C程序中
既然你已经有C程序捕获MAC地址,我们可以扩展它来提取RSSI。关键是要正确解析radiotap头(802.11帧前的额外元数据,包含RSSI等信息),注意:不要用固定偏移(比如你之前的packet+58),因为radiotap头的长度是可变的,不同网卡的头长度可能不同。
核心步骤:
- 初始化libpcap并设置监控模式
- 设置过滤规则,仅捕获关联请求帧
- 解析radiotap头,提取RSSI
- 解析802.11帧头,提取设备与路由器的MAC地址
代码示例片段:
#include <pcap.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <net/ethernet.h> #include <linux/if_ether.h> #include <linux/ieee80211.h> #include <linux/ieee80211_radiotap.h> // 回调函数:处理捕获到的数据包 void packet_handler(u_char *user_data, const struct pcap_pkthdr *pkthdr, const u_char *packet) { struct ieee80211_radiotap_header *radiotap = (struct ieee80211_radiotap_header *)packet; int radiotap_len = le16_to_cpu(radiotap->it_len); // 转换为主机字节序的radiotap长度 // 跳过radiotap头,得到802.11帧起始位置 const u_char *frame = packet + radiotap_len; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)frame; // 验证是否是关联请求帧 if ((hdr->frame_control & IEEE80211_FCTL_TYPE) != IEEE80211_FTYPE_MGMT || (hdr->frame_control & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_ASSOC_REQ) { return; } // 提取RSSI:遍历radiotap字段(因为字段顺序不固定) int rssi = -1; u_char *ptr = (u_char *)(radiotap + 1); u_char *end = (u_char *)radiotap + radiotap_len; while (ptr < end) { uint8_t id = *ptr++; uint8_t len = *ptr++; if (id == IEEE80211_RADIOTAP_DB_ANTSIGNAL) { // 信号强度(dBm) if (len >= 1) { rssi = (int)*ptr; // 注意:这个值是无符号的,实际RSSI是负数,所以要转换 rssi = -rssi; } break; } ptr += len; } // 提取MAC地址:关联请求中,源MAC是设备MAC,BSSID是路由器MAC u_char *device_mac = hdr->addr2; u_char *router_mac = hdr->addr1; // 输出结果 printf("关联请求捕获:\n"); printf("设备MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", device_mac[0], device_mac[1], device_mac[2], device_mac[3], device_mac[4], device_mac[5]); printf("路由器MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", router_mac[0], router_mac[1], router_mac[2], router_mac[3], router_mac[4], router_mac[5]); if (rssi != -1) { printf("RSSI值: %d dBm\n", rssi); } else { printf("无法获取RSSI(网卡/驱动不支持)\n"); } printf("----------------------------------------\n"); } int main() { char errbuf[PCAP_ERRBUF_SIZE]; pcap_t *handle; struct bpf_program fp; char filter_exp[] = "type mgt subtype assoc-req"; bpf_u_int32 mask; bpf_u_int32 net; // 获取网卡信息(这里用wlan0mon,根据你的实际网卡名修改) if (pcap_lookupnet("wlan0mon", &net, &mask, errbuf) == -1) { fprintf(stderr, "无法获取网卡信息: %s\n", errbuf); net = 0; mask = 0; } // 打开网卡(监控模式) handle = pcap_open_live("wlan0mon", BUFSIZ, 1, 1000, errbuf); if (handle == NULL) { fprintf(stderr, "无法打开网卡: %s\n", errbuf); return 1; } // 编译过滤规则 if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) { fprintf(stderr, "无法编译过滤规则: %s\n", pcap_geterr(handle)); return 1; } // 设置过滤规则 if (pcap_setfilter(handle, &fp) == -1) { fprintf(stderr, "无法设置过滤规则: %s\n", pcap_geterr(handle)); return 1; } // 开始捕获数据包 printf("开始捕获关联请求帧...\n"); pcap_loop(handle, 0, packet_handler, NULL); // 关闭handle pcap_close(handle); return 0; }
注意事项:
- 编译时需要链接libpcap库:
gcc -o rssi_capture rssi_capture.c -lpcap - 确保你的网卡支持监控模式,并且驱动能够在radiotap头中提供RSSI数据(部分老旧网卡可能不支持)
- 不要依赖固定偏移提取MAC地址:因为radiotap头长度可变,之前的
packet+58仅在特定网卡下有效,用802.11帧头的addr1(BSSID/路由器MAC)和addr2(源设备MAC)才是可靠的方法
内容的提问来源于stack exchange,提问作者Conor
相关产品推荐
相关产品推荐

