"网不通 / 网慢"不要猜:从下往上逐层取证,每一层用命令说话,证据足了再往下走。配套命令速查、Wireshark 过滤器与抓包纪律——所有内容手机可查。
总原则:从下往上查,每层先证明"这层没问题"再上楼。每一层给出判断命令、输出怎么读、以及"通 / 不通"两个分支的下一步。所有计数器看增量不看累计值(连看两次取差);调大任何队列 / 缓冲之前,先确认是哪一只计数器在涨。
NO-CARRIER / link detected: no → 物理层问题,到此为止不用再往上查ip -s link 的 RX errors / dropped 在增长 → 接收路径异常(它只覆盖接收侧);再用 ip -s -s link 细分与驱动计数(rx_missed_errors 等,见 ethtool -S)定位ethtool -S 里 rx_fifo / rx_ring 相关计数器增长 → ring buffer 来不及取进入第 2 层查邻居解析。
先修物理 / 驱动层:换线换口、查协商速率、看 dmesg。若是 ring 溢出 → 按下面证据链定位到具体一层再调参。
收包路径上每一层都有队列和计数器,哪只计数器在涨,包就丢在哪一层:
NIC FIFO/ring:ethtool -S eth0(各驱动字段名不同,找 *_dropped / *_fifo_*) → NAPI 软中断:cat /proc/net/softnet_stat 第 2 列(十六进制,backlog 溢出丢弃数)增长 = per-CPU backlog 满 → 协议栈:nstat -az | grep -E '^(Ip|IpExt|Tcp|TcpExt|Udp|UdpLite)' 或对旧系统 netstat -s,按 IP/TCP/UDP 分组看丢弃原因(注意 InReceives 在 Ip 组,叫 IpInReceives,不在 IpExt 里) → socket 缓冲:ss -m 看 skmem 是否顶到 rmem 上限。
关键认知(分方向):入向——tcpdump 的 tap 点在 netfilter 和协议栈之前,抓到 ≠ 协议栈收了 ≠ 应用收到,抓到之后仍可能被 iptables / rp_filter / socket 缓冲丢弃;出向相反——包先过 OUTPUT/POSTROUTING 才到抓包点,被防火墙丢掉的出向包通常根本抓不到。两端证据矛盾时先想这两点。
INCOMPLETE / FAILED → ARP 请求没人答:二层不通或目标不在线进入第 3 层查 IP 与路由。
回到第 1 层查链路;或查交换机 VLAN 划分、目标是否同网段。整网广播暴涨、MAC 表翻滚 → 怀疑二层环路。深入案例见 cases.html#case-9 →
ip route get 显示的 via / dev / src 才是实际选路,多网卡 / 多路由表时和想象常不一致IP 可达。如果症状是"能 ping IP、打不开网站"→ 直接上第 4 层查 DNS。
用 mtr 锁定最后通的一跳;MTU 黑洞则逐点抓包看 ICMP Too Big(IPv4 Type 3 Code 4 / ICMPv6 Type 2)是否生成、是否被拦。深入案例见 cases.html#case-4 →
+noedns 就好 → 对端实现不兼容 EDNS OPT上第 5 层查传输。
比对 resolver 路径,抓 53 端口确认查询发给了谁、有没有响应;EDNS 用 +noedns 单变量验证。深入案例见 cases.html#case-5 →
ListenOverflows 增量是强指向证据;ListenDrops 更宽泛(含收到 SYN 即丢、内存分配失败等),可单独增长——两者结合内核版本一起看,不要套固定机制ss -ti:吞吐近似上限 ≈ min(发送可用窗口, 对端通告窗口, cwnd×MSS) ÷ RTT——cwnd 的单位是段不是字节。cwnd 低 / retrans 涨 / rtt 抖动只是线索,要结合基线判断:丢包减窗、缓冲设小、窗口缩放未协商各有不同的处置握手正常、无异常重传 → 上第 6 层查应用。
队列溢出 → 应用 accept 太慢(线程池 / 事件循环问题),调大 backlog 只是缓冲;丢包限速 → 回第 1 层证据链找丢包点。队列溢出 cases.html#case-1 → RST 溯源 #case-6 →
nstat -az 按协议分组(过滤用 grep -E '^(Ip|IpExt|Tcp|TcpExt|Udp|UdpLite)',注意 IpInReceives 在 Ip 组而非 IpExt):TcpExt 下 ListenOverflows(强指向 accept 队列溢出)与 ListenDrops(更宽泛:收 SYN 即丢、内存失败等,可单独增长,需结合内核版本判断)、TCPBacklogDrop、PAWS 相关拒绝计数、TCPReqQFullDrop(半连接队列)。Ip/IpExt 组各 Drop 系计数对照收包量看收进来多少、丢掉多少。对旧系统用 netstat -s 看同类计数。一律连看两次取增量,累计值会骗你。
NAT / 有状态防火墙环境:conntrack 表满时新连接被静默丢弃,表现为随机超时——conntrack -C(当前条数)对照 sysctl net.netfilter.nf_conntrack_max,或看 dmesg 的 nf_conntrack: table full, dropping packet。
dns 大 → 回第 4 层;tcp−dns 大 → RTT / 握手重传,回第 3/5 层tls−tcp 大 → 证书链太大、OCSP、或中间设备干预 TLSttfb−tls 大 → 优先怀疑服务端 / 上游处理慢(也含请求上传、代理队列、重传与回程时延)——用 ss / tcpdump / 服务端 tracing 交叉验证后再下"应用慢"的结论网络全链路清白,问题在应用逻辑 / 后端依赖——用应用侧 tracing 继续。
哪段时间异常就回到对应层取证;至此每一层都有命令结论,可以拿着证据链沟通了。
ss -ti + 第 6 层分段计时)。定性错了,再勤快也是白跑。macOS 是 BSD 系工具(ifconfig / netstat / route -n get),Linux 是 iproute2(ip / ss)。Windows 的 tracert 用 ICMP Echo,Unix 的 traceroute 默认发 UDP 高端口,发现中间跳靠的都是 ICMP 时间超过。
| 目的 | Linux | macOS |
|---|---|---|
| 接口 / 地址 | ip addr · ip -s link-s 带收发与错误计数 | ifconfig en0status / media / 错误计数在末尾 |
| 链路状态 / 速率 | ethtool eth0 | networksetup -getMedia en0 |
| 路由表 | ip route · ip route get 目标IProute get 才是实际选路 | netstat -rn · route -n get 目标IP |
| 邻居表 | ip neigh · ip -6 neigh | arp -a · ndp -a |
| 连接 / 监听 | ss -tanp · ss -lnt-p 带进程; -i 单连接详情; -m socket 内存 | netstat -an -p tcp · lsof -iTCP -sTCP:LISTEN |
| 逐跳路径 | mtr -n 目标 · traceroute -n 目标 | sudo mtr -n 目标(brew,需 root/raw-socket 权限) · traceroute -n 目标 |
| 协议栈计数器 | nstat -az · netstat -s看增量; 按 IP/TCP/UDP 分组 | netstat -s字段名与 Linux 不同 |
| DNS 查询 | dig 域名 · dig @服务器 域名 · dig +trace · dig +short · dig +noedns · dig -x IP两系统 dig 行为一致; macOS 查系统 resolver 用 scutil --dns | |
抓包语法(BPF)两系统一致,但 -i any 仅 Linux 有;macOS 没有 any 接口,先 tcpdump -D 列出接口再选(en0 / utunN / lo0)。另注意 Apple 自带 /usr/sbin/tcpdump 与 Homebrew 版在 pktap 支持与权限行为上有差异。先 -w 写文件,分析时再 -r 慢慢过滤。
| 想抓什么 | 过滤式 |
|---|---|
| 到某主机的全部流量 | tcpdump -i any -w out.pcap host 93.184.216.34-i any 仅 Linux;macOS 用 tcpdump -D 选接口 |
| 主机对到 443 的流量 | tcpdump -i any -w out.pcap 'host A and host B and tcp port 443'要锁单条连接需补全四元组(双方 IP+双方端口) |
| SYN / FIN 标志包 | tcpdump -i any -r out.pcap 'tcp[tcpflags] & (tcp-syn|tcp-fin) != 0'不含纯 ACK(第三次握手)与 RST;RST 见下一行 |
| 只看 RST | tcpdump -i any -r out.pcap 'tcp[tcpflags] & tcp-rst != 0' |
| DNS 查询与响应 | tcpdump -i any -n 'udp port 53 or tcp port 53' |
| ICMP(含 PMTUD 证据) | tcpdump -i any -n icmp or icmp6 |
| VLAN 标签帧 | tcpdump -i any -n vlan |
| 隧道口(WireGuard / IPsec NAT-T) | tcpdump -i <utunN> -n · tcpdump -i any -n 'udp port 51820 or udp port 4500'utun 编号按机器变化,先 tcpdump -D 查 |
| 排除干扰(不看 SSH 自己) | tcpdump -i any -n 'not tcp port 22' |
| 长抓轮转(保故障前窗口) | tcpdump -i any -s 0 -C 200 -W 10 -w /tmp/ring.pcap10 × 200MB 循环覆盖,出事即停;-i any 仅 Linux |
tcp port 443)在抓包时生效、丢掉的包不可恢复;display filter 只在显示层过滤,原始数据都在。纪律:抓的时候宁宽勿窄(甚至不带 capture filter),分析时再用 display filter 缩小。下面是 display filter。SYN 有去无回 = 对端静默丢包(防火墙 / 队列溢出);SYN 立刻吃 RST = 端口未监听。量握手慢不慢看 initial_rtt,别信不存在的伪字段。
先看是超时重传(RTO,卡几百 ms~秒级)还是快重传(dup ACK 触发,快)。spurious = 其实没丢,ACK 只是慢到了。
判断"对 SYN 的拒绝"要结合同一 stream 里紧挨的前一个 SYN;比对序号时注意 Wireshark 默认显示 relative sequence number。定位"谁 reset":双端同时抓,比 TTL / IP ID / seq 落窗——与同方向基线对不上是可疑信号而非结论(源 MAC 逐跳本来就会变,不能当证据);最终归因要结合 seq/ack 落窗、双端抓包与设备日志。深入案例见 cases.html#case-6。
zero_window = 接收方缓冲满(应用读得慢);window_full = 发送方填满了接收端此前通告的接收窗口——指向接收侧窗口 / 应用读取慢,不是网络或 cwnd 问题。方向完全不同,别混淆。
查询发出无响应 → 看是不是 A/AAAA 双发只丢其一(固定 5s 超时模型);rcode=2(SERVFAIL) 常见于上游故障。
可见性分版本:TLS 1.2 的 Certificate 通常明文可见,证书问题在 ServerHello 之后现形;TLS 1.3 里 Certificate 与 alert 都在 ServerHello 之后加密,没有 SSLKEYLOGFILE 就看不到内容,只能凭时序和连接结果推断。
排障先确认应用实际跑了 h2 还是 h3(curl -w '%{http_version}');UDP 被 QoS / 拦截时浏览器会回退 TCP。"!quic" 命中可能只是版本太新解析器不认识,要结合 Draft/version 字段判断。
分接口看:物理接口抓到的是 ESP/NAT-T 外层——只能核 SPI 与序列号连续性,内层不可见属正常;utun 口抓到的是加密前 / 解密后的明文内层 IP 包。详见 协议图谱 → 隧道与 VPN。
抓包是唯一能看到"线上真相"的手段,但抓得不对等于没抓。六条纪律:
客户端与服务端(有 LB / 防火墙就再加上中间点)同时开抓。只有双端才能回答"包是没发、没收到、还是收到没回"——单端抓包永远分不清对端与链路的责任。
始终 -s 0(snaplen 全帧)。截断的包没法做 TCP 重组和内容分析,等于白抓。
抓的时候只 -w file.pcap 写文件,不做实时过滤显示——-w 模式的 I/O 路径最短、丢包率最低。分析时 -r 读回来慢慢过滤。
双端机器的时间差会直接毁掉时序判断。抓之前 date 对一次时、记录偏差;正式环境靠 NTP 保证两端偏差在毫秒级以内。
capture filter 丢掉的包不可恢复。接口拿不准时:Linux 用 -i any;macOS 没有 any,先 tcpdump -D 列表再逐个抓(容器环境尤其容易漏抓 veth / lo)。过滤条件只写确定的部分,其余留给 display filter 阶段。漏抓 lo 曾让真实案例误判方向。
大文件不要逐包翻:先用四元组 / tcp.stream eq N 锁定问题连接,再看 flags、seq/ack、window、options。tshark 批量导出字段做分布统计,比 GUI 翻百万个包有效。