Merge pull request #19765 from ThalesGroup/bugfix/ip_static_mroute

pimd : Added support for multi-oif static mroute
This commit is contained in:
Jafar Al-Gharaibeh 2025-10-22 23:12:38 -05:00 committed by GitHub
commit 02a31e281c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 112 additions and 73 deletions

View file

@ -468,7 +468,12 @@ 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)
{
nb_cli_enqueue_change(vty, "./oif", NB_OP_MODIFY, interface);
/* managing list of oif regarding (iif,mcast group, mcast source)*/
char xpath[XPATH_MAXLEN];
snprintf(xpath, sizeof(xpath), "./oif[.='%s']", interface);
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
if (!source_str) {
char buf[SRCDEST2STR_BUFFER];
@ -487,7 +492,12 @@ int pim_process_ip_mroute_cmd(struct vty *vty, const char *interface,
int pim_process_no_ip_mroute_cmd(struct vty *vty, const char *interface,
const char *group_str, const char *source_str)
{
nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL);
/* managing list of oif regarding (iif,mcast group, mcast source)*/
char xpath[XPATH_MAXLEN];
snprintf(xpath, sizeof(xpath), "./oif[.='%s']", interface);
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
if (!source_str) {
char buf[SRCDEST2STR_BUFFER];

View file

@ -418,13 +418,13 @@ const struct frr_yang_module_info frr_pim_info = {
.xpath = "/frr-interface:lib/interface/frr-pim:pim/address-family/mroute",
.cbs = {
.create = lib_interface_pim_address_family_mroute_create,
.destroy = lib_interface_pim_address_family_mroute_destroy,
.destroy = lib_interface_pim_address_family_mroute_oif_destroy,
}
},
{
.xpath = "/frr-interface:lib/interface/frr-pim:pim/address-family/mroute/oif",
.cbs = {
.modify = lib_interface_pim_address_family_mroute_oif_modify,
.create = lib_interface_pim_address_family_mroute_oif_create,
.destroy = lib_interface_pim_address_family_mroute_oif_destroy,
}
},

View file

@ -164,10 +164,7 @@ int lib_interface_pim_address_family_multicast_boundary_acl_modify(struct nb_cb_
int lib_interface_pim_address_family_multicast_boundary_acl_destroy(struct nb_cb_destroy_args *args);
int lib_interface_pim_address_family_mroute_create(
struct nb_cb_create_args *args);
int lib_interface_pim_address_family_mroute_destroy(
struct nb_cb_destroy_args *args);
int lib_interface_pim_address_family_mroute_oif_modify(
struct nb_cb_modify_args *args);
int lib_interface_pim_address_family_mroute_oif_create(struct nb_cb_create_args *args);
int lib_interface_pim_address_family_mroute_oif_destroy(
struct nb_cb_destroy_args *args);

View file

@ -2903,65 +2903,10 @@ int lib_interface_pim_address_family_mroute_create(
return NB_OK;
}
int lib_interface_pim_address_family_mroute_destroy(
struct nb_cb_destroy_args *args)
{
struct pim_instance *pim;
struct pim_interface *pim_iifp;
struct interface *iif;
struct interface *oif;
const char *oifname;
pim_addr source_addr;
pim_addr group_addr;
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)) {
snprintf(args->errmsg, args->errmsg_len,
"%% Enable PIM and/or IGMP on this interface first");
return NB_ERR_VALIDATION;
}
break;
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
iif = nb_running_get_entry(args->dnode, NULL, true);
pim_iifp = iif->info;
pim = pim_iifp->pim;
oifname = yang_dnode_get_string(args->dnode, "oif");
oif = if_lookup_by_name(oifname, pim->vrf->vrf_id);
if (!oif) {
snprintf(args->errmsg, args->errmsg_len,
"No such interface name %s",
oifname);
return NB_ERR_INCONSISTENCY;
}
yang_dnode_get_pimaddr(&source_addr, args->dnode, "source-addr");
yang_dnode_get_pimaddr(&group_addr, args->dnode, "group-addr");
if (pim_static_del(pim, iif, oif, group_addr, source_addr)) {
snprintf(args->errmsg, args->errmsg_len,
"Failed to remove static mroute");
return NB_ERR_INCONSISTENCY;
}
break;
}
return NB_OK;
}
/*
* XPath: /frr-interface:lib/interface/frr-pim:pim/address-family/mroute/oif
*/
int lib_interface_pim_address_family_mroute_oif_modify(
struct nb_cb_modify_args *args)
int lib_interface_pim_address_family_mroute_oif_create(struct nb_cb_create_args *args)
{
struct pim_instance *pim;
struct pim_interface *pim_iifp;
@ -3004,15 +2949,19 @@ int lib_interface_pim_address_family_mroute_oif_modify(
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
iif = nb_running_get_entry(args->dnode, NULL, true);
if_dnode = yang_dnode_get_parent(args->dnode, "interface");
iif = nb_running_get_entry(if_dnode, NULL, true);
if (!iif)
return NB_ERR_INCONSISTENCY;
pim_iifp = iif->info;
pim = pim_iifp->pim;
oifname = yang_dnode_get_string(args->dnode, NULL);
oif = if_lookup_by_name(oifname, pim->vrf->vrf_id);
if (!oif) {
snprintf(args->errmsg, args->errmsg_len,
"No such interface name %s",
snprintf(args->errmsg, args->errmsg_len, "No such interface name %s",
oifname);
return NB_ERR_INCONSISTENCY;
}
@ -3021,8 +2970,7 @@ int lib_interface_pim_address_family_mroute_oif_modify(
yang_dnode_get_pimaddr(&group_addr, args->dnode, "../group-addr");
if (pim_static_add(pim, iif, oif, group_addr, source_addr)) {
snprintf(args->errmsg, args->errmsg_len,
"Failed to add static mroute");
snprintf(args->errmsg, args->errmsg_len, "Failed to add static mroute");
return NB_ERR_INCONSISTENCY;
}
@ -3032,14 +2980,55 @@ int lib_interface_pim_address_family_mroute_oif_modify(
return NB_OK;
}
int lib_interface_pim_address_family_mroute_oif_destroy(
struct nb_cb_destroy_args *args)
int lib_interface_pim_address_family_mroute_oif_destroy(struct nb_cb_destroy_args *args)
{
struct pim_instance *pim;
struct pim_interface *pim_iifp;
struct interface *iif;
struct interface *oif;
const char *oifname;
pim_addr source_addr;
pim_addr group_addr;
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)) {
snprintf(args->errmsg, args->errmsg_len,
"%% Enable PIM and/or IGMP on this interface first");
return NB_ERR_VALIDATION;
}
break;
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
if_dnode = yang_dnode_get_parent(args->dnode, "interface");
iif = nb_running_get_entry(if_dnode, NULL, true);
if (!iif)
return NB_ERR_INCONSISTENCY;
pim_iifp = iif->info;
pim = pim_iifp->pim;
oifname = yang_dnode_get_string(args->dnode, NULL);
oif = if_lookup_by_name(oifname, pim->vrf->vrf_id);
if (!oif) {
snprintf(args->errmsg, args->errmsg_len, "No such interface name %s",
oifname);
return NB_ERR_INCONSISTENCY;
}
yang_dnode_get_pimaddr(&source_addr, args->dnode, "../source-addr");
yang_dnode_get_pimaddr(&group_addr, args->dnode, "../group-addr");
if (pim_static_del(pim, iif, oif, group_addr, source_addr)) {
snprintf(args->errmsg, args->errmsg_len, "Failed to del static mroute");
return NB_ERR_INCONSISTENCY;
}
break;
}

View file

@ -247,6 +247,49 @@ def test_memory_leak():
tgen.report_memory_leaks()
def test_pim_static_mroute():
"Test Static routes add/remove cycle"
logger.info("Testing static routes")
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
r1 = tgen.gears["r1"]
ip_mroute_json = r1.vtysh_cmd("show ip mroute json", isjson=True)
assert "239.1.1.1" not in ip_mroute_json.keys()
# Add ip mroute with iif=r1-eth0 oil=r1-eth1
r1.vtysh_cmd("conf t\ninterface r1-eth0\nip mroute r1-eth1 239.1.1.1 10.0.0.1")
# Expect to find oil=[r1-eth1] in ip mroute command
ip_mroute_json = r1.vtysh_cmd("show ip mroute json", isjson=True)
assert list(ip_mroute_json["239.1.1.1"]["10.0.0.1"]["oil"].keys()) == ["r1-eth1"]
# Add ip mroute with iif=r1-eth0 oil=r1-eth2
r1.vtysh_cmd("conf t\ninterface r1-eth0\nip mroute r1-eth2 239.1.1.1 10.0.0.1")
# Expect to find oil=[r1-eth1,r1-eth2] in ip mroute command
ip_mroute_json = r1.vtysh_cmd("show ip mroute json", isjson=True)
assert list(ip_mroute_json["239.1.1.1"]["10.0.0.1"]["oil"].keys()) == [
"r1-eth1",
"r1-eth2",
]
# Remove ip mroute with oil=r1-eth1
r1.vtysh_cmd("conf t\ninterface r1-eth0\nno ip mroute r1-eth1 239.1.1.1 10.0.0.1")
ip_mroute_json = r1.vtysh_cmd("show ip mroute json", isjson=True)
# This assert right here would break back in the day because only one oif was handled by the datamodel
assert list(ip_mroute_json["239.1.1.1"]["10.0.0.1"]["oil"].keys()) == ["r1-eth2"]
r1.vtysh_cmd("conf t\ninterface r1-eth0\nno ip mroute r1-eth2 239.1.1.1 10.0.0.1")
# Expect a clean state
ip_mroute_json = r1.vtysh_cmd("show ip mroute json", isjson=True)
assert "239.1.1.1" not in ip_mroute_json.keys()
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))

View file

@ -650,10 +650,10 @@ module frr-pim {
description
"Add multicast route.";
leaf oif {
leaf-list oif {
type frr-interface:interface-ref;
description
"Outgoing interface name.";
"List of Outgoing interface names.";
}
leaf source-addr {