描述
这类似于这个问题,但不同:
Linux 计算机有两个 NIC。eth0
静态分配一个 IP。 eth1 由现场人员分配,可以是相同的 IP 或不同的 IP。 计划是 和eth0
位于eth1
不同的网络上,并且从不交换或桥接任何数据包。 并且我的 Linux 应用程序只监听和接受eth0
和上的连接eth1
,并在接收后开始发送数据包,但从不尝试发起任何活动。
问题
两个 NIC 的 IP 可以在同一个子网中吗:192.168.1.4
和 192.168.1.5?如您所见eth0
,它们eth1
被分配到同一个子网(255.255.255.0
)中,但它们没有连接,也不应该连接。
在这种情况下,当应用程序侦听eth0
端口55555
、接受连接并返回数据包时,底层是否知道它应该返回到 eth0?或者它可能会尝试,eth1
因为操作系统认为它位于同一子网上?我需要对路由表做些什么来确保它不认为这两个 NIC 实际上位于同一子网上吗?
在这种情况下,我不仅应该避免相似的 IP,还应该避免相同的子网吗?
更新
如果 PC0 和 PC1 有相同的 IP 怎么办?
PC0 (1.100) <-------> [eth0 (1.4) My System eth1 (1.5)]<-------PC1(1.100)
我的应用程序需要监听 eth0 和 eth1 上的端口 55555,并且请求通过 eth1 传入,操作系统是否知道要通过 eth1 回复?这种配置会导致问题吗?
业务案例是,我正在构建这个嵌入式系统,并且我预先定义了 eth0 和 PC0 的 IP(PC0 也可以是 DHCP)。但我的客户已经在右侧有一个网络。如果他们有一个与 PC0 或 eht0 冲突的设备怎么办?即使 eth0 上有 DHCP 服务器,在为 PC0 分配 IP 时也无法排除右侧的 IP。如果这会产生问题,我有很多解决方案。但我想听听同行的意见,看看这是否是一个问题。
我的两个同事认为这不是问题。我的观点是,即使使用套接字(假设绑定到右侧),IP 层也不知道使用哪个接口来回复数据包。无论我们如何设置路由表,它都会选择一个。
答案1
是的,可以。您的计算机将通过与目标网络路由关联的任何接口发送返回数据包。
很可能,目标网络包含在通过默认网关的默认路由 - 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
两个接口,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
请注意,每个网关地址都设置了不同的网关地址 - .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
看起来只选择了一条默认路由 - 出于某种原因(我不知道,但我猜是因为它是编号较低的 NIC)而选择 eth0,但使用网关 .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
正如我们从“default ... dev eth0”所预期的那样,发往公共 IP 地址的数据包将离开 eth0。
请注意,源 IP 地址是什么并不重要。
不过,我们还是来检查一下吧!
我们将嗅探 eth0 和 eth1,同时从任一接口和任一源地址执行 ping 操作。
首先,使用 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'
看起来不错 - 与我们的路由表一致!
正如我们预期的那样,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'
看看为什么即使 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
... 怎么回事?
好吧,我们只是嗅探 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
(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
确实如此!现在 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
但它有效吗?
[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
好极了!
... 不过,我还是有点惊讶。
你自己的情况可能有所不同。我建议不要这么做。
在我的测试中,尝试到达本地子网上的目的地时,事情变得很棘手。
这是因为在正常系统使用中(即未明确指定出口接口),会选择一个接口。
例如,如果我 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
干杯。