我遇到了以下问题。我想运行两个服务器应用程序的本地模拟,这两个应用程序都打开一个网络套接字。这些应用程序通常在嵌入式 Linux 设备上单独运行。第三个应用程序是应该与两个服务器通信的客户端。由于无法在同一接口上打开两个套接字,因此我尝试创建三个虚拟接口并将它们与桥接器连接起来:
sudo ip link add eth10 type dummy
sudo ip link add eth11 type dummy
sudo ip link add eth12 type dummy
sudo ip link add br0 type bridge
sudo ip link set dev eth10 master br0
sudo ip link set dev eth11 master br0
sudo ip link set dev eth12 master br0
sudo ip addr add 192.168.42.42/24 dev eth10
sudo ip addr add 192.168.42.43/24 dev eth11
sudo ip addr add 192.168.42.44/24 dev eth12
sudo ip link set dev eth10 up
sudo ip link set dev eth11 up
sudo ip link set dev eth12 up
sudo ip link set dev br0 up
到目前为止,这都是可行的,我可以启动所有应用程序,客户端可以访问两个服务器。但是,服务器还应该通过 IEC61850-GOOSE 协议相互通信。但这不起作用。看起来好像没有通过虚拟网络进行第 2 层通信 - 我在 Wireshark 中也看不到预期的数据包。客户端-服务器通信(IEC61850-MMS)基于 TCP/IP 并且可以正常工作。
有人知道这是什么原因吗?
答案1
正如其他帖子中所解释的那样(https://superuser.com/a/1745471,https://superuser.com/a/1781135),网络接口不充当镜像,它们充当管道——而你的问题是软件发送的数据包朝错误的方向,离开从桥上下来回移动,而不是朝桥的方向移动。
你需要的是让操作系统认为你已经已收到数据包不会被发送。有两种常见选项:
如果您正在编写自定义网络工具:您可以创建
tap
连接到进程而不是网络(或者像虚拟接口那样连接到“无任何地方”)的接口。程序必须使用特殊的 API 来控制“tap”接口。当进程通过此 API 发送以太网帧时,操作系统(以及网桥)将收到通过“tap”接口,反之亦然。这种接口类型在 VPN 软件(例如 OpenVPN 或 ZeroTier)中非常常用;“tun”接口除了没有以太网 L2 标头外,其他都相同。
(使用 TAP 接口的示例代码:https://git.nullroute.lt/hacks/udptap.git/tree/)
如果您需要运行未修改的软件:您可以创建
veth
接口,这些接口总是成对出现,就像管道的两端一样。(创建 veth0 将自动创建 veth1,依此类推。)然后,您的程序可以使用 veth-pair 的一端以常规方式发送其以太网帧或 IP 数据包,并且数据包将通过相应的其他 veth 接口“接收”。
这通常用于容器(例如 Docker);veth 对的一端移入容器的网络命名空间,而另一端对主机仍然可见。(将多个容器的 veth 的“主机”端桥接起来以将它们放在“虚拟交换机”上确实非常常见。)
因此,您可以创建三对 (0-1、2-3、4-5),将 veth0/veth2/veth4 放入桥接器中,然后让程序通过 veth1/veth3/veth5 相互通信。可以使用标准创建接口
ip link add
。
(其他相关类型包括 macvtap 和 macvlan,它们可能在某些情况下很有用。
所有这些都假设您的软件直接发送原始以太网帧并绕过 Linux TCP/IP 堆栈。对于使用常规 IP 的程序,仅拥有接口是不够的——与本地地址的 IP 通信会被操作系统回送,即使该地址位于不同的接口上——而且您通常需要命名空间隔离每个“模拟服务器”。
命名空间是容器的工作方式,但您不必运行整个容器,而是可以选择要隔离的内容;在这种情况下,您只需要网络命名空间,而其他所有内容(文件系统等)则保持共享。您可以使用ip netns
或通用工具unshare
&创建网络命名空间nsenter
,并且可以使用 将接口(例如 veth 对的一端)移动到命名空间中ip link set <name> netns <pid>
。