BCC 实战:用 eBPF SOCKET_FILTER 解析 HTTP 流量并提取 URL

发布时间:2026/9/20 23:57:23

BCC 实战:用 eBPF SOCKET_FILTER 解析 HTTP 流量并提取 URL eBPF可观测性性能剖析网络【免费下载链接】bccBCC - Tools for BPF-based Linux IO analysis, networking, monitoring, and more项目地址https://gitcode.com/gh_mirrors/bc/bcc点击查看免费下载导读本篇文章以 BCC 仓库中的 examples/networking/http_filter 示例为线索完整讲解如何用 eBPF 程序在内核态过滤 HTTP 报文、把命中流量送回用户态再由 Python 脚本重组并打印 GET/POST 请求行与 URL。读完本文你将掌握 BPF_PROG_TYPE_SOCKET_FILTER 的加载与挂载流程、eBPF 哈希表跨内核/用户态共享的用法以及简单版与完整版两种针对跨包 URL 的流量重组策略并可以直接把示例跑起来做实验。示例概览一个读 URL 的 eBPF 应用http_filter示例是一个典型的内核过滤 用户态解析双层应用eBPF 程序http_filter作为PROG_TYPE_SOCKET_FILTER类型加载进内核并绑定到网络接口默认eth0的原始套接字上它只把载荷以 HTTP、GET、POST以及 PUT/DELETE/HEAD开头的 IP/TCP 报文以及同属一个 HTTP 会话相同的源/目的 IP 与端口四元组的后续报文返回给用户态其余报文直接丢弃。Python 脚本从套接字文件描述符读回这些原始报文自行解析以太网 / IP / TCP 头部打印请求行的第一行即包含 URL 的那一行。仓库里提供了两个版本文件都位于 examples/networking/http_filter版本eBPF 程序Python 脚本能力simple简单版http-parse-simple.chttp-parse-simple.py只处理完整落在单个报文内的 URLcomplete完整版http-parse-complete.chttp-parse-complete.py能重组跨多个报文的 URL打印完整 URL对应地CMakeLists.txt 会把这两个.c文件、两个.py脚本连同README.md一起安装到share/bcc/examples/networking/http_filter目录说明它们属于 BCC 官方分发的示例程序集。如何运行这个示例环境前提系统需要安装 BCCBPF Compiler CollectionPython 侧通过from bcc import BPF调用 BCC 的 Python 绑定需要root权限脚本内部会创建原始套接字并挂载 BPF 程序二者都需要特权内核需支持 eBPF 与 socket filter 类型的程序主流发行版默认开启。运行命令在examples/networking/http_filter目录下任选其一执行$ sudo python http-parse-simple.py $ sudo python http-parse-complete.py脚本启动后会打印binding socket to eth0随后持续把抓到的 HTTP 请求行输出到标准输出典型输出如下GET /pipermail/iovisor-dev/ HTTP/1.1 HTTP/1.1 200 OK GET /favicon.ico HTTP/1.1 HTTP/1.1 404 Not Found GET /pipermail/iovisor-dev/2016-January/thread.html HTTP/1.1 HTTP/1.1 200 OK GET /pipermail/iovisor-dev/2016-January/000046.html HTTP/1.1 HTTP/1.1 200 OK注意程序绑定的是eth0 接口的原始套接字因此抓到的流量是流经该接口的明文 HTTP 报文对 HTTPSTLS 加密流量不适用这属于该示例的明确适用范围。命令行参数两个 Python 脚本都支持-i指定网卡默认eth0$ sudo python http-parse-complete.py -i wlan0 # 绑定到 wlan0 $ sudo python http-parse-complete.py -h # 打印帮助从 http-parse-complete.py 的参数解析逻辑可以看到无参数时默认接口为eth0-h显示帮助-i if_name切换接口其余用法会打印 USAGE 并退出。实现总览内核态过滤 用户态解析的两段式架构整个应用按 README 的描述分为两大部分这一分工也直接体现在文件组织上第一部分eBPF 过滤用 C 编写的 eBPF 程序.c文件过滤出载荷以 HTTP / GET / POST 等字符串开头的 IP/TCP 报文以及属于同一会话相同ip.src, ip.dst, port.src, port.dst四元组的后续报文程序以PROG_TYPE_SOCKET_FILTER加载并挂到绑定eth0的套接字上命中报文返回用户态return -1未命中直接丢弃return 0。第二部分用户态解析Python 脚本从套接字文件描述符读取过滤后的原始报文必要时按会话重组把 HTTP GET/POST 请求的第一行打印到标准输出。eBPF 程序运行在数据包路径上只做最轻量的字节匹配避免把每个包都拷到用户态用户态只处理已被过滤的少量报文二者配合实现了低开销的在线 HTTP 行提取。理解 socket filter 的返回语义两个 C 程序头部注释都明确写出了 socket filter 的返回约定见 http-parse-complete.creturn 0→ 丢弃该报文return -1→ 保留该报文并把它送回到用户态用户态可从socket_fd读到。这是理解整段 eBPF 代码的关键程序体内到处出现的goto KEEP;和goto DROP;最终都汇合到这两个返回值上。eBPF 过滤程序逐段拆解simple 版以 http-parse-simple.c 为例它的处理流程可以概括为以太网头 → IP 头 → TCP 头 → 载荷前 7 字节的逐层解析与匹配。1. 用 cursor 逐层推进报文指针程序使用 BCC 提供的cursor_advance宏沿报文向前移动解析指针。该宏定义于 src/cc/export/helpers.h#define cursor_advance(_cursor, _len) \ ({ void *_tmp _cursor; _cursor _len; _tmp; })它返回移动前的指针、同时把游标前移_len字节配合 BCC 在 src/cc/export/proto.h 中定义的紧凑网络头结构体ethernet_t、ip_t、tcp_t即可安全地按偏移解析报文。第一步取出以太网头并过滤 IP 报文EtherType 为0x0800struct ethernet_t *ethernet cursor_advance(cursor, sizeof(*ethernet)); if (!(ethernet-type 0x0800)) { goto DROP; }第二步取出 IP 头并过滤 TCP 报文IP 协议号0x06代码里#define IP_TCP 6struct ip_t *ip cursor_advance(cursor, sizeof(*ip)); if (ip-nextp ! IP_TCP) { goto DROP; }2. 计算可变长度的 IP / TCP 头部IPv4 头部长度字段ip-hlen单位是 4 字节和 TCP 数据偏移字段tcp-offset单位也是 4 字节都是可变的代码用左移 2 位等价于乘 4换算成字节数并做最小长度校验ip_header_length ip-hlen 2; // 例如 hlen5 → 20 字节 if (ip_header_length sizeof(*ip)) { goto DROP; } void *_ cursor_advance(cursor, (ip_header_length - sizeof(*ip))); // 跳过 IP 选项 struct tcp_t *tcp cursor_advance(cursor, sizeof(*tcp)); tcp_header_length tcp-offset 2; // 例如 offset5 → 20 字节对照 src/cc/export/proto.h 中的ip_t定义ver:4 / hlen:4共享第一字节、nextp是协议号和tcp_t定义offset:4位于第 12 字节的高 4 位可以确认上述位域语义与 RFC 791 / RFC 793 的头部布局一致。3. 计算载荷偏移与长度并做最小长度防护payload_offset ETH_HLEN ip_header_length tcp_header_length; payload_length ip-tlen - ip_header_length - tcp_header_length; if (payload_length 7) { goto DROP; }ETH_HLEN为 14 字节#define ETH_HLEN 14。payload_length 7的检查基于一个经验事实HTTP 请求的最小长度也大于 7 字节示例注释引用了相关讨论这个下界既能排除空载荷报文也防止后续循环越界访问。4. 读取载荷前 7 字节并匹配 HTTP 方法socket filter 程序不能直接解引用skb指向的内存必须通过load_byte(skb, offset)逐字节读取unsigned long p[7]; for (i 0; i 7; i) { p[i] load_byte(skb, payload_offset i); }随后依次与HTTP、GET、POST、PUT、DELETE、HEAD的 ASCII 码逐字节比较命中任意一个就goto KEEPreturn -1全部不中则goto DROPreturn 0。注意 simple 版只匹配首包一旦 URL 横跨多个 TCP 报文超出第一个报文的 URL 部分就无法打印——这正是 README 中描述的 simple 版局限。完整版用 eBPF 哈希表追踪 HTTP 会话完整版在 http-parse-complete.c 中引入了一张 eBPF 哈希表sessions用来记住哪些四元组属于 HTTP 会话从而让同会话的后续报文即使载荷不以 HTTP 方法开头也能被放行到用户态。1. 定义 Key / Leaf 与哈希表struct Key { u32 src_ip; u32 dst_ip; unsigned short src_port; unsigned short dst_port; }; struct Leaf { int timestamp; // 时间戳ns }; BPF_HASH(sessions, struct Key, struct Leaf, 1024);BPF_HASH是 BCC 提供的宏展开后即一个最多 1024 项、以会话四元组为键、以时间戳为值的哈希表。这张表同时被内核态写入/查找和用户态通过bpf.get_table(sessions)获取句柄后读写/清理共享。2. 命中 HTTP 方法时登记会话在完成与 simple 版相同的以太网/IP/TCP 过滤后代码先取出四元组key.dst_ip ip-dst; key.src_ip ip-src; key.dst_port tcp-dst_port; key.src_port tcp-src_port;若载荷前几字节命中 HTTP 方法则把该四元组插入表lookup_or_try_init然后goto KEEP放行HTTP_MATCH: sessions.lookup_or_try_init(key, zero); KEEP: return -1;3. 非 HTTP 首包查表决定去留若首字节不匹配任何 HTTP 方法程序并不直接丢弃而是查表判断当前包是否属于已登记的 HTTP 会话struct Leaf *lookup_leaf sessions.lookup(key); if (lookup_leaf) { goto KEEP; // 属于已知 HTTP 会话送回用户态 } goto DROP;这样一来即使一个很长的 URL 被 TCP 拆成多个报文后续报文也会因为四元组命中而全部被送回用户态由 Python 侧负责拼接。Python 用户态脚本从原始报文到 URL 打印加载与挂载流程两个版本一致以 http-parse-complete.py 为例核心步骤是bpf BPF(src_filehttp-parse-complete.c, debug0) function_http_filter bpf.load_func(http_filter, BPF.SOCKET_FILTER) BPF.attach_raw_socket(function_http_filter, interface) socket_fd function_http_filter.sock sock socket.fromfd(socket_fd, socket.PF_PACKET, socket.SOCK_RAW, socket.IPPROTO_IP) sock.setblocking(True)BPF(src_file...)让 BCC 在运行时把 C 源码交给 LLVM/Clang 编译成 eBPF 字节码并加载进内核load_func(http_filter, BPF.SOCKET_FILTER)指定程序类型为 socket filterattach_raw_socket在指定网卡上创建原始套接字并挂上该程序程序句柄上的.sock属性即该原始套接字的文件描述符socket.fromfd把 fd 包装成 Python socket 对象并设置为阻塞模式。随后主循环用os.read(socket_fd, ...)读回被 eBPF 放行的原始报文simple 版每次读 2048 字节complete 版读 4096 字节。用户态报文解析Python 侧需要重新解析以太网/IP/TCP 头部注释中贴出了 RFC 791 IP 头与 RFC 793 TCP 头的字段布局关键计算与 C 端一一对应# 总长度IP 头第 2、3 字节大端 total_length packet_bytearray[ETH_HLEN 2] 8 packet_bytearray[ETH_HLEN 3] # IP 头长度首字节低 4 位 × 4 ip_header_length (packet_bytearray[ETH_HLEN] 0x0F) 2 # TCP 头长度第 12 字节高 4 位 × 4SHR 4 后再 SHL 2等价于 SHR 2 tcp_header_length (packet_bytearray[ETH_HLEN ip_header_length 12] 0xF0) 2 payload_offset ETH_HLEN ip_header_length tcp_header_lengthsimple 版 http-parse-simple.py 随后从payload_offset起逐字节打印直到遇到\r\n0x0D 0x0A为止即打印请求行如果想把整个请求头都打出来注释提示可以把终止条件改成\r\n\r\n。完整版的跨包 URL 重组逻辑complete 版在用户态维护了一个local_dictionary键为会话四元组的十六进制形式值为已累积的载荷片段配合内核sessions表完成三段式处理见 http-parse-complete.py当前包是 HTTP 方法首包若载荷内已含\r\n说明 URL 完整落在本包直接printUntilCRLF(payload_string)并删除sessions中对应条目若不含\r\n说明 URL 被拆包把当前载荷存入local_dictionary等待后续报文。当前包不是方法首包、但四元组命中sessions若该四元组已在local_dictionary中就把当前载荷追加进去追加后若出现\r\n则打印完整 URL 并清理内核表与本地字典若尚未出现\r\n且累计长度超过MAX_URL_STRING_LEN8192 字节打印url too long并清理。命中sessions但不在本地字典中说明内核表里存的是无效条目直接删除。其中printUntilCRLF的定义是print(s.split(b\r\n)[0].decode())与 simple 版的逐字节打印殊途同归。会话表清理机制为避免sessions哈希表无限膨胀complete 版引入了三个常量http-parse-complete.pyCLEANUP_N_PACKETS 50 # 每收到 50 个包执行一次清理 MAX_URL_STRING_LEN 8192 # URL 最大长度上限通常 8K MAX_AGE_SECONDS 30 # 会话条目最大存活时间秒cleanup()遍历sessions表时间戳为 0 的条目补上当前时间首次见到年龄超过MAX_AGE_SECONDS30 秒的条目删除。清理动作在主循环里每累计CLEANUP_N_PACKETS50 个包触发一次。这套时间戳 定期清扫机制保证了长期运行时内核哈希表不会残留过期会话。两个版本的对比与适用场景维度simple 版complete 版eBPF 程序只做单包字节匹配http-parse-simple.c增加sessions哈希表追踪会话http-parse-complete.c用户态逻辑打印首包中的请求行维护local_dictionary跨包重组 URLURL 跨包场景只显示首包中的 URL 片段打印完整 URL资源占用无额外状态内核哈希表 本地字典 定时清理适用场景演示与教学、URL 较短生产级演示、长 URL / 大请求行场景小结与延伸阅读这个示例的价值在于它浓缩了 BCC 应用的四个核心模式PROG_TYPE_SOCKET_FILTER程序类型的加载与挂载、用cursor_advance与proto.h结构体做报文逐层解析、用BPF_HASH实现内核/用户态共享状态以及内核粗过滤 用户态精处理的分层设计。你可以在 BCC 仓库中找到更多同类型示例继续对照学习同目录下的其他网络示例examples/networking含tcp_mon_block、tunnel_monitor、vlan_filter等BCC 提供的BPF类与哈希表 API 的完整定义位于 src/cc/export/helpers.h 与 src/python想了解 socket filter 之外的更多程序类型如 kprobe、tracepoint、XDP可参考 docs/reference_guide.md若需对明文 HTTP 以外的流量做解析仓库中的 tools/trace.py 等工具提供了更通用的追踪能力。需要注意的是本示例针对的是明文 HTTP 流量且默认绑定eth0在 HTTPS 普及的今天更适合作为 eBPF socket filter 与流量重组的教学样例实际生产环境建议结合流量镜像、TLS 解密的旁路方案或升级到基于 TC/XDP 的过滤路径。赞分享eBPF可观测性性能剖析网络【免费下载链接】bccBCC - Tools for BPF-based Linux IO analysis, networking, monitoring, and more项目地址https://gitcode.com/gh_mirrors/bc/bcc点击查看免费下载相关推荐Crawl4AI 多 URL 并发爬取实战Dispatcher 调度、RateLimiter 限流与 URL 级配置完整指南Crawl4AI 多 URL 并发爬取实战Dispatcher 调度、RateLimiter 限流与 URL 级配置完整指南 当爬虫面对成百上千个 URL 时网页爬虫RAG人工智能AI 应用如何用 Crawl4AI 的 arun_many 批量爬取多个 URL 并流式消费结果如何用 Crawl4AI 的 arun_many 批量爬取多个 URL 并流式消费结果 当你有一批 URL 需要并行抓取并且希望每个页面一完成就立即拿到结果进网页爬虫RAG人工智能AI 应用CICFlowMeter流量特征提取实战指南CICFlowMeter流量特征提取实战指南 网络流量分析在网络安全、性能监控和业务洞察中扮演着关键角色。CICFlowMeter作为专业的网络流量特征提取工具上一篇LongCat-Flash-Thinking-ZigZag开发者手册FlashMLA与流式稀疏注意力接口使用教程下一篇SeaQwen2-0.5B硬件优化终极指南如何在NPU和CPU上实现最佳推理速度创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/20 23:57:23

Cline 实战:TaoToken 跑通 pytest 仓库的 test_skipping 回归用例

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/20 23:52:23

GraphCast 如何用图神经网络把全球中程天气预报做到 0.25° 精度

GraphCast 如何用图神经网络把全球中程天气预报做到 0.25 精度 【免费下载链接】weathernext 项目地址: https://gitcode.com/GitHub_Trending/gr/weathernext GraphCast 是 Google DeepMind 开源的全球中程天气预测模型,可在 0.25 分辨率上逐步推演温度、风…

2026/9/21 0:47:25

RAG技术优化:检索增强生成系统的关键策略与实践

1. RAG技术体系概述检索增强生成(Retrieval-Augmented Generation)作为当前NLP领域的前沿技术,通过将信息检索与文本生成相结合,有效解决了传统大语言模型的知识固化问题。我在实际项目中发现,标准的RAG流程通常包含四…

2026/9/21 0:47:25

Claude Code 桌面版接入 DeepSeek 与离线 Skills 安装全攻略

1. 为什么我要折腾这套组合:Claude Code 桌面版 DeepSeek 离线 Skills先说清楚这套东西到底是什么。Claude Code 是 Anthropic 推出的一个命令行 AI 编程助手,它跟普通聊天式 AI 最大的区别在于:它能直接读写你本地的项目文件、执行终端命令…

2026/9/21 0:47:25

QGIS等时圈分析实战:ORS插件Key申请与参数设置避坑指南

1. 等时圈分析与ORS插件到底在做什么等时圈分析这件事,说白了就是回答一个很朴素的问题:从某个点出发,在给定时间内,我到底能走到哪些地方。做城市规划的要拿它评估公共服务覆盖范围,做商业选址的要拿它算门店辐射半径…

2026/9/21 0:47:25

普通人用AI变现,第一个工具到底该怎么选?

我见过太多人,一听说AI能变现,第一反应就是到处问:现在哪个AI工具最强?哪个能不限次数白嫖?哪个生成的内容最像真人?然后就开始了一场漫长的工具测评之旅。各种官网、教程、对比帖收藏了上百篇,…

2026/9/21 0:47:25

JDK 17.0.8免安装版Windows配置指南:从下载到环境变量

简介:JDK 17.0.8 Windows免安装版为Java开发者提供开箱即用的开发环境,无需经过复杂安装流程,解压配置环境变量即可使用。作为长期支持(LTS)版本,它包含javac编译器、Java运行环境、javadoc文档生成器、jdb…

2026/9/21 0:42:24

Xilinx 7系列FPGA入门:从选型架构到时序约束实战要点

简介:面向FPGA初学者与嵌入式开发者的Xilinx 7系列FPGA入门介绍文档,以简明方式梳理系列整体定位与核心技术要点。内容涵盖Spartan-7、Artix-7、Kintex-7、Virtex-7四个子系列的适用场景、性能参数与功耗优势,详细对比单位功耗性价比、成本削…

2026/9/20 0:04:49

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/20 0:04:49

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/21 0:02:23

OpenResearch:构建可复现的开放式研究工作流

第一次看到“OpenResearch”这个名字,我脑子里冒出的不是某个具体软件,而更像一种研究方式的宣言:开放、可复现、可验证。这三件事放在一起,其实比大多数人想象中难得多。过去几年我一直在折腾自己的研究工作流,从纯纸…

2026/9/20 4:54:47

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/20 5:01:23

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/20 5:09:33

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码