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


16

众所周知,ipv4路由缓存已在3.6 Linux内核系列中删除,这对多路径路由产生了严重影响。IPv4路由代码(与IPv6不同)以循环方式选择下一跳,因此从给定源IP到给定目标IP的数据包并不总是通过相同的下一跳。在3.6之前的版本中,路由缓存正在纠正这种情况,因为一旦选择了下一跳,就将其保留在缓存中,并且从同一源到同一目的地的所有其他数据包都将通过该下一跳。现在,为每个数据包重新选择下一跳,这会导致奇怪的事情:在路由表中有2个等价的默认路由,每个都指向一个互联网提供商,我什至无法建立TCP连接,因为初始SYN和最终ACK通过不同的路线

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


您是否具有与此类似的“拆分访问”设置:lartc.org/howto/lartc.rpdb.multiple-links.html?如果是这样,您的规则集和路由是什么样的?
2015年

尝试多次使用“ ip route get 173.194.112.247”并发布输出
c4f4t0r 2015年

谢谢你的提问。:)首先,您没有给我们一个例子。所以我想你有一个ip ro add 8.8.8.8/32 nexthop via 1.2.3.4 nexthop via 1.2.3.5正确的假设吗?
poige

是的,这是正确的,但是通常它的ip路由添加0.0.0.0/0并具有多个下一跳。
尤金(Eugene)2015年

比特兔,是的,完全一样。在我的情况下,“ provider 1”和“ provider2”是连接到我的内部网络和提供商网络的边界路由器,它们提供源NAT。在我的内部路由器上,我只有默认网关,其中有2个跃点指向provider1和provider2,没有其他路由。防火墙规则仅允许客户端计算机使用某些服务(例如HTTP),并阻止其他所有内容。
尤金(Eugene)2015年

Answers:


8

如果可能,请升级到Linux Kernel> = 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

对于使用此解决方案的任何人-也请查看:net.ipv4.fib_multipath_use_neigh,用于自动禁用“已删除”的nexthop /网关。
罗斯蒂斯拉夫·坎迪拉罗夫

6

“相对容易”是一个困难的术语,但是您可能

  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邮件列表讨论,以获取上述内容的一些变化。


不知道,但可能是简单u32获得重要参数哈希,然后在“标签”分配ip rule
poige

谢谢,但这看起来很复杂。我不太了解的是,这是什么负责“在单个流的所有数据包上标记相同的标记”?ipset魔术如何工作?我认为ipset只是一组经过哈希处理且可以在规则中匹配的特定IP。
尤金(Eugene)

您是对的ipset-它只是创建使用填充--add-set并与使用匹配的集--match-set-但这主要是针对处于NEW状态的连接。对于ESTABLISHED状态连接,使用目标--restore-mark参数将标记标记在数据包上CONNMARK-此指令将连接的标记复制到数据包。该连接的标记是先前--save-markPOSTROUTING链中使用的(属于NEW连接的数据包将通过该链)。该脚本在我看来似乎太复杂了,但是传达了这个想法。
the-wabbit

1
是的,我想,现在我有了主意。最后一个问题:您了解内核开发人员为什么不为ipv4引入基于哈希的下一跳选择吗?是否有一些理由不与路由缓存删除一起实施?ipv6的类似解决方案效果很好。对于这样一个简单的任务,难道不是所有的魔术都不过分吗?
尤金(Eugene)

1
@Eugene不幸的是,我距离IP堆栈开发(或一般来说是Linux Kernel开发)还远远不够,不能权威地回答您的任何问题,但我推测使用带有IPv4的不同提供程序的多路径被认为是太多了。一个角落的盒子,可以做更多的工作。使用netfilter CONNMARK显然看起来很麻烦,但在删除路由缓存代码的决定中,甚至可能被认为是“可用的解决方法”。
the-wabbit
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.