216 条网络命令(一):地址、路由、连接、隧道

216 条网络命令(一):地址、路由、连接、隧道

这是《216 条网络命令》系列第一篇,共四篇。清单来自一本 15 万字的网络排障书,去掉重复、按工具族分类,每条给一句「什么时候用它」和一个「最容易被误读的地方」。

这是《216 条网络命令》系列第一篇,共四篇。清单来自一本 15 万字的网络排障书,每条给一句「什么时候用它」和一个「最容易被误读的地方」。

先说三条纪律,否则清单会害了你

一、先写预期,再执行。 每条命令执行前,先在脑子里写一句「如果正常,我会看到什么」。看到的东西与预期不符,这一步就给出了方向;相符,才进下一步。没有预期就敲命令,等于在大海里舀水。

二、计数看增量,不看绝对值。 dropped 27 这六个字符本身没有意义——要看它 30 秒后还是不是 27。绝对值只会让你盯着一个历史遗留数字发呆。

三、带数字的判据必须带前提。 ping -M do -s 1392 只在 wg0 MTU = 1420 时成立;换一套 MTU 就得重算。任何不写前提的数字,等于什么都没说——这是这份清单里反复出现的规矩。


一、地址与邻居(8 条)

ip -4 addr show [dev eth0]

看内核实际生效的地址与掩码,不是配置文件里写了什么。

坑:出现 169.254.x.x 说明 DHCP 失败,很多人把它当成「网络通了」。  
坑:只看有没有地址,不看前缀长度——/24 和 /32 决定它能不能直连网关。  

ip -6 addr show scope global

scope global 过滤掉 fe80:: 链路本地,只看能路由的地址。

坑:有 global 地址 ≠ 有 v6 出口。必须再查 ip -6 route get。  
坑:RA 可能在你没配过的情况下自动给地址,valid_lft 会随前缀变化。  

ip neigh show [dev eth0]

ARP(v4)/ NDP(v6)邻居表。

关键:STALE 是正常老化态,不是故障;FAILED 才是解析失败。  
判据:同网段不通,先看这里,不要先怀疑路由。  

状态流转:INCOMPLETE → REACHABLE(约 30 秒不验证)→ STALE(过期但不查)→ 下次发包进 DELAY(5 秒窗口)→ 无人确认才 PROBE(真发 ARP)。FAILED 是解析失败。

为什么要有 DELAY 这 5 秒? 内核在说:「我先信你,但你得在 5 秒内证明一下。」TCP 用 ACK 完成了证明,UDP 没有这个能力,只能让内核亲自发 ARP 去问。

实测对照(veth 双 netns,强制置 STALE 后各发 8 秒流量):

协议 起始 过程中 抓到的 ARP 包
TCP 长连接 STALE DELAY → REACHABLE 0 个
UDP 持续发送 STALE DELAY → PROBE 2 个

所以看到一个 UDP 业务每隔几十秒发一次 ARP,这不是故障,是无连接语义在链路层的物理代价。

ip -s neigh

邻居解析的统计。配合容量参数看:

sysctl net.ipv4.neigh.default.gc_thresh3    # 硬上限,默认 1024  

撞上限之后内核直接丢弃新的 ARP 学习请求,直到 GC 清到 gc_thresh2 之下。现场证据在 dmesg 里:

neighbour: arp_cache: neighbor table overflow!  

看到这一行,「Pod 间偶发 No route to host、重试又好了」就有解释了:表没满时是 FAILED(问了没人应),表满了连问都问不出去。

arping -c 3 -A -I eth0 192.168.1.100

发免费 ARP,刷新上游交换机的 MAC 表。判据:VIP 切换成功但业务断五分钟,就是交换机 MAC 表 300 秒老化窗口。

ip -d link show type bridge / bridge link show

看 veth 拓扑与 bridge 端口状态。坑:veth 成对,netns 删了宿主侧会留孤儿。


二、路由与选路(14 条)

ip route get <目的IP> —— 整份清单里最重要的命令

直接打印内核的路由决策结论:为这个目的,选了哪个 dev、哪个 src。

ip route get 10.0.0.5  
ip route get 10.0.0.5 from 10.0.0.1      # 测源地址路由  
ip route get 10.0.0.5 mark 0x64          # 测策略路由  

回归正题:这是「内核会怎么选」,不是「配置文件想让它怎么选」。

坑:用 ip route show 代替它。路由表里有十条,但内核只选一条,选哪条才是问题。  
判据:流量不走隧道、metric 莫名其妙、被 /32 抢占——这些都先看这一条。  

ip route show [table all]

列出路由条目,用来解释上一条为什么得出那个结论。

关键:只查 main 表会漏掉 ip rule 引到别的表的流量。  
坑:metric 只在同前缀长度时比较。前缀比直连短,metric 根本没参与比较。  

ip rule show

策略路由规则,按优先级数字从小到大匹配,先命中先走。

判据:「路由表里有却不走」——一半以上是包被 ip rule 引到另一张表了。  

ip rule add from 203.0.113.9/32 lookup 100

ip route add default via 203.0.113.1 dev eth1 table 100  

源地址匹配的流量进 100 号表。这是多出口选路的标准做法。

ip route add / replace / del

ip route add 10.0.0.0/24 dev wg0 metric 50  
ip route replace default via 10.0.0.1 dev eth0 metric 100   # 原子替换  
ip route del 10.0.0.0/24 dev wg0  
坑:网关必须在直连网段内,否则内核直接拒绝(Network is unreachable)。  
坑:排障时临时加的路由,改完必须清——忘了清就是下一个故障。  
关键:replace 是原子的,但 ARP 不是。切换瞬间新网关 MAC 还没学到,会丢几个包。  
      缓解办法:切换前先 ping 一下新网关,把 ARP 提前建立。  

ip -6 route get <v6 目的>

坑:ip -6 addr 有地址就以为 v6 可用。v4/v6 的路由是两个独立世界。  

三、socket 与连接(13 条)

ss -tulnp

所有监听端口、绑定地址、归属进程。暴露面审计第一步。

坑:宿主看不到监听 ≠ 服务没起,可能在另一个 netns。用 nsenter 进去看。  
坑:管理服务绑 0.0.0.0 被当成「方便」,实际是暴露面。  

ss -tan state close-wait|time-wait|syn-recv

ss -tan state close-wait | wc -l      # 应用没 close() 的连接数  
ss -tan state syn-recv | wc -l        # 半连接队列积压  
ss -tan state time-wait | wc -l  

这四行能分开「网络问题」和「程序问题」:

CLOSE-WAIT 堆积   → 应用代码没调 close(),调内核参数完全无效  
SYN-RECV 堆积     → 半连接队列满了,或者 SYN 被丢在回程  
TIME-WAIT 多      → 主动关闭方才有,正常;调 tcp_fin_timeout 没用  

ss -lnt

LISTEN 状态下 Recv-Q / Send-Q 含义被改写,这是最常被误读的一条:

LISTEN 时:  
  Recv-Q = 已完成三次握手、但应用还没 accept() 的连接数  
  Send-Q = 队列上限(不是已发送字节)  
判据:Recv-Q 接近 Send-Q → 服务进程活着但 accept 不过来,  
      表现为「服务活着但建连超时」。这类故障 ping 和 curl -I 都测不出来。  

ss -ti [dport = :443]

单条连接的 rtt / retrans / cwnd / snd_wnd。

ss -ti 'dport = :443' | grep -o 'retrans:[0-9]*'  
关键:cwnd 和 snd_wnd 取小才是实际窗口。  
坑:只盯 cwnd 会漏掉「接收方读得慢」;只盯 retrans 会漏掉「窗口不足」。  

ss -s

坑:输出里没有 "ports" 字样。ss -s | grep ports 永远是空的。  
    端口用量要看 ip_local_port_range + established 计数。  

四、隧道(WireGuard)

wg show [wg0]

wg show wg0  
wg show wg0 latest-handshakes  
wg show wg0 transfer  
wg show wg0 allowed-ips  
关键:重握手约每 120 秒一次,30 秒内时间戳不变是正常的。阈值 < 180 秒(留 50% 余量)。  
坑:有握手 ≠ 包进了隧道,要看 AllowedIPs。  
坑:transfer 是累加值,判吞吐要用两次采样的增量。  

wg set / wg syncconf

wg set wg0 peer <公钥> allowed-ips 10.0.0.0/24  
wg set wg0 peer <公钥> persistent-keepalive 25  
wg syncconf wg0 <(wg-quick strip wg0)  

AllowedIPs 是覆盖语义不是追加,而且它同时管两件事:发送时选路、接收时准入。

坑:只改一侧 → 一半通(能发不能收)。  
坑:syncconf 与 setconf 都会删掉文件里没有的 peer,但前者不重置未变 peer。  
坑:少了 wg-quick strip,wg 会报不认识的键。  

persistent-keepalive 25 对抗的是路径上 NAT 的 UDP 映射老化(常见 30–300 秒),不是本机 conntrack 的 30 秒。 因果一错,调优方向全错。


下一篇:(二)防火墙、conntrack、抓包、MTU