两个网卡上的同一 IP 一半的流量丢失

两个网卡上的同一 IP 一半的流量丢失

我有一台机器连接到一台稍微损坏的科学仪器上,我正在尝试解决这些问题。该链接是点对点的,2x25G 以太网,不涉及其他设备。该设备以每个接口一个流的方式传输 UDP 数据,我可以配置目标 IP 和 MAC 地址,但该设备的源 IP 地址对于两个接口都是相同的。使用 tcpdump 我可以看到流量到达正确的接口,并且两个接口的目标 IP 和 MAC 地址都是正确的。

但我只能监听其中一个 IP 并获取数据,如果我监听另一个 IP,则没有流量。内核显然会丢弃流量,但我不知道原因,也不知道如何调试。

该机器运行的是 ubuntu 18.04,IP 为 10.50.5.1/16 和 10.50.5.129/16,设备的 IP 为 10.50.1.10。路由:

10.50.0.0/16 dev data0 proto kernel scope link src 10.50.5.1 
10.50.0.0/16 dev data1 proto kernel scope link src 10.50.5.129

有什么建议么?

我可以在 netstat 中看到数据包没有被丢弃,那么它们去了哪里?

Kernel Interface table
Iface      MTU    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg
data0     9600   202320      0      0 0            29      0      0      0 BMRU
data1     9600   203600      0      0 0            31      0      0      0 BMRU

答案1

由于尝试使用两个不同的接口和路由到达同一目的地,因此会出现路由问题。默认情况下,路由是按目的地进行的,而不是按源进行的。默认情况下,Linux 使用弱宿主模型(但 Ubuntu 还设置了反向路径过滤器,见下文)将选择相同的接口:在列表中首先出现的同等候选接口。因此,此处源 IP 地址 10.50.5.1 和 10.50.5.129 都将使用数据0到达 10.50.1.10,因为它目前是路由条目中两个相等条目中的第一个。

否则需要基于源的路由(又名策略路由) 在 Linux 上:让源也用于确定到目的地的路由。

出于类似的原因,在您当前的配置中:在同一个 IP LAN (10.50.0.0/16) 中有两个 IP 地址,Linux 将使用其任何 MAC 地址从其任何接口回答 LAN 中的 ARP 查询。最后,除非像在您的设置中使用永久 ARP 条目而不是执行请求的设备一样,否则通常只有一个接口可以接收所有传入流量(它甚至可能是“错误的”接口)。

最糟糕的是,即使仪器发送到正确的接口和正确的 IP 地址目的地,Ubuntu 的默认设置是严格反向路径启用,丢弃在“错误”接口上收到的数据包。这就是你丢失数据的原因:接口(尝试现代命令ip -statistics link)收到了它们,但路由堆栈之后就丢弃了它们。要验证这一点,只需询问内核的路由堆栈如何看待发送和接收 IP 数据包:

  • 发送

    # ip route get from 10.50.5.1 10.50.1.10
    10.50.1.10 from 10.50.5.1 dev data0 uid 0 
        cache 
    # ip route get from 10.50.5.129 10.50.1.10
    10.50.1.10 from 10.50.5.129 dev data0 uid 0 
        cache 
    

    两者都将使用数据0,忽略数据1

  • 接收

    # ip route get from 10.50.1.10 iif data0 10.50.5.1
    local 10.50.5.1 from 10.50.1.10 dev lo table local 
        cache <local> iif data0 
    # ip route get from 10.50.1.10 iif data1 10.50.5.129
    RTNETLINK answers: Invalid cross-device link
    

    接收数据1被严格反向路径过滤器禁止。

您可以选择禁用反向路径,但如果您需要在特定接口上发送,那么这无论如何都无济于事。

下面我提出一种更清洁的方法。


首先,为了避免与 ARP 设置混淆(以及“ARP 通量”考虑到这些因素,请考虑这两个接口的用途:点对点 IP 接口,即使在以太网链路层上使用也是如此。因此,无需使用整个 /16 LAN:只需点对点路由即可。

# ip address flush dev data0
# ip address flush dev data1

# ip address add 10.50.5.1 peer 10.50.1.10/32 dev data0
# ip address add 10.50.5.129 peer 10.50.1.10/32 dev data1

上面两个命令大多是 的快捷方式ip address add 10.50.5.N/32 dev dataX; ip route add 10.50.1.10/32 dev dataX

您可以选择保留 /16 网络设置(例如:如果最终它们用于实际 LAN 使用,后面还有一堆设备),但这也需要切换arp_filter=1在接口上以确保安全。

要解决基于源的路由问题:使用指向附加路由表的规则,这些路由表将具有主路由表中部分路由的部分副本。我为这些路由表选择了任意值 1000 和 1001(并使用任意固定规则优先级 10000 10001,即使不需要)。关于表 1000 的所有内容实际上都不是必需的(因为它是默认的),但它无论如何都更干净。

ip route add table 1000 10.50.1.10/32 dev data0 src 10.50.5.1
ip route add table 1001 10.50.1.10/32 dev data1 src 10.50.5.129

ip rule add pref 10000 from 10.50.5.1 lookup 1000
ip rule add pref 10001 from 10.50.5.129 lookup 1001

主表仍将确定默认使用哪个接口(可能数据0): 任何未绑定到特定 IP 地址的工具都将使用此 IP 地址(因此 10.50.5.1)作为默认值,如果绑定到 10.50.5.1 也是如此。但现在任何绑定到 10.50.5.129 的工具都将正确使用双向流量数据1

# ip route get 10.50.1.10
10.50.1.10 dev data0 src 10.50.5.1 uid 0 
    cache 
# ip route get from 10.50.5.1 10.50.1.10
10.50.1.10 from 10.50.5.1 dev data0 table 1000 uid 0 
    cache 
# ip route get from 10.50.5.129 10.50.1.10
10.50.1.10 from 10.50.5.129 dev data1 table 1001 uid 0 
    cache 

现在,传入的数据数据1不会被反向路径过滤器丢弃:

# ip route get from 10.50.1.10 iif data0 10.50.5.1
local 10.50.5.1 from 10.50.1.10 dev lo table local 
    cache <local> iif data0 
# ip route get from 10.50.1.10 iif data1 10.50.5.129
local 10.50.5.129 from 10.50.1.10 dev lo table local 
    cache <local> iif data1 

笔记:

附加路由表是手动填充的,而不是由内核填充的。如果关闭某个接口(或删除某个 IP),然后将其恢复(或重新添加 IP),则相应的表 1000 或 1001 将被刷新,并且不会重新填充:必须手动将其添加回去(或使用能够执行此操作的网络配置工具)。

相关内容