同一子网上的两个 NIC 但网络不同?

Spl*_*ash 6 networking linux routing iptables

描述

这与this question类似,但不同:

Linux 电脑有两个网卡。eth0静态分配了一个 IP。eth1 是由现场人员分配的,可以是相同的IP,也可以是不同的IP。该计划是为了eth0并且eth1将在不同的网络上,并且永远不会交换或桥接任何数据包。而我的Linux应用程序只监听和接受的连接eth0eth1,并开始在接收到数据包发送,但从未尝试开展任何活动。

两个网卡是否可以在同一个子网中拥有 IP:192.168.1.4和 192.168.1.5?正如您所看到的eth0eth1它们被分配到同一个子网 ( 255.255.255.0) 中,但它们没有连接,也不应该连接。

在这种情况下,当应用程序侦听eth0port 55555,接受连接并返回数据包时,底层是否知道它应该返回 eth0 ?或者它可能会尝试,eth1因为操作系统认为它在同一个子网上?我需要对路由表做些什么来确保它不认为这两个网卡实际上在同一个子网上?

在这种情况下,我应该不仅避免使用相似的 IP,还应该避免使用相同的子网吗?

更新

如果 PC0 和 PC1 的 IP 相同怎么办?

PC0 (1.100) <-------> [eth0 (1.4)  My System eth1 (1.5)]<-------PC1(1.100)
Run Code Online (Sandbox Code Playgroud)

我的应用程序需要在 eth0 和 eth1 上侦听端口 55555,并且请求通过 eth1 传入,操作系统是否知道通过 eth1 进行回复?这样的配置会不会有问题?

商业案例是我正在构建这个嵌入式系统,并且我为 eth0 和 PC0 预定义了 IP(PC0 也可以是 DHCP)。但是我的客户已经在右侧有一个网络。如果他们的设备与 PC0 或 eht0 冲突怎么办?即使在 eth0 上有 DHCP 服务器,在将 IP 分配给 PC0 时,也无法排除右侧的 IP。如果这会产生问题,我有很多解决方案。但是我想听听同行的意见是否有问题。

我的两个同事认为这不是问题。我的观点是,即使使用套接字(假设绑定到右侧),IP 层也不知道使用哪个接口来回复数据包。无论我们如何设置路由表,它都会选择一个。

小智 6

是的你可以。您的计算机将从与到目标网络的路由关联的任何接口发送返回数据包。

目标网络很可能包含在默认路由中 - 0.0.0.0/0,通过默认网关。

不相信我?
让我们检查一下:

[root@localhost ~]# ip address show label eth* | grep -v 'link\|val'
  2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
      inet 192.168.1.4/24 brd 192.168.1.255 scope global eth0
  3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
      inet 192.168.1.5/24 brd 192.168.1.255 scope global eth1
Run Code Online (Sandbox Code Playgroud)

两个接口,eth0 和 eth1,具有 IP 地址和掩码,如您的问题。

完整的启动配置:

[root@localhost ~]# cat /etc/sysconfig/network-scripts/ifcfg-eth0
  DEVICE="eth0"
  NM_CONTROLLED="no"
  ONBOOT="yes"
  TYPE=Ethernet
  DEFROUTE=yes
  IPV4_FAILURE_FATAL=yes
  IPV6INIT=no
  Name="eth0"
  BOOTPROTO=none
  IPADDR=192.168.1.4
  NETMASK=255.255.255.0
  GATEWAY=192.168.1.1

[root@localhost ~]# cat /etc/sysconfig/network-scripts/ifcfg-eth1
  DEVICE="eth1"
  NM_CONTROLLED="no"
  ONBOOT="yes"
  TYPE=Ethernet
  DEFROUTE=yes
  IPV4_FAILURE_FATAL=yes
  IPV6INIT=no
  Name="eth1"
  BOOTPROTO=none
  IPADDR=192.168.1.5
  NETMASK=255.255.255.0
  GATEWAY=192.168.1.2
Run Code Online (Sandbox Code Playgroud)

请注意,为每个 - .1 和 .2 设置了不同的网关地址。
现在让我们看看路由表:

[root@localhost ~]# ip route list
  default via 192.168.1.2 dev eth0 
  192.168.1.0/24 dev eth0  proto kernel  scope link  src 192.168.1.4 
  192.168.1.0/24 dev eth1  proto kernel  scope link  src 192.168.1.5 
Run Code Online (Sandbox Code Playgroud)

看起来只选择了一个默认路由 - 无论出于何种原因(我不知道,但我假设它是编号较低的 NIC),但使用网关 0.2。也许是因为它是后一种网关声明?如果我知道,那该死。

让我们看看内核认为给定公共地址目的地的适当路由,来自本地 IP 地址:

[root@localhost ~]# ip route get to 8.8.8.8 from 192.168.1.4
  8.8.8.8 from 192.168.1.4 via 192.168.1.2 dev eth0 
      cache 

[root@localhost ~]# ip route get to 8.8.8.8 from 192.168.1.5
  8.8.8.8 from 192.168.1.5 via 192.168.1.2 dev eth0 
      cache 
Run Code Online (Sandbox Code Playgroud)

正如我们对“default ... dev eth0”所期望的那样,发往公共 IP 地址的数据包将退出 eth0。
请注意,源 IP 地址是什么并不重要。

让我们检查一下以确保!
当我们从任一接口和任一源地址 ping 时,我们将同时嗅探 eth0 和 eth1。
首先,使用 eth0 的 IP 地址作为源 (.4) ping:

[root@localhost ~]# tcpdump -ni eth0 'icmp' & tcpdump -ni eth1 'icmp' & ping -nc 3 -I 192.168.1.4 8.8.8.8 2>&1 > /dev/null ; sleep 4 ; pkill tcpdump
  [1] 2603
  [2] 2604
  tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
  listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
  tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
  listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
  23:30:30.347429 IP 8.8.8.8 > 192.168.1.4: ICMP echo reply, id 2605, seq 1, length 64
  23:30:31.331631 IP 192.168.1.4 > 8.8.8.8: ICMP echo request, id 2605, seq 2, length 64
  23:30:31.350134 IP 8.8.8.8 > 192.168.1.4: ICMP echo reply, id 2605, seq 2, length 64
  23:30:32.333378 IP 192.168.1.4 > 8.8.8.8: ICMP echo request, id 2605, seq 3, length 64
  23:30:32.350145 IP 8.8.8.8 > 192.168.1.4: ICMP echo reply, id 2605, seq 3, length 64

  5 packets captured
  5 packets received by filter
  0 packets dropped by kernel

  0 packets captured
  0 packets received by filter
  0 packets dropped by kernel
  [1]-  Done                    tcpdump -ni eth0 'icmp'
  [2]+  Done                    tcpdump -ni eth1 'icmp'
Run Code Online (Sandbox Code Playgroud)

看起来不错——与我们的路由表一致!
正如我们所期望的那样,在 eth1(第二个摘要)上没有看到任何内容。
现在让我们从源 .5(属于 eth1)ping:

[root@localhost ~]# tcpdump -ni eth0 'icmp' & tcpdump -ni eth1 'icmp' & ping -nc 3 -I 192.168.1.5 8.8.8.8 2>&1 > /dev/null ; sleep 4 ; pkill tcpdump
  [1] 2609
  [2] 2610
  tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
  listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
  tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
  listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
  23:32:31.284113 IP 8.8.8.8 > 192.168.1.5: ICMP echo reply, id 2611, seq 1, length 64
  23:32:32.269281 IP 192.168.1.5 > 8.8.8.8: ICMP echo request, id 2611, seq 2, length 64
  23:32:32.284493 IP 8.8.8.8 > 192.168.1.5: ICMP echo reply, id 2611, seq 2, length 64
  23:32:33.270735 IP 192.168.1.5 > 8.8.8.8: ICMP echo request, id 2611, seq 3, length 64
  23:32:33.286849 IP 8.8.8.8 > 192.168.1.5: ICMP echo reply, id 2611, seq 3, length 64

  5 packets captured
  5 packets received by filter
  0 packets dropped by kernel

  0 packets captured
  0 packets received by filter
  0 packets dropped by kernel
  [1]-  Done                    tcpdump -ni eth0 'icmp'
  [2]+  Done                    tcpdump -ni eth1 'icmp'
Run Code Online (Sandbox Code Playgroud)

看看即使 ping 的源地址设置为 eth1 的 IP 地址,数据包如何离开 eth0 ?
但是!,我们可以指定从源接口而不是源地址进行 ping。
指定 eth0 会按您的预期工作(成功),但如果我们将 eth1 设置为源,则会发生一些有趣的事情:

[root@localhost ~]# tcpdump -ni eth0 'icmp' & tcpdump -ni eth1 'icmp' & ping -nc 3 -I eth1 8.8.8.8 2>&1 > /dev/null ; sleep 4 ; pkill tcpdump
  [1] 2751
  [2] 2752
  tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
  listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
  tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
  listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
Run Code Online (Sandbox Code Playgroud)

... 是什么赋予了 ?
好吧,我们只是在嗅探 ICMP 流量。因为在 eth1 之外不存在默认路由(或更具体到 8.8.8.8 的路由),所以假设目的地存在于同一个广播域中。
这意味着它会在构建要发送的数据包之前尝试获取目标的 MAC 地址。如果无法获得 MAC 地址,则不会发送数据包:

[root@localhost ~]# tcpdump -eni eth1 'icmp or arp' & ping -nc 3 -I eth1 8.8.8.8 2>&1 > /dev/null ; sleep 10 ; pkill tcpdump 
  [5] 2759
  tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
  listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
  00:00:26.075685 08:00:27:48:e7:5d > Broadcast, ethertype ARP (0x0806), length 42: Request who-has 8.8.8.8 tell 192.168.1.5, length 28
  00:00:26.075685 08:00:27:48:e7:5d > Broadcast, ethertype ARP (0x0806), length 42: Request who-has 8.8.8.8 tell 192.168.1.5, length 28
  00:00:27.075945 08:00:27:48:e7:5d > Broadcast, ethertype ARP (0x0806), length 42: Request who-has 8.8.8.8 tell 192.168.1.5, length 28
  00:00:27.075945 08:00:27:48:e7:5d > Broadcast, ethertype ARP (0x0806), length 42: Request who-has 8.8.8.8 tell 192.168.1.5, length 28
  00:00:27.075945 08:00:27:48:e7:5d > Broadcast, ethertype ARP (0x0806), length 42: Request who-has 8.8.8.8 tell 192.168.1.5, length 28
  00:00:28.077935 08:00:27:48:e7:5d > Broadcast, ethertype ARP (0x0806), length 42: Request who-has 8.8.8.8 tell 192.168.1.5, length 28
  00:00:28.077935 08:00:27:48:e7:5d > Broadcast, ethertype ARP (0x0806), length 42: Request who-has 8.8.8.8 tell 192.168.1.5, length 28
  00:00:28.077935 08:00:27:48:e7:5d > Broadcast, ethertype ARP (0x0806), length 42: Request who-has 8.8.8.8 tell 192.168.1.5, length 28
Run Code Online (Sandbox Code Playgroud)

(Google 的 DNS 服务器可能不在您的 LAN 上,就像它不在我的 LAN 上一样。)

好,好,好。
现在,如果我们将默认路由从 out eth0 替换为 out eth1 呢?
我们应该期望我们的连接情况得到反映,对吗?:

[root@localhost ~]# ip ro
  default via 192.168.1.2 dev eth0 
  192.168.1.0/24 dev eth0  proto kernel  scope link  src 192.168.1.4 
  192.168.1.0/24 dev eth1  proto kernel  scope link  src 192.168.1.5 

[root@localhost ~]# ping -nc 3 -I eth0 8.8.8.8
  PING 8.8.8.8 (8.8.8.8) from 192.168.1.4 eth0: 56(84) bytes of data.
  64 bytes from 8.8.8.8: icmp_seq=1 ttl=54 time=17.5 ms
  64 bytes from 8.8.8.8: icmp_seq=2 ttl=54 time=15.9 ms
  64 bytes from 8.8.8.8: icmp_seq=3 ttl=54 time=15.5 ms
  --- 8.8.8.8 ping statistics ---
  3 packets transmitted, 3 received, 0% packet loss, time 2002ms
  rtt min/avg/max/mdev = 15.547/16.331/17.526/0.864 ms

[root@localhost ~]# ping -nc 3 -I eth1 8.8.8.8
  PING 8.8.8.8 (8.8.8.8) from 192.168.1.5 eth1: 56(84) bytes of data.
  From 192.168.1.5 icmp_seq=1 Destination Host Unreachable
  From 192.168.1.5 icmp_seq=2 Destination Host Unreachable
  From 192.168.1.5 icmp_seq=3 Destination Host Unreachable
  --- 8.8.8.8 ping statistics ---
  3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 1999ms
  pipe 3

[root@localhost ~]# ip route replace default via 192.168.1.1 dev eth1 

[root@localhost ~]# ip ro
  default via 192.168.1.1 dev eth1 
  192.168.1.0/24 dev eth0  proto kernel  scope link  src 192.168.1.4 
  192.168.1.0/24 dev eth1  proto kernel  scope link  src 192.168.1.5 

[root@localhost ~]# ping -nc 3 -I eth0 8.8.8.8
  PING 8.8.8.8 (8.8.8.8) from 192.168.1.4 eth0: 56(84) bytes of data.
  From 192.168.1.4 icmp_seq=1 Destination Host Unreachable
  From 192.168.1.4 icmp_seq=2 Destination Host Unreachable
  From 192.168.1.4 icmp_seq=3 Destination Host Unreachable
  --- 8.8.8.8 ping statistics ---
  3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 1999ms
  pipe 3

[root@localhost ~]# ping -nc 3 -I eth1 8.8.8.8
  PING 8.8.8.8 (8.8.8.8) from 192.168.1.5 eth1: 56(84) bytes of data.
  64 bytes from 8.8.8.8: icmp_seq=1 ttl=54 time=14.7 ms
  64 bytes from 8.8.8.8: icmp_seq=2 ttl=54 time=18.8 ms
  64 bytes from 8.8.8.8: icmp_seq=3 ttl=54 time=21.0 ms
  --- 8.8.8.8 ping statistics ---
  3 packets transmitted, 3 received, 0% packet loss, time 2003ms
  rtt min/avg/max/mdev = 14.760/18.216/21.010/2.596 ms
Run Code Online (Sandbox Code Playgroud)

确实如此!现在 eth0 不能到达任何东西,但 eth1 可以。

假设您希望两个接口都“正常工作”,让我们沿着负载平衡的疯狂之路走下去……:

[root@localhost ~]# ip route delete default

[root@localhost ~]# ip route add default scope global nexthop via 192.168.1.2 dev eth0 weight 1 nexthop via 192.168.1.1 dev eth1 weight 1

[root@localhost ~]# ip ro
  default 
          nexthop via 192.168.1.2  dev eth0 weight 1
          nexthop via 192.168.1.1  dev eth1 weight 1
  192.168.1.0/24 dev eth0  proto kernel  scope link  src 192.168.1.4 
  192.168.1.0/24 dev eth1  proto kernel  scope link  src 192.168.1.5 
Run Code Online (Sandbox Code Playgroud)

但它有效吗?

[root@localhost ~]# ping -nc 3 -I eth0 8.8.8.8
  PING 8.8.8.8 (8.8.8.8) from 192.168.1.4 eth0: 56(84) bytes of data.
  64 bytes from 8.8.8.8: icmp_seq=1 ttl=54 time=17.9 ms
  64 bytes from 8.8.8.8: icmp_seq=2 ttl=54 time=16.2 ms
  64 bytes from 8.8.8.8: icmp_seq=3 ttl=54 time=17.4 ms
  --- 8.8.8.8 ping statistics ---
  3 packets transmitted, 3 received, 0% packet loss, time 2003ms
  rtt min/avg/max/mdev = 16.281/17.238/17.986/0.727 ms

[root@localhost ~]# ping -nc 3 -I eth1 8.8.8.8
  PING 8.8.8.8 (8.8.8.8) from 192.168.1.5 eth1: 56(84) bytes of data.
  64 bytes from 8.8.8.8: icmp_seq=1 ttl=54 time=16.6 ms
  64 bytes from 8.8.8.8: icmp_seq=2 ttl=54 time=17.3 ms
  64 bytes from 8.8.8.8: icmp_seq=3 ttl=54 time=26.0 ms
  --- 8.8.8.8 ping statistics ---
  3 packets transmitted, 3 received, 0% packet loss, time 2003ms
  rtt min/avg/max/mdev = 16.612/20.025/26.091/4.300 ms
Run Code Online (Sandbox Code Playgroud)

哈扎!

……虽然,连我都有些意外。
您自己的里程可能会有所不同。我建议反对这种疯狂。

在我的测试中,尝试到达本地子网上的目的地时,事情变得不稳定。
这是因为在正常系统使用中(即,未明确指定退出接口),会选择一个接口。
例如,如果我 ping 子网上一个不存在的主机 (.17),ARP 请求只会发送到 eth0。
所以理论上,如果主机确实存在于 eth1 之外,ARP 甚至可以工作吗?
可能不是:

[root@localhost ~]# ping 192.168.1.17
  PING 192.168.1.17 (192.168.1.17) 56(84) bytes of data.
  From 192.168.1.4 icmp_seq=1 Destination Host Unreachable
  From 192.168.1.4 icmp_seq=2 Destination Host Unreachable
  From 192.168.1.4 icmp_seq=3 Destination Host Unreachable
  ...

[root@localhost ~]# tcpdump -eni eth0 'arp'
  tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
  listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
  00:25:29.167575 08:00:27:f6:8f:c2 > Broadcast, ethertype ARP (0x0806), length 42: Request who-has 192.168.1.17 tell 192.168.1.4, length 28
  00:25:30.168001 08:00:27:f6:8f:c2 > Broadcast, ethertype ARP (0x0806), length 42: Request who-has 192.168.1.17 tell 192.168.1.4, length 28
  00:25:31.169967 08:00:27:f6:8f:c2 > Broadcast, ethertype ARP (0x0806), length 42: Request who-has 192.168.1.17 tell 192.168.1.4, length 28
  ...

[root@localhost ~]# tcpdump -eni eth1 'arp'
  tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
  listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes



  ^C
  0 packets captured
  0 packets received by filter
  0 packets dropped by kernel
Run Code Online (Sandbox Code Playgroud)

干杯。