pimd: implement IGMP group/source count limit

For groups we can just look at the length of the list, for sources we
need to count them on a per-interface level.

Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
This commit is contained in:
David Lamparter 2021-08-24 18:21:59 +02:00 committed by Rafael Zalamena
parent baf4c1a78f
commit f07d379b74
9 changed files with 169 additions and 0 deletions

View file

@ -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)",
@ -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);

View file

@ -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 */

View file

@ -1126,6 +1126,9 @@ 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;
@ -1416,6 +1419,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}:

View file

@ -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)
@ -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];

View file

@ -724,6 +724,18 @@ const struct frr_yang_module_info frr_gmp_info = {
.create = lib_interface_gmp_address_family_join_group_create,
.destroy = lib_interface_gmp_address_family_join_group_destroy,
}
},
{
.xpath = "/frr-interface:lib/interface/frr-gmp:gmp/address-family/max-sources",
.cbs = {
.modify = lib_interface_gm_max_sources_modify,
}
},
{
.xpath = "/frr-interface:lib/interface/frr-gmp:gmp/address-family/max-groups",
.cbs = {
.modify = lib_interface_gm_max_groups_modify,
}
},
{
.xpath = "/frr-interface:lib/interface/frr-gmp:gmp/address-family/proxy",

View file

@ -287,6 +287,8 @@ int lib_interface_gmp_address_family_static_group_create(
struct nb_cb_create_args *args);
int lib_interface_gmp_address_family_static_group_destroy(
struct nb_cb_destroy_args *args);
int lib_interface_gm_max_sources_modify(struct nb_cb_modify_args *args);
int lib_interface_gm_max_groups_modify(struct nb_cb_modify_args *args);
/*
* Callback registered with routing_nb lib to validate only

View file

@ -4396,6 +4396,72 @@ int lib_interface_gmp_address_family_last_member_query_interval_modify(
return NB_OK;
}
/*
* XPath: /frr-interface:lib/interface/frr-gmp:gmp/address-family/max-groups
*/
int lib_interface_gm_max_groups_modify(struct nb_cb_modify_args *args)
{
struct interface *ifp;
struct pim_interface *pim_ifp;
const char *ifp_name;
const struct lyd_node *if_dnode;
switch (args->event) {
case NB_EV_VALIDATE:
if_dnode = yang_dnode_get_parent(args->dnode, "interface");
if (!is_pim_interface(if_dnode)) {
ifp_name = yang_dnode_get_string(if_dnode, "name");
snprintf(args->errmsg, args->errmsg_len,
"multicast not enabled on interface %s", ifp_name);
return NB_ERR_VALIDATION;
}
break;
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
ifp = nb_running_get_entry(args->dnode, NULL, true);
pim_ifp = ifp->info;
pim_ifp->gm_group_limit = yang_dnode_get_uint32(args->dnode, NULL);
break;
}
return NB_OK;
}
/*
* XPath: /frr-interface:lib/interface/frr-gmp:gmp/address-family/max-sources
*/
int lib_interface_gm_max_sources_modify(struct nb_cb_modify_args *args)
{
struct interface *ifp;
struct pim_interface *pim_ifp;
const char *ifp_name;
const struct lyd_node *if_dnode;
switch (args->event) {
case NB_EV_VALIDATE:
if_dnode = yang_dnode_get_parent(args->dnode, "interface");
if (!is_pim_interface(if_dnode)) {
ifp_name = yang_dnode_get_string(if_dnode, "name");
snprintf(args->errmsg, args->errmsg_len,
"multicast not enabled on interface %s", ifp_name);
return NB_ERR_VALIDATION;
}
break;
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
ifp = nb_running_get_entry(args->dnode, NULL, true);
pim_ifp = ifp->info;
pim_ifp->gm_source_limit = yang_dnode_get_uint32(args->dnode, NULL);
break;
}
return NB_OK;
}
/*
* XPath: /frr-interface:lib/interface/frr-gmp:gmp/address-family/robustness-variable
*/

View file

@ -457,6 +457,20 @@ int pim_config_write(struct vty *vty, int writes, struct interface *ifp,
++writes;
}
/* IF ip igmp 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 (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);
++writes;
}
/* IF ip pim drpriority */
if (pim_ifp->pim_dr_priority != PIM_DEFAULT_DR_PRIORITY) {
vty_out(vty, " " PIM_AF_NAME " pim drpriority %u\n",

View file

@ -154,6 +154,20 @@ module frr-gmp {
"Enable IGMP proxy on the interface.";
}
leaf max-groups {
type uint32;
default "4294967295";
description
"Limit number of tracked IGMP group memberships on this interface.";
}
leaf max-sources {
type uint32;
default "4294967295";
description
"Limit number of tracked IGMPv3 sources on this interface.";
}
list join-group {
key "group-addr source-addr";
description