diff --git a/doc/user/pim.rst b/doc/user/pim.rst index ef49b076df..e8dd03e7c3 100644 --- a/doc/user/pim.rst +++ b/doc/user/pim.rst @@ -503,6 +503,10 @@ Commands available for MSDP The filtering will only take effect starting from the command application. +.. clicmd:: msdp peer A.B.C.D sa-limit + + Configure the maximum number of SAs to learn from peer. + .. clicmd:: msdp peer A.B.C.D password WORD Use MD5 authentication to connect with the remote peer. diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index bac9645759..3fabe1706c 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -7593,6 +7593,29 @@ DEFPY(msdp_shutdown, return nb_cli_apply_changes(vty, NULL); } +DEFPY(msdp_peer_sa_limit, msdp_peer_sa_limit_cmd, + "[no] msdp peer A.B.C.D$peer sa-limit ![(1-4294967294)$sa_limit]", + NO_STR + CFG_MSDP_STR + "Configure MSDP peer\n" + "MSDP peer address\n" + "Limit amount of SA\n" + "Maximum number of SA\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; + } + + nb_cli_enqueue_change(vty, "./sa-limit", NB_OP_MODIFY, sa_limit_str); + return nb_cli_apply_changes(vty, "%s", xpath); +} + static void ip_msdp_show_mesh_group(struct vty *vty, struct pim_msdp_mg *mg, struct json_object *json) { @@ -8988,6 +9011,7 @@ void pim_cmd_init(void) install_element(PIM_NODE, &msdp_log_neighbor_changes_cmd); install_element(PIM_NODE, &msdp_log_sa_changes_cmd); install_element(PIM_NODE, &msdp_shutdown_cmd); + install_element(PIM_NODE, &msdp_peer_sa_limit_cmd); install_element(PIM_NODE, &pim_bsr_candidate_rp_cmd); install_element(PIM_NODE, &pim_bsr_candidate_rp_group_cmd); diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c index ae887b2482..bd86ca502d 100644 --- a/pimd/pim_msdp.c +++ b/pimd/pim_msdp.c @@ -359,6 +359,15 @@ void pim_msdp_sa_ref(struct pim_instance *pim, struct pim_msdp_peer *mp, struct rp_info *rp_info; struct prefix grp; + /* Check peer SA limit. */ + if (mp && mp->sa_limit && mp->sa_cnt >= mp->sa_limit) { + if (pim_msdp_log_sa_events(pim)) + zlog_debug("MSDP peer %pI4 reject SA (%pI4, %pI4): SA limit %u of %u", + &mp->peer, &sg->src, &sg->grp, mp->sa_cnt, mp->sa_limit); + + return; + } + sa = pim_msdp_sa_add(pim, sg, rp); if (!sa) { return; @@ -1316,6 +1325,9 @@ bool pim_msdp_peer_config_write(struct vty *vty, struct pim_instance *pim) vty_out(vty, " msdp peer %pI4 sa-filter %s out\n", &mp->peer, mp->acl_out); + if (mp->sa_limit) + vty_out(vty, " msdp peer %pI4 sa-limit %u\n", &mp->peer, mp->sa_limit); + written = true; } diff --git a/pimd/pim_msdp.h b/pimd/pim_msdp.h index d0aa83d997..15ed685b3c 100644 --- a/pimd/pim_msdp.h +++ b/pimd/pim_msdp.h @@ -152,6 +152,9 @@ struct pim_msdp_peer { char *acl_in; /** SA output access list name. */ char *acl_out; + + /** SA maximum amount. */ + uint32_t sa_limit; }; struct pim_msdp_mg_mbr { diff --git a/pimd/pim_nb.c b/pimd/pim_nb.c index 2b39f2dcb8..b5d20419dd 100644 --- a/pimd/pim_nb.c +++ b/pimd/pim_nb.c @@ -208,6 +208,13 @@ const struct frr_yang_module_info frr_pim_info = { .destroy = pim_msdp_peer_authentication_key_destroy, } }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-peer/sa-limit", + .cbs = { + .modify = pim_msdp_peer_sa_limit_modify, + .destroy = pim_msdp_peer_sa_limit_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 f27b86680f..7d30db04be 100644 --- a/pimd/pim_nb.h +++ b/pimd/pim_nb.h @@ -76,6 +76,8 @@ int pim_msdp_peer_sa_filter_out_destroy(struct nb_cb_destroy_args *args); int pim_msdp_peer_authentication_type_modify(struct nb_cb_modify_args *args); int pim_msdp_peer_authentication_key_modify(struct nb_cb_modify_args *args); int pim_msdp_peer_authentication_key_destroy(struct nb_cb_destroy_args *args); +int pim_msdp_peer_sa_limit_modify(struct nb_cb_modify_args *args); +int pim_msdp_peer_sa_limit_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 41457ffba2..4f79a890df 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -1579,6 +1579,48 @@ int pim_msdp_peer_sa_filter_out_destroy(struct nb_cb_destroy_args *args) return NB_OK; } +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-peer/sa-limit + */ +int pim_msdp_peer_sa_limit_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); + mp->sa_limit = yang_dnode_get_uint32(args->dnode, NULL); + break; + } + + return NB_OK; +} + +int pim_msdp_peer_sa_limit_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); + mp->sa_limit = 0; + 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/test_msdp_topo1.py b/tests/topotests/msdp_topo1/test_msdp_topo1.py index 8c25eeca06..5143ef67a5 100755 --- a/tests/topotests/msdp_topo1/test_msdp_topo1.py +++ b/tests/topotests/msdp_topo1/test_msdp_topo1.py @@ -511,6 +511,42 @@ def test_msdp_sa_filter(): assert val is None, "multicast route convergence failure" +def test_msdp_sa_limit(): + "Test MSDP SA limiting." + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["r4"].vtysh_cmd( + """ + configure terminal + router pim + msdp log sa-events + msdp peer 192.168.2.1 sa-limit 4 + msdp peer 192.168.3.1 sa-limit 4 + """ + ) + + # Flow from r1 -> r4 + for multicast_address in [ + "229.1.2.10", + "229.1.2.11", + "229.1.2.12", + "229.1.2.13", + "229.1.2.14", + ]: + app_helper.run("h1", [multicast_address, "h1-eth0"]) + app_helper.run("h2", ["--send=0.7", multicast_address, "h2-eth0"]) + + def test_sa_limit_log(): + r4_log = tgen.gears["r4"].net.getLog("log", "pimd") + return re.search(r"MSDP peer .+ reject SA (.+, .+): SA limit \d+ of 4", r4_log) + + _, val = topotest.run_and_expect(test_sa_limit_log, None, count=30, wait=1) + assert val is None, "SA limit check failed" + + def test_msdp_log_events(): "Test that the enabled logs are working as expected." diff --git a/yang/frr-pim.yang b/yang/frr-pim.yang index 473226653e..6e5fc3c6ce 100644 --- a/yang/frr-pim.yang +++ b/yang/frr-pim.yang @@ -341,6 +341,12 @@ module frr-pim { } uses msdp-authentication; + + leaf sa-limit { + type uint32; + description + "Peer SA maximum limit."; + } } container mlag {