From bacdf7dcc22c71204c55e687ceaf03dfe4d947df Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 19 Aug 2021 16:46:30 +0200 Subject: [PATCH 1/3] pimd: allow restricting neighbors per interface Just filter incoming packets against a specified prefix-list. Signed-off-by: David Lamparter --- pimd/pim6_cmd.c | 30 +++++++++++++++++++++++++++ pimd/pim_cmd.c | 30 +++++++++++++++++++++++++++ pimd/pim_iface.c | 1 + pimd/pim_iface.h | 1 + pimd/pim_nb.c | 7 +++++++ pimd/pim_nb.h | 2 ++ pimd/pim_nb_config.c | 49 ++++++++++++++++++++++++++++++++++++++++++++ pimd/pim_pim.c | 38 ++++++++++++++++++++++++++++++++++ pimd/pim_vty.c | 6 ++++++ yang/frr-pim.yang | 6 ++++++ 10 files changed, 170 insertions(+) diff --git a/pimd/pim6_cmd.c b/pimd/pim6_cmd.c index 40bd7caf7d68..9847fa4cfdc6 100644 --- a/pimd/pim6_cmd.c +++ b/pimd/pim6_cmd.c @@ -1790,6 +1790,34 @@ DEFPY (interface_no_ipv6_mld_last_member_query_interval, return gm_process_no_last_member_query_interval_cmd(vty); } +DEFPY_YANG(interface_ipv6_pim_neighbor_prefix_list, + interface_ipv6_pim_neighbor_prefix_list_cmd, + "[no] ipv6 pim allowed-neighbors prefix-list PREFIXLIST6_NAME$prefix_list", + NO_STR + IP_STR + PIM_STR + "Restrict allowed PIM neighbors\n" + "Use prefix-list to filter neighbors\n" + "Name of a prefix-list\n") +{ + if (no) + nb_cli_enqueue_change(vty, "./neighbor-filter-prefix-list", NB_OP_DESTROY, NULL); + else + nb_cli_enqueue_change(vty, "./neighbor-filter-prefix-list", NB_OP_MODIFY, + prefix_list); + + return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH, FRR_PIM_AF_XPATH_VAL); +} + +ALIAS(interface_ipv6_pim_neighbor_prefix_list, + interface_no_ipv6_pim_neighbor_prefix_list_cmd, + "no ipv6 pim allowed-neighbors [prefix-list]", + NO_STR + IP_STR + PIM_STR + "Restrict allowed PIM neighbors\n" + "Use prefix-list to filter neighbors\n") + DEFPY (show_ipv6_pim_rp, show_ipv6_pim_rp_cmd, "show ipv6 pim [vrf NAME] rp-info [X:X::X:X/M$group] [json$json]", @@ -2959,6 +2987,8 @@ void pim_cmd_init(void) &interface_ipv6_mld_last_member_query_interval_cmd); install_element(INTERFACE_NODE, &interface_no_ipv6_mld_last_member_query_interval_cmd); + install_element(INTERFACE_NODE, &interface_ipv6_pim_neighbor_prefix_list_cmd); + install_element(INTERFACE_NODE, &interface_no_ipv6_pim_neighbor_prefix_list_cmd); install_element(VIEW_NODE, &show_ipv6_pim_rp_cmd); install_element(VIEW_NODE, &show_ipv6_pim_rp_vrf_all_cmd); diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index fa9c6f953793..783d91739014 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -6022,6 +6022,34 @@ DEFPY (interface_ip_igmp_proxy, } +DEFPY_YANG(interface_ip_pim_neighbor_prefix_list, + interface_ip_pim_neighbor_prefix_list_cmd, + "[no] ip pim allowed-neighbors prefix-list WORD", + NO_STR + IP_STR + "pim multicast routing\n" + "Restrict allowed PIM neighbors\n" + "Use prefix-list to filter neighbors\n" + "Name of a prefix-list\n") +{ + if (no) + nb_cli_enqueue_change(vty, "./neighbor-filter-prefix-list", NB_OP_DESTROY, NULL); + else + nb_cli_enqueue_change(vty, "./neighbor-filter-prefix-list", NB_OP_MODIFY, + prefix_list); + + return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH, FRR_PIM_AF_XPATH_VAL); +} + +ALIAS (interface_ip_pim_neighbor_prefix_list, + interface_no_ip_pim_neighbor_prefix_list_cmd, + "no ip pim allowed-neighbors [prefix-list]", + NO_STR + IP_STR + "pim multicast routing\n" + "Restrict allowed PIM neighbors\n" + "Use prefix-list to filter neighbors\n") + DEFUN (debug_igmp, debug_igmp_cmd, "debug igmp", @@ -9155,6 +9183,8 @@ void pim_cmd_init(void) install_element(INTERFACE_NODE, &interface_no_ip_pim_boundary_oil_cmd); install_element(INTERFACE_NODE, &interface_ip_pim_boundary_acl_cmd); install_element(INTERFACE_NODE, &interface_ip_igmp_query_generate_cmd); + install_element(INTERFACE_NODE, &interface_ip_pim_neighbor_prefix_list_cmd); + install_element(INTERFACE_NODE, &interface_no_ip_pim_neighbor_prefix_list_cmd); // Static mroutes NEB install_element(INTERFACE_NODE, &interface_ip_mroute_cmd); diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 8ec51ddc39f9..3408574cae5b 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -218,6 +218,7 @@ void pim_if_delete(struct interface *ifp) if (pim_ifp->bfd_config.profile) XFREE(MTYPE_TMP, pim_ifp->bfd_config.profile); + XFREE(MTYPE_PIM_PLIST_NAME, pim_ifp->nbr_plist); XFREE(MTYPE_PIM_INTERFACE, pim_ifp); ifp->info = NULL; diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index 0a7993fd27b6..fef7d4ea7b78 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -118,6 +118,7 @@ struct pim_interface { uint32_t pim_generation_id; uint16_t pim_propagation_delay_msec; /* config */ uint16_t pim_override_interval_msec; /* config */ + char *nbr_plist; struct list *pim_neighbor_list; /* list of struct pim_neighbor */ struct list *upstream_switch_list; struct pim_ifchannel_rb ifchannel_rb; diff --git a/pimd/pim_nb.c b/pimd/pim_nb.c index 62c5d531d912..db702391de75 100644 --- a/pimd/pim_nb.c +++ b/pimd/pim_nb.c @@ -314,6 +314,13 @@ const struct frr_yang_module_info frr_pim_info = { .destroy = lib_interface_pim_address_family_hello_holdtime_destroy, } }, + { + .xpath = "/frr-interface:lib/interface/frr-pim:pim/address-family/neighbor-filter-prefix-list", + .cbs = { + .modify = lib_interface_pim_address_family_nbr_plist_modify, + .destroy = lib_interface_pim_address_family_nbr_plist_destroy, + } + }, { .xpath = "/frr-interface:lib/interface/frr-pim:pim/address-family/bfd", .cbs = { diff --git a/pimd/pim_nb.h b/pimd/pim_nb.h index 1656313fc240..dfb658360655 100644 --- a/pimd/pim_nb.h +++ b/pimd/pim_nb.h @@ -110,6 +110,8 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_mc struct nb_cb_modify_args *args); int lib_interface_pim_address_family_dr_priority_modify( struct nb_cb_modify_args *args); +int lib_interface_pim_address_family_nbr_plist_modify(struct nb_cb_modify_args *args); +int lib_interface_pim_address_family_nbr_plist_destroy(struct nb_cb_destroy_args *args); int lib_interface_pim_address_family_create(struct nb_cb_create_args *args); int lib_interface_pim_address_family_destroy(struct nb_cb_destroy_args *args); int lib_interface_pim_address_family_pim_enable_modify( diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index c9266966100d..aa80420d0178 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -2163,6 +2163,55 @@ int lib_interface_pim_address_family_hello_holdtime_destroy( return NB_OK; } + +/* + * XPath: /frr-interface:lib/interface/frr-pim:pim/address-family/neighbor-filter-prefix-list + */ +int lib_interface_pim_address_family_nbr_plist_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + const char *plist; + + plist = yang_dnode_get_string(args->dnode, NULL); + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_ABORT: + case NB_EV_PREPARE: + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + pim_ifp = ifp->info; + + XFREE(MTYPE_PIM_PLIST_NAME, pim_ifp->nbr_plist); + pim_ifp->nbr_plist = XSTRDUP(MTYPE_PIM_PLIST_NAME, plist); + break; + } + + return NB_OK; +} + +int lib_interface_pim_address_family_nbr_plist_destroy(struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_ABORT: + case NB_EV_PREPARE: + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + pim_ifp = ifp->info; + XFREE(MTYPE_PIM_PLIST_NAME, pim_ifp->nbr_plist); + break; + } + + return NB_OK; +} + /* * XPath: /frr-interface:lib/interface/frr-pim:pim/address-family/bfd */ diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index a41bbacea799..fb78e390229b 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -149,6 +149,9 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len, uint32_t pim_msg_len = 0; uint16_t pim_checksum; /* received checksum */ uint16_t checksum; /* computed checksum */ + struct pim_interface *pim_ifp = ifp->info; + struct prefix src_prefix; + struct prefix_list *nbr_plist = NULL; struct pim_neighbor *neigh; struct pim_msg_header *header; bool no_fwd; @@ -205,6 +208,41 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len, return -1; } + switch (header->type) { + case PIM_MSG_TYPE_HELLO: + case PIM_MSG_TYPE_JOIN_PRUNE: + case PIM_MSG_TYPE_ASSERT: + if (pim_ifp == NULL || pim_ifp->nbr_plist == NULL) + break; + + nbr_plist = prefix_list_lookup(PIM_AFI, pim_ifp->nbr_plist); + +#if PIM_IPV == 4 + src_prefix.family = AF_INET; + src_prefix.prefixlen = IPV4_MAX_BITLEN; + src_prefix.u.prefix4 = sg.src; +#else + src_prefix.family = AF_INET6; + src_prefix.prefixlen = IPV6_MAX_BITLEN; + src_prefix.u.prefix6 = sg.src; +#endif + + if (nbr_plist && + prefix_list_apply_ext(nbr_plist, NULL, &src_prefix, true) == PREFIX_PERMIT) + break; + +#if PIM_IPV == 4 + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug("neighbor filter rejects packet %pI4 -> %pI4 on %s", + &ip_hdr->ip_src, &ip_hdr->ip_dst, ifp->name); +#else + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug("neighbor filter rejects packet %pI6 -> %pI6 on %s", &sg.src, + &sg.grp, ifp->name); +#endif + return -1; + } + /* save received checksum */ pim_checksum = header->checksum; diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index 64750a22f6d8..c0b00292f1a3 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -471,6 +471,12 @@ int pim_config_write(struct vty *vty, int writes, struct interface *ifp, ++writes; } + if (pim_ifp->nbr_plist) { + vty_out(vty, " " PIM_AF_NAME " pim allowed-neighbors prefix-list %s\n", + pim_ifp->nbr_plist); + ++writes; + } + /* IF ip pim drpriority */ if (pim_ifp->pim_dr_priority != PIM_DEFAULT_DR_PRIORITY) { vty_out(vty, " " PIM_AF_NAME " pim drpriority %u\n", diff --git a/yang/frr-pim.yang b/yang/frr-pim.yang index 6b6870f666a3..e0d8800f3ecf 100644 --- a/yang/frr-pim.yang +++ b/yang/frr-pim.yang @@ -516,6 +516,12 @@ module frr-pim { "Hello holdtime"; } + leaf neighbor-filter-prefix-list { + type plist-ref; + description + "Prefix-List to filter allowed PIM neighbors."; + } + container bfd { presence "Enable BFD support on the interface."; From 68b1051adc3edccad58f3802b6e638fe92bbfde8 Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Wed, 22 Jan 2025 11:07:15 -0300 Subject: [PATCH 2/3] topotests: test PIM neighbor filtering Add new topology for testing neighbor filtering and more features in the future. Signed-off-by: Rafael Zalamena --- .../topotests/multicast_features/r1/frr.conf | 7 ++++- .../test_multicast_features.py | 29 +++++++++++++++---- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/tests/topotests/multicast_features/r1/frr.conf b/tests/topotests/multicast_features/r1/frr.conf index bd1cc4103ce4..dcf73c08794c 100644 --- a/tests/topotests/multicast_features/r1/frr.conf +++ b/tests/topotests/multicast_features/r1/frr.conf @@ -1,5 +1,8 @@ log commands ! +!ip prefix-list pim-eth0-neighbors permit 192.168.2.0/24 +!ipv6 prefix-list pimv6-eth0-neighbors permit 2001:db8:2::/64 +! interface r1-eth0 ip address 192.168.1.1/24 ip pim @@ -9,8 +12,10 @@ interface r1-eth0 interface r1-eth1 ip address 192.168.2.1/24 ip pim + ip pim allowed-neighbors prefix-list pim-eth0-neighbors ipv6 address 2001:db8:2::1/64 ipv6 pim + ipv6 pim allowed-neighbors prefix-list pimv6-eth0-neighbors ! interface r1-eth2 ip address 192.168.100.1/24 @@ -45,4 +50,4 @@ router pim ! router pim6 rp 2001:db8:ffff::1 -! \ No newline at end of file +! diff --git a/tests/topotests/multicast_features/test_multicast_features.py b/tests/topotests/multicast_features/test_multicast_features.py index 9c1f4af99fba..cbc653d970d6 100644 --- a/tests/topotests/multicast_features/test_multicast_features.py +++ b/tests/topotests/multicast_features/test_multicast_features.py @@ -147,10 +147,14 @@ def test_pim_convergence(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - def expect_pim_peer(router, iptype, interface, peer): + def expect_pim_peer(router, iptype, interface, peer, missing=False): "Wait until peer is present." - logger.info(f"waiting peer {peer} in {router}") - expected = {interface: {peer: {"upTime": "*"}}} + if missing: + logger.info(f"waiting peer {peer} in {router} to disappear") + expected = {interface: {peer: None}} + else: + logger.info(f"waiting peer {peer} in {router}") + expected = {interface: {peer: {"upTime": "*"}}} test_func = partial( topotest.router_json_cmp, @@ -164,7 +168,15 @@ def expect_pim_peer(router, iptype, interface, peer): expect_pim_peer("r1", "ip", "r1-eth0", "192.168.1.2") expect_pim_peer("r2", "ip", "r2-eth0", "192.168.1.1") - expect_pim_peer("r1", "ip", "r1-eth1", "192.168.2.2") + + # This neighbor is denied by default + expect_pim_peer("r1", "ip", "r1-eth1", "192.168.2.2", missing=True) + # Lets configure the prefix list so the above neighbor gets accepted: + tgen.gears["r1"].vtysh_cmd(""" + configure terminal + ip prefix-list pim-eth0-neighbors permit 192.168.2.0/24 + """) + expect_pim_peer("r1", "ip", "r1-eth1", "192.168.2.2", missing=False) # # IPv6 part @@ -180,7 +192,14 @@ def expect_pim_peer(router, iptype, interface, peer): expect_pim_peer("r1", "ipv6", "r1-eth0", r2_link_address) expect_pim_peer("r2", "ipv6", "r2-eth0", r1_r2_link_address) - expect_pim_peer("r1", "ipv6", "r1-eth1", r3_link_address) + expect_pim_peer("r1", "ipv6", "r1-eth1", r3_link_address, missing=True) + + tgen.gears["r1"].vtysh_cmd(f""" + configure terminal + ipv6 prefix-list pimv6-eth0-neighbors permit {r3_link_address}/64 + """) + + expect_pim_peer("r1", "ipv6", "r1-eth1", r3_link_address, missing=False) def test_igmp_group_limit(): From 0b173fffacfa518506bdf130eced3ce6d5beab2f Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Thu, 23 Jan 2025 13:00:39 -0300 Subject: [PATCH 3/3] doc: add allowed-neighbors command Let users know about new command to filter PIM sessions based on peer address. Signed-off-by: Rafael Zalamena --- doc/user/pim.rst | 4 ++++ doc/user/pimv6.rst | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/doc/user/pim.rst b/doc/user/pim.rst index 9e4c7bb94a66..68b5e3eb988b 100644 --- a/doc/user/pim.rst +++ b/doc/user/pim.rst @@ -391,6 +391,10 @@ is in a vrf, enter the interface command with the vrf keyword at the end. reports on the interface. Refer to the next `ip igmp` command for IGMP management. +.. clicmd:: ip pim allowed-neighbors prefix-list PREFIX_LIST + + Only establish sessions with PIM neighbors allowed by the prefix-list. + .. clicmd:: ip pim use-source A.B.C.D If you have multiple addresses configured on a particular interface diff --git a/doc/user/pimv6.rst b/doc/user/pimv6.rst index bd5430f51e99..9358cb13493f 100644 --- a/doc/user/pimv6.rst +++ b/doc/user/pimv6.rst @@ -214,6 +214,10 @@ is in a vrf, enter the interface command with the vrf keyword at the end. reports on the interface. Refer to the next ``ipv6 mld`` command for MLD management. +.. clicmd:: ipv6 pim allowed-neighbors prefix-list PREFIX_LIST + + Only establish sessions with PIM neighbors allowed by the prefix-list. + .. clicmd:: ipv6 pim use-source X:X::X:X If you have multiple addresses configured on a particular interface