四层转发:把客户端 IP 传给源站
四层转发“回源携带客户端 IP”的三个选项怎么选,TOA 选项的格式与限制,在源站安装 TOA 接收端,以及怎么验证源站拿到了客户端地址。
三个选项
四层转发编辑页的“回源携带客户端 IP”有三个选项,只能选一个,都只用于 TCP。
| 选项 | 源站需要做什么 | 说明 |
|---|---|---|
| 关闭 | 无 | 源站看到的是节点地址 |
| PROXY protocol v1 | 源站程序支持 PROXY protocol(如 nginx listen ... proxy_protocol、HAProxy accept-proxy) | 改应用配置,不改内核;IPv4、IPv6 客户端都能传 |
| TOA | 源站安装 TOA 接收端(内核模块或 eBPF 版) | 应用不用改,通过 getpeername() 拿到客户端地址;只传 IPv4 |
TOA 怎么工作
节点在发往源站的 TCP 连接上,从三次握手的 ACK 开始,在最先几个数据段里加一个 TCP 选项:kind 254、长度 8,内容是客户端端口(2 字节)和 IPv4 地址(4 字节),网络字节序。源站确认收到数据后,节点就不再加这个选项。源站的 TOA 接收端读到它,让这条连接的 getpeername() 返回客户端地址。
限制:
- 只传 IPv4 客户端。IPv6 客户端的连接照常转发,但不带 TOA,源站看到节点地址;需要 IPv6 客户端地址时改用 PROXY protocol。
- 源站没装接收端时,内核忽略这个选项,连接不受影响,只是拿不到客户端地址。
- 节点需要 Linux 5.10 及以上内核。
- 节点到源站之间如果有会剥掉未知 TCP 选项的设备(部分防火墙、负载均衡),TOA 会丢失。
在源站安装接收端
我们不分发源站模块。常用的开源实现如下,使用前请按各仓库的说明和许可证自行确认:
| 实现 | 地址 | 备注 |
|---|---|---|
| 华为云 ELB TOA | https://github.com/huaweicloud/elb-toa | 内核模块 |
| Huawei TCP_option_address | https://github.com/Huawei/TCP_option_address | 内核模块,较老,新内核可能编译不过 |
| 阿里云 CDN TOA | https://github.com/aliyun/alibabacloud-cdn-tool-toa | 内核模块与 eBPF 版(eBPF 版要求 5.10 以上内核和 cgroup v2) |
内核模块一般需要与运行内核一致的头文件,例如 Debian/Ubuntu 的 linux-headers-$(uname -r)、RHEL/Rocky 的 kernel-devel-$(uname -r),再按仓库说明编译并 insmod。
验证
- 在四层转发里选择“TOA”并发布。
- 从一台客户端连接转发端口,看源站应用日志里记录的对端地址,应为客户端的公网地址。
- 不要用
ss -tn或netstat判断:内核模块版接收端只改getpeername()的返回值,不改内核连接表,这两个命令看到的仍是节点地址。 - 没生效时在源站抓包:
tcpdump -nn -v -i <网卡> 'tcp port <源站端口>',握手 ACK 的选项里应能看到 kind 254(tcpdump 显示为exp-开头)。抓得到但应用里仍是节点地址,说明接收端没装好;抓不到,检查节点内核版本和中间设备。
常见问题
装了 TOA,应用日志里还是节点地址
先抓包确认选项到了源站。到了,再确认接收端已加载、应用读的是 getpeername() 而不是自己解析别的头。
客户端是 IPv6
TOA 不传 IPv6 客户端,改用 PROXY protocol v1。