mirror of
https://github.com/FRRouting/frr.git
synced 2025-04-30 13:37:17 +02:00
pim6d: implement MLD source/group limits
Let user configure a source/group limit for MLD protocol. Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
This commit is contained in:
parent
f07d379b74
commit
a98f2b86c4
|
@ -1612,6 +1612,43 @@ DEFPY (interface_no_ipv6_mld_version,
|
||||||
"frr-routing:ipv6");
|
"frr-routing:ipv6");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFPY_YANG(interface_ipv6_mld_limits,
|
||||||
|
interface_ipv6_mld_limits_cmd,
|
||||||
|
"[no] ipv6 mld <max-sources$do_src (0-4294967295)$val"
|
||||||
|
"|max-groups$do_grp (0-4294967295)$val>",
|
||||||
|
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 <max-sources$do_src|max-groups$do_grp>",
|
||||||
|
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,
|
DEFPY (interface_ipv6_mld_query_interval,
|
||||||
interface_ipv6_mld_query_interval_cmd,
|
interface_ipv6_mld_query_interval_cmd,
|
||||||
"ipv6 mld query-interval (1-65535)$q_interval",
|
"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_no_ipv6_pim_boundary_oil_cmd);
|
||||||
install_element(INTERFACE_NODE, &interface_ipv6_mroute_cmd);
|
install_element(INTERFACE_NODE, &interface_ipv6_mroute_cmd);
|
||||||
install_element(INTERFACE_NODE, &interface_no_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 BSM command */
|
||||||
install_element(INTERFACE_NODE, &ipv6_pim_bsm_cmd);
|
install_element(INTERFACE_NODE, &ipv6_pim_bsm_cmd);
|
||||||
install_element(INTERFACE_NODE, &no_ipv6_pim_bsm_cmd);
|
install_element(INTERFACE_NODE, &no_ipv6_pim_bsm_cmd);
|
||||||
|
|
|
@ -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);
|
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,
|
static struct gm_sg *gm_sg_make(struct gm_if *gm_ifp, pim_addr grp,
|
||||||
pim_addr src)
|
pim_addr src)
|
||||||
{
|
{
|
||||||
struct gm_sg *ret, *prev;
|
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 = XCALLOC(MTYPE_GM_SG, sizeof(*ret));
|
||||||
ret->sgaddr.grp = grp;
|
ret->sgaddr.grp = grp;
|
||||||
ret->sgaddr.src = src;
|
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;
|
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)
|
* 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"));
|
zlog_debug(log_sg(sg, "dropping"));
|
||||||
|
|
||||||
gm_sgs_del(gm_ifp->sgs, sg);
|
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);
|
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:
|
case MLD_RECTYPE_CHANGE_TO_EXCLUDE:
|
||||||
/* this always replaces or creates state */
|
/* this always replaces or creates state */
|
||||||
is_excl = true;
|
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);
|
grp = gm_sg_make(pkt->iface, rechdr->grp, PIMADDR_ANY);
|
||||||
|
}
|
||||||
|
|
||||||
item = gm_packet_sg_setup(pkt, grp, is_excl, false);
|
item = gm_packet_sg_setup(pkt, grp, is_excl, false);
|
||||||
item->n_exclude = n_src;
|
item->n_exclude = n_src;
|
||||||
|
@ -700,9 +765,13 @@ static void gm_handle_v2_pass1(struct gm_packet_state *pkt,
|
||||||
struct gm_sg *sg;
|
struct gm_sg *sg;
|
||||||
|
|
||||||
sg = gm_sg_find(pkt->iface, rechdr->grp, rechdr->srcs[j]);
|
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,
|
sg = gm_sg_make(pkt->iface, rechdr->grp,
|
||||||
rechdr->srcs[j]);
|
rechdr->srcs[j]);
|
||||||
|
}
|
||||||
|
|
||||||
gm_packet_sg_setup(pkt, sg, is_excl, true);
|
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;
|
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;
|
max_entries = 1;
|
||||||
pkt = XCALLOC(MTYPE_GM_STATE,
|
pkt = XCALLOC(MTYPE_GM_STATE,
|
||||||
offsetof(struct gm_packet_state, items[max_entries]));
|
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++) {
|
for (i = 0; i < n_src; i++) {
|
||||||
sg = gm_sg_find(gm_ifp, grp, srcs[i]);
|
sg = gm_sg_find(gm_ifp, grp, srcs[i]);
|
||||||
|
if (sg == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
GM_UPDATE_SG_STATE(sg);
|
GM_UPDATE_SG_STATE(sg);
|
||||||
gm_sg_timer_start(gm_ifp, sg, timers->expire_wait);
|
gm_sg_timer_start(gm_ifp, sg, timers->expire_wait);
|
||||||
}
|
}
|
||||||
|
|
|
@ -350,6 +350,8 @@ struct gm_if {
|
||||||
struct gm_subscribers_head subscribers[1];
|
struct gm_subscribers_head subscribers[1];
|
||||||
struct gm_packet_expires_head expires[1];
|
struct gm_packet_expires_head expires[1];
|
||||||
|
|
||||||
|
size_t groups_count;
|
||||||
|
|
||||||
struct timeval started;
|
struct timeval started;
|
||||||
struct gm_if_stats stats;
|
struct gm_if_stats stats;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 =
|
pim_ifp->gm_specific_query_max_response_time_dsec =
|
||||||
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_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 */
|
/* BSM config on interface: true by default */
|
||||||
pim_ifp->bsm_enable = true;
|
pim_ifp->bsm_enable = true;
|
||||||
|
|
|
@ -1126,9 +1126,6 @@ void pim_igmp_if_init(struct pim_interface *pim_ifp, struct interface *ifp)
|
||||||
{
|
{
|
||||||
char hash_name[64];
|
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 = list_new();
|
||||||
pim_ifp->gm_socket_list->del = (void (*)(void *))igmp_sock_free;
|
pim_ifp->gm_socket_list->del = (void (*)(void *))igmp_sock_free;
|
||||||
|
|
||||||
|
|
|
@ -457,14 +457,14 @@ int pim_config_write(struct vty *vty, int writes, struct interface *ifp,
|
||||||
++writes;
|
++writes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* IF ip igmp max-sources */
|
/* IF igmp/mld max-sources */
|
||||||
if (pim_ifp->gm_source_limit != UINT32_MAX) {
|
if (pim_ifp->gm_source_limit != UINT32_MAX) {
|
||||||
vty_out(vty, " " PIM_AF_NAME " " GM_AF_DBG " max-sources %u\n",
|
vty_out(vty, " " PIM_AF_NAME " " GM_AF_DBG " max-sources %u\n",
|
||||||
pim_ifp->gm_source_limit);
|
pim_ifp->gm_source_limit);
|
||||||
++writes;
|
++writes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* IF ip igmp max-groups */
|
/* IF igmp/mld max-groups */
|
||||||
if (pim_ifp->gm_group_limit != UINT32_MAX) {
|
if (pim_ifp->gm_group_limit != UINT32_MAX) {
|
||||||
vty_out(vty, " " PIM_AF_NAME " " GM_AF_DBG " max-groups %u\n",
|
vty_out(vty, " " PIM_AF_NAME " " GM_AF_DBG " max-groups %u\n",
|
||||||
pim_ifp->gm_group_limit);
|
pim_ifp->gm_group_limit);
|
||||||
|
|
Loading…
Reference in a new issue