diff options
Diffstat (limited to 'pimd/pim6_mld.c')
| -rw-r--r-- | pimd/pim6_mld.c | 80 |
1 files changed, 78 insertions, 2 deletions
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); } |
