troglobit / smcroute

Static multicast routing for UNIX
https://troglobit.com/projects/smcroute/
GNU General Public License v2.0
244 stars 64 forks source link

Static MC route was placed which had same (S,G) but with different NIC #212

Closed dtzq01 closed 1 week ago

dtzq01 commented 2 weeks ago

Hi, I met an issue: the static MC route was placed which had same (S,G) but with different NIC. Config: ens36: (192.168.3.21,225.0.0.1) => ens33 ens38: (192.168.3.21,225.0.0.1) => ens33

After config, only got the last one(ens38 => ens33). In kernel code, I saw NIC info lost when using MRT_ADD_MFC/MRT_DEL_MFC. Maybe MRT_ADD_MFC_PROXY/MRT_DEL_MFC_PROXY is a better choice. I've changed code and added same.sh as UT, it was OK here. See it in "Pull Request".

Log:(Note the comment '//')

code@code:~$ sudo smcroutectl add ens36 192.168.3.21 225.0.0.1 ens33
code@code:~$ sudo smcroutectl 

(*,G) Template Rules
ROUTE (S,G)                                IIF    OIFS                                                                                                                                                                                   
(*, 225.0.0.1)                             ens36 

(S,G) Rules
ROUTE (S,G)                                IIF    OIFS                                                                                                                                                                                   
(192.168.3.21, 225.0.0.1)                  ens36  ens33

Kernel MFC Table
ROUTE (S,G)                                IIF    OIFS                                                                                                                                                                                   
(192.168.3.21, 225.0.0.1)                  ens36  ens33
(192.168.248.1, 239.255.255.250)           ens33 
(192.168.23.1, 239.255.255.250)            ens36 

code@code:~$ ip mroute 
(192.168.248.1,239.255.255.250)  Iif: ens33       State: resolved
(192.168.23.1,239.255.255.250)   Iif: ens36       State: resolved
(192.168.3.21,225.0.0.1)         Iif: ens36      Oifs: ens33  State: resolved        //this would be replaced
code@code:~$ sudo smcroutectl add ens38 192.168.3.21 225.0.0.1 ens33
code@code:~$ sudo smcroutectl 

(*,G) Template Rules
ROUTE (S,G)                                IIF    OIFS                                                                                                                                                                                   
(*, 225.0.0.1)                             ens36 

(S,G) Rules
ROUTE (S,G)                                IIF    OIFS                                                                                                                                                                                   
(192.168.3.21, 225.0.0.1)                  ens36  ens33
(192.168.3.21, 225.0.0.1)                  ens38  ens33

Kernel MFC Table
ROUTE (S,G)                                IIF    OIFS                                                                                                                                                                                   
(192.168.3.21, 225.0.0.1)                  ens36  ens33
(192.168.3.21, 225.0.0.1)                  ens38  ens33
(192.168.248.1, 239.255.255.250)           ens33 
(192.168.23.1, 239.255.255.250)            ens36 

code@code:~$ ip mroute 
(192.168.3.21,225.0.0.1)         Iif: ens38      Oifs: ens33  State: resolved      //Not added, replaced the ens36 route.
(192.168.23.1,239.255.255.250)   Iif: ens36       State: resolved
(192.168.248.1,239.255.255.250)  Iif: ens33       State: resolved
code@code:~$ 

Kernel code(ipmr.c)

int ip_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval,
             unsigned int optlen)
{
...
    case MRT_ADD_MFC:
    case MRT_DEL_MFC:
        parent = -1;        //lost NIC info here
        fallthrough;
    case MRT_ADD_MFC_PROXY:
    case MRT_DEL_MFC_PROXY:
        if (optlen != sizeof(mfc)) {
            ret = -EINVAL;
            break;
        }
        if (copy_from_sockptr(&mfc, optval, sizeof(mfc))) {
            ret = -EFAULT;
            break;
        }
        if (parent == 0)
            parent = mfc.mfcc_parent;
        if (optname == MRT_DEL_MFC || optname == MRT_DEL_MFC_PROXY)
            ret = ipmr_mfc_delete(mrt, &mfc, parent);
        else
            ret = ipmr_mfc_add(net, mrt, &mfc,
                       sk == rtnl_dereference(mrt->mroute_sk),
                       parent);
        break;
...
}
troglobit commented 2 weeks ago

I'm afraid this is a use-case that SMCRoute can never support. Having the same (S,G) ingressing two different interfaces is usually a sign of a layer-3 loop, or a switch-over in a layer-3 topology change. What is the setup you are using to get this?

A dynamic multicast routing daemon would in a case like this get a notification from the kernel; "Hey, remember that (S,G) we have installed on interface A? It's started arriving on interface B ...". Which would prompt the daemon to inspect the unicast routing table for the reverse-path to S -- if S has moved from A to B, the multicast route is updated, if not, and it's still behind A, the daemon waits.

Also, the new MRT_ADD_MFC_PROXY and MRT_DEL_MFC_PROXY sockopts introduced in Linux are not available on other UNIX systems which SMCRoute supports, so we cannot simply switch to them just because this "fix" somehow works for you.

I suggest looking into another solution, maybe the various multicast proxy implementations instead: igmpproxy and mcproxy.

dtzq01 commented 1 week ago

Thank you for your response; I now have a deeper understanding of the SMCROUTE situation. The scenario is as follows: NIC-A and NIC-B are actually connected to two physically isolated networks, but logically they belong to the same department and both have the requirement to send multicast traffic to NIC-C. As a multicast service for (S,G), I am not sure whether the multicast data will come from NIC-A or NIC-B, so I have added two static multicast routes to accommodate the possible multicast forwarding. Additionally, thank you for your suggestions regarding igmpproxy and mcproxy.

troglobit commented 1 week ago

In such complex setups one usually have to use SNAT in the prerouting chain.