VPN 现有两条出国线路,早先只是随机使用一条线路。现在使用 iptables statistic module 做了基于连接的负载均衡,实测连接比较平均地分布到了两条线路上。理论上,一条线路故障还能自动切换到另一条。

设源 IP 的范围为 source-ip-range(如果是自己用则省略 -s source-ip-range 这个参数)。

  1. 在 iptables PREROUTING 阶段,把基于 connection 的 mark 转化为基于 packet 的 mark。
  2. 如果还没有被 mark,则认为是一个新的连接,使用 statistic module 的 round robin 方式,打上1号或2号 mark。
  3. 把基于 packet 的 mark 转换回 connection mark。
  4. 在 policy routing 阶段,让 mark 1 先查 2000 号表再查 2001,mark 2 先查 2001 号表再查 2000,没有 mark 的查默认表。
  5. 在 2000 号表中让需要 VPN 的 IP range 走 tunnel 1,在 2001 号表中让同样一批 IP range 走 tunnel 2。

为便于理解,对配置做了些简化,贴在下面,供参考:

iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark
iptables -t mangle -A PREROUTING -s source-ip-range -m mark --mark 0x0 -m statistic --mode nth --every 2 --packet 0 -j MARK --set-mark 1
iptables -t mangle -A PREROUTING -s source-ip-range -m mark --mark 0x0 -m statistic --mode nth --every 2 --packet 1 -j MARK --set-mark 2
iptables -t mangle -A PREROUTING -j CONNMARK --save-mark
while read prefix comment; do
    ip route replace $prefix via tunnel1-gateway table 1000
    ip route replace $prefix via tunnel2-gateway table 2000
done <blocked-ip.txt

ip rule add fwmark 1 lookup 2000 pref 1000 # default
ip rule add fwmark 1 lookup 2001 pref 1001 # backup
ip rule add fwmark 2 lookup 2001 pref 1002 # default
ip rule add fwmark 2 lookup 2000 pref 1003 # backup

上述 routing policy 有如下特性:

  • 正常情况下,连接会平均分配在两个 tunnel 上。
  • 只要是内核 conntrack 模块能认出来的连接,比如 TCP 连接,一个连接内的 packet 会走同一个 tunnel。
  • 不在这些 IP range 中的连接会走默认路由表。
  • 如果一个 tunnel(比如 tunnel 1)挂了,相应的路由表项(2000 号表)会被内核自动清除,mark 1 就会查 2001 去走另一个 tunnel,mark 2 不受影响。
  • 即使两个 tunnel 都挂了,也会走默认路由,总比完全中断好。

完整代码在 https://gitgeek.net/boj/smartproxy.git