diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:53:30 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:53:30 +0000 |
commit | 2c7cac91ed6e7db0f6937923d2b57f97dbdbc337 (patch) | |
tree | c05dc0f8e6aa3accc84e3e5cffc933ed94941383 /lib/filter_nb.c | |
parent | Initial commit. (diff) | |
download | frr-upstream.tar.xz frr-upstream.zip |
Adding upstream version 8.4.4.upstream/8.4.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/filter_nb.c')
-rw-r--r-- | lib/filter_nb.c | 1865 |
1 files changed, 1865 insertions, 0 deletions
diff --git a/lib/filter_nb.c b/lib/filter_nb.c new file mode 100644 index 0000000..215a33d --- /dev/null +++ b/lib/filter_nb.c @@ -0,0 +1,1865 @@ +/* + * FRR filter northbound implementation. + * + * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF") + * Rafael Zalamena + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include "zebra.h" + +#include "lib/northbound.h" +#include "lib/prefix.h" +#include "lib/printfrr.h" + +#include "lib/filter.h" +#include "lib/plist.h" +#include "lib/plist_int.h" +#include "lib/routemap.h" + +/* Helper function. */ +static void acl_notify_route_map(struct access_list *acl, int route_map_event) +{ + switch (route_map_event) { + case RMAP_EVENT_FILTER_ADDED: + if (acl->master->add_hook) + (*acl->master->add_hook)(acl); + break; + case RMAP_EVENT_FILTER_DELETED: + if (acl->master->delete_hook) + (*acl->master->delete_hook)(acl); + break; + } + + route_map_notify_dependencies(acl->name, route_map_event); +} + +static enum nb_error prefix_list_length_validate(struct nb_cb_modify_args *args) +{ + int type = yang_dnode_get_enum(args->dnode, "../../type"); + const char *xpath_le = NULL, *xpath_ge = NULL; + struct prefix p; + uint8_t le, ge; + + if (type == YPLT_IPV4) { + yang_dnode_get_prefix(&p, args->dnode, "../ipv4-prefix"); + xpath_le = "../ipv4-prefix-length-lesser-or-equal"; + xpath_ge = "../ipv4-prefix-length-greater-or-equal"; + } else { + yang_dnode_get_prefix(&p, args->dnode, "../ipv6-prefix"); + xpath_le = "../ipv6-prefix-length-lesser-or-equal"; + xpath_ge = "../ipv6-prefix-length-greater-or-equal"; + } + + /* + * Check rule: + * prefix length <= le. + */ + if (yang_dnode_exists(args->dnode, xpath_le)) { + le = yang_dnode_get_uint8(args->dnode, xpath_le); + if (p.prefixlen > le) + goto log_and_fail; + } + + /* + * Check rule: + * prefix length <= ge. + */ + if (yang_dnode_exists(args->dnode, xpath_ge)) { + ge = yang_dnode_get_uint8(args->dnode, xpath_ge); + if (p.prefixlen > ge) + goto log_and_fail; + } + + /* + * Check rule: + * ge <= le. + */ + if (yang_dnode_exists(args->dnode, xpath_le) + && yang_dnode_exists(args->dnode, xpath_ge)) { + le = yang_dnode_get_uint8(args->dnode, xpath_le); + ge = yang_dnode_get_uint8(args->dnode, xpath_ge); + if (ge > le) + goto log_and_fail; + } + + return NB_OK; + +log_and_fail: + snprintfrr( + args->errmsg, args->errmsg_len, + "Invalid prefix range for %pFX: Make sure that mask length <= ge <= le", + &p); + return NB_ERR_VALIDATION; +} + +/** + * Sets prefix list entry to blank value. + * + * \param[out] ple prefix list entry to modify. + */ +static void prefix_list_entry_set_empty(struct prefix_list_entry *ple) +{ + ple->any = false; + memset(&ple->prefix, 0, sizeof(ple->prefix)); + ple->ge = 0; + ple->le = 0; +} + +static int +prefix_list_nb_validate_v4_af_type(const struct lyd_node *plist_dnode, + char *errmsg, size_t errmsg_len) +{ + int af_type; + + af_type = yang_dnode_get_enum(plist_dnode, "./type"); + if (af_type != YPLT_IPV4) { + snprintf(errmsg, errmsg_len, + "prefix-list type %u is mismatched.", af_type); + return NB_ERR_VALIDATION; + } + + return NB_OK; +} + +static int +prefix_list_nb_validate_v6_af_type(const struct lyd_node *plist_dnode, + char *errmsg, size_t errmsg_len) +{ + int af_type; + + af_type = yang_dnode_get_enum(plist_dnode, "./type"); + if (af_type != YPLT_IPV6) { + snprintf(errmsg, errmsg_len, + "prefix-list type %u is mismatched.", af_type); + return NB_ERR_VALIDATION; + } + + return NB_OK; +} + +static int lib_prefix_list_entry_prefix_length_greater_or_equal_modify( + struct nb_cb_modify_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + ple->ge = yang_dnode_get_uint8(args->dnode, NULL); + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +static int lib_prefix_list_entry_prefix_length_lesser_or_equal_modify( + struct nb_cb_modify_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + ple->le = yang_dnode_get_uint8(args->dnode, NULL); + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +static int lib_prefix_list_entry_prefix_length_greater_or_equal_destroy( + struct nb_cb_destroy_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + ple->ge = 0; + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +static int lib_prefix_list_entry_prefix_length_lesser_or_equal_destroy( + struct nb_cb_destroy_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + ple->le = 0; + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +/** + * Unsets the cisco style rule for addresses so it becomes disabled (the + * equivalent of setting: `0.0.0.0/32`). + * + * \param addr address part. + * \param mask mask part. + */ +static void cisco_unset_addr_mask(struct in_addr *addr, struct in_addr *mask) +{ + addr->s_addr = INADDR_ANY; + 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; + + if (strcmp(yang_dnode_get_string(dnode, "action"), ada->ada_action)) + 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; + ada->ada_seq = yang_dnode_get_uint32(dnode, "sequence"); + + 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_action = yang_dnode_get_string(entry_dnode, "action"); + 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_action = yang_dnode_get_string(entry_dnode, "action"); + 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); +} + +static void plist_dnode_to_prefix(const struct lyd_node *dnode, bool *any, + struct prefix *p, int *ge, int *le) +{ + *any = false; + *ge = 0; + *le = 0; + + if (yang_dnode_exists(dnode, "./any")) { + *any = true; + return; + } + + switch (yang_dnode_get_enum(dnode, "../type")) { + case YPLT_IPV4: + yang_dnode_get_prefix(p, dnode, "./ipv4-prefix"); + if (yang_dnode_exists(dnode, + "./ipv4-prefix-length-greater-or-equal")) + *ge = yang_dnode_get_uint8( + dnode, "./ipv4-prefix-length-greater-or-equal"); + if (yang_dnode_exists(dnode, + "./ipv4-prefix-length-lesser-or-equal")) + *le = yang_dnode_get_uint8( + dnode, "./ipv4-prefix-length-lesser-or-equal"); + break; + case YPLT_IPV6: + yang_dnode_get_prefix(p, dnode, "./ipv6-prefix"); + if (yang_dnode_exists(dnode, + "./ipv6-prefix-length-greater-or-equal")) + *ge = yang_dnode_get_uint8( + dnode, "./ipv6-prefix-length-greater-or-equal"); + if (yang_dnode_exists(dnode, + "./ipv6-prefix-length-lesser-or-equal")) + *le = yang_dnode_get_uint8( + dnode, "./ipv6-prefix-length-lesser-or-equal"); + break; + } +} + +static int _plist_is_dup(const struct lyd_node *dnode, void *arg) +{ + struct plist_dup_args *pda = arg; + struct prefix p = {}; + int ge, le; + bool any; + + /* This entry is the caller, so skip it. */ + if (pda->pda_entry_dnode + && pda->pda_entry_dnode == dnode) + return YANG_ITER_CONTINUE; + + if (strcmp(yang_dnode_get_string(dnode, "action"), pda->pda_action)) + return YANG_ITER_CONTINUE; + + plist_dnode_to_prefix(dnode, &any, &p, &ge, &le); + + if (pda->any) { + if (!any) + return YANG_ITER_CONTINUE; + } else { + if (!prefix_same(&pda->prefix, &p) || pda->ge != ge + || pda->le != le) + return YANG_ITER_CONTINUE; + } + + pda->pda_found = true; + pda->pda_seq = yang_dnode_get_uint32(dnode, "sequence"); + + return YANG_ITER_STOP; +} + +bool plist_is_dup(const struct lyd_node *dnode, struct plist_dup_args *pda) +{ + pda->pda_found = false; + + yang_dnode_iterate( + _plist_is_dup, pda, dnode, + "/frr-filter:lib/prefix-list[type='%s'][name='%s']/entry", + pda->pda_type, pda->pda_name); + + return pda->pda_found; +} + +static bool plist_is_dup_nb(const struct lyd_node *dnode) +{ + const struct lyd_node *entry_dnode = + yang_dnode_get_parent(dnode, "entry"); + struct plist_dup_args pda = {}; + + /* Initialize. */ + pda.pda_type = yang_dnode_get_string(entry_dnode, "../type"); + pda.pda_name = yang_dnode_get_string(entry_dnode, "../name"); + pda.pda_action = yang_dnode_get_string(entry_dnode, "action"); + pda.pda_entry_dnode = entry_dnode; + + plist_dnode_to_prefix(entry_dnode, &pda.any, &pda.prefix, &pda.ge, + &pda.le); + + return plist_is_dup(entry_dnode, &pda); +} + +/* + * XPath: /frr-filter:lib/access-list + */ +static int lib_access_list_create(struct nb_cb_create_args *args) +{ + struct access_list *acl = NULL; + const char *acl_name; + int type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + type = yang_dnode_get_enum(args->dnode, "./type"); + acl_name = yang_dnode_get_string(args->dnode, "./name"); + + switch (type) { + case YALT_IPV4: + acl = access_list_get(AFI_IP, acl_name); + break; + case YALT_IPV6: + acl = access_list_get(AFI_IP6, acl_name); + break; + case YALT_MAC: + acl = access_list_get(AFI_L2VPN, acl_name); + break; + } + + nb_running_set_entry(args->dnode, acl); + + return NB_OK; +} + +static int lib_access_list_destroy(struct nb_cb_destroy_args *args) +{ + struct access_list *acl; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + acl = nb_running_unset_entry(args->dnode); + access_list_delete(acl); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/remark + */ +static int lib_access_list_remark_modify(struct nb_cb_modify_args *args) +{ + struct access_list *acl; + const char *remark; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + acl = nb_running_get_entry(args->dnode, NULL, true); + if (acl->remark) + XFREE(MTYPE_TMP, acl->remark); + + remark = yang_dnode_get_string(args->dnode, NULL); + acl->remark = XSTRDUP(MTYPE_TMP, remark); + + return NB_OK; +} + +static int +lib_access_list_remark_destroy(struct nb_cb_destroy_args *args) +{ + struct access_list *acl; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + acl = nb_running_get_entry(args->dnode, NULL, true); + if (acl->remark) + XFREE(MTYPE_TMP, acl->remark); + + return NB_OK; +} + + +/* + * XPath: /frr-filter:lib/access-list/entry + */ +static int lib_access_list_entry_create(struct nb_cb_create_args *args) +{ + struct access_list *acl; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = filter_new(); + f->seq = yang_dnode_get_uint32(args->dnode, "./sequence"); + + acl = nb_running_get_entry(args->dnode, NULL, true); + f->acl = acl; + access_list_filter_add(acl, f); + nb_running_set_entry(args->dnode, f); + + return NB_OK; +} + +static int lib_access_list_entry_destroy(struct nb_cb_destroy_args *args) +{ + struct access_list *acl; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_unset_entry(args->dnode); + acl = f->acl; + access_list_filter_delete(acl, f); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/action + */ +static int +lib_access_list_entry_action_modify(struct nb_cb_modify_args *args) +{ + const char *filter_type; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + filter_type = yang_dnode_get_string(args->dnode, NULL); + if (strcmp(filter_type, "permit") == 0) + f->type = FILTER_PERMIT; + else + f->type = FILTER_DENY; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/ipv4-prefix + */ +static int +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; + + f = nb_running_get_entry(args->dnode, NULL, true); + f->cisco = 0; + fz = &f->u.zfilter; + yang_dnode_get_prefix(&fz->prefix, args->dnode, NULL); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +static int +lib_access_list_entry_ipv4_prefix_destroy(struct nb_cb_destroy_args *args) +{ + struct filter_zebra *fz; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fz = &f->u.zfilter; + memset(&fz->prefix, 0, sizeof(fz->prefix)); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/ipv4-exact-match + */ +static int +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; + + f = nb_running_get_entry(args->dnode, NULL, true); + fz = &f->u.zfilter; + fz->exact = yang_dnode_get_bool(args->dnode, NULL); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +static int +lib_access_list_entry_ipv4_exact_match_destroy(struct nb_cb_destroy_args *args) +{ + struct filter_zebra *fz; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fz = &f->u.zfilter; + fz->exact = 0; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/host + */ +static int +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; + + f = nb_running_get_entry(args->dnode, NULL, true); + f->cisco = 1; + fc = &f->u.cfilter; + yang_dnode_get_ipv4(&fc->addr, args->dnode, NULL); + fc->addr_mask.s_addr = CISCO_BIN_HOST_WILDCARD_MASK; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +static int +lib_access_list_entry_host_destroy(struct nb_cb_destroy_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + cisco_unset_addr_mask(&fc->addr, &fc->addr_mask); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/network/address + */ +static int +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; + + f = nb_running_get_entry(args->dnode, NULL, true); + f->cisco = 1; + fc = &f->u.cfilter; + yang_dnode_get_ipv4(&fc->addr, args->dnode, NULL); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/network/mask + */ +static int +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; + + f = nb_running_get_entry(args->dnode, NULL, true); + f->cisco = 1; + fc = &f->u.cfilter; + yang_dnode_get_ipv4(&fc->addr_mask, args->dnode, NULL); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/source-any + */ +static int +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; + + f = nb_running_get_entry(args->dnode, NULL, true); + f->cisco = 1; + fc = &f->u.cfilter; + fc->addr.s_addr = INADDR_ANY; + fc->addr_mask.s_addr = CISCO_BIN_ANY_WILDCARD_MASK; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +static int +lib_access_list_entry_source_any_destroy(struct nb_cb_destroy_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + cisco_unset_addr_mask(&fc->addr, &fc->addr_mask); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/destination-host + */ +static int lib_access_list_entry_destination_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; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + fc->extended = 1; + yang_dnode_get_ipv4(&fc->mask, args->dnode, NULL); + fc->mask_mask.s_addr = CISCO_BIN_HOST_WILDCARD_MASK; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +static int lib_access_list_entry_destination_host_destroy( + struct nb_cb_destroy_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + fc->extended = 0; + cisco_unset_addr_mask(&fc->mask, &fc->mask_mask); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/destination-network/address + */ +static int lib_access_list_entry_destination_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; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + fc->extended = 1; + yang_dnode_get_ipv4(&fc->mask, args->dnode, NULL); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/destination-network/mask + */ +static int lib_access_list_entry_destination_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; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + fc->extended = 1; + yang_dnode_get_ipv4(&fc->mask_mask, args->dnode, NULL); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/destination-any + */ +static int lib_access_list_entry_destination_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; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + fc->extended = 1; + fc->mask.s_addr = INADDR_ANY; + fc->mask_mask.s_addr = CISCO_BIN_ANY_WILDCARD_MASK; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +static int lib_access_list_entry_destination_any_destroy( + struct nb_cb_destroy_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + fc->extended = 0; + cisco_unset_addr_mask(&fc->mask, &fc->mask_mask); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/any + */ +static int lib_access_list_entry_any_create(struct nb_cb_create_args *args) +{ + struct filter_zebra *fz; + 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; + + f = nb_running_get_entry(args->dnode, NULL, true); + f->cisco = 0; + fz = &f->u.zfilter; + memset(&fz->prefix, 0, sizeof(fz->prefix)); + + type = yang_dnode_get_enum(args->dnode, "../../type"); + switch (type) { + case YALT_IPV4: + fz->prefix.family = AF_INET; + break; + case YALT_IPV6: + fz->prefix.family = AF_INET6; + break; + case YALT_MAC: + fz->prefix.family = AF_ETHERNET; + break; + } + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +static int lib_access_list_entry_any_destroy(struct nb_cb_destroy_args *args) +{ + struct filter_zebra *fz; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fz = &f->u.zfilter; + fz->prefix.family = AF_UNSPEC; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/prefix-list + */ +static int lib_prefix_list_create(struct nb_cb_create_args *args) +{ + struct prefix_list *pl = NULL; + const char *name; + int type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + type = yang_dnode_get_enum(args->dnode, "./type"); + name = yang_dnode_get_string(args->dnode, "./name"); + switch (type) { + case 0: /* ipv4 */ + pl = prefix_list_get(AFI_IP, 0, name); + break; + case 1: /* ipv6 */ + pl = prefix_list_get(AFI_IP6, 0, name); + break; + } + + nb_running_set_entry(args->dnode, pl); + + return NB_OK; +} + +static int lib_prefix_list_destroy(struct nb_cb_destroy_args *args) +{ + struct prefix_list *pl; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + pl = nb_running_unset_entry(args->dnode); + prefix_list_delete(pl); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/prefix-list/remark + */ +static int lib_prefix_list_remark_modify(struct nb_cb_modify_args *args) +{ + struct prefix_list *pl; + const char *remark; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + pl = nb_running_get_entry(args->dnode, NULL, true); + if (pl->desc) + XFREE(MTYPE_TMP, pl->desc); + + remark = yang_dnode_get_string(args->dnode, NULL); + pl->desc = XSTRDUP(MTYPE_TMP, remark); + + return NB_OK; +} + +static int lib_prefix_list_remark_destroy(struct nb_cb_destroy_args *args) +{ + struct prefix_list *pl; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + pl = nb_running_get_entry(args->dnode, NULL, true); + if (pl->desc) + XFREE(MTYPE_TMP, pl->desc); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry + */ +static int lib_prefix_list_entry_create(struct nb_cb_create_args *args) +{ + struct prefix_list_entry *ple; + struct prefix_list *pl; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + pl = nb_running_get_entry(args->dnode, NULL, true); + ple = prefix_list_entry_new(); + ple->pl = pl; + ple->seq = yang_dnode_get_uint32(args->dnode, "./sequence"); + prefix_list_entry_set_empty(ple); + nb_running_set_entry(args->dnode, ple); + + return NB_OK; +} + +static int lib_prefix_list_entry_destroy(struct nb_cb_destroy_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_unset_entry(args->dnode); + if (ple->installed) + prefix_list_entry_delete2(ple); + else + prefix_list_entry_free(ple); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry/action + */ +static int lib_prefix_list_entry_action_modify(struct nb_cb_modify_args *args) +{ + struct prefix_list_entry *ple; + int action_type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + action_type = yang_dnode_get_enum(args->dnode, NULL); + if (action_type == YPLA_PERMIT) + ple->type = PREFIX_PERMIT; + else + ple->type = PREFIX_DENY; + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +static int lib_prefix_list_entry_prefix_modify(struct nb_cb_modify_args *args) +{ + struct prefix_list_entry *ple; + struct prefix p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + yang_dnode_get_prefix(&ple->prefix, args->dnode, NULL); + + /* Apply mask and correct original address if necessary. */ + prefix_copy(&p, &ple->prefix); + apply_mask(&p); + if (!prefix_same(&ple->prefix, &p)) { + zlog_info("%s: bad network %pFX correcting it to %pFX", + __func__, &ple->prefix, &p); + prefix_copy(&ple->prefix, &p); + } + + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +static int lib_prefix_list_entry_prefix_destroy(struct nb_cb_destroy_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + memset(&ple->prefix, 0, sizeof(ple->prefix)); + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry/ipv4-prefix + */ +static int +lib_prefix_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args) +{ + if (args->event == NB_EV_VALIDATE) { + const struct lyd_node *plist_dnode = + yang_dnode_get_parent(args->dnode, "prefix-list"); + + if (plist_is_dup_nb(args->dnode)) { + snprintf(args->errmsg, args->errmsg_len, + "duplicated prefix list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + + return prefix_list_nb_validate_v4_af_type( + plist_dnode, args->errmsg, args->errmsg_len); + } + + return lib_prefix_list_entry_prefix_modify(args); +} + +static int +lib_prefix_list_entry_ipv4_prefix_destroy(struct nb_cb_destroy_args *args) +{ + + if (args->event != NB_EV_APPLY) + return NB_OK; + + return lib_prefix_list_entry_prefix_destroy(args); +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry/ipv6-prefix + */ +static int +lib_prefix_list_entry_ipv6_prefix_modify(struct nb_cb_modify_args *args) +{ + + if (args->event == NB_EV_VALIDATE) { + const struct lyd_node *plist_dnode = + yang_dnode_get_parent(args->dnode, "prefix-list"); + + if (plist_is_dup_nb(args->dnode)) { + snprintf(args->errmsg, args->errmsg_len, + "duplicated prefix list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + + return prefix_list_nb_validate_v6_af_type( + plist_dnode, args->errmsg, args->errmsg_len); + } + + return lib_prefix_list_entry_prefix_modify(args); +} + +static int +lib_prefix_list_entry_ipv6_prefix_destroy(struct nb_cb_destroy_args *args) +{ + + if (args->event != NB_EV_APPLY) + return NB_OK; + + return lib_prefix_list_entry_prefix_destroy(args); +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry/ipv4-prefix-length-greater-or-equal + */ +static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify( + struct nb_cb_modify_args *args) +{ + if (args->event == NB_EV_VALIDATE + && prefix_list_length_validate(args) != NB_OK) + return NB_ERR_VALIDATION; + + if (args->event == NB_EV_VALIDATE) { + const struct lyd_node *plist_dnode = + yang_dnode_get_parent(args->dnode, "prefix-list"); + + if (plist_is_dup_nb(args->dnode)) { + snprintf(args->errmsg, args->errmsg_len, + "duplicated prefix list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + + return prefix_list_nb_validate_v4_af_type( + plist_dnode, args->errmsg, args->errmsg_len); + } + + return lib_prefix_list_entry_prefix_length_greater_or_equal_modify( + args); +} + +static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_destroy( + struct nb_cb_destroy_args *args) +{ + if (args->event == NB_EV_VALIDATE) { + const struct lyd_node *plist_dnode = + yang_dnode_get_parent(args->dnode, "prefix-list"); + + return prefix_list_nb_validate_v4_af_type( + plist_dnode, args->errmsg, args->errmsg_len); + } + + return lib_prefix_list_entry_prefix_length_greater_or_equal_destroy( + args); +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry/ipv4-prefix-length-lesser-or-equal + */ +static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify( + struct nb_cb_modify_args *args) +{ + if (args->event == NB_EV_VALIDATE + && prefix_list_length_validate(args) != NB_OK) + return NB_ERR_VALIDATION; + + if (args->event == NB_EV_VALIDATE) { + const struct lyd_node *plist_dnode = + yang_dnode_get_parent(args->dnode, "prefix-list"); + + if (plist_is_dup_nb(args->dnode)) { + snprintf(args->errmsg, args->errmsg_len, + "duplicated prefix list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + + return prefix_list_nb_validate_v4_af_type( + plist_dnode, args->errmsg, args->errmsg_len); + } + + return lib_prefix_list_entry_prefix_length_lesser_or_equal_modify( + args); +} + +static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_destroy( + struct nb_cb_destroy_args *args) +{ + if (args->event == NB_EV_VALIDATE) { + const struct lyd_node *plist_dnode = + yang_dnode_get_parent(args->dnode, "prefix-list"); + + return prefix_list_nb_validate_v4_af_type( + plist_dnode, args->errmsg, args->errmsg_len); + } + + return lib_prefix_list_entry_prefix_length_lesser_or_equal_destroy( + args); +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry/ipv6-prefix-length-greater-or-equal + */ +static int lib_prefix_list_entry_ipv6_prefix_length_greater_or_equal_modify( + struct nb_cb_modify_args *args) +{ + if (args->event == NB_EV_VALIDATE + && prefix_list_length_validate(args) != NB_OK) + return NB_ERR_VALIDATION; + + if (args->event == NB_EV_VALIDATE) { + const struct lyd_node *plist_dnode = + yang_dnode_get_parent(args->dnode, "prefix-list"); + + if (plist_is_dup_nb(args->dnode)) { + snprintf(args->errmsg, args->errmsg_len, + "duplicated prefix list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + + return prefix_list_nb_validate_v6_af_type( + plist_dnode, args->errmsg, args->errmsg_len); + } + + return lib_prefix_list_entry_prefix_length_greater_or_equal_modify( + args); +} + +static int lib_prefix_list_entry_ipv6_prefix_length_greater_or_equal_destroy( + struct nb_cb_destroy_args *args) +{ + if (args->event == NB_EV_VALIDATE) { + const struct lyd_node *plist_dnode = + yang_dnode_get_parent(args->dnode, "prefix-list"); + + return prefix_list_nb_validate_v6_af_type( + plist_dnode, args->errmsg, args->errmsg_len); + } + + return lib_prefix_list_entry_prefix_length_greater_or_equal_destroy( + args); +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry/ipv6-prefix-length-lesser-or-equal + */ +static int lib_prefix_list_entry_ipv6_prefix_length_lesser_or_equal_modify( + struct nb_cb_modify_args *args) +{ + if (args->event == NB_EV_VALIDATE + && prefix_list_length_validate(args) != NB_OK) + return NB_ERR_VALIDATION; + + if (args->event == NB_EV_VALIDATE) { + const struct lyd_node *plist_dnode = + yang_dnode_get_parent(args->dnode, "prefix-list"); + + if (plist_is_dup_nb(args->dnode)) { + snprintf(args->errmsg, args->errmsg_len, + "duplicated prefix list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + + return prefix_list_nb_validate_v6_af_type( + plist_dnode, args->errmsg, args->errmsg_len); + } + + return lib_prefix_list_entry_prefix_length_lesser_or_equal_modify( + args); +} + +static int lib_prefix_list_entry_ipv6_prefix_length_lesser_or_equal_destroy( + struct nb_cb_destroy_args *args) +{ + if (args->event == NB_EV_VALIDATE) { + const struct lyd_node *plist_dnode = + yang_dnode_get_parent(args->dnode, "prefix-list"); + + return prefix_list_nb_validate_v6_af_type( + plist_dnode, args->errmsg, args->errmsg_len); + } + + return lib_prefix_list_entry_prefix_length_lesser_or_equal_destroy( + args); +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry/any + */ +static int lib_prefix_list_entry_any_create(struct nb_cb_create_args *args) +{ + struct prefix_list_entry *ple; + int type; + + if (args->event == NB_EV_VALIDATE) { + if (plist_is_dup_nb(args->dnode)) { + snprintf(args->errmsg, args->errmsg_len, + "duplicated prefix 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; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + ple->any = true; + + /* Fill prefix struct from scratch. */ + memset(&ple->prefix, 0, sizeof(ple->prefix)); + + type = yang_dnode_get_enum(args->dnode, "../../type"); + switch (type) { + case YPLT_IPV4: + ple->prefix.family = AF_INET; + ple->ge = 0; + ple->le = IPV4_MAX_BITLEN; + break; + case YPLT_IPV6: + ple->prefix.family = AF_INET6; + ple->ge = 0; + ple->le = IPV6_MAX_BITLEN; + break; + } + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +static int lib_prefix_list_entry_any_destroy(struct nb_cb_destroy_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + ple->any = false; + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +/* clang-format off */ +const struct frr_yang_module_info frr_filter_info = { + .name = "frr-filter", + .nodes = { + { + .xpath = "/frr-filter:lib/access-list", + .cbs = { + .create = lib_access_list_create, + .destroy = lib_access_list_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/remark", + .cbs = { + .modify = lib_access_list_remark_modify, + .destroy = lib_access_list_remark_destroy, + .cli_show = access_list_remark_show, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry", + .cbs = { + .create = lib_access_list_entry_create, + .destroy = lib_access_list_entry_destroy, + .cli_cmp = access_list_cmp, + .cli_show = access_list_show, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/action", + .cbs = { + .modify = lib_access_list_entry_action_modify, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/ipv4-prefix", + .cbs = { + .modify = lib_access_list_entry_ipv4_prefix_modify, + .destroy = lib_access_list_entry_ipv4_prefix_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/ipv4-exact-match", + .cbs = { + .modify = lib_access_list_entry_ipv4_exact_match_modify, + .destroy = lib_access_list_entry_ipv4_exact_match_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/host", + .cbs = { + .modify = lib_access_list_entry_host_modify, + .destroy = lib_access_list_entry_host_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/network/address", + .cbs = { + .modify = lib_access_list_entry_network_address_modify, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/network/mask", + .cbs = { + .modify = lib_access_list_entry_network_mask_modify, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/source-any", + .cbs = { + .create = lib_access_list_entry_source_any_create, + .destroy = lib_access_list_entry_source_any_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/destination-host", + .cbs = { + .modify = lib_access_list_entry_destination_host_modify, + .destroy = lib_access_list_entry_destination_host_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/destination-network/address", + .cbs = { + .modify = lib_access_list_entry_destination_network_address_modify, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/destination-network/mask", + .cbs = { + .modify = lib_access_list_entry_destination_network_mask_modify, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/destination-any", + .cbs = { + .create = lib_access_list_entry_destination_any_create, + .destroy = lib_access_list_entry_destination_any_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/ipv6-prefix", + .cbs = { + .modify = lib_access_list_entry_ipv4_prefix_modify, + .destroy = lib_access_list_entry_ipv4_prefix_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/ipv6-exact-match", + .cbs = { + .modify = lib_access_list_entry_ipv4_exact_match_modify, + .destroy = lib_access_list_entry_ipv4_exact_match_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/mac", + .cbs = { + .modify = lib_access_list_entry_ipv4_prefix_modify, + .destroy = lib_access_list_entry_ipv4_prefix_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/any", + .cbs = { + .create = lib_access_list_entry_any_create, + .destroy = lib_access_list_entry_any_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list", + .cbs = { + .create = lib_prefix_list_create, + .destroy = lib_prefix_list_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/remark", + .cbs = { + .modify = lib_prefix_list_remark_modify, + .destroy = lib_prefix_list_remark_destroy, + .cli_show = prefix_list_remark_show, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry", + .cbs = { + .create = lib_prefix_list_entry_create, + .destroy = lib_prefix_list_entry_destroy, + .cli_cmp = prefix_list_cmp, + .cli_show = prefix_list_show, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/action", + .cbs = { + .modify = lib_prefix_list_entry_action_modify, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/ipv4-prefix", + .cbs = { + .modify = lib_prefix_list_entry_ipv4_prefix_modify, + .destroy = lib_prefix_list_entry_ipv4_prefix_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/ipv4-prefix-length-greater-or-equal", + .cbs = { + .modify = lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify, + .destroy = lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/ipv4-prefix-length-lesser-or-equal", + .cbs = { + .modify = lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify, + .destroy = lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/ipv6-prefix", + .cbs = { + .modify = lib_prefix_list_entry_ipv6_prefix_modify, + .destroy = lib_prefix_list_entry_ipv6_prefix_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/ipv6-prefix-length-greater-or-equal", + .cbs = { + .modify = lib_prefix_list_entry_ipv6_prefix_length_greater_or_equal_modify, + .destroy = lib_prefix_list_entry_ipv6_prefix_length_greater_or_equal_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/ipv6-prefix-length-lesser-or-equal", + .cbs = { + .modify = lib_prefix_list_entry_ipv6_prefix_length_lesser_or_equal_modify, + .destroy = lib_prefix_list_entry_ipv6_prefix_length_lesser_or_equal_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/any", + .cbs = { + .create = lib_prefix_list_entry_any_create, + .destroy = lib_prefix_list_entry_any_destroy, + } + }, + { + .xpath = NULL, + }, + } +}; |