重传追踪

📖 概述

tcpshark --mode retransmit 通过内核跟踪点 tcp/tcp_retransmit_skbtcp/tcp_retransmit_synack 观测 TCP 重传相关活动。显式开启 TLP 后,还会观测 tcp_send_loss_probe kprobe。根据事件类型,每条事件可携带 IP 四元组、TCP 状态、拥塞控制状态、重传计数器、序列号信息,以及用于解析容器归属的 socket 元数据。

用户态分类器根据事件类型、sk_stateca_state 和乱序计数器生成连接阶段与原因标签。这些标签是用于运维分析的启发式分类,不是丢包根因的确定性证据。

过滤表达式由 internal/pcapfilter 在加载时编译并在内核中执行。过滤器只对携带 SKB 的 tcp_retransmit_skb 事件生效;SYN-ACK 和 TLP 事件会绕过 pcap 过滤器。


🎯 场景

1. TCP 网络质量与重传诊断

通过持续观测 RTO、快速重传、乱序倾向重传和 TLP 事件,识别连接建立、数据传输及连接关闭阶段的异常重传,辅助判断网络丢包、拥塞、乱序或对端不可达等问题。

2. Kubernetes 容器网络故障排查

结合容器 ID、网络命名空间和 socket cgroup 元数据定位发生重传的工作负载,并使用 --filter "tcp and port <service-port>" 聚焦特定服务流量,减少宿主机上其他连接的干扰。

3. 应用延迟与吞吐毛刺分析

将 TCP 重传事件与应用延迟、错误率和吞吐曲线对齐,分析 RTO 或连续重传是否与服务性能下降同时发生,辅助区分应用处理变慢与底层网络异常。

4. 与 dropwatch 关联定位丢包位置

在同一 huatuo-bamai 进程中同时运行 dropwatch 和 tcp_retransmit,通过 SKB 指针或连接四元组关联丢包与重传事件,辅助判断问题更可能发生在主机协议栈还是外部网络;关联结果属于启发式证据,仍需结合调用栈和网络指标确认。


🚀 使用

1. 运行 tcpshark

tcpshark --mode retransmit [flags]
参数 默认值 说明
--mode retransmit 必填 选择 TCP 重传追踪模式。
--enable-tlp--tlp 关闭 同时挂载 tcp_send_loss_probe 并输出 TLP 事件。
--bpf-path <path> 必填 tcp_retransmit.o eBPF 对象文件路径。
--filter <expr> (无) 仅用于 tcp_retransmit_skb 事件的 tcpdump 风格过滤器,见 §2。
--duration <n> 0 运行 N 秒后退出(0 表示持续运行直至 Ctrl-C)。
--max-events-per-second <n> 0 BPF 侧事件限速,0 表示不限速。
--output <json|text> text 输出格式;设置 --output-storage 时会被忽略。
--output-storage <path> (无) 通过 Unix socket 将事件发送给 huatuo-bamai。
--task-id <id> (无) toolstream 会话关联的任务 ID;必须与 --output-storage 一起使用。

显式同时指定 --output--output-storage 时,--output 会被忽略并打印警告。

1.1 常用命令

# 文本格式输出全部重传相关事件
sudo tcpshark --mode retransmit --bpf-path bpf/tcp_retransmit.o

# NDJSON 格式输出
sudo tcpshark --mode retransmit --bpf-path bpf/tcp_retransmit.o --output json

# 在 BPF 侧过滤指定目标主机和端口的常规重传 SKB
sudo tcpshark --mode retransmit --bpf-path bpf/tcp_retransmit.o --filter "dst host 10.0.0.1 and dst port 443"

# 包含 Tail Loss Probe 事件(默认关闭)
sudo tcpshark --mode retransmit --enable-tlp --bpf-path bpf/tcp_retransmit.o

# 最多输出 100 条事件/秒;超限时打印 rate limit hit 日志
sudo tcpshark --mode retransmit --bpf-path bpf/tcp_retransmit.o \
  --max-events-per-second 100

# 在用户态过滤全部格式化事件类型,只保留目标端口 443
sudo tcpshark --mode retransmit --bpf-path bpf/tcp_retransmit.o --output json \
  | jq -c 'select(.tcp_dport == 443)'

# 运行 60 秒,仅保留分类为 RTO 的事件
sudo tcpshark --mode retransmit --bpf-path bpf/tcp_retransmit.o --duration 60 --output json \
  | jq -c 'select(.tcp_reason == "RTO")'

# 将事件转发给正在运行的 huatuo-bamai 实例
sudo tcpshark --mode retransmit --bpf-path bpf/tcp_retransmit.o \
  --output-storage /var/run/huatuo-toolstream.sock

jq -c 将每条结果压缩成单行 JSON,便于保存为 NDJSON 或继续通过管道处理。

1.2 与 huatuo-bamai 集成

tcpshark 与 dropwatch 使用相同的 --output-storage 和 toolstream 流程。通用存储方式请参考 dropwatch 文档。TCP 重传追踪增加以下配置:

[EventTracing.TCPRetransmit]
    # 转发给 tcpshark --filter;仅过滤 tcp_retransmit_skb。
    # 默认值: ""
    Filter = ""

    # 设置为 true 时传入 tcpshark --enable-tlp;默认 false。
    EnableTLP = false

    # 传给 tcpshark --max-events-per-second;默认 100,0 表示不限速。
    MaxEventsPerSecond = 100

tcp_retransmit tracer 默认位于全局 BlackList 中。需要启用时,从名单中移除 tcp_retransmit 并重启 huatuo-bamai。丢包关联缓存仅在 tracer 运行期间启用,tracer 停止时会关闭并清空。启用后可通过 HTTP API 启停追踪:

curl -X PUT http://localhost:19704/tracers/tcp_retransmit/start
curl -X PUT http://localhost:19704/tracers/tcp_retransmit/stop

2. 过滤表达式

tcpshark 使用与 dropwatch 相同的 tcpdump 风格过滤表达式。完整语法、限制和更多示例请参考 dropwatch 文档

# 指定目标主机和端口
--filter "dst host 10.0.0.1 and dst port 443"

# 观察两个网段之间的双向流量
--filter "(src net 10.10.0.0/16 and dst net 10.20.0.0/16) or (src net 10.20.0.0/16 and dst net 10.10.0.0/16)"

--filter 只作用于 tcp_retransmit_skbtcp_retransmit_synack 和启用后的 tcp_send_loss_probe 不携带 SKB,因此不会应用该过滤器。


3. 事件数据结构

每条事件以 NDJSON 对象(types.TCPRetransmitTracing)表示。带 omitempty 标签的字段在值为空或零时不会输出。

字段 类型 说明
observed_timestamp string 用户态接收/格式化事件时生成的 UTC 时间(RFC3339Nano),不是内核 hook 时间。
comm string 当前内核执行上下文的进程名,不一定是 socket 所属进程。
pid uint64 当前执行上下文的 TGID,不一定是 socket 所属进程的 TGID。
container_id string huatuo-bamai 解析出的容器 ID,见 §3.2。
memory_cgroup_css_addr string 用于解析容器归属的 socket 内存 cgroup CSS 十六进制地址。
net_namespace_cookie uint64 用于解析容器归属的 socket 网络命名空间 cookie。
net_namespace_inum uint32 用于解析容器归属的 socket 网络命名空间 inum。
tcp_saddr string 源 IP 地址。
tcp_daddr string 目的 IP 地址。
tcp_sport uint16 源端口。
tcp_dport uint16 目的端口。
tcp_state string TCP socket 状态,如 ESTABLISHEDSYN_SENTNEW_SYN_RECV
phase string 分类结果:connectdataclose
tcp_reason string 分类结果:RTOfast_retransmitreorder_prone_fastTLPunknown
event_type string tcp_retransmit_skbtcp_retransmit_synacktcp_send_loss_probe
ca_state uint8 拥塞控制状态:0=Open、1=Disorder、2=CWR、3=Recovery、4=Loss。
icsk_retransmits uint8 当前重传计数器快照。
icsk_pending uint8 inet_connection_sock 中原始的待处理定时器状态,取值见下表。
reord_seen uint32 连接累计乱序计数器。
dsack_dups uint32 累计 DSACK 重复计数器。
tcp_seq uint32 SKB 事件使用 TCP_SKB_CB(skb)->seq;TLP 事件使用 snd_nxt;SYN-ACK 事件中为零。
tcp_ack_seq uint32 SKB 事件使用 tcp_sk(sk)->rcv_nxt;TLP 事件使用 snd_una;SYN-ACK 事件中为零。
tcp_end_seq uint32 SKB 事件使用 TCP_SKB_CB(skb)->end_seq;SYN-ACK 和 TLP 事件中省略。
tcp_flags string 渲染后的 TCP flag 集合,如 `SYN
skb_addr string 十六进制重传队列 SKB 指针;SYN-ACK 和 TLP 事件中不存在。
drop_location string huatuo-bamai 生成的丢包关联启发式结果,见 §5。
source string 事件来源。独立运行 tcpshark 时为 tools,由 huatuo-bamai 启动时为 events

icsk_pending 是 hook 时刻的定时器状态快照,不是重传原因的稳定枚举。TLP 分类以明确的 event_type=tcp_send_loss_probe 为准,不依赖 icsk_pending=5

内核状态 含义
0 None 当前没有待处理的发送定时器事件。
1 ICSK_TIME_RETRANS 重传超时定时器(RTO)。
2 ICSK_TIME_DACK 延迟 ACK;现代内核将该状态保存在 icsk_ack.pending 并使用独立的 delayed-ACK timer,因此通常不会出现在 icsk_pending 中。
3 ICSK_TIME_PROBE0 零窗口探测定时器。
4 版本相关 当前主线内核不再定义该值;旧内核曾将其用于 Early Retransmit,更早的内核曾用于 Keepalive。
5 ICSK_TIME_LOSS_PROBE Tail Loss Probe(TLP)定时器。
6 ICSK_TIME_REO_TIMEOUT 乱序超时定时器,主要用于 RACK 丢包判断。

3.1 文本输出格式

文本输出保留面向终端的可读布局,同时覆盖与 JSON 相同的事件变量。带 omitempty 的变量仅在非零或非空时显示,字符串值不添加 JSON 引号或转义。为兼容原文本格式,stateskbseqendackflagscaretrans 分别对应 JSON 中的 tcp_stateskb_addrtcp_seqtcp_end_seqtcp_ack_seqtcp_flagsca_stateicsk_retransmits

<timestamp> [<phase>/<tcp_reason>] <saddr>:<sport> > <daddr>:<dport> state=<STATE> event_type=<TYPE> [SYNACK] [skb=<ADDR>] seq=<N> [end=<N>] ack=<N> [flags=<FLAGS>] pid=<N> comm=<COMM> ca=<N> retrans=<N> icsk_pending=<N> [reord_seen=<N>] [dsack_dups=<N>] [container_id=<ID>] [memory_cgroup_css_addr=<ADDR>] [net_namespace_cookie=<N>] [net_namespace_inum=<N>] [drop_location=<LOCATION>] [source=<SOURCE>]

示例:

2026-07-23T02:14:40.304775546Z [data/RTO] 127.0.0.1:19996 > 127.0.0.1:42128 state=ESTABLISHED event_type=tcp_retransmit_skb skb=0xffff931c14fdf800 seq=3154974646 end=3154991030 ack=948393597 flags=ACK|PSH pid=1420 comm=kube-apiserver ca=4 retrans=4 icsk_pending=0 net_namespace_inum=4026531992

示例中的 pidcomm 表示 hook 运行时的执行上下文;工作负载归属应使用 container_id 和 socket 元数据判断。

3.2 容器 ID 解析

tcpshark 自身无法访问 Pod 管理器。独立输出时通常没有 container_id,但可用时仍会输出 socket memcg 和网络命名空间元数据。通过 huatuo-bamai 运行时,空的 container_idmemory_cgroup_css_addrnet_namespace_cookienet_namespace_inum 的顺序解析。

全部解析未命中时,事件仍会存储,但 container_id 保持为空。pidcomm 描述的是 hook 执行上下文,不能作为判断 socket 归属的回退依据。


4. 内核事件与分类

4.1 内核挂载点

挂载点 内核位置 事件含义 可用数据
tracepoint tcp/tcp_retransmit_skb __tcp_retransmit_skb() 对一个重传队列 SKB 发起了重传尝试;tcpshark 事件不保留内核发送结果。该 SKB 是 headerless 的,因此序列号来自 TCP_SKB_CB(skb),ACK 来自 tcp_sk(sk)->rcv_nxt SKB 指针、TCP seq/end_seq/ack/flags、socket 状态、CA 状态、定时器和乱序计数器。
tracepoint tcp/tcp_retransmit_synack tcp_rtx_synack() tcp_rtx_synack() 成功提交了一次被动建连 SYN-ACK 重传。 request socket 地址和端口;没有重传 SKB 指针及 TCP seq/ack。
kprobe tcp_send_loss_probe tcp_send_loss_probe() 正在准备 Tail Loss Probe;仅在指定 --enable-tlp 时采集。 socket 元数据及 snd_nxt/snd_una;没有 SKB 指针或渲染后的 TCP flags。

BPF 程序通过 BPF_CORE_READ 等辅助方法执行 CO-RE 字段读取,因此在支持的内核布局上无需为每个内核版本重新编译 C 源码。

4.2 连接阶段

常规 SKB 事件的阶段由 sk_state 决定;SYN-ACK 事件在用户态使用固定阶段。

下面以 TCP 三次握手说明 connect 阶段及对应的重传观测点:

sequenceDiagram
    participant C as 客户端
    participant S as 服务端
    Note over C,S: 初始状态:CLOSED / LISTEN
    C->>S: ① SYN
    Note left of C: SYN_SENT(2)<br/>phase=connect
    opt SYN 未被确认
        C-->>S: SYN 重传<br/>tcp_retransmit_skb
    end
    Note right of S: SYN_RECV(3) 或 NEW_SYN_RECV(12)<br/>phase=connect
    S->>C: ② SYN + ACK
    opt 最终 ACK 未到达
        S-->>C: SYN-ACK 重传<br/>tcp_retransmit_synack
    end
    C->>S: ③ ACK
    Note over C,S: ESTABLISHED(1)<br/>后续常规数据 SKB 事件 phase=data

图中的三条实线表示首次握手报文,不会产生 tcpshark 事件;只有可选框中的重传路径会被观测。主动端 SYN 重传通过 tcp_retransmit_skb 上报,被动端 SYN-ACK 重传通过 tcp_retransmit_synack 上报,两者都归类为 connect

完整阶段映射如下:

阶段 来源状态或事件 说明
connect SYN_SENT(2)、SYN_RECV(3)、NEW_SYN_RECV(12) 或 tcp_retransmit_synack 连接建立。
data ESTABLISHED(1) 或无法识别/默认状态 数据传输或默认分类。
close FIN_WAIT1(4)、FIN_WAIT2(5)、TIME_WAIT(6)、CLOSE_WAIT(8)、LAST_ACK(9)、CLOSING(11) 连接关闭。

4.3 原因分类

事件或条件 原因 含义
tcp_retransmit_synack RTO SYN-ACK 重试定时器路径的固定用户态标签。
tcp_send_loss_probe TLP 可选 Tail Loss Probe hook 的固定用户态标签。
tcp_retransmit_skbca_state=4(Loss) RTO socket 当前处于 TCP_CA_Loss。
tcp_retransmit_skbca_state=3(Recovery) fast_retransmitreorder_prone_fast Recovery 路径重传;存在累计乱序历史时使用 reorder-prone 标签。
tcp_retransmit_skbca_state=0..2,connect/close 阶段 RTO 当前分类器使用的阶段回退结果。
tcp_retransmit_skbca_state=0..2,data 阶段 unknown 当前快照不足以生成其他标签。

分类器只观察 hook 时刻的 socket 状态,无法重建完整的 ACK/丢包历史。因此应把 tcp_reason 视为聚合标签,而不是经过验证的根因。

4.4 乱序启发式判断

reord_seendsack_dups 任一累计计数器非零时,分类器会选择乱序倾向标签。连接一旦出现过乱序历史,后续 Recovery 状态的 SKB 事件就可能标记为 reorder_prone_fast。这是连接级启发式判断,不能证明当前重传由乱序触发。

4.5 运维解读

没有任何事件类型可以无条件丢弃。相比只按 event_typetcp_reason 过滤,更推荐按速率、比例和服务影响设置阈值。通用的 huatuo-bamai 噪声过滤机制请参考 dropwatch 文档

模式 通常优先级 建议
tcp_reason=RTO 排查持续增长或与服务异常相关的 RTO;它通常比 Recovery 路径重传带来更大延迟影响。
tcp_reason=fast_retransmit 结合丢包、拥塞及 SACK/RACK 行为分析。
tcp_reason=reorder_prone_fast 视上下文而定 连接存在乱序历史,但不能证明当前事件是伪重传;应检查延迟和计数器增长。
tcp_reason=TLP 视上下文而定 这是可选信号;用于告警前应确认已主动开启 TLP 采集。
event_type=tcp_retransmit_synack 单次通常较低 重复出现可能意味着握手可达性、主机出口、防火墙、客户端或网络问题。

配置告警时,应按服务或连接聚合并结合流量规模判断。繁忙主机上的少量绝对计数可能无害,而低流量关键服务上的突发事件可能具有较大影响。


5. 与 dropwatch 关联

dropwatch 和 tcpshark 向同一个 huatuo-bamai 进程发送事件时,dropwatch 事件会从到达用户态的时刻起在缓存中保留两秒。tcpshark 事件会立即按与方向无关的连接 key 查询此前已收到且尚未过期的 drop 事件。当前实现不会等待之后才到达的 drop 事件,也不会在事件存储后更新关联结果。

5.1 关联结果

内部结果 匹配条件 drop_location 安全解读方式
TCPRetransmitDropDirect 在同一连接缓存桶内,非空的 dropwatch.packet_skb_addrtcpshark.skb_addr 相等。 host_software 有较强证据表明观测到的主机丢包与重传指向同一 SKB 指针。
TCPRetransmitDrop4Tuple 缓存中的 TCP drop 与重传事件的地址和端口正向或反向匹配。 host_software 重传附近在同一连接上观测到了主机丢包,不能证明因果关系。
TCPRetransmitNoDrop 没有匹配且仍有效的缓存项。 network_or_host_hardware 只是当前实现的回退标签,不能证明发生了网络或硬件丢包。

dropwatch 未启用、过滤器未覆盖该连接、事件被抑制或丢失、投递乱序、相关 drop 超出缓存保留窗口时,同样会得到 network_or_host_hardware。四元组匹配也可能把繁忙连接上的无关报文关联到一起。缓存 key 不包含网络命名空间或容器标识,因此不同网络命名空间中地址和端口完全相同的连接也可能发生串联。

5.2 使用条件与排查方式

观测结果 检查项
host_software 且直接匹配 检查对应 dropwatch 事件的调用栈、设备和 drop 元数据。
host_software 且仅连接匹配 在判断因果前核对方向、TCP seq/ack 上下文和时间关系。
network_or_host_hardware 先确认 dropwatch 与 tcpshark 位于同一 huatuo-bamai 进程且过滤器覆盖该连接,再检查网卡和网络计数器。
drop_location 不存在 独立输出中的预期行为;关联由 huatuo-bamai 而不是 CLI 执行。

要让“未观测到主机丢包”具备较可靠的负向证据,dropwatch 必须处于运行状态,并且过滤范围至少覆盖待分析的 tcpshark 流量。当前 schema 没有单独的 unknowndropwatch_not_observed 值,因此消费者应把 network_or_host_hardware 视为排查提示,而不是事实。


🌟 结尾