From f414129b0ce109550bd3a61f6020d7a9af6f9c35 Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Fri, 4 Dec 2020 18:11:45 -0300 Subject: [PATCH] lib: disallow access list duplicated values 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 --- lib/filter.h | 27 +++++ lib/filter_nb.c | 259 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 286 insertions(+) diff --git a/lib/filter.h b/lib/filter.h index 623fb94527..c6dc269276 100644 --- a/lib/filter.h +++ b/lib/filter.h @@ -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; diff --git a/lib/filter_nb.c b/lib/filter_nb.c index 1d522bdbec..c9ac056c7e 100644 --- a/lib/filter_nb.c +++ b/lib/filter_nb.c @@ -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; -- 2.39.5