Skip to content

超时与会话管理

第 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 errors
text
3  nat44-ed-in2out-slowpath   out of ports   error

out 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 errors
text
max translations per thread: 10 fib 0
total sessions: 10 (timed out: 0)

20  nat44-ed-in2out-slowpath   maximum sessions exceeded   error

30 条流,建成 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 portsmaximum 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 timeouts
text
udp timeout: 300sec
tcp-established timeout: 7440sec
tcp-transitory timeout: 240sec
icmp timeout: 60sec
bash
# 单独调 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 udp
text
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 sessions
text
    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 thread

ED 看不到每地址的端口占用

nat44-eishow nat44 ei addresses 会显示 N busy tcp portsnat44-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 exceedednat44 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 都没有。

实验环境基于 netlab + containerlab + VPP 26.06