主题
搭建实验环境
我们要搭什么
一个五节点的拓扑:两台内网主机、一台 VPP 网关、两台外网服务器。
为什么两侧各放两台而不是各一台?因为很多 NAT 的关键行为,只有在"多"的时候才看得见:
- 两台内网主机同时出去,才能看到 NAPT 是怎么靠端口把它们区分开的
- 同一台内网主机访问两台不同的外网服务器,才能看到端点相关(ED)和端点无关(EI)的区别 —— 这是第 3 章的重头戏
- 地址池有多个地址时,才能观察 VPP 是按什么规则给内网主机挑地址的
三个组件的分工
netlab 是最上层。你只描述"有哪些节点、怎么连、各自什么地址", 它负责算出每个节点的接口名、IP、路由,并生成对应的配置脚本。 containerlab 负责真正把容器拉起来、用 veth 把它们连上。Docker 跑容器。
我们的 topology.yml 全文如下,二十几行:
yaml
name: natbase
provider: clab
addressing:
mgmt:
ipv4: 172.30.101.0/24
start: 100
nodes:
h1: { device: linux }
h2: { device: linux }
nat: { device: vpp }
s1: { device: linux }
s2: { device: linux }
links:
# 内网段:一条链路上挂三个节点 → netlab 自动做成一个二层网段
- nat: { ipv4: 10.10.0.1/24 }
h1: { ipv4: 10.10.0.11/24 }
h2: { ipv4: 10.10.0.12/24 }
prefix: { ipv4: 10.10.0.0/24 }
# 外网段
- nat: { ipv4: 203.0.113.1/24 }
s1: { ipv4: 203.0.113.11/24 }
s2: { ipv4: 203.0.113.12/24 }
prefix: { ipv4: 203.0.113.0/24 }为什么用 203.0.113.0/24 当"公网"
这是 RFC 5737 专门留给文档和示例用的地址段(TEST-NET-3),不会和任何真实公网地址冲突。 看到这个段就知道"这里在扮演公网"。
起环境
启动底座
在你的开发机上(不是实验机):
bash
./labctl up base第一次会花三十秒左右,之后重启大概十秒。
labctl 做的事很简单:把 labs/ 目录打包传到实验机,然后在那边执行对应的脚本。 它用 tar over ssh 而不是 rsync,因为实验机上不一定装了 rsync。
如果你更喜欢直接在实验机上操作,效果完全一样:
bash
ssh chenzhong@192.168.49.182
cd ~/vpp-study/labs/base-gateway
./up.sh起来之后应该看到
最后一段输出是这样的:
text
── 就绪 ──────────────────────────────────────
拓扑已启动,VPP 目前只是一台普通路由器,尚未开启任何 NAT。
h1 10.10.0.11 ─┐ ┌─ s1 203.0.113.11
├─ .1 [ VPP natbase ] .1 ──────────┤
h2 10.10.0.12 ─┘ eth1 eth2 └─ s2 203.0.113.12三个不那么显然的设置
up.sh 里有三处操作,如果照着 netlab 的默认流程走是不会有的。它们都是为了让实验现象更干净, 值得解释一下 —— 顺便这也是理解 VPP 容器怎么启动的好机会。
一、抓住 VPP 启动前的那个窗口
netlab 的 VPP 镜像不是容器一起来就跑 VPP 的。它的入口进程是一个叫 dataplane-wait.sh 的脚本, 在那里死等一个文件出现:
所以我们用的是 netlab up --no-config:让 netlab 把容器和链路都建好,但不要执行配置步骤。 这样 VPP 容器会停在 dataplane-wait 里,我们得到一个可以修改配置的窗口, 改完再手动触发 01-initial.sh 放行。
二、去掉 poll-sleep-usec
netlab 的 VPP 镜像在 startup.conf 里默认带一行 poll-sleep-usec,意思是主循环每轮之间睡一小会儿。 这是为了在跑很多个 VPP 实例的实验里省 CPU,代价是稀疏流量的时延会抖到几十毫秒。
做 NAT 实验时这个代价不能接受 —— 你 ping 一下看到 30ms,根本分不清是 NAT 的开销、 是首包建会话的开销,还是这个 sleep。所以 up.sh 在 VPP 启动前把这行删掉:
bash
sed '/^[[:space:]]*poll-sleep-usec[[:space:]]/d' /etc/vpp/startup.conf > /tmp/startup.conf三、AF_PACKET 用 v2 而不是 v3,收包用 polling
我们的 VPP 是通过 Linux 的 AF_PACKET socket 收发包的(生产环境会用 DPDK 或 AF_XDP)。 AF_PACKET 有两代内存映射接口:
| TPACKET v3(默认) | TPACKET v2(我们用的) | |
|---|---|---|
| 交付粒度 | 按 block,一整块攒满或超时才交给用户态 | 逐帧交付 |
| 吞吐 | 更高 | 略低 |
| 稀疏流量时延 | 差,要等 block 超时 | 好 |
我们的实验流量非常稀疏(几个 ping、几个短连接),v3 的 block 超时会主导时延。所以换成 v2:
bash
sed 's/^create host-interface name /create host-interface v2 name /' /etc/config/01-initial.sh再把收包队列从中断模式改成轮询:
bash
vppctl set interface rx-mode host-eth1 polling
vppctl set interface rx-mode host-eth2 pollingpolling 会吃满一个 CPU 核
这是轮询的本质:CPU 不停地问网卡"有包吗有包吗",没包也在问。 实验机是 4 核,跑一个 VPP 底座没问题,但不要同时跑多个。
up.sh 里做了保护:启动前会检查主机上有没有别的 netlab 实验在跑,有就先把它停掉。 被停掉的实验只是销毁了容器,目录和脚本都还在,随时可以回去重启。
验证这三处都生效了
bash
./labctl vppctl base show hardware-interfaces | grep 'PACKET socket'
./labctl vppctl base show interface rx-placement应该看到 Linux PACKET socket interface v2 和 (polling)。
观察 NAT 的三件工具
「你是谁」服务
up.sh 会在 s1 和 s2 上启动一个几十行的 Python 服务,监听 TCP 9000 和 UDP 9001。 你连上去,它就把它看到的源地址回给你:
bash
docker exec clab-natbase-h1 nc -w 2 203.0.113.11 9000text
[s1 tcp/9000] 我看到你的地址是 203.0.113.1:39637这是判断 NAT 有没有生效最快的方式 —— 不用抓包,不用看会话表,客户端自己就知道了。 后面几乎每个实验的自检都会用到它。
会话表
bash
./labctl vppctl base show nat44 sessionsNAT 的全部状态都在这里。第 1 章会逐字段拆解它。
报文追踪
bash
./labctl vppctl base clear trace
./labctl vppctl base trace add af-packet-input 10
# ...制造一些流量...
./labctl vppctl base show trace把接下来 10 个包经过的每个 graph node 逐行打印出来。这是排错时的终极武器,第 7 章会专门讲怎么读。
日常操作速查
| 想做的事 | 命令 |
|---|---|
| 起底座 | ./labctl up base |
| 应用某一步的配置 | ./labctl apply base 01 |
| 跑自检 | ./labctl verify base 01 |
| 清掉 NAT 配置回到干净底座 | ./labctl reset base |
| 敲 vppctl 命令 | ./labctl vppctl base show nat44 sessions |
| 进某个节点的 shell | ./labctl sh base h1 |
| 看当前跑着什么 | ./labctl status |
| 销毁 | ./labctl down base |
换章节时先 reset
每个实验步骤的 apply.sh 开头都会先把所有 NAT 插件关掉再重新配置, 所以步骤之间是互相独立的,可以按任意顺序反复练。
但如果你自己手工敲了一些命令想清干净,./labctl reset base 是最快的方式 —— 它不销毁容器,两秒就回到干净底座。
环境版本
实验环境跑在一台 Debian 13 上,4 核 15G。关键组件版本:
| 组件 | 版本 |
|---|---|
| VPP | 26.06-release |
| netlab | 26.08 |
| containerlab | 0.75.0 |
| Docker | 29.7.2 |
| 主机镜像 | python:3.13-alpine(自带 python3、busybox nc/ping/wget) |
| VPP 镜像 | netlab/vpp:latest |
这套环境不能用来测性能
AF_PACKET 是一条为了通用性而牺牲性能的收包路径,加上容器共享内核、 veth 链路本身也有开销 —— 在这里量出来的 pps 和时延,和真实部署(DPDK / AF_XDP、 物理网卡、多 worker)差得很远。
本教程用它来观察行为,不用它来衡量性能。性能该怎么测,第 7 章会讲。
下一步
环境好了,去跑通第一个 NAT —— 五分钟,三条命令。