![套接字绑定设备时macvlan接口之间的通信问题](https://linux22.com/image/119830/%E5%A5%97%E6%8E%A5%E5%AD%97%E7%BB%91%E5%AE%9A%E8%AE%BE%E5%A4%87%E6%97%B6macvlan%E6%8E%A5%E5%8F%A3%E4%B9%8B%E9%97%B4%E7%9A%84%E9%80%9A%E4%BF%A1%E9%97%AE%E9%A2%98.png)
我有一个 Linux 设置,其中两个处于桥接模式的 macvlan 接口添加到同一 IP 子网中的同一物理接口上:
ip link add link eth2 dev mvl0 type macvlan mode bridge
ip link add link eth2 dev mvl1 type macvlan mode bridge
ip addr add 192.168.42.16/24 dev mvl0
ip addr add 192.168.42.17/24 dev mvl1
ip link set dev mvl0 up
ip link set dev mvl1 up
我想在绑定到接口的套接字之间进行通信mvl0
并mvl1
使用它们进行通信,但这不起作用。例如,
# ping -I mvl0 192.168.42.17
没有得到任何回复。我可以看到内核尝试在 上执行 ARP lo
,但这不起作用,因为它没有得到回复。
有没有办法做到这一点,例如通过操纵路由或邻居表?
这应该被视为 Linux 内核中的错误吗?毕竟,
macvlan
模式下的接口bridge
应该能够互相看到。
(背景:这是在同一进程上下文中运行的两个嵌入式设备的模拟。我们的框架始终将套接字绑定到接口,以确保通信确实通过所需的接口。通信通常通过 UDP 进行。)
答案1
(编辑:以前的版本仅适用于传出数据包 (UDP),此版本适用于双向(TCP 和 ping)。)
对于像您这样的设置有各种陷阱:Linux 认为源地址与网络接口地址匹配的传入数据包是路由错误(因为在正常情况下它表示路由循环)。此外,默认情况下,内核维护的local
路由表具有最高优先级,这将阻止您的数据包从其绑定的接口发出。
策略路由可以解决第二个问题:首先,删除重叠的路由(这只会带来麻烦):
ip route list
# overlapping routes should look like:
ip route del 192.168.42.0/24 dev mvl0 proto kernel scope link src 192.168.42.16
ip route del 192.168.42.0/24 dev mvl1 proto kernel scope link src 192.168.42.17
接下来,给local
表一个较低的优先级(较高的数值):
ip rule add pref 1000 lookup local
ip rule del pref 0
我们需要通过将传入的数据包发送到local
表中来接受它们:
ip rule add pref 100 to 192.168.42.16 iif mvl0 lookup local
ip rule add pref 100 to 192.168.42.17 iif mvl1 lookup local
而所有其他(传出)数据包到相应的。目的地将使用一个特殊的表来强制它们在另一个接口中退出:
ip rule add pref 200 to 192.168.42.17 lookup 100
ip rule add pref 200 to 192.168.42.16 lookup 101
ip route add default dev mvl0 table 100
ip route add default dev mvl1 table 101
此外,我们必须通过禁用反向路径过滤(如果尚未禁用)并允许接受具有本地源的数据包来解决第一个问题:
echo "0" | tee /proc/sys/net/ipv4/conf/mvl{0,1}/rp_filter
echo "1" | tee /proc/sys/net/ipv4/conf/mvl{0,1}/accept_local
现在ping
,即使未绑定到其中一个mvl
接口,也可以正常工作。 TCP 和 UDP 也可以工作,测试使用socat
:
socat TCP4-LISTEN:9998,so-bindtodevice=mvl0 -
echo foo | socat - TCP4:192.168.42.16:9998,so-bindtodevice=mvl1
socat UDP4-RECV:9900,so-bindtodevice=mvl0 -
echo foo | socat - UDP4-SENDTO:192.168.42.16:9900,so-bindtodevice=mvl1