From a98f2b86c46dc7d0e830f4465efd34492556bdb0 Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Wed, 11 Sep 2024 11:05:07 -0300 Subject: [PATCH] pim6d: implement MLD source/group limits Let user configure a source/group limit for MLD protocol. Signed-off-by: Rafael Zalamena --- pimd/pim6_cmd.c | 40 ++++++++++++++++++++++++ pimd/pim6_mld.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++-- pimd/pim6_mld.h | 2 ++ pimd/pim_iface.c | 2 ++ pimd/pim_igmp.c | 3 -- pimd/pim_vty.c | 4 +-- 6 files changed, 124 insertions(+), 7 deletions(-) diff --git a/pimd/pim6_cmd.c b/pimd/pim6_cmd.c index 12493b7dbb..63444539ee 100644 --- a/pimd/pim6_cmd.c +++ b/pimd/pim6_cmd.c @@ -1612,6 +1612,43 @@ DEFPY (interface_no_ipv6_mld_version, "frr-routing:ipv6"); } +DEFPY_YANG(interface_ipv6_mld_limits, + interface_ipv6_mld_limits_cmd, + "[no] ipv6 mld ", + NO_STR + IPV6_STR + IFACE_MLD_STR + "Limit number of MLDv2 sources to track\n" + "Permitted number of sources\n" + "Limit number of MLD group memberships to track\n" + "Permitted number of groups\n") +{ + const char *xpath; + + assert(do_src || do_grp); + if (do_src) + xpath = "./max-sources"; + else + xpath = "./max-groups"; + + if (no) + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + else + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, val_str); + + return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH, FRR_PIM_AF_XPATH_VAL); +} + +ALIAS_YANG(interface_ipv6_mld_limits, + no_interface_ipv6_mld_limits_cmd, + "no ipv6 mld ", + NO_STR + IPV6_STR + IFACE_MLD_STR + "Limit number of MLDv2 sources to track\n" + "Limit number of MLD group memberships to track\n") + DEFPY (interface_ipv6_mld_query_interval, interface_ipv6_mld_query_interval_cmd, "ipv6 mld query-interval (1-65535)$q_interval", @@ -2865,6 +2902,9 @@ void pim_cmd_init(void) install_element(INTERFACE_NODE, &interface_no_ipv6_pim_boundary_oil_cmd); install_element(INTERFACE_NODE, &interface_ipv6_mroute_cmd); install_element(INTERFACE_NODE, &interface_no_ipv6_mroute_cmd); + install_element(INTERFACE_NODE, &interface_ipv6_mld_limits_cmd); + install_element(INTERFACE_NODE, &no_interface_ipv6_mld_limits_cmd); + /* Install BSM command */ install_element(INTERFACE_NODE, &ipv6_pim_bsm_cmd); install_element(INTERFACE_NODE, &no_ipv6_pim_bsm_cmd); diff --git a/pimd/pim6_mld.c b/pimd/pim6_mld.c index acfb0c3af3..d7e0314d3b 100644 --- a/pimd/pim6_mld.c +++ b/pimd/pim6_mld.c @@ -190,11 +190,26 @@ static struct gm_sg *gm_sg_find(struct gm_if *gm_ifp, pim_addr grp, return gm_sgs_find(gm_ifp->sgs, &ref); } +static bool gm_sg_has_group(struct gm_sgs_head *sgs, const pim_addr group) +{ + struct gm_sg *sg; + + frr_each (gm_sgs, sgs, sg) + if (pim_addr_cmp(sg->sgaddr.grp, group) == 0) + return true; + + return false; +} + static struct gm_sg *gm_sg_make(struct gm_if *gm_ifp, pim_addr grp, pim_addr src) { struct gm_sg *ret, *prev; + /* Count all unique group members. */ + if (!gm_sg_has_group(gm_ifp->sgs, grp)) + gm_ifp->groups_count++; + ret = XCALLOC(MTYPE_GM_SG, sizeof(*ret)); ret->sgaddr.grp = grp; ret->sgaddr.src = src; @@ -212,6 +227,47 @@ static struct gm_sg *gm_sg_make(struct gm_if *gm_ifp, pim_addr grp, return ret; } +static size_t gm_sg_source_count(struct gm_sgs_head *sgs, const pim_addr group) +{ + struct gm_sg *sg; + size_t source_count; + + source_count = 0; + frr_each (gm_sgs, sgs, sg) + if (pim_addr_cmp(sg->sgaddr.grp, group) == 0) + source_count++; + + return source_count; +} + +static bool gm_sg_limit_reached(struct gm_if *gm_if, const pim_addr source, const pim_addr group) +{ + const struct pim_interface *pim_interface = gm_if->ifp->info; + + if (!gm_sg_has_group(gm_if->sgs, group)) { + if (gm_if->groups_count >= pim_interface->gm_group_limit) { + if (PIM_DEBUG_GM_TRACE) + zlog_debug("interface %s has reached group limit (%u), refusing to add group %pPA", + gm_if->ifp->name, pim_interface->gm_group_limit, &group); + + return true; + } + + return false; + } + + if (gm_sg_source_count(gm_if->sgs, group) >= pim_interface->gm_source_limit) { + if (PIM_DEBUG_GM_TRACE) { + zlog_debug("interface %s has reached source limit (%u), refusing to add source %pPA (group %pPA)", + gm_if->ifp->name, pim_interface->gm_source_limit, &source, + &group); + } + return true; + } + + return false; +} + /* * interface -> packets, sorted by expiry (because add_tail insert order) */ @@ -471,6 +527,11 @@ static void gm_sg_update(struct gm_sg *sg, bool has_expired) zlog_debug(log_sg(sg, "dropping")); gm_sgs_del(gm_ifp->sgs, sg); + + /* Decrement unique group members counter. */ + if (!gm_sg_has_group(gm_ifp->sgs, sg->sgaddr.grp)) + gm_ifp->groups_count--; + gm_sg_free(sg); } } @@ -634,8 +695,12 @@ static void gm_handle_v2_pass1(struct gm_packet_state *pkt, case MLD_RECTYPE_CHANGE_TO_EXCLUDE: /* this always replaces or creates state */ is_excl = true; - if (!grp) + if (!grp) { + if (gm_sg_limit_reached(pkt->iface, PIMADDR_ANY, rechdr->grp)) + return; + grp = gm_sg_make(pkt->iface, rechdr->grp, PIMADDR_ANY); + } item = gm_packet_sg_setup(pkt, grp, is_excl, false); item->n_exclude = n_src; @@ -700,9 +765,13 @@ static void gm_handle_v2_pass1(struct gm_packet_state *pkt, struct gm_sg *sg; sg = gm_sg_find(pkt->iface, rechdr->grp, rechdr->srcs[j]); - if (!sg) + if (!sg) { + if (gm_sg_limit_reached(pkt->iface, rechdr->srcs[j], rechdr->grp)) + return; + sg = gm_sg_make(pkt->iface, rechdr->grp, rechdr->srcs[j]); + } gm_packet_sg_setup(pkt, sg, is_excl, true); } @@ -952,6 +1021,10 @@ static void gm_handle_v1_report(struct gm_if *gm_ifp, hdr = (struct mld_v1_pkt *)data; + if (!gm_sg_has_group(gm_ifp->sgs, hdr->grp) && + gm_sg_limit_reached(gm_ifp, PIMADDR_ANY, hdr->grp)) + return; + max_entries = 1; pkt = XCALLOC(MTYPE_GM_STATE, offsetof(struct gm_packet_state, items[max_entries])); @@ -1255,6 +1328,9 @@ static void gm_handle_q_groupsrc(struct gm_if *gm_ifp, for (i = 0; i < n_src; i++) { sg = gm_sg_find(gm_ifp, grp, srcs[i]); + if (sg == NULL) + continue; + GM_UPDATE_SG_STATE(sg); gm_sg_timer_start(gm_ifp, sg, timers->expire_wait); } diff --git a/pimd/pim6_mld.h b/pimd/pim6_mld.h index 183ab2fc50..c5a9708961 100644 --- a/pimd/pim6_mld.h +++ b/pimd/pim6_mld.h @@ -350,6 +350,8 @@ struct gm_if { struct gm_subscribers_head subscribers[1]; struct gm_packet_expires_head expires[1]; + size_t groups_count; + struct timeval started; struct gm_if_stats stats; }; diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 9316cebc0a..8ec51ddc39 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -128,6 +128,8 @@ struct pim_interface *pim_if_new(struct interface *ifp, bool gm, bool pim, pim_ifp->gm_specific_query_max_response_time_dsec = GM_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC; pim_ifp->gm_last_member_query_count = GM_DEFAULT_ROBUSTNESS_VARIABLE; + pim_ifp->gm_group_limit = UINT32_MAX; + pim_ifp->gm_source_limit = UINT32_MAX; /* BSM config on interface: true by default */ pim_ifp->bsm_enable = true; diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index fa915719dd..b1b4566499 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -1126,9 +1126,6 @@ void pim_igmp_if_init(struct pim_interface *pim_ifp, struct interface *ifp) { char hash_name[64]; - pim_ifp->gm_group_limit = UINT32_MAX; - pim_ifp->gm_source_limit = UINT32_MAX; - pim_ifp->gm_socket_list = list_new(); pim_ifp->gm_socket_list->del = (void (*)(void *))igmp_sock_free; diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index 9456a40ffa..64750a22f6 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -457,14 +457,14 @@ int pim_config_write(struct vty *vty, int writes, struct interface *ifp, ++writes; } - /* IF ip igmp max-sources */ + /* IF igmp/mld max-sources */ if (pim_ifp->gm_source_limit != UINT32_MAX) { vty_out(vty, " " PIM_AF_NAME " " GM_AF_DBG " max-sources %u\n", pim_ifp->gm_source_limit); ++writes; } - /* IF ip igmp max-groups */ + /* IF igmp/mld max-groups */ if (pim_ifp->gm_group_limit != UINT32_MAX) { vty_out(vty, " " PIM_AF_NAME " " GM_AF_DBG " max-groups %u\n", pim_ifp->gm_group_limit);