216 条网络命令(四):故障注入、二层配置、反例

216 条网络命令(四):故障注入、二层配置、反例

第四篇。这一篇收尾:故障注入、二层配置、反例清单,以及这份清单真正的用法。

十二、内核计数与参数(21 条)

nstat -az

协议栈层的全部计数。

nstat -az | grep -i drop  
nstat -az | grep -i conntrack  
纪律:看增量,不看绝对值。  
坑:只查网卡计数,漏掉协议栈层的丢弃。  

cat /proc/net/softnet_stat

第二列是软中断队列丢包。

判据:它涨而网卡 rx_dropped 是 0 → CPU 处理不过来。  
      这是「网卡没丢包但业务丢包」的答案。  

sysctl:真正需要看的几个

sysctl net.ipv4.ip_forward                    # 是否开了转发  
sysctl net.ipv4.conf.all.rp_filter            # 反向路径过滤  
sysctl net.ipv4.tcp_syncookies                # SYN 保护  
sysctl net.ipv4.tcp_abort_on_overflow         # 队列溢出时回 RST 还是丢  
sysctl net.ipv4.tcp_congestion_control        # 当前拥塞算法  

rp_filter 的坑值得单说:

关键:最终生效取 all 与接口两者的最大值(最严的那个)。  
坑:「规则对但不回包」就关掉它——那是拆防线。  
    正确顺序是先修路由对称(让去程和回程走同一路径)。  

调优的几个常改项

sysctl -w net.core.somaxconn=4096  
sysctl -w net.ipv4.tcp_congestion_control=bbr      # 需内核支持  
sysctl -w net.netfilter.nf_conntrack_max=524288  
sysctl -w net.ipv4.neigh.default.gc_thresh3=8192  
坑:tcp_congestion_control 只对新建连接生效。改了不重建连接,会看到「没用」的假象。  
坑:调大上限只是把撞墙时间推后。真正的解法是缩小广播域 / 降产生速率。  

cat /proc/sys/net/ipv4/ip_local_port_range

默认约 32768–60999,约 28000 个端口。  
理论上限 ≈ 端口数 / 60(TIME_WAIT 时长)。  
报「无法分配地址」时,先查这里 + ss -s。  

十三、命名空间(4 条)

nsenter -t <pid> -n ss -tulnp

nsenter -t <pid> -n ss -tulnp | grep 8080  
坑:宿主 ss 看不到容器内监听 ≠ 服务没起。  
    直接判「服务没起」,会浪费大量时间——这是容器排障的第一大坑。  

ip netns list / ip -d link show type veth

关键:veth 成对。删掉一半会留孤儿。  

十四、故障注入与守护(5 条)

flock -n 9

exec 9>/tmp/guard.lock  
flock -n 9 || exit 0  

单实例锁,比 PID 文件可靠。

关键:锁随进程退出由内核释放,所以锁文件永不删。  
反例:退出时 rm 锁文件 → 制造竞态(A 删了,B 建了,C 也拿到锁)。  

strace -e trace=connect

strace -e trace=connect curl -s -m2 http://198.51.100.77:80  

本机和远端看到的完全不一样,这一条能证明:

远端视角:100% 静默丢包,SYN 重传,几十秒后 ETIMEDOUT  
本机视角:connect(...) = -1 EINVAL,0 毫秒返回  

原因:blackhole 路由在本机发起的路径上直接把错误返回给系统调用,包根本没生成。

判据:curl 报 after 0 ms 连不上 → 查本机策略路由,不要抓包。  
      网卡上一个包都没有,tcpdump 只会给你一段空白,  
      然后你会开始怀疑抓包过滤写错了、接口选错了——那才是真正浪费时间的开始。  
      先跑 ip route show type blackhole,十秒钟就有答案。  

十五、二层配置与 mTLS(12 条)

排查之外,书里还有一类命令是「造环境」用的。它们不出现在故障现场,但你要复现故障、或者搭实验场时离不开。

VLAN 子接口

ip link add link eth0 name eth0.10 type vlan id 10  
ip addr add 192.168.10.5/24 dev eth0.10  
ip link set eth0.10 up  
坑:只加子接口不 up,地址配了也不通。  
坑:父接口 eth0 必须 up,子接口才可能 up(依赖关系)。  
判据:ip -d link show eth0.10 能看到 vlan protocol 802.1Q id 10。  

macvlan / ipvlan

ip link add mv0 link eth0 type macvlan mode bridge  
ip link add iv0 link wlan0 type ipvlan mode l2  
关键区别:  
  macvlan 给每个虚拟接口分配独立 MAC,宿主与容器同网段  
  ipvlan  共享父接口 MAC,只分 IP  
  
坑:macvlan 在无线网卡上基本不可用(AP 会丢弃非本机 MAC 的帧)。  
    这就是为什么这里 ipvlan 挂 wlan0、macvlan 挂 eth0。  
坑:macvlan 子接口与父接口之间不能直接通信(内核设计如此)。  

bond

ip link add bond0 type bond mode 802.3ad  
ip link set eth0 master bond0  
ip link set eth1 master bond0  
ip link set bond0 up  
坑:一端配了 bond、对端没配 LACP → 反而更糟(部分链路被哈希到黑洞)。  
判据:cat /proc/net/bonding/bond0 看 Partner Mac 是否为非零。  
      为零 = 对端根本没跑 LACP。  

netfilter 基线(nftables 版)

nft add table inet myfilter  
nft add chain inet myfilter input '{ type filter hook input priority 0 ; policy drop ; }'  
nft add rule inet myfilter input ct state established,related accept  
nft add rule inet myfilter input tcp dport 22 accept  
关键:priority 0 是 filter 钩子的标准位置。  
      conntrack 那条必须排在最前面,否则回程包全被 policy drop 掉。  

mTLS 客户端证书

curl --cert client.crt --key client.key https://api.example.com/  
curl --cert client-chain.crt --key client.key https://api.example.com/  
判据:第一行报错、第二行成功 → 服务端只认完整链,需要带上中间证书。  
背景:很多服务端配了 fullchain,但客户端证书链同样可能不全。  

WebSocket 握手

curl -s -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" \  
     -H "Sec-WebSocket-Version: 13" -H "Sec-WebSocket-Key: <16字节base64>" \  
     https://example.com/ws  
判据:期望 101 Switching Protocols。  
返回 200 说明代理没转发 Upgrade 头(常见于 Nginx 少了 proxy_set_header Upgrade)。  

十六、反例清单:认出来,别照抄

命令/参数 为什么是反例
net.ipv4.tcp_tw_recycle=1 NAT 下会造成部分客户端连不上,Linux 4.12 已移除
ss -s | grep ports 输出里没有这个字样,永远 grep 不到
nc -u <host> <port> 默认不做等待,「没输出」不携带任何信息
全局 rp_filter=0 拆防线。应先修路由对称
iptables -A INPUT -p icmp -j DROP 制造 PMTUD 黑洞,大包被丢小包正常,极难查
nf_conntrack_max 直接翻倍 每条约 300B 常驻不可交换,小内存机器先 OOM
-s 1392 不给前提 换 MTU 就错
tcp_tw_reuse=1 解决服务端 TIME_WAIT 只对出向连接生效,入向无效
tcp_max_tw_buckets 调得过小 TIME_WAIT 被提前回收,旧包到达会回 RST

最后:这份清单真正的用法

不是「照着敲」,是「照着排除」。

正确的顺序永远是:

1. 先定层:这个现象属于 L2 / L3 / L4 / L7 的哪一层?  
2. 再取证:那一层有哪几条计数/状态可以看?  
3. 写预期:如果正常,我会看到什么?  
4. 执行并比对:不符 → 方向;相符 → 下一层。  

命令是第 4 步的事。跳过前 3 步直接找命令,你会有 216 个选项和零个方向。


清单来自《从协议到自愈》,216 条命令、31 个工具族。书里的每条命令都带「前提 / 判据 / 出处」,这里按博客可读性重排过。