主题
超时与会话管理
第 1 章讲了会话老化的原理 —— 为什么要有超时、 懒回收是怎么回事、保活间隔和超时对不上会出什么故障。
这一篇讲运维:怎么调、怎么监控、资源耗尽时长什么样、出事了怎么手工干预。
两种耗尽,两个不同的签名
NAT 有两种独立的资源会被耗尽,症状相似但原因和解法完全不同。分清它们是排错的第一步。
一、端口耗尽
制造一次
最快的复现方式是"划分了内外侧,但根本没配地址池":
bash
./labctl reset base
./labctl vppctl base nat44 plugin enable
./labctl vppctl base set interface nat44 in host-eth1 out host-eth2
# 故意不配任何地址池
./labctl vppctl base clear errors
ssh chenzhong@192.168.49.182 \
'docker exec clab-natbase-h1 nc -w 3 203.0.113.11 9000'
./labctl vppctl base show errorstext
3 nat44-ed-in2out-slowpath out of ports errorout of ports —— 想给新会话分配一个外部端口,但拿不到。三种成因:
text
1. 压根没配地址池 ← 上面这个实验
2. 池里的端口真的用完了 ← 生产环境的真实耗尽
3. 内网主机哈希到的那个池地址满了 ← 总容量还够,但分布不均第三种最阴险:show nat44 summary 看总会话数远没到上限, 但某些用户就是连不上。原因是地址池那一篇讲的 "按内网地址哈希选池地址" —— 哈希撞在一起的用户会挤在同一个池地址上。
二、会话表满
制造一次
把上限设成 10,然后发 30 条流:
bash
./labctl reset base
./labctl vppctl base nat44 plugin enable sessions 10
./labctl vppctl base set interface nat44 in host-eth1 out host-eth2
./labctl vppctl base nat44 add interface address host-eth2在 h1 上用不同源端口打 30 个 UDP 包,然后:
bash
./labctl vppctl base show nat44 summary
./labctl vppctl base show errorstext
max translations per thread: 10 fib 0
total sessions: 10 (timed out: 0)
20 nat44-ed-in2out-slowpath maximum sessions exceeded error30 条流,建成 10 条,剩下 20 条被拒 —— 计数分毫不差。
maximum sessions exceeded —— 会话表装不下了,和端口够不够无关。
对照表
text
┌────────────────────────────┬──────────────────────┬────────────────────────┐
│ 错误计数 │ 含义 │ 解法 │
├────────────────────────────┼──────────────────────┼────────────────────────┤
│ out of ports │ 分不到外部端口 │ 加池地址;检查哈希分布 │
│ maximum sessions exceeded │ 会话表条目满 │ 调大 sessions;加 worker │
│ no translation │ 查不到对应会话 │ 内外侧配反 / 正常拒绝 │
│ non-SYN packet try to │ 非 SYN 包想建新会话 │ 会话已老化 / 发夹弯 │
│ create session │ │ │
└────────────────────────────┴──────────────────────┴────────────────────────┘把这四个计数器做成监控
show errors 的输出是累计值。做监控时取差值,任何一个开始持续增长都值得告警。
尤其是 out of ports 和 maximum sessions exceeded —— 它们意味着已经有用户受影响了, 不是预警而是事故。
调整容量
会话上限只能在启用插件时指定:
bash
vppctl nat44 plugin disable
vppctl nat44 plugin enable sessions 1000000改上限会清空所有 NAT 配置
nat44 plugin disable 会连带清掉接口归属、地址池、静态映射和整张会话表。 这是一次彻底的中断,不能在线调整。
生产环境要么一开始就把上限配足,要么安排变更窗口。
一并可以指定的参数:
bash
nat44 plugin enable \
sessions <每线程会话上限> \
inside-vrf <内侧默认VRF> \
outside-vrf <外侧默认VRF>sessions 是"每线程",不是总数
text
max translations per thread: 64512 fib 0
^^^^^^^^^^8 个 worker 就是 8 × 64512 ≈ 51.6 万。估算总容量时别忘了乘。
反过来,单个 worker 打满就会开始丢,哪怕其他 worker 还空着。 所以流量在 worker 之间是否均匀,和总容量一样重要。
调整超时
bash
# 看当前值(注意命令是 show nat timeouts,不是 show nat44 timeouts)
vppctl show nat timeoutstext
udp timeout: 300sec
tcp-established timeout: 7440sec
tcp-transitory timeout: 240sec
icmp timeout: 60secbash
# 单独调 UDP
vppctl set nat timeout udp 120
# TCP 的两个必须一起给
vppctl set nat timeout tcp-established 3600 tcp-transitory 120
# 恢复默认
vppctl set nat timeout reset超时可以在线调整,不会清空配置 —— 这一点和会话上限不同。
调短超时之前先想清楚
表快满了想调短超时,是很自然的念头,但代价往往比收益大:
| 调整 | 副作用 |
|---|---|
调短 tcp-established | 静默的长连接(SSH、数据库连接池、MQTT)被悄悄回收。症状是"放着不动一会儿就断",只影响闲置久的连接,极难复现 |
调短 udp | 依赖 NAT 保活的应用(VoIP 注册、游戏心跳)需要更频繁发保活包,否则通话中途单向没声音 |
调短 tcp-transitory | 一般安全。半开连接本来就该快点清掉 |
优先级:加地址池 > 加会话上限 > 调短 tcp-transitory > 调短其他超时。
手工删会话
调试时常需要"把这条流的状态清掉,让它重走一次慢路径"。
bash
nat44 del session in|out <addr>:<port> tcp|udp|icmp \
[vrf <id>] [external-host <addr>:<port>]nat44-ed 必须带 external-host
这是一个非常容易卡住的地方。不带 external-host 会直接失败:
bash
vppctl nat44 del session in 10.10.0.11:21000 udptext
nat44 del session: nat44_del_session returned -6带上就成功:
bash
vppctl nat44 del session in 10.10.0.11:21000 udp \
external-host 203.0.113.11:9001原因回到第 1 章:ED 的会话键包含外网端。 不给外网端,这个键就是不完整的,当然找不到。
(nat44-ei 的会话键里没有外网端,所以 nat44 ei del session 不需要这个参数。 这也是判断自己在用哪个插件的一个侧面证据。)
外网端的地址端口从会话表里读:
bash
./labctl vppctl base show nat44 sessionstext
i2o 10.10.0.11 proto UDP port 21000 fib 0
o2i 203.0.113.1 proto UDP port 21000 fib 0
external host 203.0.113.11:9001 ← 就是这个nat44 forwarding:让不匹配的流量直接过去
默认情况下,从内侧接口进来但分不到转换资源的包会被丢掉 (就是上面 out of ports 那个实验)。打开 forwarding 可以让它们不经翻译直接转发:
bash
vppctl nat44 forwarding enable对比一下
bash
./labctl reset base
./labctl vppctl base nat44 plugin enable
./labctl vppctl base set interface nat44 in host-eth1 out host-eth2
# 不配地址池
# A. forwarding 关闭(默认)→ 不通,out of ports
ssh chenzhong@192.168.49.182 'docker exec clab-natbase-h1 nc -w 3 203.0.113.11 9000'
# B. 打开 forwarding,并给 s1 加一条回内网的路由
./labctl vppctl base nat44 forwarding enable
ssh chenzhong@192.168.49.182 \
'docker exec clab-natbase-s1 ip route add 10.10.0.0/24 via 203.0.113.1
docker exec clab-natbase-h1 nc -w 3 203.0.113.11 9000'text
A:(不通) 3 nat44-ed-in2out-slowpath out of ports error
B:[s1 tcp/9000] 我看到你的地址是 10.10.0.11:36485注意 B 里 s1 看到的是 10.10.0.11 —— 内网真实地址。forwarding 就是字面意思: 不翻译,原样转发。所以它只在外网侧真的有回内网的路由时才有意义 (实验里我们特意给 s1 加了一条)。
典型用途:
text
✓ 混合场景:一部分内网网段走 NAT 出公网,
另一部分通过专线/VPN 直连对端,不需要翻译
✓ 灰度迁移:NAT 还没配全时,让未覆盖的流量先通着,
而不是一刀切全断
✗ 别当成"NAT 失败时的兜底" —— 那会让本该被翻译的内网地址
泄漏到公网上,且静默无告警日常该监控什么
bash
# 1. 会话水位(对比 max translations per thread)
./labctl vppctl base show nat44 summary
# 2. 四个关键错误计数(取差值告警)
./labctl vppctl base show errors | grep -i nat
# 3. 地址池状态
./labctl vppctl base show nat44 addresses
# 4. worker 之间是否均衡(多 worker 时)
./labctl vppctl base show nat44 summary | grep threadED 看不到每地址的端口占用
nat44-ei 的 show nat44 ei addresses 会显示 N busy tcp ports, 但 nat44-ed 没有这个信息。
所以在 ED 下你无法直接回答"哪个池地址快满了",只能:
- 用
out of ports计数间接判断 - 用
show nat44 sessions导出后按外部地址聚合统计
这是 ED 在可观测性上的实际短板,规划监控方案时要提前知道。
一个实用的水位公式:
text
会话水位 = total sessions / (max translations per thread × worker 数)
< 60% 健康
60~80% 该规划扩容了
> 80% 随时可能开始丢,且丢的是新连接(老连接不受影响)
→ 症状是"新开的应用连不上,已经开着的没事"最后那个症状描述值得记住 —— 它和"网络断了"完全不同, 用户报障时说"有的能用有的不能用",往往就是这个。
小结
| 端口耗尽 | out of ports。加池地址;注意哈希分布不均 |
| 会话表满 | maximum sessions exceeded。nat44 plugin enable sessions N |
| 改会话上限 | 必须重启插件,会清空全部 NAT 配置 |
| 改超时 | set nat timeout ...,可在线调整,不清配置 |
| 删会话 | ED 必须带 external-host,否则返回 -6 |
| forwarding | 让不匹配的流量不经翻译直接转发,需要外侧有回内网的路由 |
| 监控盲点 | ED 看不到每个池地址的端口占用 |
第 2 章到此结束。NAT44-ED 的常用能力都覆盖了:
text
出方向 接口地址伪装 → 地址池
入方向 静态映射 / 端口转发 → 负载均衡映射
特殊改写 twice-NAT
设备自身 identity mapping
多租户 VRF
运维 超时、会话上限、错误计数接下来的第 3 章会回到 nat44-ei,把第 1 章实测过的 EI/ED 差异 展开成完整的一章,包括 EI 独有的会话高可用(ha failover)和端口分配算法 (addr-port-assignment-alg)—— 这两个能力 ED 都没有。