From 0c015af44b60d41500135ce780dc9c2dfdbbd668 Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Mon, 3 May 2021 10:25:52 -0300 Subject: [PATCH] pimd: MSDP SA filtering Implement MSDP peer incoming/outgoing SA filter. Note ---- Cisco extended access list has a special meaning: the first address is the source address to filter. Example: ! The rules below filter some LAN prefix to be leaked out access-list filter-lan-source deny ip 192.168.0.0 0.0.255.255 224.0.0.0 0.255.255.255 access-list filter-lan-source permit any ip msdp peer 192.168.0.1 sa-filter filter-lan-source out ! The rules below filter some special management group from being ! learned access-list filter-management-group deny 230.0.0.0 0.255.255.255 access-list filter-management-group permit any ip msdp peer 192.168.0.1 sa-filter filter-management-group in Signed-off-by: Rafael Zalamena --- pimd/pim_cmd.c | 81 ++++++++++++++++++++ pimd/pim_msdp.c | 9 +++ pimd/pim_msdp.h | 5 ++ pimd/pim_msdp_packet.c | 166 +++++++++++++++++++++++++++++++++++------ pimd/pim_msdp_packet.h | 2 + pimd/pim_nb.c | 14 ++++ pimd/pim_nb.h | 4 + pimd/pim_nb_config.c | 88 ++++++++++++++++++++++ 8 files changed, 345 insertions(+), 24 deletions(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index a2d756a96a20..4cd95b3eb21c 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -5134,6 +5134,83 @@ DEFUN (no_ip_msdp_peer, return nb_cli_apply_changes(vty, NULL); } +DEFPY(ip_msdp_peer_sa_filter, ip_msdp_peer_sa_filter_cmd, + "ip msdp peer A.B.C.D$peer sa-filter ACL_NAME$acl_name $dir", + IP_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; + const char *vrfname; + char xpath[XPATH_MAXLEN]; + + vrfname = pim_cli_get_vrf_name(vty); + if (vrfname == NULL) + return CMD_WARNING_CONFIG_FAILED; + + snprintf(xpath, sizeof(xpath), + FRR_PIM_VRF_XPATH "/msdp-peer[peer-ip='%s']", "frr-pim:pimd", + "pim", vrfname, "frr-routing:ipv4", 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_ip_msdp_peer_sa_filter, no_ip_msdp_peer_sa_filter_cmd, + "no ip msdp peer A.B.C.D$peer sa-filter ACL_NAME $dir", + NO_STR + IP_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; + const char *vrfname; + char xpath[XPATH_MAXLEN]; + + vrfname = pim_cli_get_vrf_name(vty); + if (vrfname == NULL) + return CMD_WARNING_CONFIG_FAILED; + + snprintf(xpath, sizeof(xpath), + FRR_PIM_VRF_XPATH "/msdp-peer[peer-ip='%s']", "frr-pim:pimd", + "pim", vrfname, "frr-routing:ipv4", 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(ip_msdp_mesh_group_member, ip_msdp_mesh_group_member_cmd, "ip msdp mesh-group WORD$gname member A.B.C.D$maddr", @@ -6475,6 +6552,10 @@ void pim_cmd_init(void) install_element(VRF_NODE, &ip_msdp_peer_cmd); install_element(CONFIG_NODE, &no_ip_msdp_peer_cmd); install_element(VRF_NODE, &no_ip_msdp_peer_cmd); + install_element(CONFIG_NODE, &ip_msdp_peer_sa_filter_cmd); + install_element(VRF_NODE, &ip_msdp_peer_sa_filter_cmd); + install_element(CONFIG_NODE, &no_ip_msdp_peer_sa_filter_cmd); + install_element(VRF_NODE, &no_ip_msdp_peer_sa_filter_cmd); install_element(CONFIG_NODE, &ip_pim_ecmp_cmd); install_element(VRF_NODE, &ip_pim_ecmp_cmd); install_element(CONFIG_NODE, &no_ip_pim_ecmp_cmd); diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c index 623c14bb0391..127d9068d2b4 100644 --- a/pimd/pim_msdp.c +++ b/pimd/pim_msdp.c @@ -1321,6 +1321,15 @@ bool pim_msdp_peer_config_write(struct vty *vty, struct pim_instance *pim, vty_out(vty, "%sip msdp peer %pI4 source %pI4\n", spaces, &mp->peer, &mp->local); + + if (mp->acl_in) + vty_out(vty, "%sip msdp peer %pI4 sa-filter %s in\n", + spaces, &mp->peer, mp->acl_in); + + if (mp->acl_out) + vty_out(vty, "%sip msdp peer %pI4 sa-filter %s out\n", + spaces, &mp->peer, mp->acl_out); + written = true; } diff --git a/pimd/pim_msdp.h b/pimd/pim_msdp.h index ddc015f9b62b..c5dd1c286318 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..f71c3586a946 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,91 @@ 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 +461,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 +486,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 +534,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 +581,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 +614,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 +638,20 @@ 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 0321d076f0da..fc10da849431 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 4f1a4a18524e..388ca49db831 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 */