3.6 版以后的内核中的多路径路由

3.6 版以后的内核中的多路径路由

大家可能都知道,Linux 内核 3.6 版中已经移除了 IPv4 路由缓存,这对多路径路由产生了严重影响。IPv4 路由代码(与 IPv6 不同)以循环方式选择下一跳,因此从给定源 IP 到给定目标 IP 的数据包并不总是经过相同的下一跳。在 3.6 版之前,路由缓存可以纠正这种情况,因为一旦选择了下一跳,它就会保留在缓存中,并且从同一源到同一目的地的所有后续数据包都会经过该下一跳。现在,每个数据包都会重新选择下一跳,这会导致奇怪的事情:如果路由表中有 2 个等价默认路由,每个路由都指向一个互联网提供商,我甚至无法建立 TCP 连接,因为初始 SYN 和最终 ACK 经过不同的路由,并且由于每条路径上的 NAT,它们作为来自不同来源的数据包到达目的地。

有没有相对简单的方法可以恢复多路径路由的正常行为,以便按流而不是按数据包选择下一跳?是否有补丁可以使 IPv4 下一跳选择基于哈希,就像 IPv6 一样?或者你们都如何处理它?

答案1

“相对容易”是一个很难理解的词,但你可能

  1. 为每个链接设置路由表 - 每个链接一个表,具有一个默认网关
  2. 使用 netfilter 在单个流的所有数据包上标记相同的标记
  3. 使用 ip 规则表根据标记通过不同的路由表路由数据包
  4. 使用多下一跳加权路由来平衡网关/链路上的第一个会话数据包。

有一个netfilter 邮件列表中的讨论关于这个话题,我从中窃取了以下列表:

1.路由规则(RPDB和FIB)

ip route add default via <gw_1> lable link1
ip route add <net_gw1> dev <dev_gw1> table link1
ip route add default via <gw_2> table link2
ip route add <net_gw2> dev <dev_gw2> table link2

/sbin/ip route add default  proto static scope global table lb \
 nexthop  via <gw_1> weight 1 \
 nexthop  via <gw_2> weight 1

ip rule add prio 10 table main
ip rule add prio 20 from <net_gw1> table link1
ip rule add prio 21 from <net_gw2> table link2
ip rule add prio 50 fwmark 0x301 table link1
ip rule add prio 51 fwmark 0x302 table link2
ip rule add prio 100 table lb

ip route del default

2. 防火墙规则(使用 ipset 强制“流”LB 模式)

ipset create lb_link1 hash:ip,port,ip timeout 1200
ipset create lb_link2 hash:ip,port,ip timeout 1200

# Set firewall marks and ipset hash
iptables -t mangle -N SETMARK
iptables -t mangle -A SETMARK -o <if_gw1> -j MARK --set-mark 0x301
iptables -t mangle -A SETMARK -m mark --mark 0x301 -m set !
--match-set lb_link1 src,dstport,dst -j SET \
          --add-set lb_link1 src,dstport,dst
iptables -t mangle -A SETMARK -o <if_gw2> -j MARK --set-mark 0x302
iptables -t mangle -A SETMARK -m mark --mark 0x302 -m set !
--match-set lb_link2 src,dstport,dst -j SET \
          --add-set lb_link2 src,dstport,dst

# Reload marks by ipset hash
iptables -t mangle -N GETMARK
iptables -t mangle -A GETMARK -m mark --mark 0x0 -m set --match-set
lb_link1 src,dstport,dst -j MARK --set-mark 0x301
iptables -t mangle -A GETMARK -m mark --mark 0x0 -m set --match-set
lb_link2 src,dstport,dst -j MARK --set-mark 0x302

# Defining and save firewall marks
iptables -t mangle -N CNTRACK
iptables -t mangle -A CNTRACK -o <if_gw1> -m mark --mark 0x0 -j SETMARK
iptables -t mangle -A CNTRACK -o <if_gw2> -m mark --mark 0x0 -j SETMARK
iptables -t mangle -A CNTRACK -m mark ! --mark 0x0 -j CONNMARK --save-mark
iptables -t mangle -A POSTROUTING -j CNTRACK

# Reload all firewall marks
# Use OUTPUT chain for local access (Squid proxy, for example)
iptables -t mangle -A OUTPUT -m mark --mark 0x0 -j CONNMARK --restore-mark
iptables -t mangle -A OUTPUT -m mark --mark 0x0 -j GETMARK
iptables -t mangle -A PREROUTING -m mark --mark 0x0 -j CONNMARK --restore-mark
iptables -t mangle -A PREROUTING -m mark --mark 0x0 -j GETMARK

您可能需要关注 netfilter 邮件列表讨论以了解上述内容的一些变体。

答案2

如果可能的话,升级到 Linux 内核 >= 4.4 ....

基于哈希的多路径路由引入了,这在很多方面都比 3.6 之前的行为更好。它基于流,对源 IP 和目标 IP(忽略端口)进行哈希处理,以保持单个连接的路径稳定。一个缺点是,我相信 3.6 之前有各种算法/配置模式可用,但现在你得到了你得到的东西!weight不过,你可以使用影响路径的选择。

如果你我的情况那么您确实想要3.6 >= behaviour < 4.4但它不再受支持。

如果你确实升级到 >= 4.4 那么这应该可以解决问题,而无需所有其他命令:

ip route add default  proto static scope global \
nexthop  via <gw_1> weight 1 \
nexthop  via <gw_2> weight 1

或者通过设备:

ip route add default  proto static scope global \
 nexthop  dev <if_1> weight 1 \
 nexthop  dev <if_2> weight 1

相关内容