Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Translation PIM Join/Prune to IGMP Report/Leave #174

Open
tluciomiranda opened this issue Oct 20, 2020 · 4 comments
Open

Translation PIM Join/Prune to IGMP Report/Leave #174

tluciomiranda opened this issue Oct 20, 2020 · 4 comments

Comments

@tluciomiranda
Copy link

Hi,

Not really a problem or bug but more like a feature request.
I have a network topology where there are some routers that are not pim-aware, but can deliver multicast to the network if some client issue an IGMP report. The problem is, those routers do not talk PIM and I have no control over them, so it would be great if pimd could just send an IGMP report/leave upstream upon receipt of a PIM join/prune for specific groups that it is RP of.

Detailing a bit more, below is a drawing of my network topology (hopefully you can understand!) where:

  • R1 is the main router and interconnects four networks, one locally attached (192.168.10.0/24) and three via OpenVPN TAP tunnels (192.168.20.0/24, 192.168.30.0/24 and 192.168.40.0/24). It is also a PIM neighbor of R2, R3 and R4
  • R2, R3 and R4 are OpenVPN clients that connect to R1 and RPs for multicast groups 239.230.0.0/24, 239.250.0.0/24, 239.240.0.0/24, respectively
  • R5, R6 and R7 are routers that I have no control of and are not PIM-aware, but work as IGMP proxies to their upstream multicast networks

topology

What I want to achieve is have all the multicast groups available on every network segment, where a client issues an IGMP report for a group, it traverses through PIM domain and, reaching the RP to the specific group, it sends an IGMP report to the upstream non-pim-aware router, and forwards the traffic back to the client. This behavior could be enabled/disabled by config, for example.

A similar scenario is described in the following two documents by Cisco and Juniper:

I have explored a bit your code, and found that maybe the correct point to insert that logic is somewhere in pim_proto.c inside method receive_pim_join_prune(), but I can obviously be wrong.

If you need more info, or want to discuss a bit more please feel free to ask.

Regards,
Tiago

@troglobit
Copy link
Owner

I must say, compared to just about everything else here, this was a perfectly written feature request. Thank you! 😃

I understand what you want to achieve, and it also looks a lot like what I've been asked about over email by others as well. It shouldn't be too difficult to add support for it either. Unfortunately I've just entered release mode (about five years later than planned), so I won't be able to work on this for the upcoming v3.0, unless you're interested in pitching in yourself?

From what I can see, we need:

  • A neato syntax, e.g. an igmp-proxy option to the phyint setting, perhaps?
  • A new passive mode to phyint's, such that we don't send PIM messages on your upstream links, e.g. from R2 to R5
  • Support for (periodically) sending IGMP v2/v3 reports on igmp-proxy phyints for (*,G) we have in the MFC
  • A "hook point" for our new feature, possibly receivve_pim_join_prune() as you mention

For now I can only help out from the sidelines, cheering on, providing pointers and auditing code. Hope that's OK?

@tluciomiranda
Copy link
Author

Thank you for your interest in the feature!

Yesterday, after your response, I have navigated a bit more over the code, to try to define a possible implementation for this, but I may confess that too many questions arose, so I kindly ask you to give me some directions on how to structure this new interaction.

Regarding your points:

A neato syntax, e.g. an igmp-proxy option to the phyint setting, perhaps?

Seems OK, something like phyint _iface_ igmp-proxy

A new passive mode to phyint's, such that we don't send PIM messages on your upstream links, e.g. from R2 to R5

Found on vif.h the following definition
#define VIFF_ONEWAY 0x000800 /* Maybe one way interface */
Seems like a good mode to place the interface into

Support for (periodically) sending IGMP v2/v3 reports on igmp-proxy phyints for (*,G) we have in the MFC

This includes sending our reports/leaves upstream and answering to general queries, right ? Also, do we need two separate sockets, so we do not send reports/leaves back to PIM neighbors ?

A "hook point" for our new feature, possibly receivve_pim_join_prune() as you mention

Can you please guide me on what part of this method the code knows exacly "I am the RP of this group" ?

Apart from this, I don't mind waiting for your next release, if you prefer to develop yourself and have things structured your way.

Thanks!

@troglobit
Copy link
Owner

Hi again, sorry for the late reply! Been quite busy at $DAYJOB the last couple of weeks.

Huh, had forgotten about VIFF_ONEWAY, yup we could definitely reuse that. Not sure of the naming though, I think it may have been intended for declaring one-way of bidirectional distribution trees. I think renaming that define to VIFF_PASSIVE would be the best option. The code base is old, I'm just the current maintainer :)

Yes, sending IGMP reports upstream and answering general, or group-specific, queries. You're right about the socket, we should go with a separate socket for interfaces that have the VIFF_IGMP_PROXY flag.

Figuring out if I'm the RP is trickier. I'll have to dig in to the code a bit more for that, but one example of how to do it is in receive_pim_register(). Grep for "I am the RP"

/* I am the RP */

@tluciomiranda
Copy link
Author

tluciomiranda commented Mar 4, 2021

Hi Joachim, hope your health is doing fine!

Regarding this feature, I have been digging around through the code to try to understand things and start some writing.
Here is what I have done so far:

  • config.c --> added config file parsing to accept phyint if_name igmp-proxy and added the flag VIFF_IGMP_PROXY to the interface (should we limit to just one interface of this type ??)
  • excluded interfaces with VIFF_IGMP_PROXY from interface-related activities in init_reg_vif(), update_reg_vif(), check_vif_state(), find_vif_direct(), local_address(), find_vif_direct_local(), max_local_address()
  • prevented packet processing of PIM/IGMP packets received on interfaces with VIFF_IGMP_PROXY flag in receive_pim_hello(), receive_pim_join_prune(), receive_pim_assert(), receive_pim_bootstrap(), send_pim_bootstrap(), accept_group_report(), accept_leave_message(),
  • vif.c --> created a new method init_igmp_proxy() that creates a separate socket int igmp_proxy_socket specific to our igmp-proxy interface and registers the igmp_read() handler on it. Also created another method start_igmp_proxy_vif() that calls k_add_vif() with our new socket

What I am trying to achieve:

  • I have figured that we may have to add some processing in receive_pim_register(), receive_pim_join_prune() and also in accept_group_report() and accept_leave_message() to deal with messages received from neighbor routers or from directly attached clients, respectively. BUT, I am stuck finding a reliable way to determine if "I am the RP for this group" because check_mrtentry_rp() never matches. Obviously I'm doing it wrong, for example in accept_group_report():

    mrtentry = find_route(igmp_src, group, MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE);
    
     if (local_address(group) == NO_VIF && check_mrtentry_rp(mrtentry, group)) {
      IF_DEBUG(DEBUG_PIM_REGISTER)
         logit(LOG_DEBUG, 0, "------------------------ I AM THE RP ------------------------------");
     }
    

    Where igmp_src is the address of the client sending a JOIN and group is the MC group being joined. I think the problem might be in find_route() where a subsequential call to rp_grp_match() always enters the following ckeck:

    if ((group_h & ntohl(mask_ptr->group_mask)) != ntohl(mask_ptr->group_mask & mask_ptr->group_addr))
        continue;
    

    Could not figure exactly what this check is doing and why if fails.

Ok, so this is where I am, and kindly ask for a bit of help from you.
Also, sorry for the long post and the not-so-good code debugging method :)

Regards,
Tiago

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants