]> git.puffer.fish Git - mirror/frr.git/commitdiff
lib: disallow access list duplicated values
authorRafael Zalamena <rzalamena@opensourcerouting.org>
Fri, 4 Dec 2020 21:11:45 +0000 (18:11 -0300)
committerRafael Zalamena <rzalamena@opensourcerouting.org>
Thu, 10 Dec 2020 13:26:59 +0000 (10:26 -0300)
Don't allow users to create multiple rules in the same list with the
same value to keep the behavior previously to northbound migration.

Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
(cherry picked from commit f414129b0ce109550bd3a61f6020d7a9af6f9c35)

lib/filter.h
lib/filter_nb.c

index 623fb945276e2ea741d2c3e236741966b1c46a56..c6dc26927648f0b493f5343f3c652645c306756c 100644 (file)
@@ -176,6 +176,33 @@ enum yang_prefix_list_action {
        YPLA_PERMIT = 1,
 };
 
+struct acl_dup_args {
+       /** Access list type ("ipv4", "ipv6" or "mac"). */
+       const char *ada_type;
+       /** Access list name. */
+       const char *ada_name;
+
+#define ADA_MAX_VALUES 4
+       /** Entry XPath for value. */
+       const char *ada_xpath[ADA_MAX_VALUES];
+       /** Entry value to match. */
+       const char *ada_value[ADA_MAX_VALUES];
+
+       /** Duplicated entry found in list? */
+       bool ada_found;
+
+       /** (Optional) Already existing `dnode`. */
+       const struct lyd_node *ada_entry_dnode;
+};
+
+/**
+ * Check for duplicated entries using the candidate configuration.
+ *
+ * \param vty so we can get the candidate config.
+ * \param ada the arguments to check.
+ */
+bool acl_is_dup(const struct lyd_node *dnode, struct acl_dup_args *ada);
+
 /* filter_cli.c */
 struct lyd_node;
 struct vty;
index 1d522bdbec56b9c890aa57b6a5dc069a709c8b28..c9ac056c7ef4a1a7345ef545da4c4f06afb80e6a 100644 (file)
@@ -133,6 +133,138 @@ static void cisco_unset_addr_mask(struct in_addr *addr, struct in_addr *mask)
        mask->s_addr = CISCO_BIN_HOST_WILDCARD_MASK;
 }
 
+static int _acl_is_dup(const struct lyd_node *dnode, void *arg)
+{
+       struct acl_dup_args *ada = arg;
+       int idx;
+
+       /* This entry is the caller, so skip it. */
+       if (ada->ada_entry_dnode
+           && ada->ada_entry_dnode == dnode)
+               return YANG_ITER_CONTINUE;
+
+       /* Check if all values match. */
+       for (idx = 0; idx < ADA_MAX_VALUES; idx++) {
+               /* No more values. */
+               if (ada->ada_xpath[idx] == NULL)
+                       break;
+
+               /* Not same type, just skip it. */
+               if (!yang_dnode_exists(dnode, ada->ada_xpath[idx]))
+                       return YANG_ITER_CONTINUE;
+
+               /* Check if different value. */
+               if (strcmp(yang_dnode_get_string(dnode, ada->ada_xpath[idx]),
+                          ada->ada_value[idx]))
+                       return YANG_ITER_CONTINUE;
+       }
+
+       ada->ada_found = true;
+
+       return YANG_ITER_STOP;
+}
+
+bool acl_is_dup(const struct lyd_node *dnode, struct acl_dup_args *ada)
+{
+       ada->ada_found = false;
+
+       yang_dnode_iterate(
+               _acl_is_dup, ada, dnode,
+               "/frr-filter:lib/access-list[type='%s'][name='%s']/entry",
+               ada->ada_type, ada->ada_name);
+
+       return ada->ada_found;
+}
+
+static bool acl_cisco_is_dup(const struct lyd_node *dnode)
+{
+       const struct lyd_node *entry_dnode =
+               yang_dnode_get_parent(dnode, "entry");
+       struct acl_dup_args ada = {};
+       int idx = 0, arg_idx = 0;
+       static const char *cisco_entries[] = {
+               "./host",
+               "./network/address",
+               "./network/mask",
+               "./source-any",
+               "./destination-host",
+               "./destination-network/address",
+               "./destination-network/mask",
+               "./destination-any",
+               NULL
+       };
+
+       /* Initialize. */
+       ada.ada_type = "ipv4";
+       ada.ada_name = yang_dnode_get_string(entry_dnode, "../name");
+       ada.ada_entry_dnode = entry_dnode;
+
+       /* Load all values/XPaths. */
+       while (cisco_entries[idx] != NULL) {
+               if (!yang_dnode_exists(entry_dnode, cisco_entries[idx])) {
+                       idx++;
+                       continue;
+               }
+
+               ada.ada_xpath[arg_idx] = cisco_entries[idx];
+               ada.ada_value[arg_idx] =
+                       yang_dnode_get_string(entry_dnode, cisco_entries[idx]);
+               arg_idx++;
+               idx++;
+       }
+
+       return acl_is_dup(entry_dnode, &ada);
+}
+
+static bool acl_zebra_is_dup(const struct lyd_node *dnode,
+                            enum yang_access_list_type type)
+{
+       const struct lyd_node *entry_dnode =
+               yang_dnode_get_parent(dnode, "entry");
+       struct acl_dup_args ada = {};
+       int idx = 0, arg_idx = 0;
+       static const char *zebra_entries[] = {
+               "./ipv4-prefix",
+               "./ipv4-exact-match",
+               "./ipv6-prefix",
+               "./ipv6-exact-match",
+               "./mac",
+               "./any",
+               NULL
+       };
+
+       /* Initialize. */
+       switch (type) {
+       case YALT_IPV4:
+               ada.ada_type = "ipv4";
+               break;
+       case YALT_IPV6:
+               ada.ada_type = "ipv6";
+               break;
+       case YALT_MAC:
+               ada.ada_type = "mac";
+               break;
+       }
+       ada.ada_name = yang_dnode_get_string(entry_dnode, "../name");
+       ada.ada_entry_dnode = entry_dnode;
+
+       /* Load all values/XPaths. */
+       while (zebra_entries[idx] != NULL) {
+               if (!yang_dnode_exists(entry_dnode, zebra_entries[idx])) {
+                       idx++;
+                       continue;
+               }
+
+               ada.ada_xpath[arg_idx] = zebra_entries[idx];
+               ada.ada_value[arg_idx] =
+                       yang_dnode_get_string(entry_dnode, zebra_entries[idx]);
+               arg_idx++;
+               idx++;
+       }
+
+       return acl_is_dup(entry_dnode, &ada);
+}
+
 /*
  * XPath: /frr-filter:lib/access-list
  */
@@ -290,6 +422,19 @@ lib_access_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args)
        struct filter_zebra *fz;
        struct filter *f;
 
+       /* Don't allow duplicated values. */
+       if (args->event == NB_EV_VALIDATE) {
+               if (acl_zebra_is_dup(
+                           args->dnode,
+                           yang_dnode_get_enum(args->dnode, "../../type"))) {
+                       snprintfrr(args->errmsg, args->errmsg_len,
+                                  "duplicated access list value: %s",
+                                  yang_dnode_get_string(args->dnode, NULL));
+                       return NB_ERR_VALIDATION;
+               }
+               return NB_OK;
+       }
+
        if (args->event != NB_EV_APPLY)
                return NB_OK;
 
@@ -330,6 +475,19 @@ lib_access_list_entry_ipv4_exact_match_modify(struct nb_cb_modify_args *args)
        struct filter_zebra *fz;
        struct filter *f;
 
+       /* Don't allow duplicated values. */
+       if (args->event == NB_EV_VALIDATE) {
+               if (acl_zebra_is_dup(
+                           args->dnode,
+                           yang_dnode_get_enum(args->dnode, "../../type"))) {
+                       snprintfrr(args->errmsg, args->errmsg_len,
+                                  "duplicated access list value: %s",
+                                  yang_dnode_get_string(args->dnode, NULL));
+                       return NB_ERR_VALIDATION;
+               }
+               return NB_OK;
+       }
+
        if (args->event != NB_EV_APPLY)
                return NB_OK;
 
@@ -369,6 +527,17 @@ lib_access_list_entry_host_modify(struct nb_cb_modify_args *args)
        struct filter_cisco *fc;
        struct filter *f;
 
+       /* Don't allow duplicated values. */
+       if (args->event == NB_EV_VALIDATE) {
+               if (acl_cisco_is_dup(args->dnode)) {
+                       snprintfrr(args->errmsg, args->errmsg_len,
+                                  "duplicated access list value: %s",
+                                  yang_dnode_get_string(args->dnode, NULL));
+                       return NB_ERR_VALIDATION;
+               }
+               return NB_OK;
+       }
+
        if (args->event != NB_EV_APPLY)
                return NB_OK;
 
@@ -410,6 +579,17 @@ lib_access_list_entry_network_address_modify(struct nb_cb_modify_args *args)
        struct filter_cisco *fc;
        struct filter *f;
 
+       /* Don't allow duplicated values. */
+       if (args->event == NB_EV_VALIDATE) {
+               if (acl_cisco_is_dup(args->dnode)) {
+                       snprintfrr(args->errmsg, args->errmsg_len,
+                                  "duplicated access list value: %s",
+                                  yang_dnode_get_string(args->dnode, NULL));
+                       return NB_ERR_VALIDATION;
+               }
+               return NB_OK;
+       }
+
        if (args->event != NB_EV_APPLY)
                return NB_OK;
 
@@ -432,6 +612,17 @@ lib_access_list_entry_network_mask_modify(struct nb_cb_modify_args *args)
        struct filter_cisco *fc;
        struct filter *f;
 
+       /* Don't allow duplicated values. */
+       if (args->event == NB_EV_VALIDATE) {
+               if (acl_cisco_is_dup(args->dnode)) {
+                       snprintfrr(args->errmsg, args->errmsg_len,
+                                  "duplicated access list value: %s",
+                                  yang_dnode_get_string(args->dnode, NULL));
+                       return NB_ERR_VALIDATION;
+               }
+               return NB_OK;
+       }
+
        if (args->event != NB_EV_APPLY)
                return NB_OK;
 
@@ -454,6 +645,17 @@ lib_access_list_entry_source_any_create(struct nb_cb_create_args *args)
        struct filter_cisco *fc;
        struct filter *f;
 
+       /* Don't allow duplicated values. */
+       if (args->event == NB_EV_VALIDATE) {
+               if (acl_cisco_is_dup(args->dnode)) {
+                       snprintfrr(args->errmsg, args->errmsg_len,
+                                  "duplicated access list value: %s",
+                                  yang_dnode_get_string(args->dnode, NULL));
+                       return NB_ERR_VALIDATION;
+               }
+               return NB_OK;
+       }
+
        if (args->event != NB_EV_APPLY)
                return NB_OK;
 
@@ -495,6 +697,17 @@ static int lib_access_list_entry_destination_host_modify(
        struct filter_cisco *fc;
        struct filter *f;
 
+       /* Don't allow duplicated values. */
+       if (args->event == NB_EV_VALIDATE) {
+               if (acl_cisco_is_dup(args->dnode)) {
+                       snprintfrr(args->errmsg, args->errmsg_len,
+                                  "duplicated access list value: %s",
+                                  yang_dnode_get_string(args->dnode, NULL));
+                       return NB_ERR_VALIDATION;
+               }
+               return NB_OK;
+       }
+
        if (args->event != NB_EV_APPLY)
                return NB_OK;
 
@@ -537,6 +750,17 @@ static int lib_access_list_entry_destination_network_address_modify(
        struct filter_cisco *fc;
        struct filter *f;
 
+       /* Don't allow duplicated values. */
+       if (args->event == NB_EV_VALIDATE) {
+               if (acl_cisco_is_dup(args->dnode)) {
+                       snprintfrr(args->errmsg, args->errmsg_len,
+                                  "duplicated access list value: %s",
+                                  yang_dnode_get_string(args->dnode, NULL));
+                       return NB_ERR_VALIDATION;
+               }
+               return NB_OK;
+       }
+
        if (args->event != NB_EV_APPLY)
                return NB_OK;
 
@@ -559,6 +783,17 @@ static int lib_access_list_entry_destination_network_mask_modify(
        struct filter_cisco *fc;
        struct filter *f;
 
+       /* Don't allow duplicated values. */
+       if (args->event == NB_EV_VALIDATE) {
+               if (acl_cisco_is_dup(args->dnode)) {
+                       snprintfrr(args->errmsg, args->errmsg_len,
+                                  "duplicated access list value: %s",
+                                  yang_dnode_get_string(args->dnode, NULL));
+                       return NB_ERR_VALIDATION;
+               }
+               return NB_OK;
+       }
+
        if (args->event != NB_EV_APPLY)
                return NB_OK;
 
@@ -581,6 +816,17 @@ static int lib_access_list_entry_destination_any_create(
        struct filter_cisco *fc;
        struct filter *f;
 
+       /* Don't allow duplicated values. */
+       if (args->event == NB_EV_VALIDATE) {
+               if (acl_cisco_is_dup(args->dnode)) {
+                       snprintfrr(args->errmsg, args->errmsg_len,
+                                  "duplicated access list value: %s",
+                                  yang_dnode_get_string(args->dnode, NULL));
+                       return NB_ERR_VALIDATION;
+               }
+               return NB_OK;
+       }
+
        if (args->event != NB_EV_APPLY)
                return NB_OK;
 
@@ -623,6 +869,19 @@ static int lib_access_list_entry_any_create(struct nb_cb_create_args *args)
        struct filter *f;
        int type;
 
+       /* Don't allow duplicated values. */
+       if (args->event == NB_EV_VALIDATE) {
+               if (acl_zebra_is_dup(
+                           args->dnode,
+                           yang_dnode_get_enum(args->dnode, "../../type"))) {
+                       snprintfrr(args->errmsg, args->errmsg_len,
+                                  "duplicated access list value: %s",
+                                  yang_dnode_get_string(args->dnode, NULL));
+                       return NB_ERR_VALIDATION;
+               }
+               return NB_OK;
+       }
+
        if (args->event != NB_EV_APPLY)
                return NB_OK;