mirror of
https://github.com/FRRouting/frr.git
synced 2025-04-30 13:37:17 +02:00
Merge pull request #16861 from btrent98/igmp-proxy2
Add igmp proxy support
This commit is contained in:
commit
269d63a5c1
|
@ -302,6 +302,12 @@ is in a vrf, enter the interface command with the vrf keyword at the end.
|
|||
Add a static multicast group or source-group on an interface. This will behave
|
||||
as if there is a receiver on this interface without any IGMP reports.
|
||||
|
||||
.. clicmd:: ip igmp proxy
|
||||
|
||||
Tell pim to send proxy IGMP reports for joins occuring on all other
|
||||
interfaces on this interface. Join-groups on other interfaces will
|
||||
also be proxied. The default version is v3.
|
||||
|
||||
.. clicmd:: ip igmp query-interval (1-65535)
|
||||
|
||||
Set the IGMP query interval that PIM will use.
|
||||
|
@ -475,6 +481,10 @@ cause great confusion.
|
|||
|
||||
Display IGMP group retransmission information.
|
||||
|
||||
.. clicmd:: show ip igmp [vrf NAME] proxy [json]
|
||||
|
||||
Display IGMP proxy join information.
|
||||
|
||||
.. clicmd:: show ip igmp [vrf NAME] sources [json]
|
||||
|
||||
Display IGMP sources information.
|
||||
|
|
|
@ -567,7 +567,7 @@ static void igmp_show_interfaces_single(struct pim_instance *pim,
|
|||
}
|
||||
|
||||
static void igmp_show_interface_join(struct pim_instance *pim, struct vty *vty,
|
||||
bool uj)
|
||||
bool uj, enum gm_join_type join_type)
|
||||
{
|
||||
struct interface *ifp;
|
||||
time_t now;
|
||||
|
@ -612,6 +612,10 @@ static void igmp_show_interface_join(struct pim_instance *pim, struct vty *vty,
|
|||
char source_str[INET_ADDRSTRLEN];
|
||||
char uptime[10];
|
||||
|
||||
if (ij->join_type != join_type &&
|
||||
ij->join_type != GM_JOIN_BOTH)
|
||||
continue;
|
||||
|
||||
pim_time_uptime(uptime, sizeof(uptime),
|
||||
now - ij->sock_creation);
|
||||
pim_inet4_dump("<grp?>", ij->group_addr, group_str,
|
||||
|
@ -1784,7 +1788,7 @@ DEFUN (show_ip_igmp_join,
|
|||
if (!vrf)
|
||||
return CMD_WARNING;
|
||||
|
||||
igmp_show_interface_join(vrf->info, vty, uj);
|
||||
igmp_show_interface_join(vrf->info, vty, uj, GM_JOIN_STATIC);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
@ -1822,7 +1826,61 @@ DEFUN (show_ip_igmp_join_vrf_all,
|
|||
first = false;
|
||||
} else
|
||||
vty_out(vty, "VRF: %s\n", vrf->name);
|
||||
igmp_show_interface_join(vrf->info, vty, uj);
|
||||
igmp_show_interface_join(vrf->info, vty, uj, GM_JOIN_STATIC);
|
||||
}
|
||||
if (uj)
|
||||
vty_out(vty, "}\n");
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN (show_ip_igmp_proxy,
|
||||
show_ip_igmp_proxy_cmd,
|
||||
"show ip igmp [vrf NAME] proxy [json]",
|
||||
SHOW_STR
|
||||
IP_STR
|
||||
IGMP_STR
|
||||
VRF_CMD_HELP_STR
|
||||
"IGMP proxy join 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_join(vrf->info, vty, uj, GM_JOIN_PROXY);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN (show_ip_igmp_proxy_vrf_all,
|
||||
show_ip_igmp_proxy_vrf_all_cmd,
|
||||
"show ip igmp vrf all proxy [json]",
|
||||
SHOW_STR
|
||||
IP_STR
|
||||
IGMP_STR
|
||||
VRF_CMD_HELP_STR
|
||||
"IGMP proxy join 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_join(vrf->info, vty, uj, GM_JOIN_PROXY);
|
||||
}
|
||||
if (uj)
|
||||
vty_out(vty, "}\n");
|
||||
|
@ -5756,6 +5814,18 @@ DEFUN (interface_no_ip_pim_hello,
|
|||
return pim_process_no_ip_pim_hello_cmd(vty);
|
||||
}
|
||||
|
||||
DEFPY (interface_ip_igmp_proxy,
|
||||
interface_ip_igmp_proxy_cmd,
|
||||
"[no] ip igmp proxy",
|
||||
NO_STR
|
||||
IP_STR
|
||||
IGMP_STR
|
||||
"Proxy IGMP join/prune operations\n")
|
||||
{
|
||||
return pim_process_ip_gmp_proxy_cmd(vty, !no);
|
||||
}
|
||||
|
||||
|
||||
DEFUN (debug_igmp,
|
||||
debug_igmp_cmd,
|
||||
"debug igmp",
|
||||
|
@ -8718,6 +8788,7 @@ void pim_cmd_init(void)
|
|||
&interface_ip_igmp_last_member_query_interval_cmd);
|
||||
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_pim_activeactive_cmd);
|
||||
install_element(INTERFACE_NODE, &interface_ip_pim_ssm_cmd);
|
||||
install_element(INTERFACE_NODE, &interface_no_ip_pim_ssm_cmd);
|
||||
|
@ -8761,6 +8832,8 @@ void pim_cmd_init(void)
|
|||
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_proxy_cmd);
|
||||
install_element(VIEW_NODE, &show_ip_igmp_proxy_vrf_all_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_retransmissions_cmd);
|
||||
|
|
|
@ -420,6 +420,17 @@ int pim_process_no_ip_pim_boundary_oil_cmd(struct vty *vty)
|
|||
FRR_PIM_AF_XPATH_VAL);
|
||||
}
|
||||
|
||||
int pim_process_ip_gmp_proxy_cmd(struct vty *vty, bool enable)
|
||||
{
|
||||
if (enable)
|
||||
nb_cli_enqueue_change(vty, "./proxy", NB_OP_MODIFY, "true");
|
||||
else
|
||||
nb_cli_enqueue_change(vty, "./proxy", NB_OP_DESTROY, NULL);
|
||||
|
||||
return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH,
|
||||
FRR_PIM_AF_XPATH_VAL);
|
||||
}
|
||||
|
||||
int pim_process_ip_mroute_cmd(struct vty *vty, const char *interface,
|
||||
const char *group_str, const char *source_str)
|
||||
{
|
||||
|
|
|
@ -47,6 +47,7 @@ int pim_process_no_ip_pim_hello_cmd(struct vty *vty);
|
|||
int pim_process_ip_pim_activeactive_cmd(struct vty *vty, const char *no);
|
||||
int pim_process_ip_pim_boundary_oil_cmd(struct vty *vty, const char *oil);
|
||||
int pim_process_no_ip_pim_boundary_oil_cmd(struct vty *vty);
|
||||
int pim_process_ip_gmp_proxy_cmd(struct vty *vty, bool enable);
|
||||
int pim_process_ip_mroute_cmd(struct vty *vty, const char *interface,
|
||||
const char *group_str, const char *source_str);
|
||||
int pim_process_no_ip_mroute_cmd(struct vty *vty, const char *interface,
|
||||
|
|
|
@ -1294,7 +1294,8 @@ static int gm_join_sock(const char *ifname, ifindex_t ifindex,
|
|||
}
|
||||
|
||||
static struct gm_join *gm_join_new(struct interface *ifp, pim_addr group_addr,
|
||||
pim_addr source_addr)
|
||||
pim_addr source_addr,
|
||||
enum gm_join_type join_type)
|
||||
{
|
||||
struct pim_interface *pim_ifp;
|
||||
struct gm_join *ij;
|
||||
|
@ -1317,6 +1318,7 @@ static struct gm_join *gm_join_new(struct interface *ifp, pim_addr group_addr,
|
|||
ij->sock_fd = join_fd;
|
||||
ij->group_addr = group_addr;
|
||||
ij->source_addr = source_addr;
|
||||
ij->join_type = join_type;
|
||||
ij->sock_creation = pim_time_monotonic_sec();
|
||||
|
||||
listnode_add(pim_ifp->gm_join_list, ij);
|
||||
|
@ -1353,7 +1355,7 @@ static struct static_group *static_group_new(struct interface *ifp,
|
|||
}
|
||||
|
||||
ferr_r pim_if_gm_join_add(struct interface *ifp, pim_addr group_addr,
|
||||
pim_addr source_addr)
|
||||
pim_addr source_addr, enum gm_join_type join_type)
|
||||
{
|
||||
struct pim_interface *pim_ifp;
|
||||
struct gm_join *ij;
|
||||
|
@ -1375,10 +1377,13 @@ ferr_r pim_if_gm_join_add(struct interface *ifp, pim_addr group_addr,
|
|||
* group
|
||||
*/
|
||||
if (ij) {
|
||||
/* turn an existing join into a "both" join */
|
||||
if (ij->join_type != join_type)
|
||||
ij->join_type = GM_JOIN_BOTH;
|
||||
return ferr_ok();
|
||||
}
|
||||
|
||||
if (!gm_join_new(ifp, group_addr, source_addr)) {
|
||||
if (!gm_join_new(ifp, group_addr, source_addr, join_type)) {
|
||||
return ferr_cfg_invalid("can't join (%pPA,%pPA) on interface %s",
|
||||
&source_addr, &group_addr, ifp->name);
|
||||
}
|
||||
|
@ -1394,7 +1399,7 @@ 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,
|
||||
pim_addr source_addr)
|
||||
pim_addr source_addr, enum gm_join_type join_type)
|
||||
{
|
||||
struct pim_interface *pim_ifp;
|
||||
struct gm_join *ij;
|
||||
|
@ -1420,6 +1425,20 @@ int pim_if_gm_join_del(struct interface *ifp, pim_addr group_addr,
|
|||
return -3;
|
||||
}
|
||||
|
||||
if (ij->join_type != join_type) {
|
||||
if (ij->join_type != GM_JOIN_BOTH) {
|
||||
zlog_warn("%s: wrong " GM
|
||||
" gm_join_type %pPAs source %pPAs on interface %s",
|
||||
__func__, &group_addr, &source_addr,
|
||||
ifp->name);
|
||||
return -4;
|
||||
}
|
||||
/* drop back to a single join type from current setting of GM_JOIN_BOTH */
|
||||
ij->join_type = (join_type == GM_JOIN_STATIC ? GM_JOIN_PROXY
|
||||
: GM_JOIN_STATIC);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (close(ij->sock_fd)) {
|
||||
zlog_warn(
|
||||
"%s: failure closing sock_fd=%d for " GM
|
||||
|
@ -1456,7 +1475,8 @@ static void pim_if_gm_join_del_all(struct interface *ifp)
|
|||
return;
|
||||
|
||||
for (ALL_LIST_ELEMENTS(pim_ifp->gm_join_list, node, nextnode, ij))
|
||||
pim_if_gm_join_del(ifp, ij->group_addr, ij->source_addr);
|
||||
pim_if_gm_join_del(ifp, ij->group_addr, ij->source_addr,
|
||||
GM_JOIN_STATIC);
|
||||
}
|
||||
|
||||
ferr_r pim_if_static_group_add(struct interface *ifp, pim_addr group_addr,
|
||||
|
@ -1562,6 +1582,55 @@ static void pim_if_static_group_del_all(struct interface *ifp)
|
|||
stgrp->source_addr);
|
||||
}
|
||||
|
||||
void pim_if_gm_proxy_init(struct pim_instance *pim, struct interface *oif)
|
||||
{
|
||||
struct interface *ifp;
|
||||
|
||||
FOR_ALL_INTERFACES (pim->vrf, ifp) {
|
||||
struct pim_interface *pim_ifp = ifp->info;
|
||||
struct listnode *source_node, *group_node;
|
||||
struct gm_group *group;
|
||||
struct gm_source *src;
|
||||
|
||||
if (!pim_ifp)
|
||||
continue;
|
||||
|
||||
if (ifp == oif) /* skip the source interface */
|
||||
continue;
|
||||
|
||||
for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, group_node,
|
||||
group)) {
|
||||
for (ALL_LIST_ELEMENTS_RO(group->group_source_list,
|
||||
source_node, src)) {
|
||||
pim_if_gm_join_add(oif, group->group_addr,
|
||||
src->source_addr,
|
||||
GM_JOIN_PROXY);
|
||||
}
|
||||
}
|
||||
} /* scan interfaces */
|
||||
}
|
||||
|
||||
void pim_if_gm_proxy_finis(struct pim_instance *pim, struct interface *ifp)
|
||||
{
|
||||
struct pim_interface *pim_ifp = ifp->info;
|
||||
struct listnode *join_node;
|
||||
struct listnode *next_join_node;
|
||||
struct gm_join *join;
|
||||
|
||||
if (!pim_ifp) {
|
||||
zlog_warn("%s: multicast not enabled on interface %s", __func__,
|
||||
ifp->name);
|
||||
return;
|
||||
}
|
||||
|
||||
for (ALL_LIST_ELEMENTS(pim_ifp->gm_join_list, join_node, next_join_node,
|
||||
join)) {
|
||||
if (join)
|
||||
pim_if_gm_join_del(ifp, join->group_addr,
|
||||
join->source_addr, GM_JOIN_PROXY);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
RFC 4601
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@ struct pim_interface {
|
|||
bool pim_passive_enable : 1;
|
||||
|
||||
bool gm_enable : 1;
|
||||
bool gm_proxy : 1; /* proxy IGMP joins/prunes */
|
||||
|
||||
ifindex_t mroute_vif_index;
|
||||
struct pim_instance *pim;
|
||||
|
@ -219,9 +220,11 @@ int pim_if_t_override_msec(struct interface *ifp);
|
|||
pim_addr pim_find_primary_addr(struct interface *ifp);
|
||||
|
||||
ferr_r pim_if_gm_join_add(struct interface *ifp, pim_addr group_addr,
|
||||
pim_addr source_addr);
|
||||
pim_addr source_addr, enum gm_join_type join_type);
|
||||
int pim_if_gm_join_del(struct interface *ifp, pim_addr group_addr,
|
||||
pim_addr source_addr);
|
||||
pim_addr source_addr, enum gm_join_type join_type);
|
||||
void pim_if_gm_proxy_init(struct pim_instance *pim, struct interface *oif);
|
||||
void pim_if_gm_proxy_finis(struct pim_instance *pim, struct interface *ifp);
|
||||
|
||||
ferr_r pim_if_static_group_add(struct interface *ifp, pim_addr group_addr,
|
||||
pim_addr source_addr);
|
||||
|
|
|
@ -213,15 +213,17 @@ void igmp_source_forward_stop(struct gm_source *source)
|
|||
IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
|
||||
}
|
||||
|
||||
group = source->source_group;
|
||||
pim_oif = group->interface->info;
|
||||
|
||||
/* Prevent IGMP interface from removing multicast route multiple
|
||||
times */
|
||||
if (!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
|
||||
tib_sg_proxy_join_prune_check(pim_oif->pim, sg,
|
||||
group->interface, false);
|
||||
return;
|
||||
}
|
||||
|
||||
group = source->source_group;
|
||||
pim_oif = group->interface->info;
|
||||
|
||||
tib_sg_gm_prune(pim_oif->pim, sg, group->interface,
|
||||
&source->source_channel_oil);
|
||||
IGMP_SOURCE_DONT_FORWARDING(source->source_flags);
|
||||
|
|
|
@ -51,10 +51,13 @@
|
|||
output |= *((ptr) + 1); \
|
||||
} while (0)
|
||||
|
||||
enum gm_join_type { GM_JOIN_STATIC = 0, GM_JOIN_PROXY = 1, GM_JOIN_BOTH = 2 };
|
||||
|
||||
struct gm_join {
|
||||
pim_addr group_addr;
|
||||
pim_addr source_addr;
|
||||
int sock_fd;
|
||||
enum gm_join_type join_type;
|
||||
time_t sock_creation;
|
||||
};
|
||||
|
||||
|
|
|
@ -553,7 +553,13 @@ const struct frr_yang_module_info frr_gmp_info = {
|
|||
.destroy = lib_interface_gmp_address_family_join_group_destroy,
|
||||
}
|
||||
},
|
||||
{
|
||||
{
|
||||
.xpath = "/frr-interface:lib/interface/frr-gmp:gmp/address-family/proxy",
|
||||
.cbs = {
|
||||
.modify = lib_interface_gmp_address_family_proxy_modify,
|
||||
}
|
||||
},
|
||||
{
|
||||
.xpath = "/frr-interface:lib/interface/frr-gmp:gmp/address-family/static-group",
|
||||
.cbs = {
|
||||
.create = lib_interface_gmp_address_family_static_group_create,
|
||||
|
|
|
@ -221,6 +221,7 @@ 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_proxy_modify(struct nb_cb_modify_args *args);
|
||||
int lib_interface_gmp_address_family_static_group_create(
|
||||
struct nb_cb_create_args *args);
|
||||
int lib_interface_gmp_address_family_static_group_destroy(
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "lib_errors.h"
|
||||
#include "pim_util.h"
|
||||
#include "pim6_mld.h"
|
||||
#include "pim_igmp.h"
|
||||
|
||||
#if PIM_IPV == 6
|
||||
#define pim6_msdp_err(funcname, argtype) \
|
||||
|
@ -3381,6 +3382,33 @@ int lib_interface_gmp_address_family_robustness_variable_modify(
|
|||
return NB_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* XPath: /frr-interface:lib/interface/frr-gmp:gmp/address-family/proxy
|
||||
*/
|
||||
int lib_interface_gmp_address_family_proxy_modify(struct nb_cb_modify_args *args)
|
||||
{
|
||||
struct interface *ifp;
|
||||
struct pim_interface *pim_ifp;
|
||||
|
||||
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);
|
||||
pim_ifp = ifp->info;
|
||||
if (pim_ifp)
|
||||
pim_ifp->gm_proxy = yang_dnode_get_bool(args->dnode,
|
||||
NULL);
|
||||
|
||||
if (pim_ifp->gm_proxy)
|
||||
pim_if_gm_proxy_init(pim_ifp->pim, ifp);
|
||||
else
|
||||
pim_if_gm_proxy_finis(pim_ifp->pim, ifp);
|
||||
}
|
||||
return NB_OK;
|
||||
}
|
||||
/*
|
||||
* XPath: /frr-interface:lib/interface/frr-gmp:gmp/address-family/join-group
|
||||
*/
|
||||
|
@ -3432,7 +3460,8 @@ int lib_interface_gmp_address_family_join_group_create(
|
|||
"./source-addr");
|
||||
yang_dnode_get_pimaddr(&group_addr, args->dnode,
|
||||
"./group-addr");
|
||||
result = pim_if_gm_join_add(ifp, group_addr, source_addr);
|
||||
result = pim_if_gm_join_add(ifp, group_addr, source_addr,
|
||||
GM_JOIN_STATIC);
|
||||
if (result) {
|
||||
snprintf(args->errmsg, args->errmsg_len,
|
||||
"Failure joining " GM " group");
|
||||
|
@ -3461,7 +3490,8 @@ int lib_interface_gmp_address_family_join_group_destroy(
|
|||
"./source-addr");
|
||||
yang_dnode_get_pimaddr(&group_addr, args->dnode,
|
||||
"./group-addr");
|
||||
result = pim_if_gm_join_del(ifp, group_addr, source_addr);
|
||||
result = pim_if_gm_join_del(ifp, group_addr, source_addr,
|
||||
GM_JOIN_STATIC);
|
||||
|
||||
if (result) {
|
||||
snprintf(args->errmsg, args->errmsg_len,
|
||||
|
|
|
@ -78,6 +78,31 @@ tib_sg_oil_setup(struct pim_instance *pim, pim_sgaddr sg, struct interface *oif)
|
|||
return pim_channel_oil_add(pim, &sg, __func__);
|
||||
}
|
||||
|
||||
void tib_sg_proxy_join_prune_check(struct pim_instance *pim, pim_sgaddr sg,
|
||||
struct interface *oif, bool join)
|
||||
{
|
||||
struct interface *ifp;
|
||||
|
||||
FOR_ALL_INTERFACES (pim->vrf, ifp) {
|
||||
struct pim_interface *pim_ifp = ifp->info;
|
||||
|
||||
if (!pim_ifp)
|
||||
continue;
|
||||
|
||||
if (ifp == oif) /* skip the source interface */
|
||||
continue;
|
||||
|
||||
if (pim_ifp->gm_enable && pim_ifp->gm_proxy) {
|
||||
if (join)
|
||||
pim_if_gm_join_add(ifp, sg.grp, sg.src,
|
||||
GM_JOIN_PROXY);
|
||||
else
|
||||
pim_if_gm_join_del(ifp, sg.grp, sg.src,
|
||||
GM_JOIN_PROXY);
|
||||
}
|
||||
} /* scan interfaces */
|
||||
}
|
||||
|
||||
bool tib_sg_gm_join(struct pim_instance *pim, pim_sgaddr sg,
|
||||
struct interface *oif, struct channel_oil **oilp)
|
||||
{
|
||||
|
@ -95,6 +120,8 @@ bool tib_sg_gm_join(struct pim_instance *pim, pim_sgaddr sg,
|
|||
if (!*oilp)
|
||||
return false;
|
||||
|
||||
tib_sg_proxy_join_prune_check(pim, sg, oif, true);
|
||||
|
||||
if (PIM_I_am_DR(pim_oif) || PIM_I_am_DualActive(pim_oif)) {
|
||||
int result;
|
||||
|
||||
|
@ -137,6 +164,8 @@ void tib_sg_gm_prune(struct pim_instance *pim, pim_sgaddr sg,
|
|||
{
|
||||
int result;
|
||||
|
||||
tib_sg_proxy_join_prune_check(pim, sg, oif, false);
|
||||
|
||||
/*
|
||||
It appears that in certain circumstances that
|
||||
igmp_source_forward_stop is called when IGMP forwarding
|
||||
|
|
|
@ -16,5 +16,8 @@ extern bool tib_sg_gm_join(struct pim_instance *pim, pim_sgaddr sg,
|
|||
struct interface *oif, struct channel_oil **oilp);
|
||||
extern void tib_sg_gm_prune(struct pim_instance *pim, pim_sgaddr sg,
|
||||
struct interface *oif, struct channel_oil **oilp);
|
||||
extern void tib_sg_proxy_join_prune_check(struct pim_instance *pim,
|
||||
pim_sgaddr sg, struct interface *oif,
|
||||
bool join);
|
||||
|
||||
#endif /* _FRR_PIM_GLUE_H */
|
||||
|
|
|
@ -270,6 +270,11 @@ static int gm_config_write(struct vty *vty, int writes,
|
|||
++writes;
|
||||
}
|
||||
|
||||
if (pim_ifp->gm_proxy) {
|
||||
vty_out(vty, " ip igmp proxy\n");
|
||||
++writes;
|
||||
}
|
||||
|
||||
/* ip igmp version */
|
||||
if (pim_ifp->igmp_version != IGMP_DEFAULT_VERSION) {
|
||||
vty_out(vty, " ip igmp version %d\n", pim_ifp->igmp_version);
|
||||
|
|
|
@ -4260,6 +4260,7 @@ def verify_local_igmp_groups(tgen, dut, interface, group_addresses):
|
|||
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
|
||||
return True
|
||||
|
||||
|
||||
@retry(retry_timeout=62)
|
||||
def verify_static_groups(tgen, dut, interface, group_addresses):
|
||||
"""
|
||||
|
@ -4293,7 +4294,9 @@ def verify_static_groups(tgen, dut, interface, group_addresses):
|
|||
rnode = tgen.routers()[dut]
|
||||
|
||||
logger.info("[DUT: %s]: Verifying static groups received:", dut)
|
||||
show_static_group_json = run_frr_cmd(rnode, "show ip igmp static-group json", isjson=True)
|
||||
show_static_group_json = run_frr_cmd(
|
||||
rnode, "show ip igmp static-group json", isjson=True
|
||||
)
|
||||
|
||||
if type(group_addresses) is not list:
|
||||
group_addresses = [group_addresses]
|
||||
|
@ -4330,6 +4333,71 @@ def verify_static_groups(tgen, dut, interface, group_addresses):
|
|||
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
|
||||
return True
|
||||
|
||||
|
||||
@retry(retry_timeout=62)
|
||||
def verify_local_igmp_proxy_groups(
|
||||
tgen, dut, group_addresses_present, group_addresses_not_present
|
||||
):
|
||||
"""
|
||||
Verify igmp proxy groups are as expected by running
|
||||
"show ip igmp static-group json" command
|
||||
|
||||
Parameters
|
||||
----------
|
||||
* `tgen`: topogen object
|
||||
* `dut`: device under test
|
||||
* `group_addresses_present`: IGMP group addresses which should
|
||||
currently be proxied
|
||||
* `group_addresses_not_present`: IGMP group addresses which should
|
||||
not currently be proxied
|
||||
|
||||
Usage
|
||||
-----
|
||||
dut = "r1"
|
||||
group_addresses_present = "225.1.1.1"
|
||||
group_addresses_not_present = "225.2.2.2"
|
||||
result = verify_igmp_proxy_groups(tgen, dut, group_p, group_np)
|
||||
|
||||
Returns
|
||||
-------
|
||||
errormsg(str) or True
|
||||
"""
|
||||
|
||||
if dut not in tgen.routers():
|
||||
errormsg = "[DUT %s]: Device not found!"
|
||||
return errormsg
|
||||
|
||||
rnode = tgen.routers()[dut]
|
||||
|
||||
logger.info("[DUT: %s]: Verifying local IGMP proxy groups:", dut)
|
||||
|
||||
out = rnode.vtysh_cmd("show ip igmp proxy json", isjson=True)
|
||||
groups = [g["group"] if "group" in g else None for g in out["r1-eth1"]["groups"]]
|
||||
|
||||
if type(group_addresses_present) is not list:
|
||||
group_addresses_present = [group_addresses_present]
|
||||
if type(group_addresses_not_present) is not list:
|
||||
group_addresses_not_present = [group_addresses_not_present]
|
||||
|
||||
for test_addr in group_addresses_present:
|
||||
if not test_addr in groups:
|
||||
errormsg = (
|
||||
"[DUT %s]: Verifying local IGMP proxy joins FAILED!! "
|
||||
" Expected but not found: %s " % (dut, test_addr)
|
||||
)
|
||||
return errormsg
|
||||
|
||||
for test_addr in group_addresses_not_present:
|
||||
if test_addr in groups:
|
||||
errormsg = (
|
||||
"[DUT %s]: Verifying local IGMP proxy join removed FAILED!! "
|
||||
" Unexpected but found: %s " % (dut, test_addr)
|
||||
)
|
||||
return errormsg
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def verify_pim_interface_traffic(tgen, input_dict, return_stats=True, addr_type="ipv4"):
|
||||
"""
|
||||
Verify ip pim interface traffic by running
|
||||
|
|
29
tests/topotests/pim_basic_igmp_proxy/r1/frr.conf
Normal file
29
tests/topotests/pim_basic_igmp_proxy/r1/frr.conf
Normal file
|
@ -0,0 +1,29 @@
|
|||
hostname r1
|
||||
!
|
||||
interface r1-eth0
|
||||
ip address 10.0.20.1/24
|
||||
ip igmp
|
||||
ip pim
|
||||
ip igmp join 225.1.1.1
|
||||
ip igmp join 225.2.2.2
|
||||
!
|
||||
interface r1-eth1
|
||||
ip address 10.0.30.1/24
|
||||
ip pim
|
||||
ip igmp
|
||||
ip igmp proxy
|
||||
!
|
||||
interface r1-eth2
|
||||
ip address 10.0.40.1/24
|
||||
ip igmp
|
||||
ip pim
|
||||
ip igmp join 225.3.3.3
|
||||
ip igmp join 225.4.4.4
|
||||
!
|
||||
interface lo
|
||||
ip address 10.254.0.1/32
|
||||
ip pim
|
||||
!
|
||||
router pim
|
||||
rp 10.254.0.3
|
||||
join-prune-interval 5
|
19
tests/topotests/pim_basic_igmp_proxy/r2/frr.conf
Normal file
19
tests/topotests/pim_basic_igmp_proxy/r2/frr.conf
Normal file
|
@ -0,0 +1,19 @@
|
|||
hostname r2
|
||||
!
|
||||
interface r2-eth0
|
||||
ip address 10.0.20.2/24
|
||||
ip igmp
|
||||
ip pim
|
||||
ip igmp proxy
|
||||
!
|
||||
interface r2-eth1
|
||||
ip address 10.0.80.1/24
|
||||
ip igmp
|
||||
ip pim passive
|
||||
!
|
||||
interface lo
|
||||
ip address 10.254.0.2/32
|
||||
!
|
||||
router pim
|
||||
rp 10.254.0.3
|
||||
join-prune-interval 5
|
8
tests/topotests/pim_basic_igmp_proxy/r3/frr.conf
Normal file
8
tests/topotests/pim_basic_igmp_proxy/r3/frr.conf
Normal file
|
@ -0,0 +1,8 @@
|
|||
hostname r3
|
||||
!
|
||||
interface r3-eth0
|
||||
ip address 10.0.40.4/24
|
||||
!
|
||||
interface lo
|
||||
ip address 10.254.0.4/32
|
||||
!
|
16
tests/topotests/pim_basic_igmp_proxy/rp/frr.conf
Normal file
16
tests/topotests/pim_basic_igmp_proxy/rp/frr.conf
Normal file
|
@ -0,0 +1,16 @@
|
|||
hostname rp
|
||||
!
|
||||
interface rp-eth0
|
||||
ip address 10.0.30.3/24
|
||||
ip pim
|
||||
!
|
||||
interface lo
|
||||
ip address 10.254.0.3/32
|
||||
ip pim
|
||||
!
|
||||
router pim
|
||||
join-prune-interval 5
|
||||
rp 10.254.0.3
|
||||
register-accept-list ACCEPT
|
||||
|
||||
ip prefix-list ACCEPT seq 5 permit 10.0.20.0/24 le 32
|
319
tests/topotests/pim_basic_igmp_proxy/test_pim_igmp_proxy.py
Normal file
319
tests/topotests/pim_basic_igmp_proxy/test_pim_igmp_proxy.py
Normal file
|
@ -0,0 +1,319 @@
|
|||
#!/usr/bin/env python
|
||||
# SPDX-License-Identifier: ISC
|
||||
|
||||
#
|
||||
# test_pim_igmp_proxy.py
|
||||
#
|
||||
# Copyright (c) 2024 ATCorp
|
||||
# Barry A. Trent
|
||||
#
|
||||
|
||||
"""
|
||||
Following tests are covered to test pim igmp proxy:
|
||||
|
||||
1. TC:1 Verify correct joins were read from the config and proxied
|
||||
2. TC:2 Verify joins from another interface are proxied
|
||||
3. TC:3 Verify correct proxy disable on 'no ip igmp proxy'
|
||||
4. TC:4 Verify that proper proxy joins are set up on run-time enable
|
||||
5. TC:5 Verify igmp drops/timeouts from another interface cause
|
||||
proxy join removal
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import pytest
|
||||
import json
|
||||
import time
|
||||
from functools import partial
|
||||
|
||||
pytestmark = [pytest.mark.pimd]
|
||||
|
||||
CWD = os.path.dirname(os.path.realpath(__file__))
|
||||
sys.path.append(os.path.join(CWD, "../"))
|
||||
|
||||
# pylint: disable=C0413
|
||||
from lib import topotest
|
||||
from lib.topogen import Topogen, TopoRouter, get_topogen
|
||||
from lib.topolog import logger
|
||||
from lib.pim import verify_local_igmp_proxy_groups
|
||||
|
||||
|
||||
def build_topo(tgen):
|
||||
"Build function"
|
||||
|
||||
for routern in range(1, 4):
|
||||
tgen.add_router("r{}".format(routern))
|
||||
|
||||
tgen.add_router("rp")
|
||||
|
||||
# rp ------ r1 -------- r2 -------
|
||||
# \
|
||||
# --------- r3
|
||||
# r1 -> .1
|
||||
# r2 -> .2
|
||||
# rp -> .3
|
||||
# r3 -> .4
|
||||
# loopback network is 10.254.0.X/32
|
||||
#
|
||||
# r1 <- sw1 -> r2
|
||||
# r1-eth0 <-> r2-eth0
|
||||
# 10.0.20.0/24
|
||||
sw = tgen.add_switch("sw1")
|
||||
sw.add_link(tgen.gears["r1"])
|
||||
sw.add_link(tgen.gears["r2"])
|
||||
|
||||
# r1 <- sw2 -> rp
|
||||
# r1-eth1 <-> rp-eth0
|
||||
# 10.0.30.0/24
|
||||
sw = tgen.add_switch("sw2")
|
||||
sw.add_link(tgen.gears["r1"])
|
||||
sw.add_link(tgen.gears["rp"])
|
||||
|
||||
# 10.0.40.0/24
|
||||
sw = tgen.add_switch("sw3")
|
||||
sw.add_link(tgen.gears["r1"])
|
||||
sw.add_link(tgen.gears["r3"])
|
||||
|
||||
# Dummy interface for static joins
|
||||
tgen.gears["r2"].run("ip link add r2-eth1 type dummy")
|
||||
|
||||
|
||||
def setup_module(mod):
|
||||
"Sets up the pytest environment"
|
||||
tgen = Topogen(build_topo, mod.__name__)
|
||||
tgen.start_topology()
|
||||
|
||||
# For all registered routers, load the zebra configuration file
|
||||
for rname, router in tgen.routers().items():
|
||||
router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
|
||||
|
||||
# After loading the configurations, this function loads configured daemons.
|
||||
tgen.start_router()
|
||||
# tgen.mininet_cli()
|
||||
|
||||
|
||||
def teardown_module():
|
||||
"Teardown the pytest environment"
|
||||
tgen = get_topogen()
|
||||
|
||||
# This function tears down the whole topology.
|
||||
tgen.stop_topology()
|
||||
|
||||
|
||||
def test_pim_igmp_proxy_config():
|
||||
"Ensure correct joins were read from the config and proxied"
|
||||
logger.info("Verify initial igmp proxy setup from config file")
|
||||
tgen = get_topogen()
|
||||
|
||||
r1 = tgen.gears["r1"]
|
||||
|
||||
expected = {
|
||||
"vrf": "default",
|
||||
"r1-eth1": {
|
||||
"name": "r1-eth1",
|
||||
"groups": [
|
||||
{
|
||||
"source": "*",
|
||||
"group": "225.4.4.4",
|
||||
"primaryAddr": "10.0.30.1",
|
||||
},
|
||||
{
|
||||
"source": "*",
|
||||
"group": "225.3.3.3",
|
||||
"primaryAddr": "10.0.30.1",
|
||||
},
|
||||
{
|
||||
"source": "*",
|
||||
"group": "225.2.2.2",
|
||||
"primaryAddr": "10.0.30.1",
|
||||
},
|
||||
{
|
||||
"source": "*",
|
||||
"group": "225.1.1.1",
|
||||
"primaryAddr": "10.0.30.1",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
test_func = partial(
|
||||
topotest.router_json_cmp, r1, "show ip igmp proxy json", expected
|
||||
)
|
||||
_, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
|
||||
assertmsg = '"{}" JSON output mismatches'.format(r1.name)
|
||||
assert result is None, assertmsg
|
||||
# tgen.mininet_cli()
|
||||
|
||||
|
||||
def test_pim_igmp_proxy_learn():
|
||||
"Ensure joins learned from a neighbor are propagated"
|
||||
logger.info("Verify joins can be learned")
|
||||
tgen = get_topogen()
|
||||
|
||||
r1 = tgen.gears["r1"]
|
||||
r2 = tgen.gears["r2"]
|
||||
|
||||
r2.vtysh_cmd(
|
||||
"conf\nint r2-eth0\nip igmp join 225.5.5.5\nip igmp join 225.6.6.6\nexit\nexit"
|
||||
)
|
||||
r2.vtysh_cmd(
|
||||
"conf\nint r2-eth1\nip igmp join 225.7.7.7\nip igmp join 225.8.8.8\nexit\nexit"
|
||||
)
|
||||
expected = {
|
||||
"vrf": "default",
|
||||
"r1-eth1": {
|
||||
"name": "r1-eth1",
|
||||
"groups": [
|
||||
{
|
||||
"source": "*",
|
||||
"group": "225.5.5.5",
|
||||
"primaryAddr": "10.0.30.1",
|
||||
},
|
||||
{
|
||||
"source": "*",
|
||||
"group": "225.6.6.6",
|
||||
"primaryAddr": "10.0.30.1",
|
||||
},
|
||||
{
|
||||
"source": "*",
|
||||
"group": "225.7.7.7",
|
||||
"primaryAddr": "10.0.30.1",
|
||||
},
|
||||
{
|
||||
"source": "*",
|
||||
"group": "225.8.8.8",
|
||||
"primaryAddr": "10.0.30.1",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
test_func = partial(
|
||||
topotest.router_json_cmp, r1, "show ip igmp proxy json", expected
|
||||
)
|
||||
_, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
|
||||
assertmsg = '"{}" JSON output mismatches'.format(r1.name)
|
||||
assert result is None, assertmsg
|
||||
# tgen.mininet_cli()
|
||||
|
||||
|
||||
def test_pim_no_igmp_proxy():
|
||||
"Check for correct proxy disable"
|
||||
logger.info("Verify no ip igmp proxy")
|
||||
tgen = get_topogen()
|
||||
|
||||
r1 = tgen.gears["r1"]
|
||||
|
||||
r1.vtysh_cmd("conf\nint r1-eth1\nno ip igmp proxy\nexit\nexit")
|
||||
expected = {"vrf": "default"}
|
||||
|
||||
test_func = partial(
|
||||
topotest.router_json_cmp, r1, "show ip igmp proxy json", expected
|
||||
)
|
||||
_, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
|
||||
assertmsg = '"{}" JSON output mismatches'.format(r1.name)
|
||||
assert result is None, assertmsg
|
||||
# tgen.mininet_cli()
|
||||
|
||||
|
||||
def test_pim_igmp_proxy_restart():
|
||||
"Check that all proxy joins are captured at run-time enable"
|
||||
logger.info("Verify runtime ip igmp proxy")
|
||||
tgen = get_topogen()
|
||||
|
||||
r1 = tgen.gears["r1"]
|
||||
|
||||
r1.vtysh_cmd("conf\nint r1-eth1\nip igmp proxy\nexit\nexit")
|
||||
expected = {
|
||||
"vrf": "default",
|
||||
"r1-eth1": {
|
||||
"name": "r1-eth1",
|
||||
"groups": [
|
||||
{
|
||||
"source": "*",
|
||||
"group": "225.8.8.8",
|
||||
"primaryAddr": "10.0.30.1",
|
||||
},
|
||||
{
|
||||
"source": "*",
|
||||
"group": "225.7.7.7",
|
||||
"primaryAddr": "10.0.30.1",
|
||||
},
|
||||
{
|
||||
"source": "*",
|
||||
"group": "225.6.6.6",
|
||||
"primaryAddr": "10.0.30.1",
|
||||
},
|
||||
{
|
||||
"source": "*",
|
||||
"group": "225.5.5.5",
|
||||
"primaryAddr": "10.0.30.1",
|
||||
},
|
||||
{
|
||||
"source": "*",
|
||||
"group": "225.4.4.4",
|
||||
"primaryAddr": "10.0.30.1",
|
||||
},
|
||||
{
|
||||
"source": "*",
|
||||
"group": "225.3.3.3",
|
||||
"primaryAddr": "10.0.30.1",
|
||||
},
|
||||
{
|
||||
"source": "*",
|
||||
"group": "225.2.2.2",
|
||||
"primaryAddr": "10.0.30.1",
|
||||
},
|
||||
{
|
||||
"source": "*",
|
||||
"group": "225.1.1.1",
|
||||
"primaryAddr": "10.0.30.1",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
test_func = partial(
|
||||
topotest.router_json_cmp, r1, "show ip igmp proxy json", expected
|
||||
)
|
||||
_, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
|
||||
assertmsg = '"{}" JSON output mismatches'.format(r1.name)
|
||||
assert result is None, assertmsg
|
||||
# tgen.mininet_cli()
|
||||
|
||||
|
||||
def test_pim_igmp_proxy_leave():
|
||||
"Ensure drops/timeouts learned from a neighbor are propagated"
|
||||
logger.info("Verify joins can be dropped")
|
||||
tgen = get_topogen()
|
||||
|
||||
r1 = tgen.gears["r1"]
|
||||
r2 = tgen.gears["r2"]
|
||||
|
||||
r1.vtysh_cmd("conf\nint r1-eth0\nno ip igmp join 225.1.1.1\nexit\nexit")
|
||||
r2.vtysh_cmd("conf\nint r2-eth0\nno ip igmp join 225.6.6.6\nexit\nexit")
|
||||
r2.vtysh_cmd("conf\nint r2-eth1\nno ip igmp join 225.8.8.8\nexit\nexit")
|
||||
|
||||
joined_addresses = ["225.2.2.2", "225.3.3.3", "225.4.4.4", "225.5.5.5", "225.7.7.7"]
|
||||
deleted_addresses = ["225.1.1.1", "225.6.6.6", "225.8.8.8"]
|
||||
|
||||
result = verify_local_igmp_proxy_groups(
|
||||
tgen, "r1", joined_addresses, deleted_addresses
|
||||
)
|
||||
|
||||
assert result is True, "Error: {}".format(result)
|
||||
# tgen.mininet_cli()
|
||||
|
||||
|
||||
def test_memory_leak():
|
||||
"Run the memory leak test and report results."
|
||||
tgen = get_topogen()
|
||||
if not tgen.is_memleak_enabled():
|
||||
pytest.skip("Memory leak test/report is disabled")
|
||||
|
||||
tgen.report_memory_leaks()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = ["-s"] + sys.argv[1:]
|
||||
sys.exit(pytest.main(args))
|
|
@ -146,6 +146,13 @@ module frr-gmp {
|
|||
"Querier's Robustness Variable allows tuning for the
|
||||
expected packet loss on a network.";
|
||||
}
|
||||
|
||||
leaf proxy {
|
||||
type boolean;
|
||||
default "false";
|
||||
description
|
||||
"Enable IGMP proxy on the interface.";
|
||||
}
|
||||
|
||||
list join-group {
|
||||
key "group-addr source-addr";
|
||||
|
|
Loading…
Reference in a new issue