完全独立的网络接口

完全独立的网络接口

我有一些机器有多个网络接口,每个接口有一个或多个 IP 地址。这些 IP 地址可能位于也可能不在同一子网中。例如,结果ip a可能如下所示:

1: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000                                                                                                 
    link/ether 11:11:11:11:11:11 brd ff:ff:ff:ff:ff:ff                                                                                                                                        
    inet 10.0.1.2/24 brd 10.0.1.255 scope global eno1                                                                                                                                     
       valid_lft forever preferred_lft forever                                                                                                                                                
    inet 10.12.1.3/24 brd 10.12.1.255 scope global secondary eno1:0                                                                                                                         
       valid_lft forever preferred_lft forever                                                                                                                                                
2: eno2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000                                                                                                 
    link/ether 22:22:22:22:22:22 brd ff:ff:ff:ff:ff:ff                                                                                                                                        
    inet 10.0.1.4/24 brd 10.0.1.255 scope global eno2                                                                                                                                     
       valid_lft forever preferred_lft forever                                                                                                                                                
    inet 192.168.1.3/24 brd 192.168.1.255 scope global secondary eno2:0                                                                                                                         
       valid_lft forever preferred_lft forever   
2: eno3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000                                                                                                 
    link/ether 33:33:33:33:33:33 brd ff:ff:ff:ff:ff:ff                                                                                                                                        
    inet 172.23.1.2/24 brd 172.23.1.255 scope global eno3                                                                                                                                    
       valid_lft forever preferred_lft forever                              

我想要的是,对进入一个接口的流量的响应通过同一接口发出;并且还能够告诉应用程序通过一个特定接口发送流量,无论目的地如何。

现在情况并非如此;例如,如果我ping 10.0.1.2来自192.168.1.0/24网络中的另一台机器,响应将通过 eno2 路由,这会导致各种问题。

我怎么做?我可以设置多个路由表吗?我可以使用接口名称或其他内容标记传入数据包吗?

答案1

备注:您的设置需要知识产权网络 10.0.1.0/24 和 192.168.1.0/24 位于同一网络上以太网网络。如果不是这种情况,您必须提供详细的网络拓扑,仅提供一台主机是不够的。我准备编辑这个答案以匹配它,即使问题已经可以在这里重现和修复。这就是为什么在最后的模型设置中,我连接了两个桥:诺1埃诺2在同一个局域网内。我还添加了 IP 为 10.0.1.1/24 / 192.168.1.1/24 的路由器。

请注意,我将多次提到两个设置:rp_filterarp_filter,不要混淆他们。

具有相同解决方案的两个不同问题:

  • ARP通量(注意:链接中的解决方案中提供的两个参数已被 的使用所取代arp_filter),存在是因为默认情况下 Linux 并不真正遵循 ARP 的路由规则。当两个接口上的地址位于同一 IP LAN 中时,这会导致问题。这个问题应该得到解决,否则你可能会遇到奇怪的问题,持续几分钟。因此arp_filter必须激活它才能匹配路由。它在这里不需要额外的努力就可以工作,因为解决下一问题的策略路由也将解决这一问题。
  • 路由问题,因为默认情况下可以直接通过以下方式看到到 192.168.0/24 的路由埃诺2, 所以诺1永远不会考虑回复。设置后rp_filter,通过 eno1 的传入流量将被丢弃,甚至无法应答。如果没有设置或松动,则查询到10.0.1.2上诺1通过错误的接口得到答复:埃诺2

总结一下:

  • 修复ARP外向的行为将修复异常传入IP问题,对10.0.1.2的传入请求来自哪里埃诺2。这可以通过降低看到诺1在下面的模型设置中,一两分钟后(路由器上的 ARP 缓存过期)将收到对 10.0.1.2 的 ping 请求,并且所有答案都可以使用埃诺2。现在带回来诺1up 不会恢复传入的 IP。这是因为路由器仍然在 ARP 缓存 10.0.1.2 中埃诺2的 MAC 地址,并将通过首先对该地址执行单播查询来刷新,而不是诺1的 MAC 地址。暂时降低埃诺2并刷新路由器上的 ARP(或执行 DAD/盖普请求使用arping在主机上)将恢复到之前的情况(如果 则没有答案rp_filter=1)。

    强调我的,从linux/文档/网络/ip-sysctl.txtarp_filter

    arp_filter - BOOLEAN
    1 - 允许您在同一子网上拥有多个网络接口
    ,并应答每个接口的 ARP
    基于内核是否会从
    ARP 的 IP路由数据包
    出该接口(因此,您必须使用
    基于源的路由才能使其工作
    )。换句话说,它允许控制
    哪些卡(通常是 1 个)将响应 arp 请求。

    0 -(默认)
    内核可以使用其他接口的地址响应 arp 请求
    。这可能看起来是错误的,但通常是
    有道理的,因为它增加了成功沟通的机会。
    Linux 上的 IP 地址由整个主机拥有,而不是通过
    特定的接口。仅对于负载平衡等更复杂的设置
    ,此行为才会导致问题。

    如果conf/{all,interface}/arp_filter至少其中之一
    设置为TRUE,则接口的arp_filter将被启用,
    否则它将被禁用

  • 修理外向的IP 路由问题,您需要额外的路由表,每个 IP 需要一个路由表来修复。每个有问题的IP都应该有一个特定的规则来选择该IP使用其特定的路由表,而该路由表不会包含在内,从而忽略其他本地IP的相关路由,而是强制指定的接口。

如果您打算部署此类设置,您可能应该进行一些自动化操作。由于这种问题通常需要每个 IP 一个路由表来修复,并且现代内核上可以有 2^32 个路由表,您甚至可以将整个 IPv4 映射到路由表(考虑到少数特殊表的数量足够低)以避免被映射和覆盖,因为 IP 0.xxx/8 被保留)。我添加了一些 shell 函数来缓解这个问题。这些路由表编号不需要名称,但可以选择以/etc/iproute2/rt_tables相同的方式命名,并且出于相同的目的,可以在 中命名 IP 地址/etc/hosts。这里不需要。

现在希望已经得到解释,纠正方法如下:

在主机上运行以下脚本。

fix.sh:

#!/bin/sh

map_localip_to_interface () {
    ip -brief address show to "$1" | awk '{ print $1 }'| sed 's/@.*$//'
}

map_ip_to_table () {
        printf '%s\n' "$1" | awk -F. '{ print (((($1*256)+$2)*256)+$3)*256+$4 }'
}

#not used here
map_table_to_ip () {
    local ip
    local n="$1"
    for i in 1 2 3 4; do
        ip="$(($n%256)).$ip"
        n=$(($n/256))
    done
    printf '%s\n' "$ip" | sed 's/\.$//'
}

ip rule add from 10.0.1.2 table $(map_ip_to_table 10.0.1.2)
ip rule add from 10.0.1.4 table $(map_ip_to_table 10.0.1.4)
ip rule add from 192.168.1.3 table $(map_ip_to_table 192.168.1.3)

ip route add table $(map_ip_to_table 10.0.1.2) 10.0.1.0/24 dev $(map_localip_to_interface 10.0.1.2)
ip route add table $(map_ip_to_table 10.0.1.2) default via 10.0.1.1 dev $(map_localip_to_interface 10.0.1.2)
ip route add table $(map_ip_to_table 10.0.1.4) 10.0.1.0/24 dev $(map_localip_to_interface 10.0.1.4)
ip route add table $(map_ip_to_table 10.0.1.4) default via 10.0.1.1 dev $(map_localip_to_interface 10.0.1.4)
ip route add table $(map_ip_to_table 192.168.1.3) 192.168.1.0/24 dev $(map_localip_to_interface 192.168.1.3)
ip route add table $(map_ip_to_table 192.168.1.3) default via 192.168.1.1 dev $(map_localip_to_interface 192.168.1.3)

sysctl -q -w net.ipv4.conf.eno1.arp_filter=1
sysctl -q -w net.ipv4.conf.eno2.arp_filter=1

这将导致:

# ip rule
0:  from all lookup local 
32763:  from 192.168.1.3 lookup 3232235779 
32764:  from 10.0.1.4 lookup 167772420 
32765:  from 10.0.1.2 lookup 167772418 
32766:  from all lookup main 
32767:  from all lookup default 

# ip route show table all |grep 'table [1-9]'
default via 10.0.1.1 dev eno1 table 167772418 
10.0.1.0/24 dev eno1 table 167772418 scope link 
default via 192.168.1.1 dev eno2 table 3232235779 
192.168.1.0/24 dev eno2 table 3232235779 scope link 
default via 10.0.1.1 dev eno2 table 167772420 
10.0.1.0/24 dev eno2 table 167772420 scope link 

# sysctl net.ipv4.conf.eno{1,2}.arp_filter
net.ipv4.conf.eno1.arp_filter = 1
net.ipv4.conf.eno2.arp_filter = 1

所有这些使得对于任何有问题的 IP,在使用它时,路由堆栈会切换到专用路由表,这将强制使用正确的网络接口。 ARP 也会遵循同样的原理。

请注意,相同的设置允许本地启动的传出 IP 正常工作。如果 192.168.1.100 是某个 Web 服务器:

ping -I 10.0.1.2 192.168.1.100 # through eno1 and via router
ping -I 10.0.1.4 192.168.1.100 # through eno2 and via router
ping -I 192.168.1.3 192.168.1.100 # through eno2 directly

同样,如果在 192.168.1.100 上使用 socat (1.7.3.2) 运行此命令:

socat tcp4-listen:5555,reuseaddr,fork exec:'printenv SOCAT_PEERADDR'

然后在多宿主主机上,您可以得到回复,告诉您一切顺利:

# socat tcp4:192.168.1.100:5555 -
192.168.1.3
# socat tcp4:192.168.1.100:5555,bind=10.0.1.2 -
10.0.1.2
# socat tcp4:192.168.1.100:5555,bind=10.0.1.4 -
10.0.1.4

您可以验证tcpdump流量是否通过了预期的接口。

如果您在其他 IP 方面遇到更多路由问题,您现在应该知道该怎么办。

笔记:

  • 我在这里留下了 10.12.1.3,但因为它已经打开了诺1现在已经有了arp_filter=1,它可能还应该接收自己的路由表和规则才能正常工作。
  • 如果主机也进行路由,则可能需要改进规则和路由表。你必须进行测试。

样机设置:

以 root 身份运行以下脚本setup.sh。现在这个问题可以这样重现:

术语1:

ip netns exec mhost tcpdump -l -e -n -s0 -p -i eno1

术语2:

ip netns exec mhost tcpdump -l -e -n -s0 -p -i eno2

第3项:

ip netns exec h1921681100 ping -n 10.0.1.2

不会有任何回复。至少这样做:

ip netns exec mhost sysctl -w net.ipv4.conf.eno1.rp_filter=2

将允许不对称路由,ping 来自诺1并离开至埃诺2(或全部通过埃诺2如果“ARP 通量”效应生效)。

运行上面的脚本fix.sh将修复所有问题:

ip netns exec mhost sh fix.sh

setup.sh:

#!/bin/sh

if ip netns id | grep -qv '^ *$' ; then
        printf 'ERROR: leave netns "%s" first\n' $(ip netns id) >&2
        exit 1
fi

hosts='mhost router h1921681100'
nets='net1001 net1921681 net10121 net172231'

for ns in $hosts $nets; do
    ip netns del $ns 2>/dev/null || :
    ip netns add $ns
    ip netns exec $ns sysctl -q -w net.ipv6.conf.default.disable_ipv6=1
    ip netns exec $ns sysctl -q -w net.ipv4.conf.default.rp_filter=1
    ip netns exec $ns sysctl -q -w net.ipv4.conf.all.rp_filter=1
done

for ns in $hosts; do
    ip -n $ns link set lo up
done

bmac=1
for ns in $nets; do
    ip -n $ns link add bridge0 address 02:00:00:00:00:$(printf '%02d' $bmac) type bridge
    ip -n $ns link set bridge0 up
    bmac=$(($bmac+1))
done

link_lan () {
    [ "$1" = "$2" ] && return 1
    ip -n $2 link add p-$1 type veth peer netns $1 name p-$2 2>/dev/null || return 1
    ip -n $2 link set p-$1 master bridge0 up
    ip -n $1 link set p-$2 master bridge0 up
}

link_lan net1001 net1921681

ip -n h1921681100 link    add eth0 type veth peer netns net1921681 name p-h1921681100
ip -n h1921681100 link    set eth0 up
ip -n  net1921681 link    set p-h1921681100 master bridge0 up
ip -n h1921681100 address add 192.168.1.100/24 broadcast 192.168.1.255 dev eth0
ip -n h1921681100 route   add default via 192.168.1.1

ip netns exec router sysctl -q -w net.ipv4.conf.default.forwarding=1

ip -n router     link    add eth10011 type veth peer netns net1001 name p-router10011
ip -n router     link    set eth10011 up
ip -n net1001    link    set p-router10011 master bridge0 up
ip -n router     address add 10.0.1.1/24 broadcast 10.0.1.255 dev eth10011

ip -n router     link    add eth1921681 type veth peer netns net1921681 name p-router1921681
ip -n router     link    set eth1921681 up
ip -n net1921681 link    set p-router1921681 master bridge0 up
ip -n router     address add 192.168.1.1/24 broadcast 192.168.1.255 dev eth1921681


ip netns exec mhost sysctl -q -w net.ipv4.conf.default.forwarding=0

ip -n mhost      link    add eno1 type veth peer netns net1001 name p-mhosteno1
ip -n mhost      link    set eno1 up
ip -n net1001    link    set p-mhosteno1 master bridge0 up
ip -n mhost      address add 10.0.1.2/24 broadcast 10.0.1.255 dev eno1
ip -n mhost      address add 10.12.1.3/24 broadcast 10.12.1.255 dev eno1

ip -n mhost      link    add eno2 type veth peer netns net1921681 name p-mhosteno2
ip -n mhost      link    set eno2 up
ip -n net1921681 link    set p-mhosteno2 master bridge0 up
ip -n mhost      address add 10.0.1.4/24 broadcast 10.0.1.255 dev eno2
ip -n mhost      address add 192.168.1.3/24 broadcast 192.168.1.255 dev eno2

ip -n mhost      link    add eno3 type veth peer netns net172231 name p-mhosteno3
ip -n mhost      link    set eno3 up
ip -n net172231  link    set p-mhosteno3 master bridge0 up
ip -n mhost      address add 172.23.1.2/24 broadcast 172.23.1.255 dev eno3

相关内容