iptables NETMAP not reliably adjusting source address of multicast UDP packets

Solution 1:

Maintainer of SMCRoute here. This should definitely work. We use this exact approach, albeit with actual HW and not network namespaces, for various customers at work.

There is a very similar problem reported in the SMCRoute issue tracker, only difference from you is they don't use 1:1 NAT with netmap (yet).

I've whipped up a test case for this in preparation for the next release (v2.5). I run all tests locally and in GitHub Actions (Azure cloud) using:

cd test/
unshare -mrun ./multi.sh

The test has two separate routers (R1, and R2) in dedicated network namespaces, with a shared LAN segment between them (192.168.0.0/24). Behind each router is a private LAN (10.0.0.0/24), which is the same for both routers. An extra (dummy) interface eth1 is used to route multicast from to the shared LAN (eth0). The NETMAP rule uses the PREROUTING and POSTROUTING chain. Translating the R1 private LAN to 192.168.10.0/24 and the R2 private LAN to 192.168.20.0/24. As you can see below, the multicast routes installed in the kernel use the 1:1 mapped (global) addresses.

>> 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

It's maybe a bit hard to read, you may have to consult the test case for details. Anyway, I get consistent results in the translation, which btw is the responsibility of Linux not SMCRoute, so you may have a kernel bug or something. May personal workstation has Linux Mint with kernel 5.11.0 and the backend servers for GitHub Actions run Ubuntu 20.04 LTS, kernel 5.8.0, both quite patched distro kernels, but maybe a baseline to start from?