![iptables NETMAP 无法可靠地调整多播 UDP 数据包的源地址](https://linux22.com/image/768421/iptables%20NETMAP%20%E6%97%A0%E6%B3%95%E5%8F%AF%E9%9D%A0%E5%9C%B0%E8%B0%83%E6%95%B4%E5%A4%9A%E6%92%AD%20UDP%20%E6%95%B0%E6%8D%AE%E5%8C%85%E7%9A%84%E6%BA%90%E5%9C%B0%E5%9D%80.png)
在嵌入式/物联网用例中,我有一个运行 Linux 的管理主机,它需要能够与每个使用一组通用静态 IP 地址的多个网络通信。
这在大多数情况下都可以正常工作,包括对于 UDP 多播流量,如下所示:
- 每个嵌入式网络的网络链接(称之为
eth1
、eth2
等) - 为每个不同的嵌入式网络(称为
ns1
、ns2
等)设置单独的 Linux 网络命名空间 - 每个网络命名空间和根命名空间之间的对等链接(从网络命名空间端称为 、 等,
peer1
从根命名空间端称为、等)peer2
veth1
veth2
- 每个命名空间中都有一个 iptables NETMAP 规则,用于将冲突的静态 IP 子网(称之为
192.168.0.x
)映射到一组不冲突的静态 IP 子网(称之为192.168.1.x
、192.168.2.x
等) smcrouted
每个网络命名空间内都有一个实例,用于转发多播组注册- 对等链接的根命名空间端在不同的(非 NAT)子网中设置单独的 IP 地址,以解决这个问题(称之为
192.168.(x+100).1
)
尝试将流量可视化:
[|root namespace|::veth1] <-> [peer1::(namespace ns1)::eth1] <-> embedded network
[| |::veth2] <-> [peer2::(namespace ns2)::eth2] <-> embedded network
... etc ...
ns1
静态 IP 子网的示例 NETMAP 规则:
sudo -n ip netns exec ns1 iptables -t nat -A PREROUTING -d 192.168.1.0/24 -i peer1 -j NETMAP --to 192.168.0.0/24
sudo -n ip netns exec ns1 iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -o peer1 -j NETMAP --to 192.168.1.0/24
ns1
smcrouted
支持的多播组的配置规则示例:
mgroup from eth1 group 239.255.60.60
mgroup from peer1 group 239.255.60.60
mroute from eth1 group 239.255.60.60 to peer1
mroute from peer1 source 192.168.101.1 group 239.255.60.60 to eth1
这个问题的实际主题是源 IP 调整中存在一个奇怪的故障NETMAP
,我无法解释,只能设法解决。
我的预期行为:
- 网络命名空间内的 UDP 多播订阅将看到未修改的预 NETMAP
192.168.0.x
源地址 - 根命名空间内的 UDP 多播订阅将看到修改后的 NETMAP
192.168.1.x
源地址
但事实并非如此。相反,两个命名空间中的订阅者要么看到 NETMAP 之前的 192.168.0.x 源地址,要么看到 NETMAP 之后的 192.168.1.x 地址。
配置中规则source
的过滤器是为了防止当服务器切换到第二组行为时启动多播路由循环。mroute from peer1
smcroute
我目前还不能确定是什么导致了两种状态之间的转换,只能在源地址信息看起来错误时通过基于活动网络命名空间或源网络接口进行调整来在应用层解决问题。
提出这个问题的目的是帮助弄清楚以下哪项适用:
- 这预计不会起作用,在应用层进行补偿是最好的办法(考虑到在 Linux 容器环境中使用网络命名空间,这似乎不太可能)
- 内核、iptables 或 smcroute 中还需要配置(或取消配置)某些内容,以防止发生错误行为
(注意:这是一个非常深奥、非常具体的问题,所以我确实想知道网络工程是否更合适,但是https://networkengineering.stackexchange.com/questions/64744/linux-local-multicast-egress-follows-forward-chain-when-smcroute-is-active明确指出这是为了与商业路由器等配合使用,而不是用于 Linux 网络命名空间配置。不过,在配置 Linux 服务器时,我不太清楚 Server Fault 和 Unix & Linux 堆栈交换之间的界限)
答案1
维护者顺风车路线这里。这绝对有效。我们为工作中的各种客户使用了这种方法,尽管使用的是实际的硬件而不是网络命名空间。
在SMCRoute 问题追踪器,与您唯一的区别是他们尚未使用带有 netmap 的 1:1 NAT。
我已经准备好了测试用例为下一个版本 (v2.5) 做准备。我在本地和 GitHub Actions (Azure 云) 中使用以下方法运行所有测试:
cd test/
unshare -mrun ./multi.sh
测试在专用网络命名空间中有两个独立的路由器(R1 和 R2),它们之间有一个共享的 LAN 段(192.168.0.0/24)。每个路由器后面都有一个私有 LAN(10.0.0.0/24),两个路由器的 LAN 段相同。额外的(虚拟)接口 eth1 用于将多播路由到共享 LAN(eth0)。NETMAP 规则使用 PREROUTING 和 POSTROUTING 链。将 R1 私有 LAN 转换为 192.168.10.0/24,将 R2 私有 LAN 转换为 192.168.20.0/24。如下所示,内核中安装的多播路由使用 1:1 映射(全局)地址。
>> Starting emitters ...
R1[2811708]: New multicast data from 192.168.10.1 to group 225.1.2.3 on VIF 1
R1[2811708]: Add 192.168.10.1 -> 225.1.2.3 from VIF 1
R2[2811709]: New multicast data from 192.168.10.1 to group 225.1.2.3 on VIF 0
R2[2811709]: Add 192.168.10.1 -> 225.1.2.3 from VIF 0
R2[2811709]: New multicast data from 192.168.20.1 to group 225.1.2.3 on VIF 1
R2[2811709]: Add 192.168.20.1 -> 225.1.2.3 from VIF 1
R1[2811708]: New multicast data from 192.168.20.1 to group 225.1.2.3 on VIF 0
R1[2811708]: Add 192.168.20.1 -> 225.1.2.3 from VIF 0
>> R1 multicast routes and 1:1 NAT ...
(192.168.10.1,225.1.2.3) Iif: eth1 Oifs: eth0 State: resolved
(192.168.20.1,225.1.2.3) Iif: eth0 Oifs: eth1 State: resolved
Chain PREROUTING (policy ACCEPT 5 packets, 244 bytes)
pkts bytes target prot opt in out source destination
0 0 NETMAP all -- any any anywhere 192.168.10.0/24 to:10.0.0.0/24
Chain INPUT (policy ACCEPT 1 packets, 84 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 4 packets, 248 bytes)
pkts bytes target prot opt in out source destination
Chain POSTROUTING (policy ACCEPT 2 packets, 124 bytes)
pkts bytes target prot opt in out source destination
2 124 NETMAP all -- any any 10.0.0.0/24 anywhere to:192.168.10.0/24
>> R2 multicast routes and 1:1 NAT ...
(192.168.10.1,225.1.2.3) Iif: eth0 Oifs: eth1 State: resolved
(192.168.20.1,225.1.2.3) Iif: eth1 Oifs: eth0 State: resolved
Chain PREROUTING (policy ACCEPT 4 packets, 204 bytes)
pkts bytes target prot opt in out source destination
1 84 NETMAP all -- any any anywhere 192.168.20.0/24 to:10.0.0.0/24
Chain INPUT (policy ACCEPT 2 packets, 168 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 3 packets, 164 bytes)
pkts bytes target prot opt in out source destination
Chain POSTROUTING (policy ACCEPT 1 packets, 40 bytes)
pkts bytes target prot opt in out source destination
2 124 NETMAP all -- any any 10.0.0.0/24 anywhere to:192.168.20.0/24
>> Analyzing ...
1 0.000000000 0.000000000 192.168.10.1 → 225.1.2.3 ICMP 98 Echo (ping) request id=0xe769, seq=1/256, ttl=2
2 1.000105261 1.000105261 192.168.10.1 → 225.1.2.3 ICMP 98 Echo (ping) request id=0xe769, seq=2/512, ttl=2
3 1.000957268 0.000852007 192.168.20.1 → 225.1.2.3 ICMP 98 Echo (ping) request id=0xe76b, seq=1/256, ttl=2
4 2.024216212 1.023258944 192.168.10.1 → 225.1.2.3 ICMP 98 Echo (ping) request id=0xe769, seq=3/768, ttl=2
5 2.024216229 0.000000017 192.168.20.1 → 225.1.2.3 ICMP 98 Echo (ping) request id=0xe76b, seq=2/512, ttl=2
6 3.048426868 1.024210639 192.168.10.1 → 225.1.2.3 ICMP 98 Echo (ping) request id=0xe769, seq=4/1024, ttl=2
7 3.048426842 -0.000000026 192.168.20.1 → 225.1.2.3 ICMP 98 Echo (ping) request id=0xe76b, seq=3/768, ttl=2
8 4.072270331 1.023843489 192.168.10.1 → 225.1.2.3 ICMP 98 Echo (ping) request id=0xe769, seq=5/1280, ttl=2
9 4.072270458 0.000000127 192.168.20.1 → 225.1.2.3 ICMP 98 Echo (ping) request id=0xe76b, seq=4/1024, ttl=2
10 5.096430449 1.024159991 192.168.20.1 → 225.1.2.3 ICMP 98 Echo (ping) request id=0xe76b, seq=5/1280, ttl=2
=> 10 for group ff04::114, expected >= 8
它可能有点难以阅读,您可能需要查阅测试用例以了解详细信息。无论如何,我在翻译中得到了一致的结果,顺便说一句,这是 Linux 的责任,而不是 SMCRoute,所以你可能有一个内核错误或其他问题。我的个人工作站有 Linux Mint 和内核 5.11.0,GitHub Actions 的后端服务器运行 Ubuntu 20.04 LTS,内核 5.8.0,两个都是相当修补的发行版内核,但也许是一个开始的基准?