Skip to content

可追溯性:DET44 存在的全部理由

上一篇讲了 DET44 放弃了多少能力:没有静态映射、没有 twice-NAT、没有负载均衡、 没有高可用,端口配额还是死的。

它用这些换来一件事。这一篇讲这件事为什么值。

运营商的合规压力

运营商会收到这样的请求:

2026 年 3 月 12 日 14:23,公网地址 203.0.113.100 的 3995 端口, 对应的是哪个用户?

来源可能是执法机关、版权投诉、或者被攻击方的溯源要求。必须能回答。

传统 NAT 只有一条路:记日志。每建立一条会话就记一行,内容包括时间、 内网地址端口、外网地址端口。

text
  一个中等规模的城域网
  ────────────────────────────────────────────
  5 万用户 × 人均 100 条并发会话 = 500 万条活跃会话
  会话平均寿命按 60 秒算 → 每秒新建约 8 万条

  每条日志按 100 字节算:
    每秒     8 MB
    每天   690 GB
    留存 6 个月(常见合规要求)  ≈ 124 TB

而且这 124 TB 不能是冷存储 —— 收到查询请求时要能在合理时间内检索出来。 存储、索引、备份、审计,全都是钱。

DET44 的答案:不用记

映射是算出来的,所以不需要记录。只要知道当时的映射配置,一条命令就能回答。

亲自试一次

bash
./labctl reset base
./labctl apply base 09

# 让 h1 出去几次
ssh chenzhong@192.168.49.182 \
  'docker exec clab-natbase-h1 nc -w 2 203.0.113.11 9000'
text
[s1 tcp/9000] 我看到你的地址是 203.0.113.100:3995

现在假装你是运营商,只知道"203.0.113.100 的 3995 端口":

bash
./labctl vppctl base det44 reverse 203.0.113.100:3995
text
10.10.0.11

完了。没有查日志,没有检索,没有存储成本。

自检会把整条链路验一遍

bash
./labctl verify base 09
text
── 4) 可追溯性:拿公网端口反推内网主机 ──────────
  203.0.113.100:3995   → 10.10.0.11
  203.0.113.100:3881   → 10.10.0.11
  203.0.113.100:3813   → 10.10.0.11
  203.0.113.100:4127   → 10.10.0.12
  203.0.113.100:4295   → 10.10.0.12
✓ 每个端口都能准确反推回内网主机

两个方向的查询

bash
# 正向:这个用户占用了哪些公网端口?
vppctl det44 forward 10.10.0.11
text
203.0.113.100:<3796-4047>
bash
# 反向:这个公网端口是谁在用?
vppctl det44 reverse 203.0.113.100:3995
text
10.10.0.11

正向查询在另一类场景里有用:用户投诉自己被封了。 拿到用户的内网地址,算出他占用的公网端口区间,就能去对方的封禁列表里比对。

最重要的一个坑:反查结果依赖当时的配置

改了映射配置,历史反查会静默给出错误答案

这是 DET44 用于合规场景时最危险的地方。

同一个端口 203.0.113.100:3995,在不同映射配置下的反查结果:

text
  映射配置                              det44 reverse 203.0.113.100:3995
  ──────────────────────────────────   ────────────────────────────────
  in 10.10.0.0/24 out 203.0.113.100/32  →  10.10.0.11
  in 10.10.0.0/24 out 203.0.113.96/28   →  10.10.0.64      ← 完全不同!
  (映射被删除)                          →  no match

改了共享比之后,同一个查询返回了另一台主机。它不报错,不警告, 就是给你一个看起来很正常的错误答案。

如果你在 3 月配的是 /32,5 月改成了 /28,然后 6 月收到一个关于 3 月的查询 —— 按当前配置查出来的是 10.10.0.64,而真正的用户是 10.10.0.11你会把无辜的人交出去。

所以必须记什么

DET44 免掉的是会话日志,不是配置变更记录。你仍然需要:

text
  必须留存                              可以不留存
  ────────────────────────────────      ──────────────────────
  每次 det44 add / del 的时间和内容      每条会话的建立和关闭
  内网地址与真实用户的对应关系            (这才是 TB 级的那部分)
  (谁在用 10.10.0.11,从什么时候开始)

第二条同样关键。det44 reverse 只能告诉你内网地址, 从内网地址到"张三这个账号"还需要另一套映射 —— DHCP 租约、 PPPoE 会话记录、或者静态分配表。这部分 DET44 帮不上忙。

一个可行的做法

把映射配置纳入配置管理(Git / Ansible),每次变更自动打时间戳归档。 查询时先按时间找到当时的配置,再据此计算 —— 而不是拿当前配置去算历史。

更保险的是:尽量不改共享比。规划时留足余量, 让映射配置在整个留存周期内保持不变。

会话表仍然可以查

虽然不需要日志,实时排障时还是要看活跃会话:

bash
./labctl vppctl base show det44 sessions
text
NAT44 deterministic sessions:
  in 10.10.0.11:55001 out 203.0.113.100:3861 external host 203.0.113.11:9001
     state: udp-active expire: 632

一行里有完整的四元信息:内网端、公网端、外部主机、状态和剩余时间。

手工关闭会话

bash
det44 close session in  <内网地>:<> <外部地>:<>
det44 close session out <公网地>:<> <外部地>:<>

close session out 在 26.06 上不工作

实测对着一条明确存在的会话:

text
  in 10.10.0.11:55002 out 203.0.113.100:3862 external host 203.0.113.11:9001
     state: udp-active
bash
vppctl det44 close session out 203.0.113.100:3862 203.0.113.11:9001
text
no match

而同一条会话用 in 的写法就能正常关闭:

bash
vppctl det44 close session in 10.10.0.11:55002 203.0.113.11:9001

(会话数从 1 变成 0)

所以在 26.06 上只用 close session in 如果手上只有公网侧的信息,先用 det44 reverse 反查出内网地址, 再用 in 的写法关闭。

IPFIX 日志

DET44 免掉了必须记日志的压力,但 VPP 仍然提供了 IPFIX 导出,用于需要更细粒度审计的场景:

bash
vppctl nat ipfix logging enable
vppctl nat ipfix logging enable domain 1 src-port 4739
vppctl nat ipfix logging disable

没有对应的 show 命令

命令执行返回成功,但 VPP 没有提供 show nat ipfix 之类的命令来确认状态。 要验证是否真的在导出,只能在收集器那一侧看有没有数据进来。

日志级别可以调:

bash
vppctl nat set logging level 3

用 DET44 还开 IPFIX 看似矛盾,其实有合理场景: DET44 保证了"端口→内网地址"可算,但如果你还想知道 这个用户当时在访问哪个外部目的地,那还是得记录。 只是记录量比传统 NAT 小得多 —— 不需要记映射关系,只记访问行为。

什么时候该选 DET44

text
  ✓ 适合                                  ✗ 不适合
  ──────────────────────────────────      ──────────────────────────────
  运营商 CGN,有明确的溯源合规要求          企业出口(需要端口转发发布服务)
  用户数巨大,日志存储成本无法接受           用户并发差异大(有人几条有人上千条)
  用户行为可预测,能算准端口配额             需要高可用(DET44 没有)
  能接受"用完就断",不需要弹性借用           需要 twice-NAT / 负载均衡 / VRF

最大的业务风险是配额刚性

再强调一次上一篇的要点:DET44 的端口配额用完就断,不能借

共享比 256 时每台主机只有 252 个端口。一个用户打开几个现代网页 (每个页面几十条连接)就可能到顶,之后所有新连接直接失败 —— 而隔壁用户的 252 个端口可能一个都没用。

上线前必须用真实流量画像估算峰值并发,并留 1.5~2 倍余量。 这个账算错了,故障表现是"部分用户间歇性打不开网页", 而且监控上看不出任何异常 —— 没有池耗尽,没有会话表满, 每个用户都在自己的配额内正常工作,只是有些人的配额不够用。

小结

解决什么用算法替代日志,实现零存储成本的溯源
正向查询det44 forward <内网地址> → 公网地址和端口区间
反向查询det44 reverse <公网地址>:<端口> → 内网地址
省掉的存储会话日志(TB 级)
仍必须留存映射配置的变更历史 + 内网地址到真实用户的对应关系
最危险的坑改了映射配置后,历史反查静默返回错误答案
26.06 的 bugclose session out 对存在的会话返回 no match,只用 in
最大业务风险端口配额刚性,超标即失败且监控无异常

第 4 章到此结束。CGN 的核心矛盾其实是一句话:

text
  弹性(nat44 的地址池)   ←→   可追溯(det44 的算法)

  想要弹性,就得记日志买存储
  想要省存储,就得接受配额刚性

接下来第 5 章进入 IPv6 过渡:nat64 让纯 IPv6 客户端访问 IPv4 服务, 以及 nat66。那需要一个新的实验底座 —— 内网是 IPv6-only 的。

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