216 条网络命令(三):DNS、TLS、队列、内核参数

216 条网络命令(三):DNS、TLS、队列、内核参数

第三篇。上一篇是 netfilter 与抓包;这一篇上到应用层:DNS、TLS、队列与内核参数。

九、DNS(13 条)

dig 家族

dig example.com A                    # 基本查询  
dig +short NS example.com            # 权威服务器  
dig @ns1.example.com example.com A   # 直接问权威  
dig @127.0.0.53 example.com A        # 问本地 stub  
坑:只看一次结果,不比对解析器与 TTL。  
    三个公共解析器结果不一致 = 缓存未过期或劫持。  

dig +tcp vs dig +notcp —— 区分两种失败

dig +tcp example.com TXT       # 走 TCP  
dig +notcp example.com DNSKEY  # 强制 UDP  
dig +bufsize=512 +ignore example.com TXT  
判据:+tcp 能返回、+notcp 超时 → 大 UDP 包在路径上被丢了。  
      A 记录正常不代表 DNS 全好,大记录类型(TXT/DNSKEY)走的是另一条路。  

getent ahosts example.com

走 glibc 的解析路径,和 dig 不是一条路。

为什么重要:dig 直接发 DNS 查询,getent 走 NSS + nscd + glibc。  
            「dig 能解析但应用解析不了」时,差别就在这里。  

5 秒 DNS 延迟的物质基础

strace -f -e trace=network -s 60 python3 -c "socket.getaddrinfo('x.example.com',80)"  

输出里会有这么一行:

sendmmsg(3, [{qtype=A}, {qtype=AAAA}], 2, MSG_NOSIGNAL) = 2  

一次系统调用,同一个 socket、同一个源端口,两个包背靠背发出。

报文里 \0\1 是 A(type 1),\0\34 是 AAAA(type 28)。两个包五元组完全相同 → 对 conntrack 来说是一条会话,两条报文几乎同时到达,争抢同一个表项创建槽位。

改配置后的对照:

配置 系统调用序列 两包间隔
默认 sendmmsg(fd3, [A][AAAA], 2) 微秒级
single-request-reopen sendto(A) → recvfrom → connect → sendto(AAAA) 70 ms

后者把并发改成了串行,竞态消失。

两条修复路径:

  • 改客户端(single-request-reopen):从 glibc 侧消除并发
  • 改路径(NodeLocal DNSCache):让查询不出节点,不经过 conntrack

两条都不需要动 DNS 服务器——问题从来不在那一侧。

resolvectl flush-caches

关键:缓存有六层(浏览器/系统/本地 stub/运营商/公共递归/权威)。  
      你只能清第 3 层。清了还不生效,说明等的是 TTL。  

十、TLS 与 HTTP(22 条)

openssl s_client

openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null \  
  2>&1 | grep "Verify return code"  
关键:-servername 传 SNI,不传会拿到默认证书。  
判据:Verify return code: 0,且证书链至少 2 张(叶子 + 中间)。  
背景:浏览器有补链缓存,curl/Java 没有。  
      所以「浏览器正常、命令行报错」最常见的根因就是链不全。  

openssl verify

openssl verify -CAfile ca.pem -untrusted intermediate.crt server.crt  
坑:只验叶子不验链。  

openssl x509

openssl x509 -in client.crt -noout -text | grep -A2 "Extended Key Usage"  
判据:mTLS 的客户端证书 EKU 必须是 clientAuth。  
坑:「证书尚未生效」常见于容器时钟漂移,只有部分机器报错。  

curl -w —— 一条命令拆开三层耗时

curl -sS -o /dev/null -w \  
  "dns=%{time_namelookup} tcp=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n" \  
  https://example.com  

哪个数大,问题就在那一层。

坑:只看 total,不知道卡在 DNS 还是 TLS。  

curl -4 / -6 / --http2 -v

curl -6 -s -o /dev/null -w "%{time_total}\n" https://example.com  
curl -sv --http2 https://example.com 2>&1 | grep ALPN  
判据:v4 与 v6 耗时差应 < 1 秒。  
坑:ALPN 协商成功 ≠ 请求真的走了 h2。要看响应头是不是 HTTP/2。  

curl -I 看缓存与 Range

curl -sI https://example.com | grep -iE "cache-control|age|etag"  
curl -sI -H "Range: bytes=0-99" https://example.com | head -3  
关键:no-cache 是「可存但用前验证」,no-store 才是「不存」。  
判据:Range 请求应返回 206。返回 200 说明 Range 被代理剥掉了。  

十一、队列与网卡(14 条)

ip -s link show <iface>

ip -s link show eth0  

输出里的 dropped 分两个方向,含义完全不同:

rx_dropped 涨  → 本机来不及处理(软中断/缓冲区)  
tx_dropped 涨  → 本机发不出去(无路由/无 peer/队列满)  
rx_errors + rx_crc_errors 涨 → 线缆/光模块/双工  
tx_carrier_errors 涨         → 链路闪断  

完整统计在 /sys/class/net/<dev>/statistics/,那里有 21 个具体错误计数器,比 ip 合并的 errors/dropped 精确得多:

for f in /sys/class/net/eth0/statistics/*; do  
  printf '%-28s %s\n' "$(basename $f)" "$(cat $f)"  
done  

21 个全为 0,说明从驱动到链路层没有一处出错。

ethtool

ethtool eth0 | grep -E "Speed|Duplex|Link detected"  
ethtool -S eth0 | grep -iE "err|drop|crc|fcs"  
ethtool -a eth0  
判据:Speed 符合预期、Duplex: Full。  
坑:一端自协商、一端写死,是最容易出双工不匹配的配法。  
     链路起来了、小流量正常,很多人就到这儿停了。  
坑:坏帧在进内核之前就被网卡丢掉了,tcpdump 根本看不到。  

容器里没有 ethtool 怎么办

ip -d link show eth0  

输出里有:

gso_max_size 65536  gso_max_segs 65535  
tso_max_size 524280 tso_max_segs 65535  

这比 ethtool -k 的 on/off 更有用——它给出聚合的上限数值。

判据:平均 TX 包大小超过 MTU,说明聚合在生效(不是错误)。  
      1686 B 的均值意味着「聚合过,但很不充分」。  
      上限是 512 KB,说明流量都是并发小请求,攒不起大包。  

tc -s qdisc show dev <iface>

tc -s qdisc show dev eth0  
关键:多队列网卡会列出多个实例(mq / fq_codel)。  
判据:dropped 不增长。  
坑:丢包发生在主机队列时,链路层计数器看不出来。  

tc qdisc add ... netem —— 造故障

tc qdisc add dev eth0 root netem delay 100ms  
tc qdisc change dev eth0 root netem delay 100ms 20ms      # 100ms ± 20ms 抖动  
tc qdisc change dev eth0 root netem loss 1%  
tc qdisc change dev eth0 root netem delay 10ms reorder 25%  
tc qdisc change dev eth0 root netem delay 50ms rate 10mbit  
tc qdisc del dev eth0 root                                # 清理!  
关键:只在出口方向生效。加在不同接口,测的是不同链路。  
铁律:演练完必须清理。忘了 tc qdisc del,就是留下一个持续故障。  


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