解决 rp_filter 反向路径过滤导致的多网卡(多WAN)服务器无法联网
背景
最近遇到一个机器接了一个以太网和一个USB网卡,在 curl 指定 USB 网卡访问互联网时发现无法收到正常的响应,使用tcpdump排查发现实际上有回包,但是应用层完全没有响应
22:43:48.730252 IP 10.121.60.168.37668 > 223.119.30.220.53: Flags [S] ← SYN 发
22:43:48.929587 IP 223.119.30.220.53 > 10.121.60.168.37668: Flags [S.] ← SYN-ACK 回
22:43:49.747409 IP 10.121.60.168.37668 > 223.119.30.220.53: Flags [S] ← 在重传 SYN
凶手就是 rp_filter。
rp_filter 是什么
Reverse Path Filter(反向路径过滤)是 Linux 内核的一个防 IP 欺骗机制,定义在 RFC 3704。它的逻辑是:当数据包从某个接口进入,内核需要判断——
"如果要回复这个包的源地址,应该从哪个接口发出去?"
然后与包实际进入的接口比对。三个取值:
| 值 | 模式 | 行为 |
|---|---|---|
| 0 | 关闭 | 不校验 |
| 1 | strict | 回程接口必须与入接口完全一致,否则丢弃 |
| 2 | loose | 源地址在路由表中可达即放行,不要求同接口 |
他解决了这个问题,典型如攻击者从公网发来源地址伪造成你内网网段的包:内核一查,"回这个地址应该走内网口,包却从公网口进来"——路径矛盾,丢弃。对单出口的服务器,这个检查零成本且有效,所以多数发行版默认开 strict。
多出口环境的问题
问题在于 strict 模式的前提是:一个目的地只有一条合理路径。多 WAN、策略路由、VPN 分流的机器上会导致严重的问题
回到开头的案例,主机的路由表是:
default via 192.168.3.1 dev eth0 ← 主出口,metric 低
default via 10.121.60.169 dev wwan0 metric 5000 ← USB网卡,metric 高
SYN-ACK 从 wwan0 进来,内核做反向校验:"回 223.119.30.220 走哪个口?"查主路由表——eth0 的 metric 更低,答案是 eth0。但包是从 wwan0 进来的。不一致于是丢弃。
于是出现问题:tcpdump 能看到包(抓包点在校验之前),协议栈却永远收不到。这也是 rp_filter 问题最好的诊断特征——抓包有回包、但是连接无法建立
解决办法
修改内核参数为 loose 模式,仍能拦住纯伪造地址(源地址必须真实可达),但不再苛求进出同口:
sudo sysctl -w net.ipv4.conf.all.rp_filter=2
sudo sysctl -w net.ipv4.conf.wwan0.rp_filter=2
坑:内核取的是 max,不是覆盖
rp_filter 的实际生效值是:
生效值 = max(conf.all.rp_filter, conf.<接口>.rp_filter)
注意 max 比较的是数字大小,不是严格程度,而 1(strict)偏偏比 2(loose)小。这带来一个经典陷阱:
# 想彻底关闭某接口的校验
net.ipv4.conf.all.rp_filter = 1 # 全局仍是 strict
net.ipv4.conf.wwan0.rp_filter = 0 # 接口设为 0
# 生效值 = max(1, 0) = 1 ← 还是 strict,设置是不生效的
所以稳妥做法是全局和接口一起设,不依赖当前系统状态,结果确定可预测。
持久化
接口是会热插拔的,新出现的接口从 conf.default 继承初始值,所以持久化配置要写 all + default :
cat > /etc/sysctl.d/99-multiwan.conf << 'EOF'
# 多出口环境:严格反向路径校验会丢弃非对称路由的回包
net.ipv4.conf.all.rp_filter = 2
net.ipv4.conf.default.rp_filter = 2
EOF
sudo sysctl --system

浙公网安备 33010602011771号