From f4d3222d10fb9775cc10dca0e7e57d43c28cd846 Mon Sep 17 00:00:00 2001 From: "Barry A. Trent" Date: Tue, 20 Aug 2024 14:34:26 -0700 Subject: [PATCH] pimd: add proxy join/prune functionality Use existing igmp static join infrastructure. Add an enum to distinguish static from proxy joins. Signed-off-by: Barry A. Trent --- pimd/pim_iface.c | 79 +++++++++++++++++++++++++++++++++++++++++--- pimd/pim_iface.h | 6 ++-- pimd/pim_igmp.c | 8 +++-- pimd/pim_igmp.h | 3 ++ pimd/pim_nb_config.c | 12 +++++-- pimd/pim_tib.c | 29 ++++++++++++++++ pimd/pim_tib.h | 3 ++ 7 files changed, 128 insertions(+), 12 deletions(-) diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 125d35ac46..7f873f45dc 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -1294,7 +1294,8 @@ static int gm_join_sock(const char *ifname, ifindex_t ifindex, } static struct gm_join *gm_join_new(struct interface *ifp, pim_addr group_addr, - pim_addr source_addr) + pim_addr source_addr, + enum gm_join_type join_type) { struct pim_interface *pim_ifp; struct gm_join *ij; @@ -1317,6 +1318,7 @@ static struct gm_join *gm_join_new(struct interface *ifp, pim_addr group_addr, ij->sock_fd = join_fd; ij->group_addr = group_addr; ij->source_addr = source_addr; + ij->join_type = join_type; ij->sock_creation = pim_time_monotonic_sec(); listnode_add(pim_ifp->gm_join_list, ij); @@ -1353,7 +1355,7 @@ static struct static_group *static_group_new(struct interface *ifp, } ferr_r pim_if_gm_join_add(struct interface *ifp, pim_addr group_addr, - pim_addr source_addr) + pim_addr source_addr, enum gm_join_type join_type) { struct pim_interface *pim_ifp; struct gm_join *ij; @@ -1375,10 +1377,13 @@ ferr_r pim_if_gm_join_add(struct interface *ifp, pim_addr group_addr, * group */ if (ij) { + /* turn an existing join into a "both" join */ + if (ij->join_type != join_type) + ij->join_type = GM_JOIN_BOTH; return ferr_ok(); } - if (!gm_join_new(ifp, group_addr, source_addr)) { + if (!gm_join_new(ifp, group_addr, source_addr, join_type)) { return ferr_cfg_invalid("can't join (%pPA,%pPA) on interface %s", &source_addr, &group_addr, ifp->name); } @@ -1394,7 +1399,7 @@ ferr_r pim_if_gm_join_add(struct interface *ifp, pim_addr group_addr, } int pim_if_gm_join_del(struct interface *ifp, pim_addr group_addr, - pim_addr source_addr) + pim_addr source_addr, enum gm_join_type join_type) { struct pim_interface *pim_ifp; struct gm_join *ij; @@ -1420,6 +1425,20 @@ int pim_if_gm_join_del(struct interface *ifp, pim_addr group_addr, return -3; } + if (ij->join_type != join_type) { + if (ij->join_type != GM_JOIN_BOTH) { + zlog_warn("%s: wrong " GM + " gm_join_type %pPAs source %pPAs on interface %s", + __func__, &group_addr, &source_addr, + ifp->name); + return -4; + } + /* drop back to a single join type from current setting of GM_JOIN_BOTH */ + ij->join_type = (join_type == GM_JOIN_STATIC ? GM_JOIN_PROXY + : GM_JOIN_STATIC); + return 0; + } + if (close(ij->sock_fd)) { zlog_warn( "%s: failure closing sock_fd=%d for " GM @@ -1456,7 +1475,8 @@ static void pim_if_gm_join_del_all(struct interface *ifp) return; for (ALL_LIST_ELEMENTS(pim_ifp->gm_join_list, node, nextnode, ij)) - pim_if_gm_join_del(ifp, ij->group_addr, ij->source_addr); + pim_if_gm_join_del(ifp, ij->group_addr, ij->source_addr, + GM_JOIN_STATIC); } ferr_r pim_if_static_group_add(struct interface *ifp, pim_addr group_addr, @@ -1562,6 +1582,55 @@ static void pim_if_static_group_del_all(struct interface *ifp) stgrp->source_addr); } +void pim_if_gm_proxy_init(struct pim_instance *pim, struct interface *oif) +{ + struct interface *ifp; + + FOR_ALL_INTERFACES (pim->vrf, ifp) { + struct pim_interface *pim_ifp = ifp->info; + struct listnode *source_node, *group_node; + struct gm_group *group; + struct gm_source *src; + + if (!pim_ifp) + continue; + + if (ifp == oif) /* skip the source interface */ + continue; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, group_node, + group)) { + for (ALL_LIST_ELEMENTS_RO(group->group_source_list, + source_node, src)) { + pim_if_gm_join_add(oif, group->group_addr, + src->source_addr, + GM_JOIN_PROXY); + } + } + } /* scan interfaces */ +} + +void pim_if_gm_proxy_finis(struct pim_instance *pim, struct interface *ifp) +{ + struct pim_interface *pim_ifp = ifp->info; + struct listnode *join_node; + struct listnode *next_join_node; + struct gm_join *join; + + if (!pim_ifp) { + zlog_warn("%s: multicast not enabled on interface %s", __func__, + ifp->name); + return; + } + + for (ALL_LIST_ELEMENTS(pim_ifp->gm_join_list, join_node, next_join_node, + join)) { + if (join) + pim_if_gm_join_del(ifp, join->group_addr, + join->source_addr, GM_JOIN_PROXY); + } +} + /* RFC 4601 diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index 77e3f5191d..95bac084d2 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -220,9 +220,11 @@ int pim_if_t_override_msec(struct interface *ifp); pim_addr pim_find_primary_addr(struct interface *ifp); ferr_r pim_if_gm_join_add(struct interface *ifp, pim_addr group_addr, - pim_addr source_addr); + pim_addr source_addr, enum gm_join_type join_type); int pim_if_gm_join_del(struct interface *ifp, pim_addr group_addr, - pim_addr source_addr); + pim_addr source_addr, enum gm_join_type join_type); +void pim_if_gm_proxy_init(struct pim_instance *pim, struct interface *oif); +void pim_if_gm_proxy_finis(struct pim_instance *pim, struct interface *ifp); ferr_r pim_if_static_group_add(struct interface *ifp, pim_addr group_addr, pim_addr source_addr); diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index 063ba6edd2..1ba9bc45a2 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -213,15 +213,17 @@ void igmp_source_forward_stop(struct gm_source *source) IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); } + group = source->source_group; + pim_oif = group->interface->info; + /* Prevent IGMP interface from removing multicast route multiple times */ if (!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) { + tib_sg_proxy_join_prune_check(pim_oif->pim, sg, + group->interface, false); return; } - group = source->source_group; - pim_oif = group->interface->info; - tib_sg_gm_prune(pim_oif->pim, sg, group->interface, &source->source_channel_oil); IGMP_SOURCE_DONT_FORWARDING(source->source_flags); diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h index de0ec01a65..83524e3980 100644 --- a/pimd/pim_igmp.h +++ b/pimd/pim_igmp.h @@ -51,10 +51,13 @@ output |= *((ptr) + 1); \ } while (0) +enum gm_join_type { GM_JOIN_STATIC = 0, GM_JOIN_PROXY = 1, GM_JOIN_BOTH = 2 }; + struct gm_join { pim_addr group_addr; pim_addr source_addr; int sock_fd; + enum gm_join_type join_type; time_t sock_creation; }; diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index 781bd5a9a1..0366d8a857 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -26,6 +26,7 @@ #include "lib_errors.h" #include "pim_util.h" #include "pim6_mld.h" +#include "pim_igmp.h" #if PIM_IPV == 6 #define pim6_msdp_err(funcname, argtype) \ @@ -3400,6 +3401,11 @@ int lib_interface_gmp_address_family_proxy_modify(struct nb_cb_modify_args *args if (pim_ifp) pim_ifp->gm_proxy = yang_dnode_get_bool(args->dnode, NULL); + + if (pim_ifp->gm_proxy) + pim_if_gm_proxy_init(pim_ifp->pim, ifp); + else + pim_if_gm_proxy_finis(pim_ifp->pim, ifp); } return NB_OK; } @@ -3454,7 +3460,8 @@ int lib_interface_gmp_address_family_join_group_create( "./source-addr"); yang_dnode_get_pimaddr(&group_addr, args->dnode, "./group-addr"); - result = pim_if_gm_join_add(ifp, group_addr, source_addr); + result = pim_if_gm_join_add(ifp, group_addr, source_addr, + GM_JOIN_STATIC); if (result) { snprintf(args->errmsg, args->errmsg_len, "Failure joining " GM " group"); @@ -3483,7 +3490,8 @@ int lib_interface_gmp_address_family_join_group_destroy( "./source-addr"); yang_dnode_get_pimaddr(&group_addr, args->dnode, "./group-addr"); - result = pim_if_gm_join_del(ifp, group_addr, source_addr); + result = pim_if_gm_join_del(ifp, group_addr, source_addr, + GM_JOIN_STATIC); if (result) { snprintf(args->errmsg, args->errmsg_len, diff --git a/pimd/pim_tib.c b/pimd/pim_tib.c index 4081786c1e..e1cc69efa9 100644 --- a/pimd/pim_tib.c +++ b/pimd/pim_tib.c @@ -78,6 +78,31 @@ tib_sg_oil_setup(struct pim_instance *pim, pim_sgaddr sg, struct interface *oif) return pim_channel_oil_add(pim, &sg, __func__); } +void tib_sg_proxy_join_prune_check(struct pim_instance *pim, pim_sgaddr sg, + struct interface *oif, bool join) +{ + struct interface *ifp; + + FOR_ALL_INTERFACES (pim->vrf, ifp) { + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (ifp == oif) /* skip the source interface */ + continue; + + if (pim_ifp->gm_enable && pim_ifp->gm_proxy) { + if (join) + pim_if_gm_join_add(ifp, sg.grp, sg.src, + GM_JOIN_PROXY); + else + pim_if_gm_join_del(ifp, sg.grp, sg.src, + GM_JOIN_PROXY); + } + } /* scan interfaces */ +} + bool tib_sg_gm_join(struct pim_instance *pim, pim_sgaddr sg, struct interface *oif, struct channel_oil **oilp) { @@ -95,6 +120,8 @@ bool tib_sg_gm_join(struct pim_instance *pim, pim_sgaddr sg, if (!*oilp) return false; + tib_sg_proxy_join_prune_check(pim, sg, oif, true); + if (PIM_I_am_DR(pim_oif) || PIM_I_am_DualActive(pim_oif)) { int result; @@ -137,6 +164,8 @@ void tib_sg_gm_prune(struct pim_instance *pim, pim_sgaddr sg, { int result; + tib_sg_proxy_join_prune_check(pim, sg, oif, false); + /* It appears that in certain circumstances that igmp_source_forward_stop is called when IGMP forwarding diff --git a/pimd/pim_tib.h b/pimd/pim_tib.h index 081ad908b5..a41d0a8475 100644 --- a/pimd/pim_tib.h +++ b/pimd/pim_tib.h @@ -16,5 +16,8 @@ extern bool tib_sg_gm_join(struct pim_instance *pim, pim_sgaddr sg, struct interface *oif, struct channel_oil **oilp); extern void tib_sg_gm_prune(struct pim_instance *pim, pim_sgaddr sg, struct interface *oif, struct channel_oil **oilp); +extern void tib_sg_proxy_join_prune_check(struct pim_instance *pim, + pim_sgaddr sg, struct interface *oif, + bool join); #endif /* _FRR_PIM_GLUE_H */