我的要求是创建多个分接接口,每个接口都有同一子网上的 IP 地址。
我尝试通过创建桥梁来实现这一点
br0 (192.168.1.199)
___________|_____________________________________
| | | | | |
eth0 tap0 tap1 tap2 tap3 tap4
(192.168.1.150) (.151) (.152) (.153) (.154)
我需要所有 tap 接口都可从外部 PC 访问。当我从 tap0 ping 到外部计算机时,显示 192.168.1.200
ping -I tap0 192.168.1.200 -- the ping is not going through.
但是当我从 192.168.1.200 ping 到 192.168.1.150(tap0)时它可以工作,但我得到的是网桥(br0)的 mac 地址
我有两个问题:
- 如何从 Tap 接口 ping 到外部
- 从外部 ping 时,如何获取正确的 tap 接口的 mac 地址。
答案1
这个任务看起来像XY 问题但我会尽力澄清问题所在并提供解决方案。
当你在接口上分配 IP 地址时,内核会自动在主路由表中创建直连路由(你在中看到的内容
ip route list
)和特殊路由(local
以及表broadcast
中的local
;你在中看到的内容ip route list table local
)。当你在多个接口上分配重叠或相同的地址空间时,你将在路由表中得到几条相同的路由。显然,这不是一件好事。
因此,特定路由的选择取决于地址分配顺序和其他一些因素。要检查它,您可以使用命令
ip route get 192.168.1.200
。当您尝试重新创建桥接接口时,您可能会完全失去连接,因为到192.168.1.0/24
子网的路由将指向其中一个分接接口。此配置仅在一种情况下有效:当桥接接口上的地址之前已分配,然后分配到具有相同子网的其他接口上时,但不能保证。因此,主要结果是:如果你不完全理解它的工作原理,请不要在多个接口上分配来自同一子网的地址。
使此配置正常工作的更正确方法是在 tap 接口上分配具有
/32
前缀长度的地址。在大多数情况下,它都能按预期工作。在 Linux 内核中,IP 地址与接口不是硬链接的。简单演示一下:如果您的主机有两个具有不同地址的 NIC,并且您物理断开其中一个 NIC 的电缆,您将能够从外部通过工作 NIC ping 到断开连接的 NIC 上的 IP 地址。
当您在 ping 选项中指定 IP 地址时,路由配置会定义与外部主机通信的接口选择
-I
。它只指定传出数据包的源地址,但不指定输出接口。mac 地址在发送数据包的最后一步填写。您可以使用命令检查实际路由ip route get
:
gw:~# ip r ls table local
broadcast 127.0.0.0 dev lo proto kernel scope link src 127.0.0.1
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1
broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1
broadcast 192.168.1.0 dev br0 proto kernel scope link src 192.168.1.199
local 192.168.1.150 dev tap0 proto kernel scope host src 192.168.1.150
local 192.168.1.151 dev tap1 proto kernel scope host src 192.168.1.151
local 192.168.1.152 dev tap2 proto kernel scope host src 192.168.1.152
local 192.168.1.153 dev tap3 proto kernel scope host src 192.168.1.153
local 192.168.1.154 dev tap4 proto kernel scope host src 192.168.1.154
local 192.168.1.199 dev br0 proto kernel scope host src 192.168.1.199
broadcast 192.168.1.255 dev br0 proto kernel scope link src 192.168.1.199
gw:~# ip -4 r ls
192.168.1.0/24 dev br0 proto kernel scope link src 192.168.1.199
gw:~# ip r g 192.168.1.200
192.168.1.200 dev br0 src 192.168.1.199 uid 0
cache
gw:~# ip r g 192.168.1.200 from 192.168.1.150
192.168.1.200 from 192.168.1.150 dev br0 uid 0
cache
但是,当您在 ping 选项中指定接口名称时
-I
,将使用原始套接字。在这种情况下,输出接口是硬定义的,并且跳过了路由步骤。但是,如其他答案中所述,内核将尝试通过指定接口(非活动分接头)发送数据包。显然,它失败了。所以我们有 XY 问题的一个很好的例子。我认为主题发起者想要测试模拟单独主机的桥接。实现这一点的简单方法之一是使用网络命名空间。
小例子
IP 地址的简要示例192.168.1.150
。
- 创建网络命名空间,模拟虚拟主机:
ip netns add VM150
- 创建 veth 接口对。将对等接口移出到网络命名空间。剩余的 veth 接口
br0
作为桥接端口附加到该接口。
ip link add name vethVM150 type veth peer vethIntVM150
ip link set dev vethIntVM150 netns VM150
ip link set dev vethVM150 master br0
ip link set dev vethVM150 up
- 在 VM150 网络命名空间内配置网络。然后
192.168.1.200
从中 ping 主机:
ip netns exec VM150 bash
ip netns identify
output> VM150
ip link set up dev lo
ip link set up dev vethIntVM150
ip address add 192.168.1.150/24 dev vethIntVM150
ping 192.168.1.200
...
- 使用 tcpdump 和 bridge 实用程序验证配置(在主机的其他控制台中运行;最好使用
GNU screen
或tmux
)。您将在以太网标头中看到不同的 mac 地址。
tcpdump -ni eth0 -e
15:38:54.084416 0a:28:39:f0:04:ad > 0c:d6:26:25:f9:00, ethertype IPv4 (0x0800), length 98: 192.168.1.150 > 192.168.1.200: ICMP echo request, id 2037, seq 72, length 64
15:38:54.088262 0c:d6:26:25:f9:00 > 0a:28:39:f0:04:ad, ethertype IPv4 (0x0800), length 98: 192.168.1.200 > 192.168.1.150: ICMP echo reply, id 2037, seq 72, length 64
15:38:54.403666 0c:d6:26:c2:70:00 > 0c:d6:26:25:f9:00, ethertype IPv4 (0x0800), length 98: 192.168.1.199 > 192.168.1.200: ICMP echo request, id 2043, seq 15, length 64
15:38:54.407580 0c:d6:26:25:f9:00 > 0c:d6:26:c2:70:00, ethertype IPv4 (0x0800), length 98: 192.168.1.200 > 192.168.1.199: ICMP echo reply, id 2043, seq 15, length 64
15:38:55.085501 0a:28:39:f0:04:ad > 0c:d6:26:25:f9:00, ethertype IPv4 (0x0800), length 98: 192.168.1.150 > 192.168.1.200: ICMP echo request, id 2037, seq 73, length 64
15:38:55.087252 0c:d6:26:25:f9:00 > 0a:28:39:f0:04:ad, ethertype IPv4 (0x0800), length 98: 192.168.1.200 > 192.168.1.150: ICMP echo reply, id 2037, seq 73, length 64
15:38:55.405129 0c:d6:26:c2:70:00 > 0c:d6:26:25:f9:00, ethertype IPv4 (0x0800), length 98: 192.168.1.199 > 192.168.1.200: ICMP echo request, id 2043, seq 16, length 64
15:38:55.407533 0c:d6:26:25:f9:00 > 0c:d6:26:c2:70:00, ethertype IPv4 (0x0800), length 98: 192.168.1.200 > 192.168.1.199: ICMP echo reply, id 2043, seq 16, length 64
15:38:56.087472 0a:28:39:f0:04:ad > 0c:d6:26:25:f9:00, ethertype IPv4 (0x0800), length 98: 192.168.1.150 > 192.168.1.200: ICMP echo request, id 2037, seq 74, length 64
15:38:56.091242 0c:d6:26:25:f9:00 > 0a:28:39:f0:04:ad, ethertype IPv4 (0x0800), length 98: 192.168.1.200 > 192.168.1.150: ICMP echo reply, id 2037, seq 74, length 64
- 主机上的验证
192.168.1.200
非常简单 - 检查 ARP 表就足够了
host:~# ip n ls dev eth0
192.168.1.150 lladdr 0a:28:39:f0:04:ad REACHABLE
192.168.1.199 lladdr 0c:d6:26:c2:70:00 REACHABLE
答案2
关于问题 1:
ping -I tap0
告诉 ping 在 tap0 上发送 ping 数据包。这将绕过网桥,实际上只在指定的“物理”接口上发送。因此,实际上,您不是“从”tap 接口 ping,而是“向”它 ping。
如果您想要“从”tap 接口 ping 东西,您需要将某些东西附加到它(例如 OpenVPN)并从 tap 接口连接到的虚拟电缆的另一端发送 ping。
问题 2:你可以尝试类似
arp -i br0 -Ds 192.168.1.150 tap0 pub
ETC。