两个 NIC,一个子网 - Linux 似乎主要在一个 NIC 上进行传输,而在另一个 NIC 上进行接收。如何实现?

两个 NIC,一个子网 - Linux 似乎主要在一个 NIC 上进行传输,而在另一个 NIC 上进行接收。如何实现?

我最近从存储中取出一些硬件并组装了一个文件服务器,不知为何,主板上有两个千兆位网卡。由于我正在修修补补,我想我不妨把它们都插上,看看会发生什么。我最初的猜测是 Ubuntu 会将一个网卡分配为具有更高优先级的主要路由,类似于同时连接到 WiFi 和以太网时发生的情况。

但遗憾的是,我发现它主要在一个接口上传输,在另一个接口上接收。这真的让我很困惑:我不明白它是如何或为什么这样做的。我的想法是,服务器在同一个连接上发送和接收数据更有意义,因为使用不同的接口发送确认会使事情变得复杂。我已经使用 iSCSI 和 NFS 对此进行了测试,结果相同。

IP路由:

ashten@mass-storage:~$ ip route
default via 192.168.4.1 dev enp6s0 proto dhcp src 192.168.4.191 metric 100 
default via 192.168.4.1 dev enp5s0 proto dhcp src 192.168.4.57 metric 100 
192.168.4.0/24 dev enp6s0 proto kernel scope link src 192.168.4.191 
192.168.4.0/24 dev enp5s0 proto kernel scope link src 192.168.4.57 
192.168.4.1 dev enp6s0 proto dhcp scope link src 192.168.4.191 metric 100 
192.168.4.1 dev enp5s0 proto dhcp scope link src 192.168.4.57 metric 100

ip-6路由:

::1 dev lo proto kernel metric 256 pref medium
2601:1c1:8804:57b3::/64 dev enp5s0 proto ra metric 100 expires 86394sec pref medium
2601:1c1:8804:57b3::/64 dev enp6s0 proto ra metric 100 expires 86394sec pref medium
fe80::/64 dev enp5s0 proto kernel metric 256 pref medium
fe80::/64 dev enp6s0 proto kernel metric 256 pref medium
default proto ra metric 100 expires 3594sec mtu 1500 
    nexthop via fe80::226d:31ff:fe01:2b8 dev enp6s0 weight 1 
    nexthop via fe80::226d:31ff:fe01:2b8 dev enp5s0 weight 1 pref medium

ifconfig:

ashten@mass-storage:~$ ifconfig
enp5s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.4.57  netmask 255.255.255.0  broadcast 192.168.4.255
        inet6 fe80::6ef0:49ff:fe57:3fc  prefixlen 64  scopeid 0x20<link>
        inet6 2601:1c1:8804:57b3:6ef0:49ff:fe57:3fc  prefixlen 64  scopeid 0x0<global>
        ether 6c:f0:49:57:03:fc  txqueuelen 1000  (Ethernet)
        RX packets 34563916  bytes 49179525874 (49.1 GB)
        RX errors 0  dropped 128937  overruns 0  frame 0
        TX packets 138348330  bytes 30171509363 (30.1 GB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

enp6s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.4.191  netmask 255.255.255.0  broadcast 192.168.4.255
        inet6 fe80::6ef0:49ff:fe57:3ec  prefixlen 64  scopeid 0x20<link>
        inet6 2601:1c1:8804:57b3:6ef0:49ff:fe57:3ec  prefixlen 64  scopeid 0x0<global>
        ether 6c:f0:49:57:03:ec  txqueuelen 1000  (Ethernet)
        RX packets 401203508  bytes 604740226354 (604.7 GB)
        RX errors 0  dropped 121190  overruns 0  frame 0
        TX packets 109130  bytes 99996128 (99.9 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

到目前为止,我主要将文件推送到服务器,因此两者的 TX 字节数都有点低。但请注意,在 enp6s0 上,RX 字节数为 600GB,但 TX 字节数低于 100MB。使用bmon我可以观察到同样的事情:只有一个并发文件传输,服务器在 enp6s0 上接收,同时在 enp5s0 上传输。bmon还显示正在发送和接收的是 IPv6 数据包。IPv6 是否有关于允许这样做的多个 IP 地址的规则?如果是这样,我正在努力寻找该规则。还是我的研究完全偏离了基础?

答案1

我最初的猜测是 Ubuntu 会将一个 NIC 分配为具有更高优先级的主路由,类似于同时连接 WiFi 和以太网时发生的情况。

是的,这通常也是我期望发生的情况(对于 IPv4)。我不确定为什么内核甚至允许您拥有多个具有相同“度量”值的 IPv4 默认路由,我不知道在这种情况下它如何选择“最佳”路由。

但是 Linux 上的 IPv6 略有不同:相同的路由允许,并且它们实际上会自动合并到具有两个网关的“多路径”路由中 - 内核在每个连接的两个接口之间进行负载平衡。

  • 注意 #1:这是 Linux 特有的。其他操作系统(Windows、BSD 等)不支持多路径路由,只会为 IPv4 和 IPv6 选择一个首选接口,正如您所期望的那样。

  • 注意 #2:Linux 也支持 IPv4 多路径路由,但必须手动定义。您看到的自动合并是一个怪癖,与 IPv6 多路径路由最初的实现方式有关(或者更确切地说,不是)。

但遗憾的是,我发现它主要在一个接口上传输,在另一个接口上接收。这真的让我很困惑:我不明白它是怎么做到的,为什么会这样。

机器不会“拉”数据包并通过特定接口接收它们——这个决定比这更间接一些:

  1. 您的主机已从 192.168.4.57 发送了一些内容,而另一个设备想要发送回复。

  2. 发送者想要将 192.168.4.57 解析为 MAC 地址,并广播 ARP 查询。

  3. 您的主机在两个接口上接收两次 ARP 查询。

  4. Linux 默认使用 IPv4 的“弱主机”模型,因此它会检查请求的 IP 地址是否属于整个机器,不是该地址是否属于特定接口。

  5. 您的主持人回答了这两个问题,表示 192.168.4.57 位于 <某个 MAC 地址>。也就是说,它响应两个都其 MAC 地址。

    (您的交换机还会了解源 MAC 地址并将其关联到相应的物理端口。)

  6. 发送者会收到两个 ARP 响应,并且(我可能错了)这两个响应都是按顺序处理的,因此较晚的响应会覆盖较早的响应。因此,发送者“知道”192.168.4.57 位于 6C:F0:49:57:03:EC。

  7. 当来自发送方的数据包到达您的以太网交换机时,它会查看其 MAC 表以确定正确的输出端口 - 因为数据包的地址是 6C:F0:49:57:03:EC,所以它们通过连接到您的 enp6s0 的交换机端口退出。

因此,为了将每个 IP 地址与其对应的接口“紧密”关联,您需要修复步骤 3– 实际上,sysctl 可以告诉 Linux 只响应到达的 ARP 查询正确的接口,就像 IPv6 一样。通过 sysctl.d 设置:

net.ipv4.conf.all.arp_ignore = 1

IPv6 是否有关于允许这种情况的多个 IP 地址的规则?如果有,我正在努力寻找所述规则。

如上所述,Linux 内核在处理 IPv6 NDP 查询时比 IPv4 ARP 查询要严格一些,并且将仅有的如果所需地址与查询属于同一接口,则响应。(我相信这是“强主机”模型的一部分。)

因此,当发送方尝试将 2601:1c1:8804:57b3:6ef0:49ff:fe57:3fc 解析为 MAC 地址时,您的主机将仅从 enp5s0 接口响应。

可能还有其他因素,但这是我能想到的主要原因。

我的想法是,服务器在同一个连接上发送和接收数据更有意义,因为使用不同的接口发送确认会使事情复杂化。

这确实使事情变得复杂一些方式,但只要 IP 能够完成将数据包从 A 传送到 B 的工作,它就会继续工作,TCP 层甚至不会注意到差异。非对称路由对于互联网流量来说相对常见 - 例如,根据您的 ISP 连接情况,您可能通过 Cogent 发送 HTTP 请求并通过 Telia 接收响应...

(什么真正使事情复杂化的是,如果属于同一连接的数据包被发送同时通过两个接口发送消息,因为这可能导致接收方无序接收消息,从而给 TCP 带来问题。但是,当您使用多路径路由或链路聚合时,操作系统会通过将每个 TCP 连接映射到单个接口来尝试避免这种情况。)


附注:你应该调查一下链路聚合(又称“绑定”或“组合”)。Linux 支持 LACP 和几种类型的静态配置 - 这将为您提供一个bond0只有一个 IP 地址和一个 MAC 地址但同时使用两个以太网连接的单一接口。

与多路径路由(仅在一个方向上有帮助)不同,链路聚合将在发送时合并两个链路的带宽在接收时,尽管大多数模式仍然存在单个 TCP 流将仅限于单个以太网连接的限制。

必须支持聚合两个都以太网电缆的两端——如果以太网连接进入交换机,则需要在该交换机上激活聚合。

答案2

基本网络规则:如果您将一台机器多次连接到某个子网,就会出现一些奇怪的事情。除非您了解这些事情并有计划缓解它们,否则不要这样做。

我最初的猜测是 Ubuntu 会分配一个具有更高优先级的 NIC 作为主路由,

它无法知道两个 LAN 端口是否连接到同一个路由器,所以它不会这样做。该机制的工作方式与将两个端口连接到不同的 LAN 段时一样(这是正常用例)。

类似于同时连接 WiFi 和以太网时发生的情况。

这是一个黑客行为,因为它可以区分 WLAN 和 LAN。

但遗憾的是,我发现它似乎主要在一个接口上传输,在另一个接口上接收。

当同一子网中有两个具有不同 IP 地址的接口时,传出连接基本上会随机选择一个。您在接口上看到的数据包比另一个接口上看到的数据包多,这可能也是某种随机效应,例如某种服务会发送大量偶然绑定到一个地址的数据包。

它还应该在仅具有源地址的接口上接收数据包作为答案。我不知道为什么您在接口上看到更多的接收数据包;同样,这可能是由随机效应引起的,即发送少量数据包但接收大量响应的连接碰巧随机选择另一个接口。

tcpdump您可以轻松地在两个接口上使用或检查所有这些wireshark,然后使用curl或 等打开连接并查看会发生什么。传出连接的答案应该只来自同一个接口。

仅仅查看统计数据并不能帮助我们了解实际情况。

相关内容