Skip to content

identity mapping:别让 NAT 吃掉设备自己的流量

这一篇解决一个很容易踩、但踩到时完全摸不着头脑的问题: NAT 网关配好之后,这台设备自己的服务突然连不上了。

问题是怎么产生的

回想接口地址伪装那一步:

bash
vppctl nat44 add interface address host-eth2

这条命令把出接口地址 203.0.113.1 加进了地址池。从此以后,所有目的地址是 203.0.113.1 的入向包,都会被当成"某个内网主机的回包",交给 out2in 分支去查会话表。

但这台设备自己也要对外提供服务 —— BGP、SSH、SNMP、BFD、监控 agent…… 这些流量的目的地址同样是 203.0.113.1,同样会掉进 out2in 分支, 同样查不到会话,同样被丢掉。

NAT 设备并不知道这个包是发给它自己的。它眼里只有"池地址 + 查表"。

亲眼看一次

应用配置

bash
./labctl reset base
./labctl apply base 05

这一步会在 VPP 自己的 dataplane netns 里起一个监听 tcp/1179 的服务 (用 1179 是为了让人联想到 BGP 的 179),然后配上 identity mapping。

跑自检

bash
./labctl verify base 05

自检会先拆掉 identity mapping 看失败现象,再加回来看成功现象。

没有 identity mapping 时:

text
  s1 尝试连接 VPP 自己的服务 203.0.113.1:1179 ...
✓ 连不上 —— 流量被 NAT 的 out2in 分支拦下了
  丢包计数: 3 nat44-ed-out2in-slowpath no translation error

服务明明在监听:

bash
./labctl sh base nat
# 在容器里:
ip netns exec dataplane ss -lntp | grep 1179
text
LISTEN 0 16 0.0.0.0:1179 0.0.0.0:* users:(("python3",pid=937,fd=4))

加上 identity mapping 之后:

bash
vppctl nat44 add identity mapping external host-eth2 tcp 1179
text
  VPP 自己的服务回话:我看到你的地址是 203.0.113.11:35549
✓ 看到的是 s1 的真实地址,说明源地址也没被动过

通了,而且源地址原样保留 —— identity mapping 的语义就是完全不翻译,原样放行

命令

bash
nat44 add identity mapping <ip4-addr>|external <interface> \
    [<protocol> <port>] [vrf <table-id>] [del]

三种常见写法:

bash
# 1. 按接口 + 协议端口(推荐)—— 接口地址变了会自动跟随
nat44 add identity mapping external host-eth2 tcp 179

# 2. 按具体地址 + 协议端口
nat44 add identity mapping 203.0.113.1 tcp 179

# 3. 只给地址,不给协议端口 —— 这个地址的所有流量都不翻译
nat44 add identity mapping 203.0.113.100

第三种要慎用:它把整个地址从翻译里摘了出去。如果这个地址还在地址池里, 就会出现"池里有它但用不上"的尴尬局面。

生产环境该给哪些端口配

凡是这台设备自己监听、且需要从外网访问的端口,都要配:

服务端口备注
BGPtcp/179和上游对等体建邻居
SSHtcp/22带外管理走管理口的话可以不配
SNMPudp/161监控轮询
BFDudp/3784, 3785快速故障检测
NETCONFtcp/830自动化配置

漏配的典型症状是"NAT 上线后监控告警说设备失联,但业务流量正常"。

一个会吓人的显示

配完之后 show nat44 static mappings 会看到两行:

text
NAT44 static mappings:
 identity mapping TCP 203.0.113.1:1179 vrf 0
 TCP local 64.154.183.118:1179 external host-eth2:1179 vrf -1

第二行那个 local 64.154.183.118 是哪来的?它不是你配的,也不是任何真实地址。

原因是:按接口声明的映射会以"待解析"形式(vrf -1)额外存一份, 而 identity mapping 压根不用 local 字段,VPP 却照样把这块内存当地址打印出来了。 这是显示层的问题,不影响功能 —— 重复添加删除,这个值也不会变。

不要试图去删那一行

它和第一行是同一条配置。用你添加时的完整命令加 del 才能删掉:

bash
vppctl nat44 add identity mapping external host-eth2 tcp 1179 del

和其他方案的比较

除了 identity mapping,还有两条路能让设备自己的服务不被 NAT 影响:

text
  方案                          优点                    缺点
  ──────────────────────────    ──────────────────      ────────────────────────
  identity mapping              精确,只豁免指定端口      每个服务都要配一条
                                地址变化自动跟随

  管理流量走独立的带外管理口      彻底隔离,最干净          需要多一个物理/逻辑口
                                                       BGP 这类必须走业务口的没法用

  用地址池代替接口地址伪装        设备地址天然不在池里      需要多一段公网地址
                                (见地址池那一篇)        上游要为这段地址路由

第三条其实是最省心的:只要转换地址和设备地址不是同一个,这个问题根本不会出现。 所以规模稍大的部署普遍用独立地址池,而不是接口地址伪装。

identity mapping 主要服务于"只有一个公网地址、又必须在这个地址上跑服务"的场景 —— 也就是小型网关和边缘设备。

小结

解决什么出接口地址被当池地址后,设备自身服务被 out2in 分支误伤
命令nat44 add identity mapping external <接口> <协议> <端口>
语义完全不翻译,源和目的都原样放行
症状签名no translation 计数上涨,同时设备自身服务从外部连不上
显示怪象会多出一行 local <乱码地址> 的待解析条目,无害
更省心的替代用独立地址池,让设备地址天然不在池里

下一篇:负载均衡映射 —— 一个公网端口分流给多台内网服务器。

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