本文主要记录在 DPVS 的 FullNAT 模式下,如何在 RS 上编译并加载官方 toa 内核模块,使业务侧能够拿到真实客户端 IP;并补充 NAT64 场景下 nginx 补丁的注意事项。
此前我们写过 DPVS 简介与部署 以及 DPDK 在 DPVS 中的应用,需要背景的话可以先看这两篇。FullNAT 是 DPVS 的重要能力之一:RS 上默认看到的是 LB 地址,必须通过 TOA(TCP Option Address)一类机制才能还原客户端源地址。
背景说明
在 FullNAT 路径上,RS 收到的报文源地址通常是负载均衡器地址。若业务依赖 $remote_addr、访问日志中的客户端 IP 或基于源 IP 的限流/审计,需要在 RS 内核中加载 TOA 模块,从 TCP option 中解析真实地址。
本文以 DPVS 官方仓库自带的 kmod/toa 为准(自 v1.7 起内置,源自 Alibaba TOA,并支持 IPv6 / NAT64 相关能力)。环境不同时,内核版本与编译依赖需要按实际情况调整。
环境信息
本文验证环境示例:
| 项目 | 取值(示例) |
|---|---|
| 系统 | CentOS Linux 7.9 |
| 编译器 | GCC 4.8.5(Red Hat 4.8.5-44) |
| 模块来源 | iqiyi/dpvs 的 kmod/toa |
| 角色 | FullNAT 后端 RS |
需要注意的是:当前运行内核版本必须与 kernel-devel 对应版本一致,否则模块无法编译或无法加载。
准备工作
这里我们先安装编译依赖:
yum group install "Development Tools"
yum install patch libnuma-devel numactl numactl-devel kernel-devel openssl-devel popt-devel -y
确认 uname -r 与 /lib/modules/$(uname -r)/build 存在且匹配。若内核与 devel 包不一致,建议先对齐内核再继续。
编译并加载 toa 模块
官方说明:TOA 源码位于 DPVS 的 kmod/toa,自 v1.7 起纳入主仓。
这里我们克隆仓库并编译模块:
git clone https://github.com/iqiyi/dpvs.git
cd dpvs/kmod/toa/
make
编译成功后会生成 toa.ko。加载并确认模块在列表中:
insmod ./toa.ko
lsmod | grep toa
若输出中可以看到 toa 行,说明模块已加载。
为保证开机自动加载,可在 rc.local(或发行版等价的开机脚本 / systemd unit)中加入:
/usr/sbin/insmod /path/to/toa.ko
请将路径替换为实际 toa.ko 位置。
兼容性:RHEL/CentOS 6 类系统
在较老的 2.6.32 内核(例如 RHEL 6.9)上,make 有时仍能产出 toa.ko,但加载阶段可能失败,例如:
insmod: error inserting 'toa.ko': -1 Unknown symbol in module
编译阶段还可能出现 kallsyms_lookup_name 未定义一类警告。实测表明:不建议在 AS6 / RHEL 6 一类老内核上依赖该官方 toa 模块;请优先在 CentOS 7 及以上、与模块匹配的内核上使用。
测试验证
最简单的方式是在 RS 上用 nginx 回显 $remote_addr:
server {
listen 80 default_server;
location / {
default_type text/plain;
return 200 "$remote_addr\n";
}
}
从不同客户端经 VIP / FullNAT 访问该 RS。若 TOA 生效,业务侧看到的应是客户端真实 IP,而不是负载均衡器地址。
示意(IP 已脱敏):
curl http://<rs-or-vip>/
# 期望输出为客户端地址,例如 <client-ip>
若始终只看到 LB 地址,需要检查:模块是否加载、FullNAT 是否真正下发 TOA option、路径是否经过该 RS,以及应用是否读取了正确的变量。
NAT64 TOA 与 nginx 补丁
DPVS 还为 NAT64 场景提供了面向 nginx 的示例补丁:当 VIP 为 IPv6、RS 为 IPv4 时,可在 nginx 中通过额外变量获取用户真实 IPv6 地址。补丁位于官方仓库 kmod/toa/example_nat64/nginx/,文件名表明其基于 nginx 1.14.0 制作。
在 nginx 1.14.0 上打补丁
cd /path/to/nginx-1.14.0
cp /path/to/dpvs/kmod/toa/example_nat64/nginx/nginx-1.14.0-nat64-toa.patch ./
patch -p1 < nginx-1.14.0-nat64-toa.patch
若 patch 全部成功,可按常规流程 ./configure && make && make install。完成后可在访问日志或配置中使用 $toa_remote_addr、$toa_remote_port 等变量(以补丁实际导出为准)。
在较新 nginx(如 1.21.x)上的差异
直接对 1.21.x 应用同一 1.14.0 补丁时,部分 hunk 会失败(例如 ngx_connection.h),原因是上游源码结构变化。需要对照 reject 文件,把 TOA 相关结构体、getsockopt 取址逻辑和 HTTP 变量注册手动移植到新版本对应位置,再编译。
补丁的核心改动通常包括:
- 在连接结构中增加 NAT64 peer 信息
- 在
accept路径通过getsockopt查询 TOA - 注册
toa_remote_addr/toa_remote_port一类变量
不同 nginx 小版本行号会变,不能假设「同一 patch 文件一键适用所有版本」。生产变更前应在测试机完整编译并做业务验证。
注意事项
- 内核与
kernel-devel必须一致,否则make或insmod会失败。 - FullNAT + TOA 是「LB 编码 + RS 解码」的配合关系,只改 RS 或只改 LB 都不够。
- RHEL 6 / 2.6.32 环境不建议使用当前官方 toa 模块。
- NAT64 nginx 补丁以 1.14.0 为基准,升级 nginx 需要重新移植与回归测试。
- 本文命令与路径为示例,请按本机目录与发行版替换。