diff --git a/doc/user/pim.rst b/doc/user/pim.rst index 90d37b2d7e3e..20a4f1f7abe3 100644 --- a/doc/user/pim.rst +++ b/doc/user/pim.rst @@ -342,7 +342,7 @@ MSDP can be setup in different ways: .. note:: - MSDP default peer and SA filtering is not implemented. + MSDP default peer is not implemented. MSDP configuration is available under 'router pim' @@ -377,6 +377,15 @@ Commands available for MSDP: Create a regular MSDP session with peer using the specified source address. +.. clicmd:: msdp peer A.B.C.D sa-filter ACL_NAME + + Configure incoming or outgoing SA filtering rule. + + .. note:: + + The filtering will only take effect starting from the command + application. + .. _show-pim-information: diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 92214eced4ae..1e3e090868b0 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -6486,6 +6486,69 @@ DEFPY_ATTR(no_ip_pim_msdp_peer, return ret; } +DEFPY(msdp_peer_sa_filter, msdp_peer_sa_filter_cmd, + "msdp peer A.B.C.D$peer sa-filter ACL_NAME$acl_name $dir", + CFG_MSDP_STR + "Configure MSDP peer\n" + "MSDP Peer address\n" + "SA access-list filter\n" + "SA access-list name\n" + "Filter incoming SAs\n" + "Filter outgoing SAs\n") +{ + const struct lyd_node *peer_node; + char xpath[XPATH_MAXLEN + 24]; + + snprintf(xpath, sizeof(xpath), "%s/msdp-peer[peer-ip='%s']", + VTY_CURR_XPATH, peer_str); + peer_node = yang_dnode_get(vty->candidate_config->dnode, xpath); + if (peer_node == NULL) { + vty_out(vty, "%% MSDP peer %s not yet configured\n", peer_str); + return CMD_SUCCESS; + } + + if (strcmp(dir, "in") == 0) + nb_cli_enqueue_change(vty, "./sa-filter-in", NB_OP_MODIFY, + acl_name); + else + nb_cli_enqueue_change(vty, "./sa-filter-out", NB_OP_MODIFY, + acl_name); + + return nb_cli_apply_changes(vty, "%s", xpath); +} + +DEFPY(no_msdp_peer_sa_filter, no_ip_msdp_peer_sa_filter_cmd, + "no msdp peer A.B.C.D$peer sa-filter ACL_NAME $dir", + NO_STR + CFG_MSDP_STR + "Configure MSDP peer\n" + "MSDP Peer address\n" + "SA access-list filter\n" + "SA access-list name\n" + "Filter incoming SAs\n" + "Filter outgoing SAs\n") +{ + const struct lyd_node *peer_node; + char xpath[XPATH_MAXLEN + 24]; + + snprintf(xpath, sizeof(xpath), "%s/msdp-peer[peer-ip='%s']", + VTY_CURR_XPATH, peer_str); + peer_node = yang_dnode_get(vty->candidate_config->dnode, xpath); + if (peer_node == NULL) { + vty_out(vty, "%% MSDP peer %s not yet configured\n", peer_str); + return CMD_SUCCESS; + } + + if (strcmp(dir, "in") == 0) + nb_cli_enqueue_change(vty, "./sa-filter-in", NB_OP_DESTROY, + NULL); + else + nb_cli_enqueue_change(vty, "./sa-filter-out", NB_OP_DESTROY, + NULL); + + return nb_cli_apply_changes(vty, "%s", xpath); +} + DEFPY(pim_msdp_mesh_group_member, pim_msdp_mesh_group_member_cmd, "msdp mesh-group WORD$gname member A.B.C.D$maddr", @@ -8259,6 +8322,8 @@ void pim_cmd_init(void) install_element(PIM_NODE, &no_pim_msdp_peer_cmd); install_element(PIM_NODE, &pim_msdp_timers_cmd); install_element(PIM_NODE, &no_pim_msdp_timers_cmd); + install_element(PIM_NODE, &msdp_peer_sa_filter_cmd); + install_element(PIM_NODE, &no_ip_msdp_peer_sa_filter_cmd); install_element(PIM_NODE, &pim_msdp_mesh_group_member_cmd); install_element(PIM_NODE, &no_pim_msdp_mesh_group_member_cmd); install_element(PIM_NODE, &pim_msdp_mesh_group_source_cmd); diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c index 3393cebdd2df..0bb2d93a3a0b 100644 --- a/pimd/pim_msdp.c +++ b/pimd/pim_msdp.c @@ -1317,6 +1317,15 @@ bool pim_msdp_peer_config_write(struct vty *vty, struct pim_instance *pim) vty_out(vty, " msdp peer %pI4 source %pI4\n", &mp->peer, &mp->local); + + if (mp->acl_in) + vty_out(vty, " msdp peer %pI4 sa-filter %s in\n", + &mp->peer, mp->acl_in); + + if (mp->acl_out) + vty_out(vty, " msdp peer %pI4 sa-filter %s out\n", + &mp->peer, mp->acl_out); + written = true; } diff --git a/pimd/pim_msdp.h b/pimd/pim_msdp.h index 80ca003dc595..a45726cb85a6 100644 --- a/pimd/pim_msdp.h +++ b/pimd/pim_msdp.h @@ -138,6 +138,11 @@ struct pim_msdp_peer { /* timestamps */ int64_t uptime; + + /** SA input access list name. */ + char *acl_in; + /** SA output access list name. */ + char *acl_out; }; struct pim_msdp_mg_mbr { diff --git a/pimd/pim_msdp_packet.c b/pimd/pim_msdp_packet.c index 4324a96bef89..27f4966a1cc3 100644 --- a/pimd/pim_msdp_packet.c +++ b/pimd/pim_msdp_packet.c @@ -6,7 +6,9 @@ #include #include +#include #include +#include #include #include "frrevent.h" #include @@ -322,8 +324,8 @@ void pim_msdp_pkt_ka_tx(struct pim_msdp_peer *mp) pim_msdp_pkt_send(mp, s); } -static void pim_msdp_pkt_sa_push_to_one_peer(struct pim_instance *pim, - struct pim_msdp_peer *mp) +static void pim_msdp_pkt_sa_push(struct pim_instance *pim, + struct pim_msdp_peer *mp) { struct stream *s; @@ -338,25 +340,6 @@ static void pim_msdp_pkt_sa_push_to_one_peer(struct pim_instance *pim, } } -/* push the stream into the obuf fifo of all the peers */ -static void pim_msdp_pkt_sa_push(struct pim_instance *pim, - struct pim_msdp_peer *mp) -{ - struct listnode *mpnode; - - if (mp) { - pim_msdp_pkt_sa_push_to_one_peer(pim, mp); - } else { - for (ALL_LIST_ELEMENTS_RO(pim->msdp.peer_list, mpnode, mp)) { - if (PIM_DEBUG_MSDP_INTERNAL) { - zlog_debug("MSDP peer %s pim_msdp_pkt_sa_push", - mp->key_str); - } - pim_msdp_pkt_sa_push_to_one_peer(pim, mp); - } - } -} - static int pim_msdp_pkt_sa_fill_hdr(struct pim_instance *pim, int local_cnt, struct in_addr rp) { @@ -384,6 +367,90 @@ static void pim_msdp_pkt_sa_fill_one(struct pim_msdp_sa *sa) stream_put_ipv4(sa->pim->msdp.work_obuf, sa->sg.src.s_addr); } +static bool msdp_cisco_match(const struct filter *filter, + const struct in_addr *source, + const struct in_addr *group) +{ + const struct filter_cisco *cfilter = &filter->u.cfilter; + uint32_t source_addr; + uint32_t group_addr; + + group_addr = group->s_addr & ~cfilter->mask_mask.s_addr; + + if (cfilter->extended) { + source_addr = source->s_addr & ~cfilter->addr_mask.s_addr; + if (group_addr == cfilter->mask.s_addr && + source_addr == cfilter->addr.s_addr) + return true; + } else if (group_addr == cfilter->addr.s_addr) + return true; + + return false; +} + +static enum filter_type msdp_access_list_apply(struct access_list *access, + const struct in_addr *source, + const struct in_addr *group) +{ + struct filter *filter; + struct prefix group_prefix; + + if (access == NULL) + return FILTER_DENY; + + for (filter = access->head; filter; filter = filter->next) { + if (filter->cisco) { + if (msdp_cisco_match(filter, source, group)) + return filter->type; + } else { + group_prefix.family = AF_INET; + group_prefix.prefixlen = IPV4_MAX_BITLEN; + group_prefix.u.prefix4.s_addr = group->s_addr; + if (access_list_apply(access, &group_prefix)) + return filter->type; + } + } + + return FILTER_DENY; +} + +bool msdp_peer_sa_filter(const struct pim_msdp_peer *mp, + const struct pim_msdp_sa *sa) +{ + struct access_list *acl; + + /* No output filter configured, just quit. */ + if (mp->acl_out == NULL) + return false; + + /* Find access list and test it. */ + acl = access_list_lookup(AFI_IP, mp->acl_out); + if (msdp_access_list_apply(acl, &sa->sg.src, &sa->sg.grp) == FILTER_DENY) + return true; + + return false; +} + +/** Count the number of SAs to be sent for a specific peer. */ +static size_t pim_msdp_peer_sa_count(const struct pim_instance *pim, + const struct pim_msdp_peer *peer) +{ + const struct pim_msdp_sa *sa; + const struct listnode *node; + size_t sa_count = 0; + + for (ALL_LIST_ELEMENTS_RO(pim->msdp.sa_list, node, sa)) { + if (!CHECK_FLAG(sa->flags, PIM_MSDP_SAF_LOCAL)) + continue; + if (msdp_peer_sa_filter(peer, sa)) + continue; + + sa_count++; + } + + return sa_count; +} + static void pim_msdp_pkt_sa_gen(struct pim_instance *pim, struct pim_msdp_peer *mp) { @@ -393,7 +460,7 @@ static void pim_msdp_pkt_sa_gen(struct pim_instance *pim, struct prefix group_all; struct in_addr rp; int sa_count; - int local_cnt = pim->msdp.local_cnt; + int local_cnt = pim_msdp_peer_sa_count(pim, mp); sa_count = 0; if (PIM_DEBUG_MSDP_INTERNAL) { @@ -418,6 +485,15 @@ static void pim_msdp_pkt_sa_gen(struct pim_instance *pim, * peers */ continue; } + + if (msdp_peer_sa_filter(mp, sa)) { + if (PIM_DEBUG_MSDP_EVENTS) + zlog_debug("MSDP peer %pI4 filter SA out %s", + &mp->peer, sa->sg_str); + + continue; + } + /* add sa into scratch pad */ pim_msdp_pkt_sa_fill_one(sa); ++sa_count; @@ -457,15 +533,32 @@ static void pim_msdp_pkt_sa_tx_done(struct pim_instance *pim) void pim_msdp_pkt_sa_tx(struct pim_instance *pim) { - pim_msdp_pkt_sa_gen(pim, NULL /* mp */); + struct pim_msdp_peer *mp; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(pim->msdp.peer_list, node, mp)) + pim_msdp_pkt_sa_gen(pim, mp); + pim_msdp_pkt_sa_tx_done(pim); } void pim_msdp_pkt_sa_tx_one(struct pim_msdp_sa *sa) { + struct pim_msdp_peer *mp; + struct listnode *node; + pim_msdp_pkt_sa_fill_hdr(sa->pim, 1 /* cnt */, sa->rp); pim_msdp_pkt_sa_fill_one(sa); - pim_msdp_pkt_sa_push(sa->pim, NULL); + for (ALL_LIST_ELEMENTS_RO(sa->pim->msdp.peer_list, node, mp)) { + if (msdp_peer_sa_filter(mp, sa)) { + if (PIM_DEBUG_MSDP_EVENTS) + zlog_debug("MSDP peer %pI4 filter SA out %s", + &mp->peer, sa->sg_str); + continue; + } + + pim_msdp_pkt_sa_push(sa->pim, mp); + } pim_msdp_pkt_sa_tx_done(sa->pim); } @@ -487,6 +580,15 @@ void pim_msdp_pkt_sa_tx_one_to_one_peer(struct pim_msdp_peer *mp, /* Fills the message contents. */ sa.pim = mp->pim; sa.sg = sg; + + /* Don't push it if filtered. */ + if (msdp_peer_sa_filter(mp, &sa)) { + if (PIM_DEBUG_MSDP_EVENTS) + zlog_debug("MSDP peer %pI4 filter SA out (%pI4, %pI4)", + &mp->peer, &sa.sg.src, &sa.sg.grp); + return; + } + pim_msdp_pkt_sa_fill_one(&sa); /* Pushes the message. */ @@ -511,6 +613,7 @@ static void pim_msdp_pkt_ka_rx(struct pim_msdp_peer *mp, int len) static void pim_msdp_pkt_sa_rx_one(struct pim_msdp_peer *mp, struct in_addr rp) { + struct access_list *acl; int prefix_len; pim_sgaddr sg; struct listnode *peer_node; @@ -534,6 +637,19 @@ static void pim_msdp_pkt_sa_rx_one(struct pim_msdp_peer *mp, struct in_addr rp) if (PIM_DEBUG_MSDP_PACKETS) { zlog_debug(" sg %pSG", &sg); } + + /* Filter incoming SA with configured access list. */ + if (mp->acl_in) { + acl = access_list_lookup(AFI_IP, mp->acl_in); + if (msdp_access_list_apply(acl, &sg.src, &sg.grp) == + FILTER_DENY) { + if (PIM_DEBUG_MSDP_EVENTS) + zlog_debug("MSDP peer %pI4 filter SA in (%pI4, %pI4)", + &mp->peer, &sg.src, &sg.grp); + return; + } + } + pim_msdp_sa_ref(mp->pim, mp, &sg, rp); /* Forwards the SA to the peers that are not in the RPF to the RP nor in diff --git a/pimd/pim_msdp_packet.h b/pimd/pim_msdp_packet.h index 1584a2453927..3af8d936852d 100644 --- a/pimd/pim_msdp_packet.h +++ b/pimd/pim_msdp_packet.h @@ -57,5 +57,7 @@ void pim_msdp_pkt_sa_tx_one(struct pim_msdp_sa *sa); void pim_msdp_pkt_sa_tx_to_one_peer(struct pim_msdp_peer *mp); void pim_msdp_pkt_sa_tx_one_to_one_peer(struct pim_msdp_peer *mp, struct in_addr rp, pim_sgaddr sg); +bool msdp_peer_sa_filter(const struct pim_msdp_peer *mp, + const struct pim_msdp_sa *sa); #endif diff --git a/pimd/pim_nb.c b/pimd/pim_nb.c index 339935f81a57..6f4e32522093 100644 --- a/pimd/pim_nb.c +++ b/pimd/pim_nb.c @@ -163,6 +163,20 @@ const struct frr_yang_module_info frr_pim_info = { .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_peer_source_ip_modify, } }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-peer/sa-filter-in", + .cbs = { + .modify = pim_msdp_peer_sa_filter_in_modify, + .destroy = pim_msdp_peer_sa_filter_in_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-peer/sa-filter-out", + .cbs = { + .modify = pim_msdp_peer_sa_filter_out_modify, + .destroy = pim_msdp_peer_sa_filter_out_destroy, + } + }, { .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/mlag", .cbs = { diff --git a/pimd/pim_nb.h b/pimd/pim_nb.h index 2d854d73de5e..56153bafbac0 100644 --- a/pimd/pim_nb.h +++ b/pimd/pim_nb.h @@ -65,6 +65,10 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ms struct nb_cb_destroy_args *args); int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_peer_source_ip_modify( struct nb_cb_modify_args *args); +int pim_msdp_peer_sa_filter_in_modify(struct nb_cb_modify_args *args); +int pim_msdp_peer_sa_filter_in_destroy(struct nb_cb_destroy_args *args); +int pim_msdp_peer_sa_filter_out_modify(struct nb_cb_modify_args *args); +int pim_msdp_peer_sa_filter_out_destroy(struct nb_cb_destroy_args *args); int routing_control_plane_protocols_control_plane_protocol_pim_address_family_mlag_create( struct nb_cb_create_args *args); int routing_control_plane_protocols_control_plane_protocol_pim_address_family_mlag_destroy( diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index be0be8588b30..49bd9a5ce71a 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -1286,6 +1286,94 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ms } #endif /* PIM_IPV != 6 */ +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-peer/sa-filter-in + */ +int pim_msdp_peer_sa_filter_in_modify(struct nb_cb_modify_args *args) +{ + struct pim_msdp_peer *mp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + mp = nb_running_get_entry(args->dnode, NULL, true); + XFREE(MTYPE_TMP, mp->acl_in); + mp->acl_in = XSTRDUP(MTYPE_TMP, + yang_dnode_get_string(args->dnode, NULL)); + break; + } + + return NB_OK; +} + +int pim_msdp_peer_sa_filter_in_destroy(struct nb_cb_destroy_args *args) +{ + struct pim_msdp_peer *mp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + mp = nb_running_get_entry(args->dnode, NULL, true); + XFREE(MTYPE_TMP, mp->acl_in); + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-peer/sa-filter-out + */ +int pim_msdp_peer_sa_filter_out_modify(struct nb_cb_modify_args *args) +{ + struct pim_msdp_peer *mp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + mp = nb_running_get_entry(args->dnode, NULL, true); + XFREE(MTYPE_TMP, mp->acl_out); + mp->acl_out = XSTRDUP(MTYPE_TMP, + yang_dnode_get_string(args->dnode, NULL)); + break; + } + + return NB_OK; +} + +int pim_msdp_peer_sa_filter_out_destroy(struct nb_cb_destroy_args *args) +{ + struct pim_msdp_peer *mp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + mp = nb_running_get_entry(args->dnode, NULL, true); + XFREE(MTYPE_TMP, mp->acl_out); + break; + } + + return NB_OK; +} + /* * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/mlag */ diff --git a/tests/topotests/msdp_topo1/r4/pimd.conf b/tests/topotests/msdp_topo1/r4/pimd.conf index 28085913fb9c..46de4fbe384a 100644 --- a/tests/topotests/msdp_topo1/r4/pimd.conf +++ b/tests/topotests/msdp_topo1/r4/pimd.conf @@ -20,3 +20,16 @@ ip msdp peer 192.168.2.1 source 192.168.2.2 ip msdp peer 192.168.3.1 source 192.168.3.2 ip pim rp 10.254.254.4 ip pim join-prune-interval 5 +! +access-list forbidden-multicast seq 5 deny 229.2.1.0 0.0.0.255 +access-list forbidden-multicast seq 1000 permit any +access-list local-only-multicast seq 5 deny 229.3.1.0 0.0.0.255 +access-list local-only-multicast seq 6 deny ip 192.168.4.100 0.0.0.0 229.10.1.0 0.0.0.255 +access-list local-only-multicast seq 1000 permit any +! +router pim + msdp peer 192.168.2.1 sa-filter forbidden-multicast in + msdp peer 192.168.2.1 sa-filter local-only-multicast out + msdp peer 192.168.3.1 sa-filter forbidden-multicast in + msdp peer 192.168.3.1 sa-filter local-only-multicast out +! diff --git a/tests/topotests/msdp_topo1/test_msdp_topo1.py b/tests/topotests/msdp_topo1/test_msdp_topo1.py index 4b54ef29ffc4..ff80052d2665 100755 --- a/tests/topotests/msdp_topo1/test_msdp_topo1.py +++ b/tests/topotests/msdp_topo1/test_msdp_topo1.py @@ -66,7 +66,9 @@ def build_topo(tgen): # Create a host connected and direct at r4: tgen.add_host("h1", "192.168.4.100/24", "via 192.168.4.1") + tgen.add_host("h3", "192.168.4.120/24", "via 192.168.4.1") switch.add_link(tgen.gears["h1"]) + switch.add_link(tgen.gears["h3"]) # Create a host connected and direct at r1: switch = tgen.add_switch("s6") @@ -82,7 +84,6 @@ def setup_module(mod): router_list = tgen.routers() for rname, router in router_list.items(): - daemon_file = "{}/{}/zebra.conf".format(CWD, rname) if os.path.isfile(daemon_file): router.load_config(TopoRouter.RD_ZEBRA, daemon_file) @@ -428,6 +429,87 @@ def test_msdp(): assert val is None, "multicast route convergence failure" +def test_msdp_sa_filter(): + "Start a number of multicast streams and check if filtering works" + + tgen = get_topogen() + + # Flow from r1 -> r4 + for multicast_address in ["229.2.1.1", "229.2.1.2", "229.2.2.1"]: + app_helper.run("h1", [multicast_address, "h1-eth0"]) + app_helper.run("h2", ["--send=0.7", multicast_address, "h2-eth0"]) + + # Flow from r4 -> r1 + for multicast_address in ["229.3.1.1", "229.3.1.2", "229.3.2.1"]: + app_helper.run("h1", ["--send=0.7", multicast_address, "h1-eth0"]) + app_helper.run("h2", [multicast_address, "h2-eth0"]) + + # Flow from r4 -> r1 but with more sources + for multicast_address in ["229.10.1.1", "229.11.1.1"]: + app_helper.run("h1", ["--send=0.7", multicast_address, "h1-eth0"]) + app_helper.run("h2", [multicast_address, "h2-eth0"]) + app_helper.run("h3", ["--send=0.7", multicast_address, "h3-eth0"]) + + # Test that we don't learn any filtered multicast streams. + r4_sa_expected = { + "229.2.1.1": None, + "229.2.1.2": None, + "229.2.2.1": { + "192.168.10.100": { + "local": "no", + "sptSetup": "yes", + } + }, + } + test_func = partial( + topotest.router_json_cmp, + tgen.gears["r4"], + "show ip msdp sa json", + r4_sa_expected, + ) + logger.info("Waiting for r4 MDSP SA data") + _, val = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert val is None, "multicast route convergence failure" + + # Test that we don't send any filtered multicast streams. + r1_sa_expected = { + "229.3.1.1": None, + "229.3.1.2": None, + "229.3.2.1": { + "192.168.4.100": { + "local": "no", + "sptSetup": "yes", + } + }, + "229.10.1.1": { + "192.168.4.100": None, + "192.168.4.120": { + "local": "no", + "sptSetup": "yes", + }, + }, + "229.11.1.1": { + "192.168.4.100": { + "local": "no", + "sptSetup": "yes", + }, + "192.168.4.120": { + "local": "no", + "sptSetup": "yes", + }, + }, + } + test_func = partial( + topotest.router_json_cmp, + tgen.gears["r1"], + "show ip msdp sa json", + r1_sa_expected, + ) + logger.info("Waiting for r1 MDSP SA data") + _, val = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert val is None, "multicast route convergence failure" + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() diff --git a/yang/frr-pim.yang b/yang/frr-pim.yang index 732a38a9e3ae..cc8d6445aa6b 100644 --- a/yang/frr-pim.yang +++ b/yang/frr-pim.yang @@ -5,6 +5,10 @@ module frr-pim { prefix frr-pim; + import frr-filter { + prefix frr-filter; + } + import frr-interface { prefix frr-interface; } @@ -267,6 +271,18 @@ module frr-pim { description "MSDP source IP address."; } + + leaf sa-filter-in { + type frr-filter:access-list-name; + description + "Access list name used to filter the incoming SAs exchanged."; + } + + leaf sa-filter-out { + type frr-filter:access-list-name; + description + "Access list name used to filter the outgoing SAs exchanged."; + } } container mlag {