summaryrefslogtreecommitdiffstats
path: root/lib/filter_nb.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/filter_nb.c1865
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,
+ },
+ }
+};