Skip to content

pimd,pim6d: implement GMP group / source limits #18032

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions doc/user/pim.rst
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,14 @@ is in a vrf, enter the interface command with the vrf keyword at the end.

Set the IGMP version used on this interface. The default value is 3.

.. clicmd:: ip igmp max-groups (0-4294967295)

Set the maximum number of IGMP groups that the can be joined on an interface.

.. clicmd:: ip igmp max-sources (0-4294967295)

Set the maximum number of IGMP sources to learn per group.

.. clicmd:: ip multicast boundary oil WORD

Set a PIM multicast boundary, based upon the WORD prefix-list. If a PIM join
Expand Down
12 changes: 12 additions & 0 deletions doc/user/pimv6.rst
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,14 @@ is in a vrf, enter the interface command with the vrf keyword at the end.

Set the MLD version used on this interface. The default value is 2.

.. clicmd:: ipv6 mld max-groups (0-4294967295)

Set the maximum number of MLD groups that the can be joined on an interface.

.. clicmd:: ipv6 mld max-sources (0-4294967295)

Set the maximum number of MLD sources to learn per group.

.. clicmd:: ipv6 multicast boundary oil WORD

Set a PIMv6 multicast boundary, based upon the WORD prefix-list. If a PIMv6
Expand Down Expand Up @@ -481,6 +489,10 @@ PIMv6 Clear Commands

Clear commands reset various variables.

.. clicmd:: clear ipv6 mld [vrf NAME] interfaces

Reset learned multicast groups / sources.

.. clicmd:: clear ipv6 mroute

Reset multicast routes.
Expand Down
67 changes: 67 additions & 0 deletions pimd/pim6_cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 <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,
interface_ipv6_mld_query_interval_cmd,
"ipv6 mld query-interval (1-65535)$q_interval",
Expand Down Expand Up @@ -2341,6 +2378,32 @@ DEFPY (show_ipv6_pim_bsrp,
return pim_show_group_rp_mappings_info_helper(vrf, vty, !!json);
}

DEFPY(clear_ipv6_mld_interfaces,
clear_ipv6_mld_interfaces_cmd,
"clear ipv6 mld [vrf NAME$vrf_name] interfaces",
CLEAR_STR
IPV6_STR
"MLD clear commands\n"
VRF_CMD_HELP_STR
"Reset MLD interfaces\n")
{
struct interface *ifp;
struct vrf *vrf;

vrf = vrf_name ? vrf_lookup_by_name(vrf_name) : vrf_lookup_by_id(VRF_DEFAULT);
if (!vrf) {
vty_out(vty, "Specified VRF: %s does not exist\n", vrf_name);
return CMD_WARNING;
}

FOR_ALL_INTERFACES (vrf, ifp)
pim_if_addr_del_all(ifp);
FOR_ALL_INTERFACES (vrf, ifp)
pim_if_addr_add_all(ifp);

return CMD_SUCCESS;
}

DEFPY (clear_ipv6_pim_statistics,
clear_ipv6_pim_statistics_cmd,
"clear ipv6 pim statistics [vrf NAME]$name",
Expand Down Expand Up @@ -2865,6 +2928,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);
Expand Down Expand Up @@ -2935,6 +3001,7 @@ void pim_cmd_init(void)
install_element(VIEW_NODE, &show_ipv6_pim_bsr_cmd);
install_element(VIEW_NODE, &show_ipv6_pim_bsm_db_cmd);
install_element(VIEW_NODE, &show_ipv6_pim_bsrp_cmd);
install_element(ENABLE_NODE, &clear_ipv6_mld_interfaces_cmd);
install_element(ENABLE_NODE, &clear_ipv6_pim_statistics_cmd);
install_element(ENABLE_NODE, &clear_ipv6_mroute_cmd);
install_element(ENABLE_NODE, &clear_ipv6_pim_oil_cmd);
Expand Down
80 changes: 78 additions & 2 deletions pimd/pim6_mld.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
*/
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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]));
Expand Down Expand Up @@ -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);
}
Expand Down
2 changes: 2 additions & 0 deletions pimd/pim6_mld.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down
39 changes: 39 additions & 0 deletions pimd/pim_cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -5656,6 +5656,43 @@ DEFUN (interface_no_ip_igmp_last_member_query_interval,
return gm_process_no_last_member_query_interval_cmd(vty);
}

DEFPY_YANG(interface_ip_igmp_limits,
interface_ip_igmp_limits_cmd,
"[no] ip igmp <max-sources$do_src (0-4294967295)$val"
"|max-groups$do_grp (0-4294967295)$val>",
NO_STR
IP_STR
IFACE_IGMP_STR
"Limit number of IGMPv3 sources to track\n"
"Permitted number of sources\n"
"Limit number of IGMP 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_ip_igmp_limits,
no_interface_ip_igmp_limits_cmd,
"no ip igmp <max-sources$do_src|max-groups$do_grp>",
NO_STR
IP_STR
IFACE_IGMP_STR
"Limit number of IGMPv3 sources to track\n"
"Limit number of IGMP group memberships to track\n")

DEFUN (interface_ip_pim_drprio,
interface_ip_pim_drprio_cmd,
"ip pim drpriority (0-4294967295)",
Expand Down Expand Up @@ -9101,6 +9138,8 @@ void pim_cmd_init(void)
install_element(INTERFACE_NODE,
&interface_no_ip_igmp_last_member_query_interval_cmd);
install_element(INTERFACE_NODE, &interface_ip_igmp_proxy_cmd);
install_element(INTERFACE_NODE, &interface_ip_igmp_limits_cmd);
install_element(INTERFACE_NODE, &no_interface_ip_igmp_limits_cmd);
install_element(INTERFACE_NODE, &interface_ip_pim_activeactive_cmd);
install_element(INTERFACE_NODE, &interface_ip_pim_ssm_cmd);
install_element(INTERFACE_NODE, &interface_no_ip_pim_ssm_cmd);
Expand Down
2 changes: 2 additions & 0 deletions pimd/pim_iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions pimd/pim_iface.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ struct pim_interface {

struct gm_if *mld;

uint32_t gm_source_limit, gm_group_limit;

int pim_sock_fd; /* PIM socket file descriptor */
struct event *t_pim_sock_read; /* thread for reading PIM socket */
int64_t pim_sock_creation; /* timestamp of PIM socket creation */
Expand Down
8 changes: 8 additions & 0 deletions pimd/pim_igmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1416,6 +1416,14 @@ struct gm_group *igmp_add_group_by_addr(struct gm_sock *igmp,
__func__, &group_addr);
return NULL;
}

if (listcount(pim_ifp->gm_group_list) >= pim_ifp->gm_group_limit) {
if (PIM_DEBUG_GM_TRACE)
zlog_debug("interface %s has reached group limit (%u), refusing to add group %pI4",
igmp->interface->name, pim_ifp->gm_group_limit, &group_addr);
return NULL;
}

/*
Non-existant group is created as INCLUDE {empty}:

Expand Down
9 changes: 9 additions & 0 deletions pimd/pim_igmpv3.c
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ struct gm_source *igmp_find_source_by_addr(struct gm_group *group,
struct gm_source *igmp_get_source_by_addr(struct gm_group *group,
struct in_addr src_addr, bool *new)
{
const struct pim_interface *pim_interface = group->interface->info;
struct gm_source *src;

if (new)
Expand All @@ -432,6 +433,14 @@ struct gm_source *igmp_get_source_by_addr(struct gm_group *group,
if (src)
return src;

if (listcount(group->group_source_list) >= pim_interface->gm_source_limit) {
if (PIM_DEBUG_GM_TRACE)
zlog_debug("interface %s has reached source limit (%u), refusing to add source %pI4 (group %pI4)",
group->interface->name, pim_interface->gm_source_limit,
&src_addr, &group->group_addr);
return NULL;
}

if (PIM_DEBUG_GM_TRACE) {
char group_str[INET_ADDRSTRLEN];
char source_str[INET_ADDRSTRLEN];
Expand Down
Loading
Loading