资讯动态

Linux内核抓包实战:从网卡到用户态,如何抓取到达网卡的所有报文

发布时间:2026/10/20 2:43:06 来源:尧图企业网站定制
1. 为什么 netfilter 抓不全从一次 GOOSE 报文丢失说起Linux 内核抓包这件事很多人第一反应是 netfilter 钩子或者干脆用 libpcap 在用户态收。但只要你的目标是「抓取到达网卡的所有报文」尤其是链路层报文就会发现事情没那么简单。我之前做一个变电站协议解析的项目需要从指定网卡拿到所有流经的帧包括 GOOSE、LLDP 这类非 IP 报文结果用 netfilter 折腾了两天抓到的永远只有目的 MAC 是本地网卡的那部分。问题的根子在于抓包点的位置。netfilter 的钩子挂在网络层报文得先过链路层、被内核判定为「发给本机」或者「需要转发」才会走到NF_INET_PRE_ROUTING这些钩子上。换句话说目的 MAC 不是本机、又不是广播/组播的帧在链路层就被网卡驱动或者__netif_receive_skb_core里的分发逻辑丢掉了根本轮不到 netfilter 看一眼。LLDP 用的是ETH_P_LLDPGOOSE 是自定义以太类型它们连 IP 层都不进netfilter 自然一无所获。那用dev_add_pack()注册ETH_P_ALL行不行我试过效果和 netfilter 一样。原因在于dev_add_pack注册的packet_type虽然能收到所有以太类型的帧但内核在__netif_receive_skb_core里对ptype_all的处理有个前提报文得先通过skb-pkt_type的判定。如果网卡没开混杂模式驱动层就把目的 MAC 不匹配的帧过滤掉了skb-pkt_type会是PACKET_OTHERHOST这类帧默认不会往上送。所以真正卡住你的不是钩子类型而是网卡的工作模式。这里就引出一个关键结论要抓取到达网卡的所有报文必须同时满足两个条件——网卡处于 UP 状态并且开启混杂模式promiscuous mode。混杂模式让网卡驱动不再按目的 MAC 过滤把所有帧都交给内核UP 状态保证驱动正常收包。缺一个你看到的报文就是残缺的。我踩过的坑是一开始以为用网桥能解决因为配了网桥之后确实抓到了 GOOSE。后来翻内核源码才发现起作用的不是网桥的转发逻辑而是创建网桥时内核自动把成员网卡设成了混杂模式。这个发现让我把方案从「绕道网桥」改成了「直接开混杂 dev_add_pack」代码干净了一大截。所以这篇内容会沿着「网卡驱动收包 → 内核协议栈分发 → 用户态抓包」这条链路把抓包点选择、混杂模式开启、过滤规则设置、结果核对这几件事讲透。适合做工业协议解析、网络设备调试、内核模块开发的读者尤其是那些发现「wireshark 能抓到但我自己写的模块抓不到」的人。下面先从环境准备和 TaoToken 的接入讲起因为后面验证模型辅助分析报文时会用到。2. TaoToken 前置准备拿 Key、配 Base URL、选模型在动手写内核模块之前先把验证环节要用的模型服务准备好。抓包过程中经常需要让模型帮忙解读报文结构、比对协议字段或者生成过滤规则有个稳定的 API 入口会省很多事。TaoToken 提供统一的 API 接入Base URL 是https://taotoken.net/api兼容 OpenAI 风格的调用方式Claude Code、Cline、Codex 这类工具都能直接接。第一步是拿 API Key。打开https://taotoken.net/api-keys登录后创建一个新的 Key复制出来保存好。这个 Key 只在创建时完整显示一次丢了就得重新生成。注意不要把它硬编码进内核模块或者提交到 git后面配置里我们用环境变量或者配置文件来管理。第二步是确认 Base URL。所有请求都走https://taotoken.net/api注意这个地址不带任何查询参数是纯粹的 API 入口。如果你用的是 OpenAI SDK把base_url设成这个值如果是 Claude Code 这类工具在配置文件里填对应的字段。第三步是选模型。TaoToken 的模型列表可以在https://taotoken.net/models查看做报文分析建议选长上下文、推理能力强的模型因为一个抓包文件动辄几千行字段嵌套也深。我一般用 Claude 系列做协议字段解读用 GPT 系列做过滤规则生成效果都还行。如果你打算长期做内核抓包和协议分析可以考虑 Coding Planhttps://taotoken.net/coding-plan它适合需要频繁调用模型辅助编码和调试的场景。单纯验证一两次的话按量付费就够了。配置的时候有个细节要注意不同工具对 Base URL 的写法要求不一样。有的要求带/v1有的要求不带。TaoToken 的 API 入口是https://taotoken.net/api如果工具报 404先检查是不是多加了或者少加了路径段。下面第三节会给出具体的配置文件片段包括 Claude Code 的 settings、Cline 的 MCP 配置以及 Codex 的 auth.json你可以直接复制。环境准备好之后就可以进入内核模块的编写了。记住模型只是辅助真正的抓包逻辑还是得靠内核代码和网卡配置。3. 可复制配置内核模块 混杂模式 工具接入这一节给出完整的可复制配置。先讲内核模块再讲网卡设置最后给出模型工具的配置文件片段。3.1 内核模块代码与 Makefile核心思路是用dev_add_pack()注册一个ETH_P_ALL类型的packet_type回调函数里处理sk_buff并且不把报文交回协议栈返回NET_RX_DROP这样就能「截获」所有到达网卡的帧。注意这个方案生效的前提是网卡已开混杂模式否则驱动层就过滤掉了。// brcm_capture.c #include linux/module.h #include linux/netdevice.h #include linux/if_ether.h #define DRV_VERSION 0.2 static struct packet_type brcm_packet_type; static int brcm_skb_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype, struct net_device *orig_dev) { struct ethhdr *eth; if (!dev || !strcmp(dev-name, lo)) goto drop; eth eth_hdr(skb); if (!eth) goto drop; printk(KERN_INFO dev%s len%u proto0x%04x src%pM dst%pM\n, dev-name, skb-len, ntohs(eth-h_proto), eth-h_source, eth-h_dest); drop: kfree_skb(skb); return NET_RX_DROP; } static int __init brcm_proto_init(void) { brcm_packet_type.type __constant_htons(ETH_P_ALL); brcm_packet_type.func brcm_skb_recv; brcm_packet_type.dev NULL; /* NULL 表示所有网卡 */ dev_add_pack(brcm_packet_type); printk(KERN_INFO brcm_capture loaded, version %s\n, DRV_VERSION); return 0; } static void __exit brcm_cleanup_module(void) { dev_remove_pack(brcm_packet_type); printk(KERN_INFO brcm_capture unloaded\n); } module_init(brcm_proto_init); module_exit(brcm_cleanup_module); MODULE_LICENSE(GPL); MODULE_VERSION(DRV_VERSION);对应的 Makefileobj-m brcm_capture.o KDIR : /lib/modules/$(shell uname -r)/build PWD : $(shell pwd) all: $(MAKE) -C $(KDIR) M$(PWD) modules clean: $(MAKE) -C $(KDIR) M$(PWD) clean编译命令make sudo insmod brcm_capture.ko dmesg -w | grep brcm如果你只想抓指定网卡把brcm_packet_type.dev设成dev_get_by_name(init_net, eth0)的返回值但记得在卸载时dev_put()否则引用计数会泄漏。3.2 网卡混杂模式与 UP 设置模块加载后必须确保目标网卡是 UP 且处于混杂模式。用ip命令sudo ip link set eth0 up sudo ip link set eth0 promisc on ip link show eth0输出里看到BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP就对了。PROMISC标志是关键没有它目的 MAC 不是本机的帧在驱动层就被丢了。如果你用ifconfig等价命令是sudo ifconfig eth0 up promisc注意ifconfig在新发行版里可能没装优先用ip。另外混杂模式是网卡级别的重启或者网卡 down/up 之后会失效需要重新设置。生产环境里建议写进 systemd 服务或者网络配置脚本。3.3 模型工具接入配置抓包分析时我会用模型辅助解读报文下面是几个工具的配置片段。Claude Code 的 settings.json路径~/.claude/settings.json{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: 你的_API_Key, ANTHROPIC_MODEL: claude-sonnet-4-20250514 } }Cline 的 MCP 配置在 Cline 设置里填{ mcpServers: { taotoken: { command: npx, args: [-y, taotoken/mcp-server], env: { TAOTOKEN_BASE_URL: https://taotoken.net/api, TAOTOKEN_API_KEY: 你的_API_Key, TAOTOKEN_MODEL: claude-sonnet-4-20250514 } } } }Codex 的 auth.json路径~/.codex/auth.json{ base_url: https://taotoken.net/api, api_key: 你的_API_Key, model: gpt-4o }三件套就是 Base URL、API Key、Model ID缺一不可。填完之后用curl测一下连通性curl -s https://taotoken.net/api/models \ -H Authorization: Bearer 你的_API_Key | head -c 500返回模型列表就说明配置通了。如果报 401检查 Key 有没有多余空格报 404检查 Base URL 是不是写成了https://taotoken.net/api/v1。4. 验证请求与成功结果从 dmesg 到报文核对配置完成后怎么确认真的抓到了所有到达网卡的报文这一节给出验证步骤和结果核对方法。4.1 加载模块并观察 dmesg先清空 dmesg 缓冲方便观察sudo dmesg -C sudo insmod brcm_capture.ko然后在另一个终端制造流量。最简单的办法是 ping 一个外部地址同时用arping发一个目的 MAC 不是本机的帧ping -c 3 8.8.8.8 sudo arping -I eth0 -c 3 192.168.1.254观察 dmesgdmesg | grep deveth0你应该能看到类似输出deveth0 len98 proto0x0800 srcxx:xx:xx:xx:xx:xx dstxx:xx:xx:xx:xx:xx deveth0 len42 proto0x0806 srcxx:xx:xx:xx:xx:xx dstff:ff:ff:ff:ff:ff deveth0 len60 proto0x88cc srcxx:xx:xx:xx:xx:xx dst01:80:c2:00:00:0eproto0x88cc就是 LLDP0x0806是 ARP0x0800是 IPv4。如果只看到0x0800且目的 MAC 都是本机说明混杂模式没生效回去检查ip link show的PROMISC标志。4.2 用 tcpdump 交叉验证内核模块抓到的报文要和 tcpdump 的结果对得上。tcpdump 底层用的是 AF_PACKET同样依赖混杂模式。开两个终端一个跑 tcpdump一个跑你的模块然后发一批流量sudo tcpdump -i eth0 -e -n -c 20-e打印链路层头部-n不做 DNS 解析。对比 tcpdump 输出的 MAC 地址、以太类型、长度和 dmesg 里的记录应该一致。如果 tcpdump 能抓到但模块抓不到检查dev_add_pack的type是不是ETH_P_ALL以及回调函数有没有被其他模块覆盖。4.3 核对报文完整性要确认「所有到达网卡的报文」都抓到了可以做一个压力测试用tcpreplay重放一个 pcap 文件统计发送帧数和模块记录的帧数。sudo tcpreplay -i eth0 --loop1 test.pcap dmesg | grep -c deveth0两者数量应该接近。如果有明显差距可能是以下原因网卡驱动有硬件过滤比如 VLAN 过滤、多播过滤需要在驱动层关闭或者skb在到达你的回调之前被其他ptype_all消费者处理了。排查时可以用cat /proc/net/ptype看当前注册的packet_type列表cat /proc/net/ptype输出里Type Device Function三列ALL类型的就是ETH_P_ALL消费者。如果有多条说明有别的模块也在抓可能互相影响。4.4 用模型辅助分析抓包结果把 dmesg 输出或者 tcpdump 的文本保存下来丢给模型做字段解读。比如dmesg | grep deveth0 capture.log然后用配置好的 Claude Code 或者 Cline把capture.log作为上下文问「这些以太类型分别对应什么协议哪些是链路层报文」。模型能快速给出0x88ccLLDP、0x88b8GOOSE这类映射省去查表时间。注意别把包含敏感 IP 的日志直接传上去先脱敏。验证通过后说明抓包链路是通的。接下来看常见报错怎么排查。5. 常见报错排查401、local proxy failed、reading choices、OAuth这一节对照真实报错给出排查路径。这些错误大多出现在模型工具接入环节但也会影响抓包分析流程。5.1 401 Unauthorized现象curl或者工具调用返回{error:{message:Invalid API key,type:invalid_request_error}}。排查先确认 Key 有没有复制完整前后有没有空格。然后检查请求头是不是Authorization: Bearer key注意Bearer和 Key 之间有一个空格。如果用的是 Claude Code检查settings.json里ANTHROPIC_API_KEY字段名有没有写错。TaoToken 的 Key 在https://taotoken.net/api-keys管理如果确认 Key 没问题还是 401可能是 Key 被禁用或者额度耗尽重新生成一个。5.2 local proxy failed现象工具报local proxy failed或者connection refused。排查这个错误通常出现在工具试图走本地代理端口的时候。检查环境变量HTTP_PROXY、HTTPS_PROXY有没有设成127.0.0.1:xxxx如果本地没有对应的代理服务就会连接失败。把这两个变量清掉unset HTTP_PROXY HTTPS_PROXY然后重新调用。另外检查 Base URL 是不是写成了https://taotoken.net/api不要多加路径。5.3 reading choices 报错现象返回error reading choices或者unexpected end of JSON input。排查这类错误一般是响应体不完整或者格式不对。先用curl -v看原始响应curl -v https://taotoken.net/api/chat/completions \ -H Authorization: Bearer 你的_API_Key \ -H Content-Type: application/json \ -d {model:claude-sonnet-4-20250514,messages:[{role:user,content:hi}]}如果响应是 HTML 而不是 JSON说明 Base URL 打到了错误的端点。确认路径是/api/chat/completions或者工具要求的对应路径。如果响应被截断检查网络中间设备有没有做内容过滤。5.4 OAuth 相关报错现象Claude Code 报OAuth token expired或者failed to refresh token。排查Claude Code 默认走 OAuth 登录如果你用 API Key 接入需要在settings.json里显式设置ANTHROPIC_API_KEY和ANTHROPIC_BASE_URL并且确保没有残留的 OAuth 凭证。可以删掉~/.claude/credentials.json再重启。如果还是报 OAuth 错误检查是不是同时设了ANTHROPIC_AUTH_TOKEN这个变量优先级更高会覆盖 API Key。5.5 内核模块相关报错insmod报Invalid module format内核版本和编译时的头文件不匹配用uname -r确认重新make。dmesg里没有任何输出检查网卡是不是 UP混杂模式有没有开以及dev_add_pack的返回值虽然它不返回错误但可以用cat /proc/net/ptype确认注册成功。kfree_skb导致内核 panic确认回调函数里没有重复释放NET_RX_DROP返回后内核不会再碰这个skb。排查完这些抓包链路基本就稳了。最后说一下后续怎么用。6. 抓包链路打通后从验证到长期使用的接入建议内核模块能稳定抓到所有到达网卡的报文之后下一步通常是做协议解析或者流量统计。这时候模型辅助的价值就体现出来了把抓到的原始帧按以太类型分类让模型生成解析代码或者直接解读字段含义。我一般会把dmesg输出按proto字段聚合然后针对每种协议单独问模型。如果你只是偶尔验证一下抓包结果用模型对话就够了打开https://taotoken.net/models选个模型把报文日志贴进去问。如果是要长期做协议开发、写解析器、调试内核模块建议用 Coding Planhttps://taotoken.net/coding-plan它适合高频调用和长上下文场景配合 Claude Code 或者 Cline 能省不少查文档的时间。接入的时候记住三件套Base URL 用https://taotoken.net/apiAPI Key 从https://taotoken.net/api-keys拿Model ID 按任务选。配置文件里路径和字段名要对Claude Code 用settings.jsonCline 用 MCP 配置Codex 用auth.json具体片段在第三节。文档在https://taotoken.net/doc遇到接入问题先翻文档比搜索引擎快。最后提醒一个实操细节混杂模式会增大网卡和 CPU 的负担在高流量环境下建议配合ptype过滤或者 BPF 规则只抓你关心的以太类型。内核模块里可以在回调函数开头判断eth-h_proto不匹配的直接kfree_skb返回减少printk开销。printk本身在高频抓包时是瓶颈生产环境建议改成环形缓冲或者tracepoint用perf或者ftrace读取。这些优化等你把基础链路跑通之后再考虑先把「抓全」这件事做扎实。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑