Skip to content

地址池

接口地址伪装的天花板是一个地址的端口数。这一篇讲怎么突破它, 以及一个几乎所有人第一次配地址池时都会问的问题:外网怎么知道这些地址在哪。

解决的问题

三种情况下你需要独立地址池:

  1. 容量不够 —— 一个地址约 64512 个可用端口,人均 100 条并发会话就是几百个用户封顶
  2. 想把业务流量和管理流量分开 —— 转换用池地址,设备管理用接口地址,抓包时一眼分得清
  3. 要按租户分配不同地址 —— 多租户场景下,不同 VRF 用不同的池

配置

应用配置

bash
./labctl reset base
./labctl apply base 02
bash
vppctl nat44 plugin enable
vppctl set interface nat44 in host-eth1 out host-eth2
vppctl nat44 add address 203.0.113.100 - 203.0.113.102

地址池的写法:

bash
nat44 add address 203.0.113.100                      # 单个地址
nat44 add address 203.0.113.100 - 203.0.113.110      # 一段范围
nat44 add address 203.0.113.100 - 203.0.113.110 tenant-vrf 10   # 绑定到 VRF
nat44 add address 203.0.113.200 twice-nat            # 用于 twice-NAT 的独立池
nat44 add address 203.0.113.100 del                  # 删除

别和接口地址伪装混用

技术上两者可以共存,但那样"这个包为什么用了这个地址"会变得很难讲清楚。 所以实验步骤的 apply.sh 里,配地址池前会先把接口地址从池里摘掉:

bash
nat44 add interface address host-eth2 del

生产环境里同样建议二选一。

那个必然会遇到的问题:外网怎么知道池地址在哪

池里的地址 203.0.113.100-102 并没有配在任何接口上。s1 收到一个源地址是 203.0.113.101 的包,要回包时会问:这个地址的 MAC 是多少? 然后发 ARP 请求。

谁来应答?

如果换成 Linux + iptables,你多半得手工配 proxy-arp 或者在接口上加辅助地址。 VPP 不用 —— 它在你执行 nat44 add address 的那一刻,就把这些地址装进了 FIB。

亲自看一眼

bash
./labctl vppctl base show ip fib 203.0.113.100
text
203.0.113.100/32 fib:0 index:28 locks:2
  nat-low refs:1 entry-flags:connected,exclusive,local,
    path-list:[47] locks:2 flags:local,exclusive,
      path:[74] ip4 weight=1 pref=0 receive:
        [@0]: dpo-receive: 203.0.113.100 on host-eth2

三个关键词:

  • local —— VPP 认为这是自己的地址
  • dpo-receive —— 收到目的是这个地址的包,交给本机处理(而不是转发)
  • on host-eth2 —— 关联在出接口上,所以 VPP 会在这个接口上替它应答 ARP

验证一下 s1 的 ARP 表:

bash
./labctl vppctl base show nat44 addresses   # 先产生一些流量
ssh chenzhong@192.168.49.182 'docker exec clab-natbase-s1 ip neigh'
text
203.0.113.102 dev eth1 lladdr aa:c1:ab:40:e9:0d ref 1 REACHABLE
203.0.113.1   dev eth1 lladdr aa:c1:ab:40:e9:0d STALE
203.0.113.101 dev eth1 lladdr aa:c1:ab:40:e9:0d ref 1 DELAY

池地址和接口地址指向同一个 MAC —— 都是 VPP 在应答。零配置。

如果池地址不在直连网段呢

上面这套只在池地址属于出接口所在网段时才够用。如果你的池是一段独立网段 (比如出接口是 198.51.100.1/30 的点对点链路,池是 203.0.113.0/24), 就没有 ARP 的事了 —— 需要上游路由器有一条指向本设备的路由, 把这段地址的流量送过来。这在运营商环境里是常态,通常靠 BGP 或静态路由宣告。

验证

跑自检

bash
./labctl verify base 02
text
  内网主机          转换后地址
  ───────────────   ─────────────────────
  10.10.0.11 (h1)   203.0.113.101:42803
  10.10.0.12 (h2)   203.0.113.102:45373

两台内网主机落到了不同的池地址上。

VPP 是怎么挑池地址的

不是轮询,也不是随机 —— 是按内网地址算的。同一个内网地址的所有会话, 会稳定地落在同一个池地址上。

这个性质有实际意义:

text
  ✓ 好处
    · 服务器端看到的源地址稳定,依赖源 IP 的会话保持不会失效
    · 排障时"这个用户用哪个公网地址出去"是确定的,日志好对
    · 某些风控/限流按源 IP 计数,稳定映射不会误伤

  ✗ 代价
    · 分布不均匀。哈希撞在一起时,两台内网主机会共用一个池地址,
      而另一个池地址闲着
    · 池地址数量少时尤其明显(我们只有 3 个)

自己验证稳定性

反复让 h1 连几次,看池地址会不会变:

bash
for i in 1 2 3; do
  ssh chenzhong@192.168.49.182 \
    'docker exec clab-natbase-h1 nc -w 2 203.0.113.11 9000'
done

地址应该每次都一样,只有端口在变。

容量与监控

bash
./labctl vppctl base show nat44 addresses
./labctl vppctl base show nat44 summary
text
NAT44 pool addresses:
203.0.113.100
  tenant VRF independent
203.0.113.101
  tenant VRF independent
203.0.113.102
  tenant VRF independent

nat44-ed 不显示每地址的端口占用

nat44-eishow nat44 ei addresses 会带上 N busy tcp ports 这样的统计, 但 ED 没有。想在 ED 下判断端口压力,只能靠:

  • show nat44 summary 的会话总数(逼近 max translations per thread 就危险了)
  • show errors 里的分配失败计数

这是 ED 在可观测性上的一个实际短板,规划监控时要注意。

容量估算:

text
  可用端口 ≈ 64512 × 池地址数
  支持用户数 ≈ 可用端口 / 人均并发会话数

  例:3 个地址,人均 100 条会话
      64512 × 3 / 100 ≈ 1900 个用户

池地址数量的经验法则

哈希分布不均是常态。规划时按峰值需求的 1.3~1.5 倍配池大小, 不要卡着理论值配 —— 否则会出现"总容量看起来够,但某个池地址先被打满"的情况。

什么时候不该用地址池

text
  ✓ 适合                              ✗ 不适合
  ──────────────────────────────      ────────────────────────────
  用户规模超过单地址容量               只有一个公网地址可用
  需要区分业务流量和管理流量           公网地址是动态的(DHCP)
  多租户,按 VRF 分配不同池            —— 池地址写死了,地址一变就失效
  上游能为这段地址路由过来              上游不肯为你多宣告一段地址

最后一条在实际项目里最常卡住:地址池是一段需要被路由到你这台设备的地址。 在企业环境里这通常意味着要和网络组协调,不是配一条命令就完事。

小结

命令nat44 add address <起> - <止>
ARP 怎么办不用管,VPP 自动把池地址装成 FIB local,代答 ARP
地址怎么挑按内网地址哈希,同一内网主机稳定用同一池地址
容量≈ 64512 × 池地址数个端口
监控短板ED 不显示每地址端口占用,只能看会话总数和错误计数

下一篇:静态映射与端口转发 —— 让外网能主动连进来。

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