]> git.puffer.fish Git - matthieu/frr.git/commitdiff
pimd: MSDP SA filtering
authorRafael Zalamena <rzalamena@opensourcerouting.org>
Mon, 3 May 2021 13:25:52 +0000 (10:25 -0300)
committerRafael Zalamena <rzalamena@opensourcerouting.org>
Sat, 27 Jul 2024 14:32:30 +0000 (11:32 -0300)
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
  router pim
   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
  router pim
   msdp peer 192.168.0.1 sa-filter filter-management-group in

Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
pimd/pim_cmd.c
pimd/pim_msdp.c
pimd/pim_msdp.h
pimd/pim_msdp_packet.c
pimd/pim_msdp_packet.h
pimd/pim_nb.c
pimd/pim_nb.h
pimd/pim_nb_config.c

index 92214eced4ae614655c618fbdfda223482cc00d5..1e3e090868b0e6a20a0cf0ed614789d28a137c0b 100644 (file)
@@ -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 <in|out>$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 <in|out>$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);
index 3393cebdd2df34bd1dfab541c9f1ba1e92f0a470..0bb2d93a3a0b35dbaf8fba01eab1defe1087b2c5 100644 (file)
@@ -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;
        }
 
index 80ca003dc5955856c0b907d8fb15d4b636f3e426..a45726cb85a68f29ede9e12e932b4f8ef7789fde 100644 (file)
@@ -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 {
index 4324a96bef8962cb0fa818b9f3609f2a276e7985..27f4966a1cc35439609d474f6bd8b00c910a41ea 100644 (file)
@@ -6,7 +6,9 @@
 #include <zebra.h>
 
 #include <lib/log.h>
+#include <lib/filter.h>
 #include <lib/network.h>
+#include <lib/prefix.h>
 #include <lib/stream.h>
 #include "frrevent.h"
 #include <lib/vty.h>
@@ -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
index 1584a2453927b00cbb107944ce63169b1a2984bd..3af8d936852dcfa90d3facc4a1486fde31dc1dee 100644 (file)
@@ -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
index 339935f81a57b4c757743aa9a3f9cf1c63669ed1..6f4e32522093f52efc1970569106d03c7d48777d 100644 (file)
@@ -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 = {
index 2d854d73de5ec493de7cb0987a6267c48bac5cd8..56153bafbac0271ce2628d01edbe4bf564138317 100644 (file)
@@ -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(
index be0be8588b3094ff43e255b2b11259b6bc2faf9e..49bd9a5ce71a3b566c8d97ac5c4958c73a87953e 100644 (file)
@@ -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
  */