原始套接字BPF过滤失效:TCP 22端口过滤代码不生效
问题分析与解决方案
你的问题核心在于:tcpdump生成的BPF过滤器是针对包含链路层(如以太网)头部的数据包设计的,但你的原始套接字(SOCK_RAW)接收的数据包是从IP头部开始的,导致过滤器偏移量全部错位,无法匹配目标数据包。
具体问题点
- BPF过滤器偏移错误:tcpdump生成的BPF第一条指令就会读取以太网头部的
EtherType字段(偏移0xc),但你的原始套接字没有链路层头部,这部分检查直接失败,后续逻辑根本不会执行。 - 冗余的
IP_HDRINCL设置:该选项仅在发送自定义IP头部的数据包时需要,接收数据包时完全不需要,属于无效操作。
修正后的代码
替换原有BPF过滤器为适配IP头部起始的版本,并移除不必要的IP_HDRINCL设置:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <linux/filter.h> #include <linux/if_ether.h> #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) int main() { // BPF过滤器:匹配127.0.0.1且TCP端口为22的数据包(IP头部起始) struct sock_filter code[] = { // 检查IP协议是否为TCP(0x06) {0x30, 0, 0, 0x00000009}, {0x15, 0, 12, 0x00000006}, // 检查源IP是否为127.0.0.1 {0x20, 0, 0, 0x0000000c}, {0x15, 2, 0, 0x7f000001}, // 检查目标IP是否为127.0.0.1 {0x20, 0, 0, 0x00000010}, {0x15, 0, 8, 0x7f000001}, // 计算IP头部长度(IHL字段 *4) {0x30, 0, 0, 0x00000000}, {0x54, 0, 0, 0x0000000f}, {0x48, 0, 0, 0x00000004}, {0x07, 0, 0, 0x00000000}, // 检查TCP源端口是否为22(0x16) {0xb2, 0, 0, 0x00000000}, {0x15, 1, 0, 0x00000016}, // 检查TCP目标端口是否为22 {0xb2, 0, 0, 0x00000002}, {0x15, 0, 1, 0x00000016}, // 匹配成功,返回允许接收的长度 {0x06, 0, 0, 0x00040000}, // 匹配失败,丢弃数据包 {0x06, 0, 0, 0x00000000}, }; struct sock_fprog bpf = { .len = ARRAY_SIZE(code), .filter = code, }; int sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP); if (sock < 0) { perror("socket failed"); exit(-1); } int ret = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)); if (ret < 0) { perror("attach filter failed"); close(sock); exit(-1); } char buffer[4096]; while (1) { memset(buffer, 0, sizeof(buffer)); ssize_t recv_len = recv(sock, buffer, sizeof(buffer), 0); if (recv_len < 0) { perror("recv failed"); close(sock); exit(-1); } printf("捕获到符合条件的数据包,长度:%zd\n", recv_len); } close(sock); return 0; }
关键说明
- BPF过滤器逻辑:
- 先验证IP协议为TCP
- 检查源/目标IP是否为127.0.0.1
- 根据IP头部的IHL字段计算头部长度,定位到TCP头部起始位置
- 检查TCP的源/目标端口是否为22
- 运行要求:必须以root权限运行(和tcpdump一样),因为原始套接字需要管理员权限。
内容的提问来源于stack exchange,提问作者AFloiris
相关产品推荐
相关产品推荐

