本文主要记录在 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/dpvskmod/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 必须一致,否则 makeinsmod 会失败。
  • FullNAT + TOA 是「LB 编码 + RS 解码」的配合关系,只改 RS 或只改 LB 都不够。
  • RHEL 6 / 2.6.32 环境不建议使用当前官方 toa 模块。
  • NAT64 nginx 补丁以 1.14.0 为基准,升级 nginx 需要重新移植与回归测试。
  • 本文命令与路径为示例,请按本机目录与发行版替换。

参考资料