网络丢包
📖 概述
dropwatch 是 HUATUO 提供的网络丢包观测工具。它通过 tracepoint/skb/kfree_skb 采集软件丢包,并通过 raw_tracepoint/devlink_trap_report 采集支持 devlink trap 的硬件丢包,输出协议类型、IP 五元组、网络设备、丢包原因和内核调用栈。
dropwatch 支持基于 tcpdump 风格过滤表达式的内核侧过滤,过滤逻辑由内置的纯 Go pcap 编译器 internal/pcapfilter 在加载时编译为 eBPF 字节码,过滤完全在内核态执行,只有匹配的数据包才会上报到用户空间,降低对宿主机的性能影响。
此外,dropwatch 支持设备白名单/黑名单过滤、全局上报限速,并可与 huatuo-bamai 集成,将丢包事件存储至 Elasticsearch 进行长期分析。
🎯 场景
1. Kubernetes 云原生网络丢包诊断
在容器漂移、Pod 频繁重启、Service 端口冲突等场景下,通过 dropwatch 实时捕获 kfree_skb 事件并关联到具体容器,快速定位丢包根因。结合 --filter "tcp and port <service-port>" 过滤特定业务流量,将平均故障定位时间从小时级降低至分钟级。
2. 网络性能毛刺分析
针对间歇性网络延迟突增、吞吐下降等问题,通过 dropwatch 采集丢包事件,结合内核调用栈定位丢包发生的具体内核函数(如 tcp_v4_rcv、ip_output 等),辅助区分是防火墙丢弃、路由失败还是缓冲区溢出等原因。
3. 多租户环境网络隔离故障排查
在共享网络命名空间或 veth 设备的容器环境中,通过 --device 过滤指定网络设备,结合 --filter 过滤特定协议,精确采集目标容器的丢包事件,避免其他租户流量干扰诊断结果。
4. 与可观测性平台集成
通过 --output-storage 将丢包事件发送给 huatuo-bamai,存储至 Elasticsearch 后与指标、日志进行多维关联分析。将丢包事件叠加到 Grafana 时间线上,与应用错误率、延迟曲线对齐,实现内核丢包与应用异常的精确关联。
🚀 使用
1. 过滤表达式
过滤表达式采用 tcpdump 语法,由内置的纯 Go pcap 编译器 internal/pcapfilter 在加载时编译为 eBPF 字节码,过滤完全在内核侧执行,降低对宿主机影响,只有匹配的数据包才会上报到用户空间。
1.1 支持的表达式
internal/pcapfilter 支持 tcpdump 标准语法的一个子集,下列原语可以可靠使用:
协议
ip ip6 tcp udp icmp icmp6 igmp pim esp ah vrrp arp rarp
ip proto tcp ip6 proto udp (仅协议名,不支持数字协议号)
主机地址
host 10.0.0.1
src host 10.0.0.1
dst host 10.0.0.1
端口
port 80
src port 443
dst port 8080
网段(CIDR)
net 10.0.0.0/8
src net 192.168.1.0/24
dst net 172.16.0.0/12
组播与以太地址
ip multicast ip6 multicast multicast ether multicast
ether host 00:11:22:33:44:55
布尔运算与分组
tcp and port 80
tcp or udp
not arp
tcp and (port 80 or port 443)
ip and src net 192.168.1.0/24 and tcp dst port 3306
1.2 不支持的表达式
下列表达式不支持,使用后会导致编译失败或产生错误的匹配结果:
| 表达式 | 原因 |
|---|---|
tcp[tcpflags] & tcp-syn != 0、ip[8]、tcp[0:4] |
字节偏移表达式(proto[offset:size])未实现 |
ip proto 6、ip6 proto 17 |
不支持数字协议号,请改用协议名(如 ip proto tcp) |
ether proto 0x0800 |
不支持十六进制 EtherType,请改用名字(如 ether proto ip) |
sctp |
关键字未识别 |
portrange 80-90、tcp portrange 1-100 |
不支持端口范围 |
less N、greater N |
不支持按报文长度过滤 |
ip broadcast、ether broadcast |
不支持广播匹配 |
vlan、mpls、pppoes |
不支持隧道/封装关键字 |
gateway |
不支持 |
1.3 推荐写法示例
# 监控所有 TCP 丢包(默认值——L2 和 L3 上下文均可靠)
--filter "tcp"
# TCP 和 UDP
--filter "tcp or udp"
# 指定目标主机(TCP 和 UDP 均适用)
--filter "dst host 10.0.0.1"
# 指定端口
--filter "tcp and port 443"
# 排除噪声主机
--filter "tcp and not host 169.254.169.254"
# 指定子网 + 指定端口
--filter "src net 192.168.1.0/24 and tcp dst port 3306"
# 监控非 TCP 的丢包(仅 UDP 和 ICMP——不要用 "not tcp",会捕获到未知 L3 事件)
--filter "udp or icmp"
# 仅监控 ARP 丢包(仅 L2 上下文有效,L3 永远不匹配)
--filter "arp"
--filter "ip"/--filter "ip6"现可正确匹配对应 IP 协议族(L2 按 EtherType、L3 按版本 nibble)。若只关心特定传输层或主机,仍建议用更精确的tcp、udp、host或ip proto <name>。
2. 运行 dropwatch
dropwatch [flags]
| 参数 | 默认值 | 说明 |
|---|---|---|
--bpf-path <path> |
必填 | dropwatch eBPF 对象文件路径 |
--filter <expr> |
(无) | tcpdump 风格过滤表达式 |
--device <names> |
(无) | 设备白名单:只采集这些设备的丢包,多个设备用逗号分隔(如 eth0,eth1) |
--device-excluded <names> |
(无) | 设备黑名单:排除这些设备的丢包;与 --device 互斥 |
--duration <n> |
0 | 运行 N 秒后退出(0 表示持续运行直至 Ctrl-C) |
--output <json|text> |
text |
输出格式;设置 --output-storage 时会被忽略 |
--output-storage <path> |
(无) | 通过 Unix socket 将事件发送给 huatuo-bamai |
--task-id <id> |
(无) | 关联本次会话的任务 ID;通常与 --output-storage 一起使用 |
--max-events-per-second <n> |
0 | 全局上报限速,0 表示不限速;在 --device / --filter 后生效 |
--filter 与设备过滤相互正交,同时指定时两者均生效(AND 语义)。不指定 --device / --device-excluded 时采集所有设备。--device 和 --device-excluded 不能同时使用;白名单模式会丢弃没有 net_device 的 SKB,黑名单模式会放行没有 net_device 的 SKB。
dropwatch 启动时自动检测 devlink:devlink_trap_report。内核支持时同时加载软硬件丢包探针;不支持时记录 warning 并仅加载软件丢包探针。硬件丢包采集还要求网卡驱动注册 devlink drop trap,并将目标 trap action 配置为 trap。action 为 drop 时硬件不会向 CPU 提供报文副本,dropwatch 无法获取报文。
devlink 硬件丢包检测
dropwatch 无需额外启动参数。使用前确认内核、驱动和目标 trap 均满足条件:
# 1. 确认内核提供 devlink trap tracepoint
test -e /sys/kernel/tracing/events/devlink/devlink_trap_report/id || \
test -e /sys/kernel/debug/tracing/events/devlink/devlink_trap_report/id
# 2. 查看 devlink 设备及驱动注册的 trap
sudo devlink dev show
sudo devlink trap show <bus/device>
# 3. 为目标 DROP trap 启用报文上报
sudo devlink trap set <bus/device> trap <trap-name> action trap
# 4. 启动 dropwatch,并只查看硬件丢包
sudo dropwatch --bpf-path bpf/dropwatch.o --output json 2>/dev/null | \
jq -c 'select(.drop_source == "hardware")'
<bus/device> 使用 devlink dev show 返回的设备标识,例如 pci/0000:03:00.0。诊断结束后应将 trap 恢复为变更前的 action。
该能力只采集驱动通过 DEVLINK_TRAP_TYPE_DROP 上报的报文,不会采集所有硬件报文,也不等同于网卡硬件丢包计数器。硬件队列满等丢包只有在驱动将其实现为 devlink drop trap 并上报时才可见;exception 和 control 类型的 trap 不会上报为丢包事件。
--filter、--device、--device-excluded 和 --max-events-per-second 同时作用于软件与硬件事件。文本输出将硬件原因表示为 reason=<group>/<trap> drop_source=hardware;JSON 输出使用独立的 drop_reason_group、drop_reason 和 drop_source 字段。
常用命令
# 文本格式输出,监控所有设备的 TCP 丢包
sudo dropwatch --bpf-path bpf/dropwatch.o --filter "tcp"
# 只监控 eth0 上的丢包
sudo dropwatch --bpf-path bpf/dropwatch.o --device eth0 --output json
# 排除 loopback
sudo dropwatch --bpf-path bpf/dropwatch.o --device-excluded lo --output json
# 设备过滤与协议过滤组合
sudo dropwatch --bpf-path bpf/dropwatch.o --device eth0 --filter "tcp and port 443" --output json
# 抓取 60 秒后退出
sudo dropwatch --bpf-path bpf/dropwatch.o --filter "tcp and port 443" --duration 60 --output json
# 将事件转发给正在运行的 huatuo-bamai 实例
sudo dropwatch --bpf-path bpf/dropwatch.o --filter "tcp" --output-storage /var/run/huatuo-toolstream.sock
# 通过 jq 过滤仅显示 RST 包
sudo dropwatch --bpf-path bpf/dropwatch.o --output json 2>/dev/null | jq 'select(.layers.tcp.flags == "RST")'
# 采集 10 秒 JSON 输出,并排除调用栈包含 ip_finish_output 的事件
sudo dropwatch --output json --duration 10 --bpf-path bpf/dropwatch.o | jq -c 'select(.stack | test("ip_finish_output") | not)'
# 采集 10 秒 JSON 输出,只打印除 stack 之外的字段
sudo dropwatch --output json --duration 10 --bpf-path bpf/dropwatch.o | jq -c 'del(.stack)'
jq -c 会把每条匹配事件压缩成单行 JSON,便于保存为 NDJSON 或继续用管道处理。test("ip_finish_output") 判断 stack 是否匹配该正则,not 会把结果取反,因此上面的命令会排除包含 ip_finish_output 的调用栈;去掉 | not 后,就是只保留包含 ip_finish_output 的事件。del(.stack) 只从 jq 输出中删除 stack 字段,适合只查看时间、设备、进程、packet_* 元数据和 layers 协议字段。如需在存储前由用户态按调用栈过滤,可通过 huatuo-bamai 配置 EventTracing.IssuesList 实现(参见第 4 节)。
3. 事件数据结构
每条丢包事件以 NDJSON 对象(types.DropWatchTracing)表示。
| 字段 | 类型 | 说明 |
|---|---|---|
observed_timestamp |
string | 用户态接收/格式化事件时生成的 UTC 时间(RFC3339Nano),不是内核 hook 时间 |
type |
string | 预留 TCP 事件类型,当前未设置(1 普通丢包、2 SYN flood、3/4 listen overflow) |
drop_source |
string | 丢包来源:software 表示内核协议栈,hardware 表示 devlink DROP trap |
drop_reason |
string | 软件丢包为 SKB_DROP_REASON_*;无法从内核 BTF 解析时记录 warning 并回退为数字。硬件丢包为 devlink trap 名称 |
drop_reason_group |
string | devlink trap 分组名称,用于归类硬件丢包;软件丢包不输出该字段 |
drop_location |
string | 软件丢包的 kfree_skb 调用地址(十六进制);硬件丢包不输出该字段 |
source |
string | 事件来源;独立运行 dropwatch 时为 tools,由 huatuo-bamai 启动时为 events |
comm |
string | 丢包时的进程名 |
pid |
uint64 | 进程 TGID |
container_id |
string | 容器 ID(由 huatuo-bamai 解析填充,omitempty) |
memory_cgroup_css_addr |
string | 内存 cgroup CSS 地址,用于容器归属解析 |
net_namespace_cookie |
uint64 | 网络命名空间 cookie,用于容器归属解析 |
net_namespace_inum |
uint32 | 网络命名空间 inum,用于容器归属解析 |
netdev_name |
string | 网络设备名(如 eth0) |
netdev_ifindex |
uint32 | 网络接口索引 |
netdev_queue_mapping |
uint32 | TX 队列映射 |
netdev_linkstatus |
[]string | 网络设备链路标志 |
packet_skb_addr |
string | SKB 地址(十六进制,omitempty) |
packet_eth_proto |
string | 原始 EtherType(十六进制,如 0x0800) |
packet_len |
uint32 | 数据包长度(字节) |
layers |
object | 分层协议解析结果,缺失的层会省略 |
stack |
string | 内核调用栈(换行分隔) |
硬件事件的 stack 表示驱动调用 devlink trap 上报接口时的内核调用栈,不代表 ASIC 内部的实际丢弃位置。定位硬件原因时应以 drop_reason_group、drop_reason、设备信息和驱动文档为主。
layers 使用固定字段表达协议栈,不再依赖单独的协议枚举:
| 字段 | 说明 |
|---|---|
layers.label |
协议组合标签,如 IPv4/TCP、IPv6/UDP、ARP、unknown |
layers.ether |
存在真实 Ethernet header 时输出二层字段:saddr、daddr、type、len;仅 IEEE 802.3 framing 的 len 非零 |
layers.ipv4 |
IPv4 字段:version、ihl、tos、len、id、flags、frag_offset、ttl、protocol、checksum、saddr、daddr |
layers.ipv6 |
IPv6 字段:version、traffic_class、flow_label、len、next_header、hop_limit、saddr、daddr |
layers.tcp |
TCP 字段:sport、dport、seq、ack_seq、data_offset、flags、window、checksum、urgent、sk_state |
layers.udp |
UDP 字段:sport、dport、len、checksum |
layers.icmp |
ICMP/ICMPv6 字段:type、code、checksum、id、seq |
layers.arp |
ARP 字段:addr_type、protocol、hw_address_size、prot_address_size、operation、sender_mac、sender_ip、target_mac、target_ip |
4. 与 huatuo-bamai 集成
huatuo-bamai 以子进程形式启动 dropwatch,并通过 --output-storage 将事件发送到内置处理流程,并最终存储到 Elasticsearch。典型参数如下:
dropwatch \
--bpf-path <CoreBpfDir>/dropwatch.o \
--output-storage /var/run/huatuo-toolstream.sock \
--filter "tcp"
4.1 配置项参考(huatuo-bamai.conf)
[EventTracing]
# 可选调用栈过滤。dropwatch 会丢弃 stack 匹配已配置正则的事件。
# 默认值: []
IssuesList = []
[EventTracing.Dropwatch]
# tcpdump 过滤表达式,转发给 dropwatch --filter。
# 默认值: "tcp"
Filter = "tcp"
# 转发给 dropwatch --max-events-per-second。
# 默认值: 100
MaxEventsPerSecond = 100
4.2 噪声过滤
默认不启用任何调用栈噪声过滤。配置 EventTracing.IssuesList 后,huatuo-bamai 才会丢弃匹配事件。下表是可由运维人员配置的候选模式;启用前应结合本机内核和工作负载验证:
| 模式 | 调用栈帧前缀 | 原因 |
|---|---|---|
| ARP/邻居表到期 | neigh_invalidate/ |
邻居表项到期清理,不影响任何活跃数据流。可从 EventTracing.IssuesList 移除对应规则以关闭过滤。 |
| bnxt 网卡 TX 完成 | bnxt_tx_int/ 或 __bnxt_tx_int/ |
Broadcom bnxt 网卡驱动在 DMA 发送完成后调用 kfree_skb 释放 SKB,此为正常行为,非丢包。 |