网络丢包

📖 概述

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_rcvip_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 != 0ip[8]tcp[0:4] 字节偏移表达式(proto[offset:size])未实现
ip proto 6ip6 proto 17 不支持数字协议号,请改用协议名(如 ip proto tcp
ether proto 0x0800 不支持十六进制 EtherType,请改用名字(如 ether proto ip
sctp 关键字未识别
portrange 80-90tcp portrange 1-100 不支持端口范围
less Ngreater N 不支持按报文长度过滤
ip broadcastether broadcast 不支持广播匹配
vlanmplspppoes 不支持隧道/封装关键字
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)。若只关心特定传输层或主机,仍建议用更精确的 tcpudphostip 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 无法获取报文。

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 并上报时才可见;exceptioncontrol 类型的 trap 不会上报为丢包事件。

--filter--device--device-excluded--max-events-per-second 同时作用于软件与硬件事件。文本输出将硬件原因表示为 reason=<group>/<trap> drop_source=hardware;JSON 输出使用独立的 drop_reason_groupdrop_reasondrop_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_groupdrop_reason、设备信息和驱动文档为主。

layers 使用固定字段表达协议栈,不再依赖单独的协议枚举:

字段 说明
layers.label 协议组合标签,如 IPv4/TCPIPv6/UDPARPunknown
layers.ether 存在真实 Ethernet header 时输出二层字段:saddrdaddrtypelen;仅 IEEE 802.3 framing 的 len 非零
layers.ipv4 IPv4 字段:versionihltoslenidflagsfrag_offsetttlprotocolchecksumsaddrdaddr
layers.ipv6 IPv6 字段:versiontraffic_classflow_labellennext_headerhop_limitsaddrdaddr
layers.tcp TCP 字段:sportdportseqack_seqdata_offsetflagswindowchecksumurgentsk_state
layers.udp UDP 字段:sportdportlenchecksum
layers.icmp ICMP/ICMPv6 字段:typecodechecksumidseq
layers.arp ARP 字段:addr_typeprotocolhw_address_sizeprot_address_sizeoperationsender_macsender_iptarget_mactarget_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,此为正常行为,非丢包。

🌟 结尾