summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/filter.h54
-rw-r--r--lib/filter_cli.c218
-rw-r--r--lib/filter_nb.c386
-rw-r--r--lib/hash.h2
-rw-r--r--lib/yang.c2
-rw-r--r--lib/zclient.c13
-rw-r--r--lib/zclient.h12
7 files changed, 673 insertions, 14 deletions
diff --git a/lib/filter.h b/lib/filter.h
index 623fb94527..091a5197f6 100644
--- a/lib/filter.h
+++ b/lib/filter.h
@@ -176,6 +176,60 @@ enum yang_prefix_list_action {
YPLA_PERMIT = 1,
};
+struct acl_dup_args {
+ /** Access list type ("ipv4", "ipv6" or "mac"). */
+ const char *ada_type;
+ /** Access list name. */
+ const char *ada_name;
+
+#define ADA_MAX_VALUES 4
+ /** Entry XPath for value. */
+ const char *ada_xpath[ADA_MAX_VALUES];
+ /** Entry value to match. */
+ const char *ada_value[ADA_MAX_VALUES];
+
+ /** Duplicated entry found in list? */
+ bool ada_found;
+
+ /** (Optional) Already existing `dnode`. */
+ const struct lyd_node *ada_entry_dnode;
+};
+
+/**
+ * Check for duplicated entries using the candidate configuration.
+ *
+ * \param vty so we can get the candidate config.
+ * \param ada the arguments to check.
+ */
+bool acl_is_dup(const struct lyd_node *dnode, struct acl_dup_args *ada);
+
+struct plist_dup_args {
+ /** Access list type ("ipv4" or "ipv6"). */
+ const char *pda_type;
+ /** Access list name. */
+ const char *pda_name;
+
+#define PDA_MAX_VALUES 4
+ /** Entry XPath for value. */
+ const char *pda_xpath[PDA_MAX_VALUES];
+ /** Entry value to match. */
+ const char *pda_value[PDA_MAX_VALUES];
+
+ /** Duplicated entry found in list? */
+ bool pda_found;
+
+ /** (Optional) Already existing `dnode`. */
+ const struct lyd_node *pda_entry_dnode;
+};
+
+/**
+ * Check for duplicated entries using the candidate configuration.
+ *
+ * \param vty so we can get the candidate config.
+ * \param pda the arguments to check.
+ */
+bool plist_is_dup(const struct lyd_node *dnode, struct plist_dup_args *pda);
+
/* filter_cli.c */
struct lyd_node;
struct vty;
diff --git a/lib/filter_cli.c b/lib/filter_cli.c
index a8230f3a9a..54b6cda9a5 100644
--- a/lib/filter_cli.c
+++ b/lib/filter_cli.c
@@ -162,10 +162,36 @@ DEFPY_YANG(
"Wildcard bits\n")
{
int64_t sseq;
+ struct acl_dup_args ada = {};
char xpath[XPATH_MAXLEN];
char xpath_entry[XPATH_MAXLEN + 128];
/*
+ * Backward compatibility: don't complain about duplicated values,
+ * just silently accept.
+ */
+ if (seq_str == NULL) {
+ ada.ada_type = "ipv4";
+ ada.ada_name = name;
+ if (host_str && mask_str == NULL) {
+ ada.ada_xpath[0] = "./host";
+ ada.ada_value[0] = host_str;
+ } else if (host_str && mask_str) {
+ ada.ada_xpath[0] = "./network/address";
+ ada.ada_value[0] = host_str;
+ ada.ada_xpath[1] = "./network/mask";
+ ada.ada_value[1] = mask_str;
+ } else {
+ ada.ada_xpath[0] = "./source-any";
+ ada.ada_value[0] = "true";
+ }
+
+ /* Duplicated entry without sequence, just quit. */
+ if (acl_is_dup(vty->candidate_config->dnode, &ada))
+ return CMD_SUCCESS;
+ }
+
+ /*
* Create the access-list first, so we can generate sequence if
* none given (backward compatibility).
*/
@@ -270,11 +296,59 @@ DEFPY_YANG(
"Destination address to match\n"
"Any destination host\n")
{
+ int idx = 0;
int64_t sseq;
+ struct acl_dup_args ada = {};
char xpath[XPATH_MAXLEN];
char xpath_entry[XPATH_MAXLEN + 128];
/*
+ * Backward compatibility: don't complain about duplicated values,
+ * just silently accept.
+ */
+ if (seq_str == NULL) {
+ ada.ada_type = "ipv4";
+ ada.ada_name = name;
+ if (src_str && src_mask_str == NULL) {
+ ada.ada_xpath[idx] = "./host";
+ ada.ada_value[idx] = src_str;
+ idx++;
+ } else if (src_str && src_mask_str) {
+ ada.ada_xpath[idx] = "./network/address";
+ ada.ada_value[idx] = src_str;
+ idx++;
+ ada.ada_xpath[idx] = "./network/mask";
+ ada.ada_value[idx] = src_mask_str;
+ idx++;
+ } else {
+ ada.ada_xpath[idx] = "./source-any";
+ ada.ada_value[idx] = "true";
+ idx++;
+ }
+
+ if (dst_str && dst_mask_str == NULL) {
+ ada.ada_xpath[idx] = "./destination-host";
+ ada.ada_value[idx] = dst_str;
+ idx++;
+ } else if (dst_str && dst_mask_str) {
+ ada.ada_xpath[idx] = "./destination-network/address";
+ ada.ada_value[idx] = dst_str;
+ idx++;
+ ada.ada_xpath[idx] = "./destination-network/mask";
+ ada.ada_value[idx] = dst_mask_str;
+ idx++;
+ } else {
+ ada.ada_xpath[idx] = "./destination-any";
+ ada.ada_value[idx] = "true";
+ idx++;
+ }
+
+ /* Duplicated entry without sequence, just quit. */
+ if (acl_is_dup(vty->candidate_config->dnode, &ada))
+ return CMD_SUCCESS;
+ }
+
+ /*
* Create the access-list first, so we can generate sequence if
* none given (backward compatibility).
*/
@@ -419,10 +493,36 @@ DEFPY_YANG(
"Match any IPv4\n")
{
int64_t sseq;
+ struct acl_dup_args ada = {};
char xpath[XPATH_MAXLEN];
char xpath_entry[XPATH_MAXLEN + 128];
/*
+ * Backward compatibility: don't complain about duplicated values,
+ * just silently accept.
+ */
+ if (seq_str == NULL) {
+ ada.ada_type = "ipv4";
+ ada.ada_name = name;
+
+ if (prefix_str) {
+ ada.ada_xpath[0] = "./ipv4-prefix";
+ ada.ada_value[0] = prefix_str;
+ if (exact) {
+ ada.ada_xpath[1] = "./ipv4-exact-match";
+ ada.ada_value[1] = "true";
+ }
+ } else {
+ ada.ada_xpath[0] = "./any";
+ ada.ada_value[0] = "true";
+ }
+
+ /* Duplicated entry without sequence, just quit. */
+ if (acl_is_dup(vty->candidate_config->dnode, &ada))
+ return CMD_SUCCESS;
+ }
+
+ /*
* Create the access-list first, so we can generate sequence if
* none given (backward compatibility).
*/
@@ -590,10 +690,36 @@ DEFPY_YANG(
"Match any IPv6\n")
{
int64_t sseq;
+ struct acl_dup_args ada = {};
char xpath[XPATH_MAXLEN];
char xpath_entry[XPATH_MAXLEN + 128];
/*
+ * Backward compatibility: don't complain about duplicated values,
+ * just silently accept.
+ */
+ if (seq_str == NULL) {
+ ada.ada_type = "ipv6";
+ ada.ada_name = name;
+
+ if (prefix_str) {
+ ada.ada_xpath[0] = "./ipv6-prefix";
+ ada.ada_value[0] = prefix_str;
+ if (exact) {
+ ada.ada_xpath[1] = "./ipv6-exact-match";
+ ada.ada_value[1] = "true";
+ }
+ } else {
+ ada.ada_xpath[0] = "./any";
+ ada.ada_value[0] = "true";
+ }
+
+ /* Duplicated entry without sequence, just quit. */
+ if (acl_is_dup(vty->candidate_config->dnode, &ada))
+ return CMD_SUCCESS;
+ }
+
+ /*
* Create the access-list first, so we can generate sequence if
* none given (backward compatibility).
*/
@@ -765,10 +891,32 @@ DEFPY_YANG(
"Match any MAC address\n")
{
int64_t sseq;
+ struct acl_dup_args ada = {};
char xpath[XPATH_MAXLEN];
char xpath_entry[XPATH_MAXLEN + 128];
/*
+ * Backward compatibility: don't complain about duplicated values,
+ * just silently accept.
+ */
+ if (seq_str == NULL) {
+ ada.ada_type = "mac";
+ ada.ada_name = name;
+
+ if (mac_str) {
+ ada.ada_xpath[0] = "./mac";
+ ada.ada_value[0] = mac_str;
+ } else {
+ ada.ada_xpath[0] = "./any";
+ ada.ada_value[0] = "true";
+ }
+
+ /* Duplicated entry without sequence, just quit. */
+ if (acl_is_dup(vty->candidate_config->dnode, &ada))
+ return CMD_SUCCESS;
+ }
+
+ /*
* Create the access-list first, so we can generate sequence if
* none given (backward compatibility).
*/
@@ -1171,10 +1319,45 @@ DEFPY_YANG(
"Maximum prefix length\n")
{
int64_t sseq;
+ int arg_idx = 0;
+ struct plist_dup_args pda = {};
char xpath[XPATH_MAXLEN];
char xpath_entry[XPATH_MAXLEN + 128];
/*
+ * Backward compatibility: don't complain about duplicated values,
+ * just silently accept.
+ */
+ if (seq_str == NULL) {
+ pda.pda_type = "ipv4";
+ pda.pda_name = name;
+ if (prefix_str) {
+ pda.pda_xpath[arg_idx] = "./ipv4-prefix";
+ pda.pda_value[arg_idx] = prefix_str;
+ arg_idx++;
+ if (ge_str) {
+ pda.pda_xpath[arg_idx] =
+ "./ipv4-prefix-length-greater-or-equal";
+ pda.pda_value[arg_idx] = ge_str;
+ arg_idx++;
+ }
+ if (le_str) {
+ pda.pda_xpath[arg_idx] =
+ "./ipv4-prefix-length-lesser-or-equal";
+ pda.pda_value[arg_idx] = le_str;
+ arg_idx++;
+ }
+ } else {
+ pda.pda_xpath[0] = "./any";
+ pda.pda_value[0] = "";
+ }
+
+ /* Duplicated entry without sequence, just quit. */
+ if (plist_is_dup(vty->candidate_config->dnode, &pda))
+ return CMD_SUCCESS;
+ }
+
+ /*
* Create the prefix-list first, so we can generate sequence if
* none given (backward compatibility).
*/
@@ -1331,10 +1514,45 @@ DEFPY_YANG(
"Minimum prefix length\n")
{
int64_t sseq;
+ int arg_idx = 0;
+ struct plist_dup_args pda = {};
char xpath[XPATH_MAXLEN];
char xpath_entry[XPATH_MAXLEN + 128];
/*
+ * Backward compatibility: don't complain about duplicated values,
+ * just silently accept.
+ */
+ if (seq_str == NULL) {
+ pda.pda_type = "ipv6";
+ pda.pda_name = name;
+ if (prefix_str) {
+ pda.pda_xpath[arg_idx] = "./ipv6-prefix";
+ pda.pda_value[arg_idx] = prefix_str;
+ arg_idx++;
+ if (ge_str) {
+ pda.pda_xpath[arg_idx] =
+ "./ipv6-prefix-length-greater-or-equal";
+ pda.pda_value[arg_idx] = ge_str;
+ arg_idx++;
+ }
+ if (le_str) {
+ pda.pda_xpath[arg_idx] =
+ "./ipv6-prefix-length-lesser-or-equal";
+ pda.pda_value[arg_idx] = le_str;
+ arg_idx++;
+ }
+ } else {
+ pda.pda_xpath[0] = "./any";
+ pda.pda_value[0] = "";
+ }
+
+ /* Duplicated entry without sequence, just quit. */
+ if (plist_is_dup(vty->candidate_config->dnode, &pda))
+ return CMD_SUCCESS;
+ }
+
+ /*
* Create the prefix-list first, so we can generate sequence if
* none given (backward compatibility).
*/
diff --git a/lib/filter_nb.c b/lib/filter_nb.c
index 1d522bdbec..2007b37cdf 100644
--- a/lib/filter_nb.c
+++ b/lib/filter_nb.c
@@ -133,6 +133,220 @@ static void cisco_unset_addr_mask(struct in_addr *addr, struct in_addr *mask)
mask->s_addr = CISCO_BIN_HOST_WILDCARD_MASK;
}
+static int _acl_is_dup(const struct lyd_node *dnode, void *arg)
+{
+ struct acl_dup_args *ada = arg;
+ int idx;
+
+ /* This entry is the caller, so skip it. */
+ if (ada->ada_entry_dnode
+ && ada->ada_entry_dnode == dnode)
+ return YANG_ITER_CONTINUE;
+
+ /* Check if all values match. */
+ for (idx = 0; idx < ADA_MAX_VALUES; idx++) {
+ /* No more values. */
+ if (ada->ada_xpath[idx] == NULL)
+ break;
+
+ /* Not same type, just skip it. */
+ if (!yang_dnode_exists(dnode, ada->ada_xpath[idx]))
+ return YANG_ITER_CONTINUE;
+
+ /* Check if different value. */
+ if (strcmp(yang_dnode_get_string(dnode, ada->ada_xpath[idx]),
+ ada->ada_value[idx]))
+ return YANG_ITER_CONTINUE;
+ }
+
+ ada->ada_found = true;
+
+ return YANG_ITER_STOP;
+}
+
+bool acl_is_dup(const struct lyd_node *dnode, struct acl_dup_args *ada)
+{
+ ada->ada_found = false;
+
+ yang_dnode_iterate(
+ _acl_is_dup, ada, dnode,
+ "/frr-filter:lib/access-list[type='%s'][name='%s']/entry",
+ ada->ada_type, ada->ada_name);
+
+ return ada->ada_found;
+}
+
+static bool acl_cisco_is_dup(const struct lyd_node *dnode)
+{
+ const struct lyd_node *entry_dnode =
+ yang_dnode_get_parent(dnode, "entry");
+ struct acl_dup_args ada = {};
+ int idx = 0, arg_idx = 0;
+ static const char *cisco_entries[] = {
+ "./host",
+ "./network/address",
+ "./network/mask",
+ "./source-any",
+ "./destination-host",
+ "./destination-network/address",
+ "./destination-network/mask",
+ "./destination-any",
+ NULL
+ };
+
+ /* Initialize. */
+ ada.ada_type = "ipv4";
+ ada.ada_name = yang_dnode_get_string(entry_dnode, "../name");
+ ada.ada_entry_dnode = entry_dnode;
+
+ /* Load all values/XPaths. */
+ while (cisco_entries[idx] != NULL) {
+ if (!yang_dnode_exists(entry_dnode, cisco_entries[idx])) {
+ idx++;
+ continue;
+ }
+
+ ada.ada_xpath[arg_idx] = cisco_entries[idx];
+ ada.ada_value[arg_idx] =
+ yang_dnode_get_string(entry_dnode, cisco_entries[idx]);
+ arg_idx++;
+ idx++;
+ }
+
+ return acl_is_dup(entry_dnode, &ada);
+}
+
+static bool acl_zebra_is_dup(const struct lyd_node *dnode,
+ enum yang_access_list_type type)
+{
+ const struct lyd_node *entry_dnode =
+ yang_dnode_get_parent(dnode, "entry");
+ struct acl_dup_args ada = {};
+ int idx = 0, arg_idx = 0;
+ static const char *zebra_entries[] = {
+ "./ipv4-prefix",
+ "./ipv4-exact-match",
+ "./ipv6-prefix",
+ "./ipv6-exact-match",
+ "./mac",
+ "./any",
+ NULL
+ };
+
+ /* Initialize. */
+ switch (type) {
+ case YALT_IPV4:
+ ada.ada_type = "ipv4";
+ break;
+ case YALT_IPV6:
+ ada.ada_type = "ipv6";
+ break;
+ case YALT_MAC:
+ ada.ada_type = "mac";
+ break;
+ }
+ ada.ada_name = yang_dnode_get_string(entry_dnode, "../name");
+ ada.ada_entry_dnode = entry_dnode;
+
+ /* Load all values/XPaths. */
+ while (zebra_entries[idx] != NULL) {
+ if (!yang_dnode_exists(entry_dnode, zebra_entries[idx])) {
+ idx++;
+ continue;
+ }
+
+ ada.ada_xpath[arg_idx] = zebra_entries[idx];
+ ada.ada_value[arg_idx] =
+ yang_dnode_get_string(entry_dnode, zebra_entries[idx]);
+ arg_idx++;
+ idx++;
+ }
+
+ return acl_is_dup(entry_dnode, &ada);
+}
+
+static int _plist_is_dup(const struct lyd_node *dnode, void *arg)
+{
+ struct plist_dup_args *pda = arg;
+ int idx;
+
+ /* This entry is the caller, so skip it. */
+ if (pda->pda_entry_dnode
+ && pda->pda_entry_dnode == dnode)
+ return YANG_ITER_CONTINUE;
+
+ /* Check if all values match. */
+ for (idx = 0; idx < PDA_MAX_VALUES; idx++) {
+ /* No more values. */
+ if (pda->pda_xpath[idx] == NULL)
+ break;
+
+ /* Not same type, just skip it. */
+ if (!yang_dnode_exists(dnode, pda->pda_xpath[idx]))
+ return YANG_ITER_CONTINUE;
+
+ /* Check if different value. */
+ if (strcmp(yang_dnode_get_string(dnode, pda->pda_xpath[idx]),
+ pda->pda_value[idx]))
+ return YANG_ITER_CONTINUE;
+ }
+
+ pda->pda_found = true;
+
+ 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 = {};
+ int idx = 0, arg_idx = 0;
+ static const char *entries[] = {
+ "./ipv4-prefix",
+ "./ipv4-prefix-length-greater-or-equal",
+ "./ipv4-prefix-length-lesser-or-equal",
+ "./ipv6-prefix",
+ "./ipv6-prefix-length-greater-or-equal",
+ "./ipv6-prefix-length-lesser-or-equal",
+ "./any",
+ NULL
+ };
+
+ /* Initialize. */
+ pda.pda_type = yang_dnode_get_string(entry_dnode, "../type");
+ pda.pda_name = yang_dnode_get_string(entry_dnode, "../name");
+ pda.pda_entry_dnode = entry_dnode;
+
+ /* Load all values/XPaths. */
+ while (entries[idx] != NULL) {
+ if (!yang_dnode_exists(entry_dnode, entries[idx])) {
+ idx++;
+ continue;
+ }
+
+ pda.pda_xpath[arg_idx] = entries[idx];
+ pda.pda_value[arg_idx] =
+ yang_dnode_get_string(entry_dnode, entries[idx]);
+ arg_idx++;
+ idx++;
+ }
+
+ return plist_is_dup(entry_dnode, &pda);
+}
+
/*
* XPath: /frr-filter:lib/access-list
*/
@@ -290,6 +504,19 @@ lib_access_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args)
struct filter_zebra *fz;
struct filter *f;
+ /* Don't allow duplicated values. */
+ if (args->event == NB_EV_VALIDATE) {
+ if (acl_zebra_is_dup(
+ args->dnode,
+ yang_dnode_get_enum(args->dnode, "../../type"))) {
+ snprintfrr(args->errmsg, args->errmsg_len,
+ "duplicated access list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -330,6 +557,19 @@ lib_access_list_entry_ipv4_exact_match_modify(struct nb_cb_modify_args *args)
struct filter_zebra *fz;
struct filter *f;
+ /* Don't allow duplicated values. */
+ if (args->event == NB_EV_VALIDATE) {
+ if (acl_zebra_is_dup(
+ args->dnode,
+ yang_dnode_get_enum(args->dnode, "../../type"))) {
+ snprintfrr(args->errmsg, args->errmsg_len,
+ "duplicated access list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -369,6 +609,17 @@ lib_access_list_entry_host_modify(struct nb_cb_modify_args *args)
struct filter_cisco *fc;
struct filter *f;
+ /* Don't allow duplicated values. */
+ if (args->event == NB_EV_VALIDATE) {
+ if (acl_cisco_is_dup(args->dnode)) {
+ snprintfrr(args->errmsg, args->errmsg_len,
+ "duplicated access list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -410,6 +661,17 @@ lib_access_list_entry_network_address_modify(struct nb_cb_modify_args *args)
struct filter_cisco *fc;
struct filter *f;
+ /* Don't allow duplicated values. */
+ if (args->event == NB_EV_VALIDATE) {
+ if (acl_cisco_is_dup(args->dnode)) {
+ snprintfrr(args->errmsg, args->errmsg_len,
+ "duplicated access list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -432,6 +694,17 @@ lib_access_list_entry_network_mask_modify(struct nb_cb_modify_args *args)
struct filter_cisco *fc;
struct filter *f;
+ /* Don't allow duplicated values. */
+ if (args->event == NB_EV_VALIDATE) {
+ if (acl_cisco_is_dup(args->dnode)) {
+ snprintfrr(args->errmsg, args->errmsg_len,
+ "duplicated access list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -454,6 +727,17 @@ lib_access_list_entry_source_any_create(struct nb_cb_create_args *args)
struct filter_cisco *fc;
struct filter *f;
+ /* Don't allow duplicated values. */
+ if (args->event == NB_EV_VALIDATE) {
+ if (acl_cisco_is_dup(args->dnode)) {
+ snprintfrr(args->errmsg, args->errmsg_len,
+ "duplicated access list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -495,6 +779,17 @@ static int lib_access_list_entry_destination_host_modify(
struct filter_cisco *fc;
struct filter *f;
+ /* Don't allow duplicated values. */
+ if (args->event == NB_EV_VALIDATE) {
+ if (acl_cisco_is_dup(args->dnode)) {
+ snprintfrr(args->errmsg, args->errmsg_len,
+ "duplicated access list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -537,6 +832,17 @@ static int lib_access_list_entry_destination_network_address_modify(
struct filter_cisco *fc;
struct filter *f;
+ /* Don't allow duplicated values. */
+ if (args->event == NB_EV_VALIDATE) {
+ if (acl_cisco_is_dup(args->dnode)) {
+ snprintfrr(args->errmsg, args->errmsg_len,
+ "duplicated access list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -559,6 +865,17 @@ static int lib_access_list_entry_destination_network_mask_modify(
struct filter_cisco *fc;
struct filter *f;
+ /* Don't allow duplicated values. */
+ if (args->event == NB_EV_VALIDATE) {
+ if (acl_cisco_is_dup(args->dnode)) {
+ snprintfrr(args->errmsg, args->errmsg_len,
+ "duplicated access list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -581,6 +898,17 @@ static int lib_access_list_entry_destination_any_create(
struct filter_cisco *fc;
struct filter *f;
+ /* Don't allow duplicated values. */
+ if (args->event == NB_EV_VALIDATE) {
+ if (acl_cisco_is_dup(args->dnode)) {
+ snprintfrr(args->errmsg, args->errmsg_len,
+ "duplicated access list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -623,6 +951,19 @@ static int lib_access_list_entry_any_create(struct nb_cb_create_args *args)
struct filter *f;
int type;
+ /* Don't allow duplicated values. */
+ if (args->event == NB_EV_VALIDATE) {
+ if (acl_zebra_is_dup(
+ args->dnode,
+ yang_dnode_get_enum(args->dnode, "../../type"))) {
+ snprintfrr(args->errmsg, args->errmsg_len,
+ "duplicated access list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -817,15 +1158,12 @@ lib_prefix_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args)
struct prefix p;
if (args->event == NB_EV_VALIDATE) {
- /*
- * TODO: validate prefix_entry_dup_check() passes.
- *
- * This needs to be implemented using YANG lyd_node
- * navigation, because the `priv` data structures are not
- * available at `NB_EV_VALIDATE` phase. An easier
- * alternative would be mark `ipvx-prefix` as unique
- * (see RFC 7950, Section 7.8.3. The list "unique" Statement).
- */
+ 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;
}
@@ -888,6 +1226,16 @@ static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify(
prefix_list_length_validate(args) != NB_OK)
return NB_ERR_VALIDATION;
+ 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;
@@ -937,6 +1285,16 @@ static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify(
prefix_list_length_validate(args) != NB_OK)
return NB_ERR_VALIDATION;
+ 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;
@@ -982,6 +1340,16 @@ 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;
diff --git a/lib/hash.h b/lib/hash.h
index 6fbdc67cc4..23e93b6d7d 100644
--- a/lib/hash.h
+++ b/lib/hash.h
@@ -137,7 +137,7 @@ extern struct hash *hash_create(unsigned int (*hash_key)(const void *),
*
* hash_cmp
* comparison function used for resolving collisions; when called with two
- * data items, should return nonzero if the two items are equal and 0
+ * data items, should return true if the two items are equal and false
* otherwise
*
* name
diff --git a/lib/yang.c b/lib/yang.c
index 22fe938e4c..a3e2a395d7 100644
--- a/lib/yang.c
+++ b/lib/yang.c
@@ -468,7 +468,7 @@ void yang_dnode_iterate(yang_dnode_iter_cb cb, void *arg,
dnode = set->set.d[i];
ret = (*cb)(dnode, arg);
if (ret == YANG_ITER_STOP)
- return;
+ break;
}
ly_set_free(set);
diff --git a/lib/zclient.c b/lib/zclient.c
index ba94b7fb99..cb4555650d 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -1182,6 +1182,12 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_TABLEID))
stream_putl(s, api->tableid);
+ if (CHECK_FLAG(api->message, ZAPI_MESSAGE_OPAQUE)) {
+ assert(api->opaque.length <= ZAPI_MESSAGE_OPAQUE_LENGTH);
+
+ stream_putw(s, api->opaque.length);
+ stream_write(s, api->opaque.data, api->opaque.length);
+ }
/* Put length at the first point of the stream. */
stream_putw_at(s, 0, stream_get_endp(s));
@@ -1403,6 +1409,13 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_TABLEID))
STREAM_GETL(s, api->tableid);
+ if (CHECK_FLAG(api->message, ZAPI_MESSAGE_OPAQUE)) {
+ STREAM_GETW(s, api->opaque.length);
+ assert(api->opaque.length < ZAPI_MESSAGE_OPAQUE_LENGTH);
+
+ STREAM_GET(api->opaque.data, s, api->opaque.length);
+ }
+
return 0;
stream_failure:
return -1;
diff --git a/lib/zclient.h b/lib/zclient.h
index af4707289f..2af448a20c 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -391,14 +391,14 @@ struct zclient {
/* Backup nexthops are present */
#define ZAPI_MESSAGE_BACKUP_NEXTHOPS 0x40
#define ZAPI_MESSAGE_NHG 0x80
-
/*
* This should only be used by a DAEMON that needs to communicate
* the table being used is not in the VRF. You must pass the
* default vrf, else this will be ignored.
*/
-#define ZAPI_MESSAGE_TABLEID 0x0080
-#define ZAPI_MESSAGE_SRTE 0x0100
+#define ZAPI_MESSAGE_TABLEID 0x0100
+#define ZAPI_MESSAGE_SRTE 0x0200
+#define ZAPI_MESSAGE_OPAQUE 0x0400
#define ZSERV_VERSION 6
/* Zserv protocol message header */
@@ -572,6 +572,12 @@ struct zapi_route {
/* SR-TE color (used for nexthop updates only). */
uint32_t srte_color;
+
+#define ZAPI_MESSAGE_OPAQUE_LENGTH 1024
+ struct {
+ uint16_t length;
+ uint8_t data[ZAPI_MESSAGE_OPAQUE_LENGTH];
+ } opaque;
};
struct zapi_labels {