技术指南
Linux 抓包排障:从 socket 证据到 TCP 会话
从 socket、路由和客户端基线出发,用受限 tcpdump 捕获与 Wireshark 字段解释 TCP 会话,同时控制隐私与误判。
抓包最有价值的时刻,不是“网络不通”后收集所有接口的海量流量,而是已有一个可复现请求、明确抓包点和待验证假设时。先用应用日志、curl、路由与 socket 状态判断问题在哪一层;只有现有证据无法区分“包没到、端口没监听、握手失败还是应用没响应”时,才捕获最小必要会话。
先把故障写成可证伪问题
| 问题 | 先看什么 | 抓包能补充什么 |
|---|---|---|
| 端口是否监听 | ss、服务状态、network namespace | SYN 到达后本机是否回复 |
| DNS 是否给出预期地址 | 权威/递归查询与系统解析 | 查询实际发向谁、是否收到响应 |
| TCP 是否建立 | curl/客户端错误与 socket 状态 | SYN、SYN-ACK、ACK、RST 与重传时序 |
| TLS 或应用是否响应 | 客户端详细输出、代理与应用日志 | 握手之后是否还有双向字节;加密内容本身通常不可见 |
| 是否真的丢包 | 两端指标、应用延迟、路径测试 | 特定抓包点看到的序列缺口与重传线索 |
事件卡至少记录 UTC 起止时间、客户端与服务端地址、协议和端口、主机/容器、部署版本、请求 ID 与用户看到的错误。若这些值会变,先固定一次受控复现;否则抓到的可能是健康检查、另一个实例或重试流量。
抓包前完成零侵入基线
`ss` 读取本机 socket,`ip route get` 显示内核对具体目的地址的选路结果,客户端详细输出说明失败停在哪一层。它们通常比第一时间开启混杂捕获更快,也能暴露“服务只监听回环”“请求走错地址族”或“命令运行在错误 namespace”这类问题。
TARGET_IP='192.0.2.10'
PORT='443'
URL='https://www.example.com/health'
date -u --iso-8601=seconds
ip -brief address
ip route get "$TARGET_IP"
ss -H -ltnp "sport = :$PORT"
curl --verbose --connect-timeout 3 --max-time 10 "$URL"选择能回答问题的抓包点
- 先用 `ip route get` 的 `dev` 结果确定出站接口;回环请求使用 `lo`,不要期待在物理网卡看到它。
- 容器、Pod 或代理链会经过多个 namespace 与虚拟接口;记录命令在哪个 namespace 执行。
- `-i any` 便于发现接口,但链路层格式、方向过滤和重复可见性可能与单接口不同;需要精确分析时回到具体接口。
- 只在客户端抓包无法证明服务端收到了包;只在服务端抓包也无法定位此前哪一跳丢失。必要时同步两端时钟并做双点捕获。
- 镜像端口、负载均衡和隧道外层看到的是不同封装层;先画清捕获点相对 NAT、TLS 终止和代理的位置。
先编译 capture filter,再开始写盘
tcpdump 与 Wireshark 的 capture filter 使用 libpcap 语法,在包进入用户态分析前缩小集合。把主机、协议和端口组合起来,并用 `-d` 只编译不捕获,可以提前发现语法或地址族错误。过滤表达式要整体引用,避免 shell 把括号、`!` 或 `&` 当成控制符。
INTERFACE='eth0'
PEER_IP='192.0.2.10'
PORT='443'
FILTER="host $PEER_IP and tcp port $PORT"
sudo tcpdump -i "$INTERFACE" -d "$FILTER"数字端口配合 `-nn` 可避免名称解析制造额外 DNS 流量或混淆服务名。`host` 默认匹配源或目的地址;需要单向问题时再收紧为 `src`/`dst`。不要用“排除 SSH 后抓其余全部流量”代替明确目标。
让捕获自动停止并保留统计
包数与时间要同时有上限。snaplen 应按问题选择:较短截断减少数据量和暴露面,但可能截断 TLS ClientHello、扩展头或应用字段;`-s 0` 保留完整包,也会保留更多敏感内容。tcpdump 4.99.4 的默认 snaplen 是 262144 字节,不等于天然最小化。
CASE_DIR='/var/tmp/net-case-20260815'
CAPTURE_GROUP='tcpdump'
INTERFACE='eth0'
FILTER='host 192.0.2.10 and tcp port 443'
sudo install -d -o root -g "$CAPTURE_GROUP" -m 0750 "$CASE_DIR"
sudo env CASE_DIR="$CASE_DIR" INTERFACE="$INTERFACE" FILTER="$FILTER" \
sh -c 'timeout --signal=INT 30 tcpdump \
-i "$INTERFACE" -nn -s 256 -c 500 \
-w "$CASE_DIR/trace.pcap" "$FILTER" \
2> "$CASE_DIR/capture.stats"'
sudo chown root:root "$CASE_DIR/trace.pcap" "$CASE_DIR/capture.stats"
sudo chmod 0600 "$CASE_DIR/trace.pcap" "$CASE_DIR/capture.stats"
sudo sha256sum "$CASE_DIR/trace.pcap"先验证捕获本身没有失真
停止时保存 tcpdump 的 stderr:`packets captured` 是交给 tcpdump 的数量,`packets received by filter` 的含义受操作系统影响,`packets dropped by kernel` 表示捕获缓冲层丢弃。零 kernel drop 只说明本次抓包路径没有报告丢包,不能证明业务网络零丢包;非零则意味着当前 pcap 本身存在观察缺口。
- 确认接口、link type、snaplen、开始/结束 UTC 时间与过滤器。
- 核对文件大小、包数、owner/mode 和 SHA-256;把 capture.stats 与 pcap 一起保留。
- 检查目标请求确实发生在捕获窗口,并能在五元组中找到。
- 若包为零,先检查 namespace、接口、地址族、过滤器与触发动作,不直接判定“流量没到”。
- 高流量下出现 kernel drop 时,先进一步收紧 filter、缩短窗口或改善捕获路径,而不是把缺口解释为链路丢包。
离线读取时保持数字地址和绝对时间
TRACE='/var/tmp/net-case-20260815/trace.pcap'
sudo tcpdump -nn -tttt -r "$TRACE"
sudo tcpdump -nn -r "$TRACE" \
'tcp[tcpflags] & (tcp-syn|tcp-ack|tcp-rst|tcp-fin) != 0'
sudo tcpdump --count -nn -r "$TRACE"pcap 保存的是原始包字节而非 tcpdump 的打印文本。按 `$3`、点号或空格切列会在 IPv6、不同链路层、时间格式和 flags 组合下误判;需要统计时使用 Wireshark/TShark 字段、流记录或结构化遥测。
不要混用捕获过滤与显示过滤
Wireshark/TShark display filter 在捕获之后按协议字段隐藏或显示记录,不会从 pcap 删除被隐藏的包。把 display-filter 后的文件直接外发并没有完成脱敏;应从受控副本导出明确 packet range,重新检查内容与元数据。
# Wireshark / TShark display filters
tcp.stream eq 0
tcp.flags.reset == 1
tcp.flags.syn == 1 and tcp.flags.ack == 0
tcp.analysis.retransmission
tcp.analysis.lost_segment把 TCP 图样写成证据,不写成判决
| 捕获图样 | 可以确认 | 仍不能单独确认 |
|---|---|---|
| SYN → SYN-ACK → ACK | 该抓包点看到了 TCP 建连交换 | TLS、认证或应用一定健康 |
| SYN → RST/ACK | 某端点或中间设备主动复位 | 一定是目标进程未启动 |
| 重复 SYN,无回复 | 发送端在该点重试且未见回复 | 包到达服务端、具体丢弃设备或防火墙规则 |
| 握手完成后长时间无数据 | 传输层建立后缺少可见应用进展 | 服务端一定卡死;也可能在等客户端、TLS 或上游 |
| 分析器标记重传/乱序 | 序列观察存在重复或缺口线索 | 线路真实丢包率;还需排除抓包丢失、乱序与捕获点影响 |
| FIN/ACK 关闭 | 看到了有序关闭的控制包 | 业务事务一定成功 |
RFC 9293 定义了 SYN、ACK、RST、FIN 与 TCP 状态处理,但同一个 RST 可由应用、协议栈或中间设备产生。把源/目的、序列、时间与 `ss`、服务日志和客户端错误放在一起,才能把“关闭端口的内核复位”与“已建立连接被应用中止”等情形分开。
识别抓包点的 offload 假象
在发送主机上,抓包可能发生在网卡填充 checksum 或拆分大段之前。Wireshark 因而可能把本机发出的 checksum 显示为无效,或看到大于线上 MTU 的 superpacket;这不自动代表线缆上的包损坏。不要为消除红色提示直接修改生产 offload,先对照接收端/镜像点捕获并查看当前 Wireshark 的 partial-checksum 解释。
TLS 流量优先关联日志而不是追求解密
TLS pcap 通常能显示地址、端口、时序、记录长度与部分握手元数据,却看不到加密后的 HTTP 内容。排查 4xx/5xx、请求路由和应用延迟时,优先关联 request ID、代理日志、应用日志和客户端 TLS 输出。只有在隔离测试环境、所有者明确授权且能按密钥处理的情况下才考虑会话密钥;不要从生产进程导出密钥只为“看清请求”。
高频来源不是自动封禁依据
短窗口内某个地址的 SYN 或 HTTP 请求很多,只说明该样本中的频率。NAT、健康检查、重试、爬虫、扫描与攻击都可能产生相似形状;采样、IPv6、代理地址和非对称路径也会扭曲统计。禁止把 tcpdump 文本管道直接接到 UFW/iptables。先结合边缘/流量指标、连接状态、应用日志、资源压力和业务基线,由可回滚的访问策略处理。
本次隔离演练验证了什么
VPScope 在 `127.0.0.1:18082` 启动一次性 Python HTTP 服务,只在 `lo` 上用 `host 127.0.0.1 and tcp port 18082` 捕获 12 个包。pcap 依次呈现 SYN、SYN-ACK、ACK、HTTP 请求/响应、FIN 与最终 ACK;tcpdump 报告 12 captured、48 received by filter、0 dropped by kernel。关闭的 `18083` 则捕获 SYN 与 RST/ACK,curl 以 7 退出。
同一合成 pcap 的 ASCII 视图能直接看到 `GET / HTTP/1.1`、Host 和 User-Agent,证明即使是短暂本机抓包也会保留应用内容。Ubuntu tcpdump 4.99.4 创建的两个文件初始为 0644,尽管调用 shell 使用 `umask 077`;受限目录阻止其他用户遍历,但移动文件前仍必须显式改为 0600。校验和记录后,服务、端口、pcap、日志和临时目录全部清理,生产网络配置未改变。
结束抓包前的验收清单
- 故障请求、UTC 时间窗、五元组、namespace、接口与捕获点可复核。
- 抓包前已保存 socket、路由、客户端与应用日志基线。
- capture filter 通过 `-d` 编译,并同时限制时长、包数和必要 snaplen。
- capture.stats、pcap owner/mode、包数、kernel drop 与 SHA-256 已记录。
- TCP/Wireshark 图样只作为证据线索,并与第二抓包点或日志交叉验证。
- pcap 副本完成最小化和脱敏;display filter 未被误当成删除。
- 没有自动封禁、关闭 offload 或修改防火墙;临时进程、端口和文件均已清理。