summaryrefslogtreecommitdiff
path: root/pimd/pim6_mld.c
diff options
context:
space:
mode:
Diffstat (limited to 'pimd/pim6_mld.c')
-rw-r--r--pimd/pim6_mld.c80
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);
}