单纯靠ip rule无法实现负载均衡,必须结合iptables/nftables标记、ip rule查mark、多路由表协同及禁用rp_filter等避坑措施,才能实现按流一致、跨连接均衡的多出口策略路由。

单纯靠 ip rule 本身无法实现真正的负载均衡,它只负责“分流”,不负责“轮询”或“哈希分发”。要让多出口流量在连接粒度上合理分担,必须结合 iptables(或 nftables)打标记 + ip rule 查标记 + 多路由表协同,才能构建出可控、稳定、可验证的多出口策略路由系统。
明确目标:不是“随机选线”,而是“按流一致、跨连接均衡”
很多初学者误以为加两条默认路由就能负载,但 Linux 内核对出站默认路由不支持 per-packet 负载。真实需求通常是:
- 同一 TCP 连接的所有包必须走同一出口(避免回程路径不一致被丢弃)
- 不同新连接(如 HTTP 请求、SSH 会话)应尽量均匀分配到各 ISP 线路
- UDP 流量(如 DNS、VoIP)也需五元组哈希固定路径,不能每包乱跳
核心四步:标记 → 分流 → 路由 → 防坑
1. 定义路由表并配置出口路径
先在 /etc/iproute2/rt_tables 添加:
101 isp_unicom
再为每张表添加对应网关和直连路由(注意 via 与 dev 的区别):
-
ip route add default via 10.0.1.1 dev eth1 table isp_telecom(假设电信是 /24 网段,需网关可达) ip route add 10.0.1.0/24 dev eth1 src 10.0.1.2 table isp_telecomip route add default via 10.0.2.1 dev eth2 table isp_unicomip route add 10.0.2.0/24 dev eth2 src 10.0.2.2 table isp_unicom
2. 用 iptables 对新连接打标记(CONNMARK)
在 mangle 表的 OUTPUT 或 PREROUTING 链中,对新连接做哈希或轮询标记:
- 简单轮询(适合小规模):
iptables -t mangle -A OUTPUT -m state --state NEW -m statistic --mode nth --every 2 --packet 0 -j CONNMARK --save-mark --nfmask 0xffffffff --ctmask 0xffffffffiptables -t mangle -A OUTPUT -m state --state NEW -m statistic --mode nth --every 2 --packet 1 -j CONNMARK --save-mark --nfmask 0xffffffff --ctmask 0xffffffff - 更稳的源 IP 哈希(推荐):
iptables -t mangle -A OUTPUT -m state --state NEW -m hashlimit --hashlimit-mode srcip --hashlimit-name conn_hash -j CONNMARK --save-mark
3. 用 ip rule 查 fwmark 分流到对应路由表
标记存入连接跟踪后,在路由前查 mark:
ip rule add fwmark 0x1 table isp_telecomip rule add fwmark 0x2 table isp_unicom
注意:fwmark 是 32 位整数,常用 0x1、0x2 等十六进制值,确保与 iptables 中 --set-mark 一致。
4. 关键避坑项必须做
-
禁用反向路径过滤:否则回程包因不在原入口子网而被丢弃
echo 0 > /proc/sys/net/ipv4/conf/all/rp_filterecho 0 > /proc/sys/net/ipv4/conf/eth1/rp_filterecho 0 > /proc/sys/net/ipv4/conf/eth2/rp_filter -
确认规则优先级不冲突:用
ip rule show检查,新规则 pref 值应小于 32766(main 表默认优先级),例如设为pref 100 -
测试不用 ping,用 ip route get:
ip route get 1.1.1.1 from 192.168.1.100 iif eth0 oif eth1
能直接看到内核选了哪条规则、哪张表、下一跳是谁
持久化与运维建议
重启后规则丢失,建议写成 systemd service 或放入网络启动脚本。不要依赖 /etc/rc.local(现代发行版已弱化)。可封装为一个带校验的 Bash 脚本,每次启动自动检查:
- 所有自定义路由表是否存在有效 default 路由
- 所有 ip rule 是否绑定到正确 table 且 pref 无重叠
- iptables mangle 规则是否加载,CONNMARK 是否启用
线上环境务必配合 tcpdump -i eth1 port 53 和 conntrack -L 实时观察标记与流向,避免 DNS 或 HTTPS 因路径分裂失效。



















