pimd, yang: Implement igmp static-group command

This will add a static IGMP group that does not rely on an underlying
socket join which sends traffic to the cpu unneccesarily. Instead, the
groups are joined directly without any IGMP interactions.
New command is under interfaces, 'ip igmp static-group ...'.
Added an alias for 'ip igmp join ...' to 'ip igmp join-group'.
Moved IGMP join groups to new yang list "join-group" and reused
the "static-group" list for the IGMP static groups.

Signed-off-by: Nathan Bahr <nbahr@atcorp.com>
This commit is contained in:
Nathan Bahr 2024-06-26 12:41:45 -05:00
parent aa9d66e922
commit 0cb1bf7873
12 changed files with 596 additions and 116 deletions

View file

@ -1363,46 +1363,57 @@ DEFPY_ATTR(no_ipv6_ssmpingd,
return ret; return ret;
} }
DEFPY (interface_ipv6_mld_join, DEFPY_YANG_HIDDEN (interface_ipv6_mld_join,
interface_ipv6_mld_join_cmd, interface_ipv6_mld_join_cmd,
"ipv6 mld join X:X::X:X$group [X:X::X:X$source]", "[no] ipv6 mld join X:X::X:X$grp [X:X::X:X]$src",
IPV6_STR NO_STR
IFACE_MLD_STR IPV6_STR
"MLD join multicast group\n" IFACE_MLD_STR
"Multicast group address\n" "MLD join multicast group\n"
"Source address\n") "Multicast group address\n"
"Source address\n")
{ {
char xpath[XPATH_MAXLEN]; nb_cli_enqueue_change(vty, ".", (!no ? NB_OP_CREATE : NB_OP_DESTROY),
NULL);
if (!IN6_IS_ADDR_MULTICAST(&group)) { return nb_cli_apply_changes(vty, FRR_GMP_JOIN_GROUP_XPATH,
vty_out(vty, "Invalid Multicast Address\n"); "frr-routing:ipv6", grp_str,
return CMD_WARNING_CONFIG_FAILED; (src_str ? src_str : "::"));
}
if (source_str) {
if (IPV6_ADDR_SAME(&source, &in6addr_any)) {
vty_out(vty, "Bad source address %s\n", source_str);
return CMD_WARNING_CONFIG_FAILED;
}
} else
source_str = "::";
snprintf(xpath, sizeof(xpath), FRR_GMP_JOIN_XPATH, "frr-routing:ipv6",
group_str, source_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
return nb_cli_apply_changes(vty, NULL);
} }
ALIAS (interface_ipv6_mld_join,
DEFPY (interface_no_ipv6_mld_join, interface_ipv6_mld_join_group_cmd,
interface_no_ipv6_mld_join_cmd, "[no] ipv6 mld join-group X:X::X:X$grp [X:X::X:X]$src",
"no ipv6 mld join X:X::X:X$group [X:X::X:X$source]",
NO_STR NO_STR
IPV6_STR IPV6_STR
IFACE_MLD_STR IFACE_MLD_STR
"MLD join multicast group\n" "MLD join multicast group\n"
"Multicast group address\n" "Multicast group address\n"
"Source address\n");
DEFPY_YANG (interface_ipv6_mld_static_group,
interface_ipv6_mld_static_group_cmd,
"[no] ipv6 mld static-group X:X::X:X$grp [X:X::X:X]$src",
NO_STR
IPV6_STR
IFACE_MLD_STR
"Static multicast group\n"
"Multicast group address\n"
"Source address\n")
{
nb_cli_enqueue_change(vty, ".", (!no ? NB_OP_CREATE : NB_OP_DESTROY),
NULL);
return nb_cli_apply_changes(vty, FRR_GMP_STATIC_GROUP_XPATH,
"frr-routing:ipv6", grp_str,
(src_str ? src_str : "::"));
}
DEFPY (interface_no_ipv6_mld_static_group,
interface_no_ipv6_mld_static_group_cmd,
"no ipv6 mld static-group X:X::X:X$group [X:X::X:X$source]",
NO_STR
IPV6_STR
IFACE_MLD_STR
"Static multicast group\n"
"Multicast group address\n"
"Source address\n") "Source address\n")
{ {
char xpath[XPATH_MAXLEN]; char xpath[XPATH_MAXLEN];
@ -1415,8 +1426,8 @@ DEFPY (interface_no_ipv6_mld_join,
} else } else
source_str = "::"; source_str = "::";
snprintf(xpath, sizeof(xpath), FRR_GMP_JOIN_XPATH, "frr-routing:ipv6", snprintf(xpath, sizeof(xpath), FRR_GMP_STATIC_GROUP_XPATH,
group_str, source_str); "frr-routing:ipv6", group_str, source_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
@ -2669,7 +2680,8 @@ void pim_cmd_init(void)
install_element(INTERFACE_NODE, &interface_ipv6_mld_cmd); install_element(INTERFACE_NODE, &interface_ipv6_mld_cmd);
install_element(INTERFACE_NODE, &interface_no_ipv6_mld_cmd); install_element(INTERFACE_NODE, &interface_no_ipv6_mld_cmd);
install_element(INTERFACE_NODE, &interface_ipv6_mld_join_cmd); install_element(INTERFACE_NODE, &interface_ipv6_mld_join_cmd);
install_element(INTERFACE_NODE, &interface_no_ipv6_mld_join_cmd); install_element(INTERFACE_NODE, &interface_ipv6_mld_join_group_cmd);
install_element(INTERFACE_NODE, &interface_ipv6_mld_static_group_cmd);
install_element(INTERFACE_NODE, &interface_ipv6_mld_version_cmd); install_element(INTERFACE_NODE, &interface_ipv6_mld_version_cmd);
install_element(INTERFACE_NODE, &interface_no_ipv6_mld_version_cmd); install_element(INTERFACE_NODE, &interface_no_ipv6_mld_version_cmd);
install_element(INTERFACE_NODE, &interface_ipv6_mld_query_interval_cmd); install_element(INTERFACE_NODE, &interface_ipv6_mld_query_interval_cmd);

View file

@ -682,6 +682,91 @@ static void igmp_show_interface_join(struct pim_instance *pim, struct vty *vty,
vty_json(vty, json); vty_json(vty, json);
} }
static void igmp_show_interface_static_group(struct pim_instance *pim,
struct vty *vty, bool uj)
{
struct interface *ifp;
json_object *json = NULL;
json_object *json_iface = NULL;
json_object *json_grp = NULL;
json_object *json_grp_arr = NULL;
if (uj) {
json = json_object_new_object();
json_object_string_add(json, "vrf",
vrf_id_to_name(pim->vrf->vrf_id));
} else {
vty_out(vty,
"Interface Address Source Group\n");
}
FOR_ALL_INTERFACES (pim->vrf, ifp) {
struct pim_interface *pim_ifp;
struct listnode *node;
struct static_group *stgrp;
struct in_addr pri_addr;
char pri_addr_str[INET_ADDRSTRLEN];
pim_ifp = ifp->info;
if (!pim_ifp)
continue;
if (!pim_ifp->static_group_list)
continue;
pri_addr = pim_find_primary_addr(ifp);
pim_inet4_dump("<pri?>", pri_addr, pri_addr_str,
sizeof(pri_addr_str));
for (ALL_LIST_ELEMENTS_RO(pim_ifp->static_group_list, node,
stgrp)) {
char group_str[INET_ADDRSTRLEN];
char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<grp?>", stgrp->group_addr, group_str,
sizeof(group_str));
pim_inet4_dump("<src?>", stgrp->source_addr, source_str,
sizeof(source_str));
if (uj) {
json_object_object_get_ex(json, ifp->name,
&json_iface);
if (!json_iface) {
json_iface = json_object_new_object();
json_object_string_add(json_iface,
"name",
ifp->name);
json_object_object_add(json, ifp->name,
json_iface);
json_grp_arr = json_object_new_array();
json_object_object_add(json_iface,
"groups",
json_grp_arr);
}
json_grp = json_object_new_object();
json_object_string_add(json_grp, "source",
source_str);
json_object_string_add(json_grp, "group",
group_str);
json_object_string_add(json_grp, "primaryAddr",
pri_addr_str);
json_object_array_add(json_grp_arr, json_grp);
} else {
vty_out(vty, "%-16s %-15s %-15s %-15s\n",
ifp->name, pri_addr_str, source_str,
group_str);
}
} /* for (pim_ifp->static_group_list) */
} /* for (iflist) */
if (uj)
vty_json(vty, json);
}
static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty, static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty,
const char *ifname, bool uj) const char *ifname, bool uj)
{ {
@ -1724,6 +1809,15 @@ DEFUN (show_ip_igmp_join,
return CMD_SUCCESS; return CMD_SUCCESS;
} }
ALIAS (show_ip_igmp_join,
show_ip_igmp_join_group_cmd,
"show ip igmp [vrf NAME] join-group [json]",
SHOW_STR
IP_STR
IGMP_STR
VRF_CMD_HELP_STR
"IGMP static join information\n"
JSON_STR);
DEFUN (show_ip_igmp_join_vrf_all, DEFUN (show_ip_igmp_join_vrf_all,
show_ip_igmp_join_vrf_all_cmd, show_ip_igmp_join_vrf_all_cmd,
@ -1756,6 +1850,69 @@ DEFUN (show_ip_igmp_join_vrf_all,
return CMD_SUCCESS; return CMD_SUCCESS;
} }
ALIAS (show_ip_igmp_join_vrf_all,
show_ip_igmp_join_group_vrf_all_cmd,
"show ip igmp vrf all join-group [json]",
SHOW_STR
IP_STR
IGMP_STR
VRF_CMD_HELP_STR
"IGMP static join information\n"
JSON_STR);
DEFUN (show_ip_igmp_static_group,
show_ip_igmp_static_group_cmd,
"show ip igmp [vrf NAME] static-group [json]",
SHOW_STR
IP_STR
IGMP_STR
VRF_CMD_HELP_STR
"Static group information\n"
JSON_STR)
{
int idx = 2;
bool uj = use_json(argc, argv);
struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);
if (!vrf)
return CMD_WARNING;
igmp_show_interface_static_group(vrf->info, vty, uj);
return CMD_SUCCESS;
}
DEFUN (show_ip_igmp_static_group_vrf_all,
show_ip_igmp_static_group_vrf_all_cmd,
"show ip igmp vrf all static-group [json]",
SHOW_STR
IP_STR
IGMP_STR
VRF_CMD_HELP_STR
"Static group information\n"
JSON_STR)
{
bool uj = use_json(argc, argv);
struct vrf *vrf;
bool first = true;
if (uj)
vty_out(vty, "{ ");
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
if (uj) {
if (!first)
vty_out(vty, ", ");
vty_out(vty, " \"%s\": ", vrf->name);
first = false;
} else
vty_out(vty, "VRF: %s\n", vrf->name);
igmp_show_interface_static_group(vrf->info, vty, uj);
}
if (uj)
vty_out(vty, "}\n");
return CMD_SUCCESS;
}
DEFPY(show_ip_igmp_groups, DEFPY(show_ip_igmp_groups,
show_ip_igmp_groups_cmd, show_ip_igmp_groups_cmd,
@ -4890,71 +5047,47 @@ DEFUN (interface_no_ip_igmp,
"frr-routing:ipv4"); "frr-routing:ipv4");
} }
DEFUN (interface_ip_igmp_join, DEFPY_YANG_HIDDEN (interface_ip_igmp_join,
interface_ip_igmp_join_cmd, interface_ip_igmp_join_cmd,
"ip igmp join A.B.C.D [A.B.C.D]", "[no] ip igmp join A.B.C.D$grp [A.B.C.D]$src",
IP_STR NO_STR
IFACE_IGMP_STR IP_STR
"IGMP join multicast group\n" IFACE_IGMP_STR
"Multicast group address\n" "IGMP join multicast group\n"
"Source address\n") "Multicast group address\n"
"Source address\n")
{ {
int idx_group = 3; nb_cli_enqueue_change(vty, ".", (!no ? NB_OP_CREATE : NB_OP_DESTROY),
int idx_source = 4; NULL);
const char *source_str; return nb_cli_apply_changes(vty, FRR_GMP_JOIN_GROUP_XPATH,
char xpath[XPATH_MAXLEN]; "frr-routing:ipv4", grp_str,
(src_str ? src_str : "0.0.0.0"));
if (argc == 5) {
source_str = argv[idx_source]->arg;
if (strcmp(source_str, "0.0.0.0") == 0) {
vty_out(vty, "Bad source address %s\n",
argv[idx_source]->arg);
return CMD_WARNING_CONFIG_FAILED;
}
} else
source_str = "0.0.0.0";
snprintf(xpath, sizeof(xpath), FRR_GMP_JOIN_XPATH,
"frr-routing:ipv4", argv[idx_group]->arg, source_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
return nb_cli_apply_changes(vty, NULL);
} }
ALIAS(interface_ip_igmp_join,
interface_ip_igmp_join_group_cmd,
"[no] ip igmp join-group A.B.C.D$grp [A.B.C.D]$src",
NO_STR
IP_STR
IFACE_IGMP_STR
"IGMP join multicast group\n"
"Multicast group address\n"
"Source address\n");
DEFUN (interface_no_ip_igmp_join, DEFPY_YANG (interface_ip_igmp_static_group,
interface_no_ip_igmp_join_cmd, interface_ip_igmp_static_group_cmd,
"no ip igmp join A.B.C.D [A.B.C.D]", "[no] ip igmp static-group A.B.C.D$grp [A.B.C.D]$src",
NO_STR NO_STR
IP_STR IP_STR
IFACE_IGMP_STR IFACE_IGMP_STR
"IGMP join multicast group\n" "Static multicast group\n"
"Multicast group address\n" "Multicast group address\n"
"Source address\n") "Source address\n")
{ {
int idx_group = 4; nb_cli_enqueue_change(vty, ".", (!no ? NB_OP_CREATE : NB_OP_DESTROY),
int idx_source = 5; NULL);
const char *source_str; return nb_cli_apply_changes(vty, FRR_GMP_STATIC_GROUP_XPATH,
char xpath[XPATH_MAXLEN]; "frr-routing:ipv4", grp_str,
(src_str ? src_str : "0.0.0.0"));
if (argc == 6) {
source_str = argv[idx_source]->arg;
if (strcmp(source_str, "0.0.0.0") == 0) {
vty_out(vty, "Bad source address %s\n",
argv[idx_source]->arg);
return CMD_WARNING_CONFIG_FAILED;
}
} else
source_str = "0.0.0.0";
snprintf(xpath, sizeof(xpath), FRR_GMP_JOIN_XPATH,
"frr-routing:ipv4", argv[idx_group]->arg, source_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
return nb_cli_apply_changes(vty, NULL);
} }
DEFUN (interface_ip_igmp_query_interval, DEFUN (interface_ip_igmp_query_interval,
@ -8268,7 +8401,8 @@ void pim_cmd_init(void)
install_element(INTERFACE_NODE, &interface_ip_igmp_cmd); install_element(INTERFACE_NODE, &interface_ip_igmp_cmd);
install_element(INTERFACE_NODE, &interface_no_ip_igmp_cmd); install_element(INTERFACE_NODE, &interface_no_ip_igmp_cmd);
install_element(INTERFACE_NODE, &interface_ip_igmp_join_cmd); install_element(INTERFACE_NODE, &interface_ip_igmp_join_cmd);
install_element(INTERFACE_NODE, &interface_no_ip_igmp_join_cmd); install_element(INTERFACE_NODE, &interface_ip_igmp_join_group_cmd);
install_element(INTERFACE_NODE, &interface_ip_igmp_static_group_cmd);
install_element(INTERFACE_NODE, &interface_ip_igmp_version_cmd); install_element(INTERFACE_NODE, &interface_ip_igmp_version_cmd);
install_element(INTERFACE_NODE, &interface_no_ip_igmp_version_cmd); install_element(INTERFACE_NODE, &interface_no_ip_igmp_version_cmd);
install_element(INTERFACE_NODE, &interface_ip_igmp_query_interval_cmd); install_element(INTERFACE_NODE, &interface_ip_igmp_query_interval_cmd);
@ -8328,7 +8462,11 @@ void pim_cmd_init(void)
install_element(VIEW_NODE, &show_ip_igmp_interface_cmd); install_element(VIEW_NODE, &show_ip_igmp_interface_cmd);
install_element(VIEW_NODE, &show_ip_igmp_interface_vrf_all_cmd); install_element(VIEW_NODE, &show_ip_igmp_interface_vrf_all_cmd);
install_element(VIEW_NODE, &show_ip_igmp_join_cmd); install_element(VIEW_NODE, &show_ip_igmp_join_cmd);
install_element(VIEW_NODE, &show_ip_igmp_join_group_cmd);
install_element(VIEW_NODE, &show_ip_igmp_join_vrf_all_cmd); install_element(VIEW_NODE, &show_ip_igmp_join_vrf_all_cmd);
install_element(VIEW_NODE, &show_ip_igmp_join_group_vrf_all_cmd);
install_element(VIEW_NODE, &show_ip_igmp_static_group_cmd);
install_element(VIEW_NODE, &show_ip_igmp_static_group_vrf_all_cmd);
install_element(VIEW_NODE, &show_ip_igmp_groups_cmd); install_element(VIEW_NODE, &show_ip_igmp_groups_cmd);
install_element(VIEW_NODE, &show_ip_igmp_groups_vrf_all_cmd); install_element(VIEW_NODE, &show_ip_igmp_groups_vrf_all_cmd);
install_element(VIEW_NODE, &show_ip_igmp_groups_retransmissions_cmd); install_element(VIEW_NODE, &show_ip_igmp_groups_retransmissions_cmd);

View file

@ -37,10 +37,12 @@
#include "pim_jp_agg.h" #include "pim_jp_agg.h"
#include "pim_igmp_join.h" #include "pim_igmp_join.h"
#include "pim_vxlan.h" #include "pim_vxlan.h"
#include "pim_tib.h"
#include "pim6_mld.h" #include "pim6_mld.h"
static void pim_if_gm_join_del_all(struct interface *ifp); static void pim_if_gm_join_del_all(struct interface *ifp);
static void pim_if_static_group_del_all(struct interface *ifp);
static int gm_join_sock(const char *ifname, ifindex_t ifindex, static int gm_join_sock(const char *ifname, ifindex_t ifindex,
pim_addr group_addr, pim_addr source_addr, pim_addr group_addr, pim_addr source_addr,
@ -144,6 +146,7 @@ struct pim_interface *pim_if_new(struct interface *ifp, bool gm, bool pim,
pim_ifp->gm_enable = gm; pim_ifp->gm_enable = gm;
pim_ifp->gm_join_list = NULL; pim_ifp->gm_join_list = NULL;
pim_ifp->static_group_list = NULL;
pim_ifp->pim_neighbor_list = NULL; pim_ifp->pim_neighbor_list = NULL;
pim_ifp->upstream_switch_list = NULL; pim_ifp->upstream_switch_list = NULL;
pim_ifp->pim_generation_id = 0; pim_ifp->pim_generation_id = 0;
@ -188,9 +191,11 @@ void pim_if_delete(struct interface *ifp)
assert(pim_ifp); assert(pim_ifp);
pim_ifp->pim->mcast_if_count--; pim_ifp->pim->mcast_if_count--;
if (pim_ifp->gm_join_list) { if (pim_ifp->gm_join_list)
pim_if_gm_join_del_all(ifp); pim_if_gm_join_del_all(ifp);
}
if (pim_ifp->static_group_list)
pim_if_static_group_del_all(ifp);
pim_ifchannel_delete_all(ifp); pim_ifchannel_delete_all(ifp);
#if PIM_IPV == 4 #if PIM_IPV == 4
@ -1218,6 +1223,11 @@ static void gm_join_free(struct gm_join *ij)
XFREE(MTYPE_PIM_IGMP_JOIN, ij); XFREE(MTYPE_PIM_IGMP_JOIN, ij);
} }
static void static_group_free(struct static_group *stgrp)
{
XFREE(MTYPE_PIM_STATIC_GROUP, stgrp);
}
static struct gm_join *gm_join_find(struct list *join_list, pim_addr group_addr, static struct gm_join *gm_join_find(struct list *join_list, pim_addr group_addr,
pim_addr source_addr) pim_addr source_addr)
{ {
@ -1232,7 +1242,25 @@ static struct gm_join *gm_join_find(struct list *join_list, pim_addr group_addr,
return ij; return ij;
} }
return 0; return NULL;
}
static struct static_group *static_group_find(struct list *static_group_list,
pim_addr group_addr,
pim_addr source_addr)
{
struct listnode *node;
struct static_group *stgrp;
assert(static_group_list);
for (ALL_LIST_ELEMENTS_RO(static_group_list, node, stgrp)) {
if ((!pim_addr_cmp(group_addr, stgrp->group_addr)) &&
(!pim_addr_cmp(source_addr, stgrp->source_addr)))
return stgrp;
}
return NULL;
} }
static int gm_join_sock(const char *ifname, ifindex_t ifindex, static int gm_join_sock(const char *ifname, ifindex_t ifindex,
@ -1296,6 +1324,34 @@ static struct gm_join *gm_join_new(struct interface *ifp, pim_addr group_addr,
return ij; return ij;
} }
static struct static_group *static_group_new(struct interface *ifp,
pim_addr group_addr,
pim_addr source_addr)
{
struct pim_interface *pim_ifp;
struct static_group *stgrp;
pim_sgaddr sg;
pim_ifp = ifp->info;
assert(pim_ifp);
stgrp = XCALLOC(MTYPE_PIM_STATIC_GROUP, sizeof(*stgrp));
stgrp->group_addr = group_addr;
stgrp->source_addr = source_addr;
stgrp->oilp = NULL;
memset(&sg, 0, sizeof(sg));
sg.src = source_addr;
sg.grp = group_addr;
tib_sg_gm_join(pim_ifp->pim, sg, ifp, &(stgrp->oilp));
listnode_add(pim_ifp->static_group_list, stgrp);
return stgrp;
}
ferr_r pim_if_gm_join_add(struct interface *ifp, pim_addr group_addr, ferr_r pim_if_gm_join_add(struct interface *ifp, pim_addr group_addr,
pim_addr source_addr) pim_addr source_addr)
{ {
@ -1379,7 +1435,6 @@ int pim_if_gm_join_del(struct interface *ifp, pim_addr group_addr,
return 0; return 0;
} }
__attribute__((unused))
static void pim_if_gm_join_del_all(struct interface *ifp) static void pim_if_gm_join_del_all(struct interface *ifp)
{ {
struct pim_interface *pim_ifp; struct pim_interface *pim_ifp;
@ -1401,6 +1456,109 @@ static void pim_if_gm_join_del_all(struct interface *ifp)
pim_if_gm_join_del(ifp, ij->group_addr, ij->source_addr); pim_if_gm_join_del(ifp, ij->group_addr, ij->source_addr);
} }
ferr_r pim_if_static_group_add(struct interface *ifp, pim_addr group_addr,
pim_addr source_addr)
{
struct pim_interface *pim_ifp;
struct static_group *stgrp;
pim_ifp = ifp->info;
if (!pim_ifp) {
return ferr_cfg_invalid("multicast not enabled on interface %s",
ifp->name);
}
if (!pim_ifp->static_group_list) {
pim_ifp->static_group_list = list_new();
pim_ifp->static_group_list->del =
(void (*)(void *))static_group_free;
}
stgrp = static_group_find(pim_ifp->static_group_list, group_addr,
source_addr);
/* This interface has already been configured with this static group
*/
if (stgrp)
return ferr_ok();
(void)static_group_new(ifp, group_addr, source_addr);
if (PIM_DEBUG_GM_EVENTS) {
zlog_debug("%s: Added static group (S,G)=(%pPA,%pPA) on interface %s",
__func__, &source_addr, &group_addr, ifp->name);
}
return ferr_ok();
}
int pim_if_static_group_del(struct interface *ifp, pim_addr group_addr,
pim_addr source_addr)
{
struct pim_interface *pim_ifp;
struct static_group *stgrp;
pim_sgaddr sg;
pim_ifp = ifp->info;
if (!pim_ifp) {
zlog_warn("%s: multicast not enabled on interface %s", __func__,
ifp->name);
return -1;
}
if (!pim_ifp->static_group_list) {
zlog_warn("%s: no static groups on interface %s", __func__,
ifp->name);
return -2;
}
stgrp = static_group_find(pim_ifp->static_group_list, group_addr,
source_addr);
if (!stgrp) {
zlog_warn("%s: could not find static group %pPAs source %pPAs on interface %s",
__func__, &group_addr, &source_addr, ifp->name);
return -3;
}
memset(&sg, 0, sizeof(sg));
sg.src = source_addr;
sg.grp = group_addr;
tib_sg_gm_prune(pim_ifp->pim, sg, ifp, &(stgrp->oilp));
listnode_delete(pim_ifp->static_group_list, stgrp);
static_group_free(stgrp);
if (listcount(pim_ifp->static_group_list) < 1) {
list_delete(&pim_ifp->static_group_list);
pim_ifp->static_group_list = 0;
}
return 0;
}
static void pim_if_static_group_del_all(struct interface *ifp)
{
struct pim_interface *pim_ifp;
struct listnode *node;
struct listnode *nextnode;
struct static_group *stgrp;
pim_ifp = ifp->info;
if (!pim_ifp) {
zlog_warn("%s: multicast not enabled on interface %s", __func__,
ifp->name);
return;
}
if (!pim_ifp->static_group_list)
return;
for (ALL_LIST_ELEMENTS(pim_ifp->static_group_list, node, nextnode,
stgrp))
pim_if_static_group_del(ifp, stgrp->group_addr,
stgrp->source_addr);
}
/* /*
RFC 4601 RFC 4601

View file

@ -98,6 +98,7 @@ struct pim_interface {
*/ */
struct list *gm_socket_list; /* list of struct IGMP or MLD sock */ struct list *gm_socket_list; /* list of struct IGMP or MLD sock */
struct list *gm_join_list; /* list of struct IGMP or MLD join */ struct list *gm_join_list; /* list of struct IGMP or MLD join */
struct list *static_group_list; /* list of struct static group */
struct list *gm_group_list; /* list of struct IGMP or MLD group */ struct list *gm_group_list; /* list of struct IGMP or MLD group */
struct hash *gm_group_hash; struct hash *gm_group_hash;
@ -222,6 +223,11 @@ ferr_r pim_if_gm_join_add(struct interface *ifp, pim_addr group_addr,
int pim_if_gm_join_del(struct interface *ifp, pim_addr group_addr, int pim_if_gm_join_del(struct interface *ifp, pim_addr group_addr,
pim_addr source_addr); pim_addr source_addr);
ferr_r pim_if_static_group_add(struct interface *ifp, pim_addr group_addr,
pim_addr source_addr);
int pim_if_static_group_del(struct interface *ifp, pim_addr group_addr,
pim_addr source_addr);
void pim_if_update_could_assert(struct interface *ifp); void pim_if_update_could_assert(struct interface *ifp);
void pim_if_assert_on_neighbor_down(struct interface *ifp, pim_addr neigh_addr); void pim_if_assert_on_neighbor_down(struct interface *ifp, pim_addr neigh_addr);

View file

@ -58,6 +58,12 @@ struct gm_join {
time_t sock_creation; time_t sock_creation;
}; };
struct static_group {
pim_addr group_addr;
pim_addr source_addr;
struct channel_oil *oilp;
};
struct gm_sock { struct gm_sock {
int fd; int fd;
struct interface *interface; struct interface *interface;

View file

@ -14,6 +14,7 @@ DEFINE_MGROUP(PIMD, "pimd");
DEFINE_MTYPE(PIMD, PIM_CHANNEL_OIL, "PIM SSM (S,G) channel OIL"); DEFINE_MTYPE(PIMD, PIM_CHANNEL_OIL, "PIM SSM (S,G) channel OIL");
DEFINE_MTYPE(PIMD, PIM_INTERFACE, "PIM interface"); DEFINE_MTYPE(PIMD, PIM_INTERFACE, "PIM interface");
DEFINE_MTYPE(PIMD, PIM_IGMP_JOIN, "PIM interface IGMP static join"); DEFINE_MTYPE(PIMD, PIM_IGMP_JOIN, "PIM interface IGMP static join");
DEFINE_MTYPE(PIMD, PIM_STATIC_GROUP, "PIM interface IGMP static group");
DEFINE_MTYPE(PIMD, PIM_IGMP_SOCKET, "PIM interface IGMP socket"); DEFINE_MTYPE(PIMD, PIM_IGMP_SOCKET, "PIM interface IGMP socket");
DEFINE_MTYPE(PIMD, PIM_IGMP_GROUP, "PIM interface IGMP group"); DEFINE_MTYPE(PIMD, PIM_IGMP_GROUP, "PIM interface IGMP group");
DEFINE_MTYPE(PIMD, PIM_IGMP_GROUP_SOURCE, "PIM interface IGMP source"); DEFINE_MTYPE(PIMD, PIM_IGMP_GROUP_SOURCE, "PIM interface IGMP source");

View file

@ -13,6 +13,7 @@ DECLARE_MGROUP(PIMD);
DECLARE_MTYPE(PIM_CHANNEL_OIL); DECLARE_MTYPE(PIM_CHANNEL_OIL);
DECLARE_MTYPE(PIM_INTERFACE); DECLARE_MTYPE(PIM_INTERFACE);
DECLARE_MTYPE(PIM_IGMP_JOIN); DECLARE_MTYPE(PIM_IGMP_JOIN);
DECLARE_MTYPE(PIM_STATIC_GROUP);
DECLARE_MTYPE(PIM_IGMP_SOCKET); DECLARE_MTYPE(PIM_IGMP_SOCKET);
DECLARE_MTYPE(PIM_IGMP_GROUP); DECLARE_MTYPE(PIM_IGMP_GROUP);
DECLARE_MTYPE(PIM_IGMP_GROUP_SOURCE); DECLARE_MTYPE(PIM_IGMP_GROUP_SOURCE);

View file

@ -413,6 +413,13 @@ const struct frr_yang_module_info frr_gmp_info = {
.modify = lib_interface_gmp_address_family_robustness_variable_modify, .modify = lib_interface_gmp_address_family_robustness_variable_modify,
} }
}, },
{
.xpath = "/frr-interface:lib/interface/frr-gmp:gmp/address-family/join-group",
.cbs = {
.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/static-group", .xpath = "/frr-interface:lib/interface/frr-gmp:gmp/address-family/static-group",
.cbs = { .cbs = {
@ -425,4 +432,3 @@ const struct frr_yang_module_info frr_gmp_info = {
}, },
} }
}; };

View file

@ -175,6 +175,10 @@ int lib_interface_gmp_address_family_last_member_query_interval_modify(
struct nb_cb_modify_args *args); struct nb_cb_modify_args *args);
int lib_interface_gmp_address_family_robustness_variable_modify( int lib_interface_gmp_address_family_robustness_variable_modify(
struct nb_cb_modify_args *args); struct nb_cb_modify_args *args);
int lib_interface_gmp_address_family_join_group_create(
struct nb_cb_create_args *args);
int lib_interface_gmp_address_family_join_group_destroy(
struct nb_cb_destroy_args *args);
int lib_interface_gmp_address_family_static_group_create( int lib_interface_gmp_address_family_static_group_create(
struct nb_cb_create_args *args); struct nb_cb_create_args *args);
int lib_interface_gmp_address_family_static_group_destroy( int lib_interface_gmp_address_family_static_group_destroy(
@ -212,8 +216,11 @@ int routing_control_plane_protocols_name_validate(
"./frr-gmp:gmp/address-family[address-family='%s']" "./frr-gmp:gmp/address-family[address-family='%s']"
#define FRR_GMP_ENABLE_XPATH \ #define FRR_GMP_ENABLE_XPATH \
"%s/frr-gmp:gmp/address-family[address-family='%s']/enable" "%s/frr-gmp:gmp/address-family[address-family='%s']/enable"
#define FRR_GMP_JOIN_XPATH \ #define FRR_GMP_JOIN_GROUP_XPATH \
"./frr-gmp:gmp/address-family[address-family='%s']/" \ "./frr-gmp:gmp/address-family[address-family='%s']/" \
"join-group[group-addr='%s'][source-addr='%s']"
#define FRR_GMP_STATIC_GROUP_XPATH \
"./frr-gmp:gmp/address-family[address-family='%s']/" \
"static-group[group-addr='%s'][source-addr='%s']" "static-group[group-addr='%s'][source-addr='%s']"
#endif /* _FRR_PIM_NB_H_ */ #endif /* _FRR_PIM_NB_H_ */

View file

@ -2821,9 +2821,9 @@ int lib_interface_gmp_address_family_robustness_variable_modify(
} }
/* /*
* XPath: /frr-interface:lib/interface/frr-gmp:gmp/address-family/static-group * XPath: /frr-interface:lib/interface/frr-gmp:gmp/address-family/join-group
*/ */
int lib_interface_gmp_address_family_static_group_create( int lib_interface_gmp_address_family_join_group_create(
struct nb_cb_create_args *args) struct nb_cb_create_args *args)
{ {
struct interface *ifp; struct interface *ifp;
@ -2881,7 +2881,7 @@ int lib_interface_gmp_address_family_static_group_create(
return NB_OK; return NB_OK;
} }
int lib_interface_gmp_address_family_static_group_destroy( int lib_interface_gmp_address_family_join_group_destroy(
struct nb_cb_destroy_args *args) struct nb_cb_destroy_args *args)
{ {
struct interface *ifp; struct interface *ifp;
@ -2916,3 +2916,94 @@ int lib_interface_gmp_address_family_static_group_destroy(
return NB_OK; return NB_OK;
} }
/*
* XPath: /frr-interface:lib/interface/frr-gmp:gmp/address-family/static-group
*/
int lib_interface_gmp_address_family_static_group_create(
struct nb_cb_create_args *args)
{
struct interface *ifp;
pim_addr source_addr;
pim_addr group_addr;
int result;
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;
}
yang_dnode_get_pimaddr(&group_addr, args->dnode, "./group-addr");
#if PIM_IPV == 4
if (pim_is_group_224_0_0_0_24(group_addr)) {
snprintf(args->errmsg, args->errmsg_len,
"Groups within 224.0.0.0/24 are reserved and cannot be joined");
return NB_ERR_VALIDATION;
}
#else
if (ipv6_mcast_reserved(&group_addr)) {
snprintf(args->errmsg, args->errmsg_len,
"Groups within ffx2::/16 are reserved and cannot be joined");
return NB_ERR_VALIDATION;
}
#endif
break;
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
ifp = nb_running_get_entry(args->dnode, NULL, true);
yang_dnode_get_pimaddr(&source_addr, args->dnode,
"./source-addr");
yang_dnode_get_pimaddr(&group_addr, args->dnode, "./group-addr");
result = pim_if_static_group_add(ifp, group_addr, source_addr);
if (result) {
snprintf(args->errmsg, args->errmsg_len,
"Failure adding static group");
return NB_ERR_INCONSISTENCY;
}
}
return NB_OK;
}
int lib_interface_gmp_address_family_static_group_destroy(
struct nb_cb_destroy_args *args)
{
struct interface *ifp;
pim_addr source_addr;
pim_addr group_addr;
int result;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
ifp = nb_running_get_entry(args->dnode, NULL, true);
yang_dnode_get_pimaddr(&source_addr, args->dnode,
"./source-addr");
yang_dnode_get_pimaddr(&group_addr, args->dnode, "./group-addr");
result = pim_if_static_group_del(ifp, group_addr, source_addr);
if (result) {
snprintf(args->errmsg, args->errmsg_len,
"%% Failure removing static group %pPAs %pPAs on interface %s: %d",
&source_addr, &group_addr, ifp->name, result);
return NB_ERR_INCONSISTENCY;
}
break;
}
return NB_OK;
}

View file

@ -306,21 +306,38 @@ static int gm_config_write(struct vty *vty, int writes,
++writes; ++writes;
} }
/* IF ip igmp join */ /* IF ip igmp join-group */
if (pim_ifp->gm_join_list) { if (pim_ifp->gm_join_list) {
struct listnode *node; struct listnode *node;
struct gm_join *ij; struct gm_join *ij;
for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_join_list, node, ij)) { for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_join_list, node, ij)) {
if (pim_addr_is_any(ij->source_addr)) if (pim_addr_is_any(ij->source_addr))
vty_out(vty, " ip igmp join %pPAs\n", vty_out(vty, " ip igmp join-group %pPAs\n",
&ij->group_addr); &ij->group_addr);
else else
vty_out(vty, " ip igmp join %pPAs %pPAs\n", vty_out(vty, " ip igmp join-group %pPAs %pPAs\n",
&ij->group_addr, &ij->source_addr); &ij->group_addr, &ij->source_addr);
++writes; ++writes;
} }
} }
/* IF ip igmp static-group */
if (pim_ifp->static_group_list) {
struct listnode *node;
struct static_group *stgrp;
for (ALL_LIST_ELEMENTS_RO(pim_ifp->static_group_list, node,
stgrp)) {
if (pim_addr_is_any(stgrp->source_addr))
vty_out(vty, " ip igmp static-group %pPAs\n",
&stgrp->group_addr);
else
vty_out(vty,
" ip igmp static-group %pPAs %pPAs\n",
&stgrp->group_addr, &stgrp->source_addr);
++writes;
}
}
return writes; return writes;
} }
#else #else
@ -358,21 +375,41 @@ static int gm_config_write(struct vty *vty, int writes,
vty_out(vty, " ipv6 mld last-member-query-interval %d\n", vty_out(vty, " ipv6 mld last-member-query-interval %d\n",
pim_ifp->gm_specific_query_max_response_time_dsec); pim_ifp->gm_specific_query_max_response_time_dsec);
/* IF ipv6 mld join */ /* IF ipv6 mld join-group */
if (pim_ifp->gm_join_list) { if (pim_ifp->gm_join_list) {
struct listnode *node; struct listnode *node;
struct gm_join *ij; struct gm_join *ij;
for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_join_list, node, ij)) { for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_join_list, node, ij)) {
if (pim_addr_is_any(ij->source_addr)) if (pim_addr_is_any(ij->source_addr))
vty_out(vty, " ipv6 mld join %pPAs\n", vty_out(vty, " ipv6 mld join-group %pPAs\n",
&ij->group_addr); &ij->group_addr);
else else
vty_out(vty, " ipv6 mld join %pPAs %pPAs\n", vty_out(vty,
" ipv6 mld join-group %pPAs %pPAs\n",
&ij->group_addr, &ij->source_addr); &ij->group_addr, &ij->source_addr);
++writes; ++writes;
} }
} }
/* IF ipv6 mld static-group */
if (pim_ifp->static_group_list) {
struct listnode *node;
struct static_group *stgrp;
for (ALL_LIST_ELEMENTS_RO(pim_ifp->static_group_list, node,
stgrp)) {
if (pim_addr_is_any(stgrp->source_addr))
vty_out(vty, " ipv6 mld static-group %pPAs\n",
&stgrp->group_addr);
else
vty_out(vty,
" ipv6 mld static-group %pPAs %pPAs\n",
&stgrp->group_addr, &stgrp->source_addr);
++writes;
}
}
return writes; return writes;
} }
#endif #endif

View file

@ -147,11 +147,28 @@ module frr-gmp {
expected packet loss on a network."; expected packet loss on a network.";
} }
list join-group {
key "group-addr source-addr";
description
"A static GMP join, (*,G) or (S,G).
The version of IGMP must be 3 to support (S,G).";
leaf group-addr {
type rt-types:ip-multicast-group-address;
description
"Multicast group address.";
}
leaf source-addr {
type inet:ip-address;
description
"Multicast source address.";
}
}
list static-group { list static-group {
key "group-addr source-addr"; key "group-addr source-addr";
description description
"A static multicast route, (*,G) or (S,G). "A static multicast group without GMP, (*,G) or (S,G).";
The version of IGMP must be 3 to support (S,G).";
leaf group-addr { leaf group-addr {
type rt-types:ip-multicast-group-address; type rt-types:ip-multicast-group-address;