diff options
Diffstat (limited to '')
-rw-r--r-- | lib/filter_cli.c | 1773 |
1 files changed, 1773 insertions, 0 deletions
diff --git a/lib/filter_cli.c b/lib/filter_cli.c new file mode 100644 index 0000000..39b58f5 --- /dev/null +++ b/lib/filter_cli.c @@ -0,0 +1,1773 @@ +/* + * FRR filter CLI 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 "northbound.h" +#include "prefix.h" + +#include "lib/command.h" +#include "lib/filter.h" +#include "lib/northbound_cli.h" +#include "lib/plist.h" +#include "lib/plist_int.h" +#include "lib/printfrr.h" + +#ifndef VTYSH_EXTRACT_PL +#include "lib/filter_cli_clippy.c" +#endif /* VTYSH_EXTRACT_PL */ + +#define ACCESS_LIST_STR "Access list entry\n" +#define ACCESS_LIST_ZEBRA_STR "Access list name\n" +#define ACCESS_LIST_SEQ_STR \ + "Sequence number of an entry\n" \ + "Sequence number\n" +#define ACCESS_LIST_ACTION_STR \ + "Specify packets to reject\n" \ + "Specify packets to forward\n" +#define ACCESS_LIST_REMARK_STR "Access list entry comment\n" +#define ACCESS_LIST_REMARK_LINE_STR "Comment up to 100 characters\n" + +#define PREFIX_LIST_NAME_STR "Prefix list entry name\n" + +/* + * Helper function to generate a sequence number for legacy commands. + */ +static int acl_get_seq_cb(const struct lyd_node *dnode, void *arg) +{ + int64_t *seq = arg; + int64_t cur_seq = yang_dnode_get_uint32(dnode, "sequence"); + + if (cur_seq > *seq) + *seq = cur_seq; + + return YANG_ITER_CONTINUE; +} + +/** + * Helper function that iterates over the XPath `xpath` on the candidate + * configuration in `vty->candidate_config`. + * + * \param[in] vty shell context with the candidate configuration. + * \param[in] xpath the XPath to look for the sequence leaf. + * \returns next unused sequence number. + */ +static long acl_get_seq(struct vty *vty, const char *xpath) +{ + int64_t seq = 0; + + yang_dnode_iterate(acl_get_seq_cb, &seq, vty->candidate_config->dnode, + "%s/entry", xpath); + + return seq + 5; +} + +static int acl_remove_if_empty(struct vty *vty, const char *iptype, + const char *name) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='%s'][name='%s']/remark", + iptype, name); + /* List is not empty if there is a remark, check that: */ + if (yang_dnode_exists(vty->candidate_config->dnode, xpath)) + return CMD_SUCCESS; + + /* Check if we have any entries: */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='%s'][name='%s']", iptype, + name); + /* + * NOTE: if the list is empty it will return the first sequence + * number: 5. + */ + if (acl_get_seq(vty, xpath) != 5) + return CMD_SUCCESS; + + /* Nobody is using this list, lets remove it. */ + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static int acl_remove(struct vty *vty, const char *iptype, const char *name, + int64_t sseq) +{ + char xpath[XPATH_MAXLEN]; + int rv; + + snprintfrr( + xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='%s'][name='%s']/entry[sequence='%" PRId64 "']", + iptype, name, sseq); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) + return acl_remove_if_empty(vty, iptype, name); + + return rv; +} + +/* + * Cisco (legacy) access lists. + */ +DEFPY_YANG( + access_list_std, access_list_std_cmd, + "access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask>", + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "A single host address\n" + "Address to match\n" + "Address to match\n" + "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. + */ + ada.ada_type = "ipv4"; + ada.ada_name = name; + ada.ada_action = action; + 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] = ""; + } + + 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). + */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (seq_str == NULL) { + /* Use XPath to find the next sequence number. */ + sseq = acl_get_seq(vty, xpath); + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + } else + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%s']", xpath, seq_str); + + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL); + + nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action); + if (host_str != NULL && mask_str == NULL) { + nb_cli_enqueue_change(vty, "./host", NB_OP_MODIFY, host_str); + } else if (host_str != NULL && mask_str != NULL) { + nb_cli_enqueue_change(vty, "./network/address", NB_OP_MODIFY, + host_str); + nb_cli_enqueue_change(vty, "./network/mask", NB_OP_MODIFY, + mask_str); + } else { + nb_cli_enqueue_change(vty, "./source-any", NB_OP_CREATE, NULL); + } + + return nb_cli_apply_changes(vty, xpath_entry); +} + +DEFPY_YANG( + no_access_list_std, no_access_list_std_cmd, + "no access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask>", + NO_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "A single host address\n" + "Address to match\n" + "Address to match\n" + "Wildcard bits\n") +{ + int64_t sseq; + struct acl_dup_args ada = {}; + + /* If the user provided sequence number, then just go for it. */ + if (seq_str != NULL) + return acl_remove(vty, "ipv4", name, seq); + + /* Otherwise, to keep compatibility, we need to figure it out. */ + ada.ada_type = "ipv4"; + ada.ada_name = name; + ada.ada_action = action; + 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] = ""; + } + + if (acl_is_dup(vty->candidate_config->dnode, &ada)) + sseq = ada.ada_seq; + else + return CMD_WARNING_CONFIG_FAILED; + + return acl_remove(vty, "ipv4", name, sseq); +} + +DEFPY_YANG( + access_list_ext, access_list_ext_cmd, + "access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>", + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "IPv4 address\n" + "Source address to match\n" + "Source address mask to apply\n" + "Single source host\n" + "Source address to match\n" + "Any source host\n" + "Destination address to match\n" + "Destination address mask to apply\n" + "Single destination host\n" + "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. + */ + ada.ada_type = "ipv4"; + ada.ada_name = name; + ada.ada_action = action; + 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] = ""; + 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] = ""; + idx++; + } + + 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). + */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (seq_str == NULL) { + /* Use XPath to find the next sequence number. */ + sseq = acl_get_seq(vty, xpath); + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + } else + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%s']", xpath, seq_str); + + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL); + + nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action); + if (src_str != NULL && src_mask_str == NULL) { + nb_cli_enqueue_change(vty, "./host", NB_OP_MODIFY, src_str); + } else if (src_str != NULL && src_mask_str != NULL) { + nb_cli_enqueue_change(vty, "./network/address", NB_OP_MODIFY, + src_str); + nb_cli_enqueue_change(vty, "./network/mask", NB_OP_MODIFY, + src_mask_str); + } else { + nb_cli_enqueue_change(vty, "./source-any", NB_OP_CREATE, NULL); + } + + if (dst_str != NULL && dst_mask_str == NULL) { + nb_cli_enqueue_change(vty, "./destination-host", NB_OP_MODIFY, + dst_str); + } else if (dst_str != NULL && dst_mask_str != NULL) { + nb_cli_enqueue_change(vty, "./destination-network/address", + NB_OP_MODIFY, dst_str); + nb_cli_enqueue_change(vty, "./destination-network/mask", + NB_OP_MODIFY, dst_mask_str); + } else { + nb_cli_enqueue_change(vty, "./destination-any", NB_OP_CREATE, + NULL); + } + + return nb_cli_apply_changes(vty, xpath_entry); +} + +DEFPY_YANG( + no_access_list_ext, no_access_list_ext_cmd, + "no access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>", + NO_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "Any Internet Protocol\n" + "Source address to match\n" + "Source address mask to apply\n" + "Single source host\n" + "Source address to match\n" + "Any source host\n" + "Destination address to match\n" + "Destination address mask to apply\n" + "Single destination host\n" + "Destination address to match\n" + "Any destination host\n") +{ + int idx = 0; + int64_t sseq; + struct acl_dup_args ada = {}; + + /* If the user provided sequence number, then just go for it. */ + if (seq_str != NULL) + return acl_remove(vty, "ipv4", name, seq); + + /* Otherwise, to keep compatibility, we need to figure it out. */ + ada.ada_type = "ipv4"; + ada.ada_name = name; + ada.ada_action = action; + 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] = ""; + 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] = ""; + idx++; + } + + if (acl_is_dup(vty->candidate_config->dnode, &ada)) + sseq = ada.ada_seq; + else + return CMD_WARNING_CONFIG_FAILED; + + return acl_remove(vty, "ipv4", name, sseq); +} + +/* + * Zebra access lists. + */ +DEFPY_YANG( + access_list, access_list_cmd, + "access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <A.B.C.D/M$prefix [exact-match$exact]|any>", + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "Prefix to match. e.g. 10.0.0.0/8\n" + "Exact match of the prefixes\n" + "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. + */ + ada.ada_type = "ipv4"; + ada.ada_name = name; + ada.ada_action = action; + + 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] = ""; + } + + 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). + */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (seq_str == NULL) { + /* Use XPath to find the next sequence number. */ + sseq = acl_get_seq(vty, xpath); + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + } else + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%s']", xpath, seq_str); + + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL); + + nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action); + if (prefix_str != NULL) { + nb_cli_enqueue_change(vty, "./ipv4-prefix", NB_OP_MODIFY, + prefix_str); + nb_cli_enqueue_change(vty, "./ipv4-exact-match", NB_OP_MODIFY, + exact ? "true" : "false"); + } else { + nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL); + } + + return nb_cli_apply_changes(vty, xpath_entry); +} + +DEFPY_YANG( + no_access_list, no_access_list_cmd, + "no access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <A.B.C.D/M$prefix [exact-match$exact]|any>", + NO_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "Prefix to match. e.g. 10.0.0.0/8\n" + "Exact match of the prefixes\n" + "Match any IPv4\n") +{ + int64_t sseq; + struct acl_dup_args ada = {}; + + /* If the user provided sequence number, then just go for it. */ + if (seq_str != NULL) + return acl_remove(vty, "ipv4", name, seq); + + /* Otherwise, to keep compatibility, we need to figure it out. */ + ada.ada_type = "ipv4"; + ada.ada_name = name; + ada.ada_action = action; + + 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] = ""; + } + + if (acl_is_dup(vty->candidate_config->dnode, &ada)) + sseq = ada.ada_seq; + else + return CMD_WARNING_CONFIG_FAILED; + + return acl_remove(vty, "ipv4", name, sseq); +} + +DEFPY_YANG( + no_access_list_all, no_access_list_all_cmd, + "no access-list WORD$name", + NO_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + access_list_remark, access_list_remark_cmd, + "access-list WORD$name remark LINE...", + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) +{ + int rv; + char *remark; + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + remark = argv_concat(argv, argc, 3); + nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark); + rv = nb_cli_apply_changes(vty, xpath); + XFREE(MTYPE_TMP, remark); + + return rv; +} + +DEFPY_YANG( + no_access_list_remark, no_access_list_remark_cmd, + "no access-list WORD$name remark", + NO_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR) +{ + char xpath[XPATH_MAXLEN]; + int rv; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']/remark", + name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) + return acl_remove_if_empty(vty, "ipv4", name); + + return rv; +} + +ALIAS( + no_access_list_remark, no_access_list_remark_line_cmd, + "no access-list WORD$name remark LINE...", + NO_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) + +DEFPY_YANG( + ipv6_access_list, ipv6_access_list_cmd, + "ipv6 access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X::X:X/M$prefix [exact-match$exact]|any>", + IPV6_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "IPv6 prefix\n" + "Exact match of the prefixes\n" + "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. + */ + ada.ada_type = "ipv6"; + ada.ada_name = name; + ada.ada_action = action; + + 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] = ""; + } + + 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). + */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv6'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (seq_str == NULL) { + /* Use XPath to find the next sequence number. */ + sseq = acl_get_seq(vty, xpath); + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + } else + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%s']", xpath, seq_str); + + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL); + + nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action); + if (prefix_str != NULL) { + nb_cli_enqueue_change(vty, "./ipv6-prefix", NB_OP_MODIFY, + prefix_str); + nb_cli_enqueue_change(vty, "./ipv6-exact-match", NB_OP_MODIFY, + exact ? "true" : "false"); + } else { + nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL); + } + + return nb_cli_apply_changes(vty, xpath_entry); +} + +DEFPY_YANG( + no_ipv6_access_list, no_ipv6_access_list_cmd, + "no ipv6 access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X::X:X/M$prefix [exact-match$exact]|any>", + NO_STR + IPV6_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "IPv6 prefix\n" + "Exact match of the prefixes\n" + "Match any IPv6\n") +{ + int64_t sseq; + struct acl_dup_args ada = {}; + + /* If the user provided sequence number, then just go for it. */ + if (seq_str != NULL) + return acl_remove(vty, "ipv6", name, seq); + + /* Otherwise, to keep compatibility, we need to figure it out. */ + ada.ada_type = "ipv6"; + ada.ada_name = name; + ada.ada_action = action; + + 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] = ""; + } + + if (acl_is_dup(vty->candidate_config->dnode, &ada)) + sseq = ada.ada_seq; + else + return CMD_WARNING_CONFIG_FAILED; + + return acl_remove(vty, "ipv6", name, sseq); +} + +DEFPY_YANG( + no_ipv6_access_list_all, no_ipv6_access_list_all_cmd, + "no ipv6 access-list WORD$name", + NO_STR + IPV6_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv6'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + ipv6_access_list_remark, ipv6_access_list_remark_cmd, + "ipv6 access-list WORD$name remark LINE...", + IPV6_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) +{ + int rv; + char *remark; + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv6'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + remark = argv_concat(argv, argc, 4); + nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark); + rv = nb_cli_apply_changes(vty, xpath); + XFREE(MTYPE_TMP, remark); + + return rv; +} + +DEFPY_YANG( + no_ipv6_access_list_remark, no_ipv6_access_list_remark_cmd, + "no ipv6 access-list WORD$name remark", + NO_STR + IPV6_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR) +{ + char xpath[XPATH_MAXLEN]; + int rv; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv6'][name='%s']/remark", + name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) + return acl_remove_if_empty(vty, "ipv6", name); + + return rv; +} + +ALIAS( + no_ipv6_access_list_remark, no_ipv6_access_list_remark_line_cmd, + "no ipv6 access-list ACCESSLIST6_NAME$name remark LINE...", + NO_STR + IPV6_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) + +DEFPY_YANG( + mac_access_list, mac_access_list_cmd, + "mac access-list ACCESSLIST_MAC_NAME$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X:X:X:X:X$mac|any>", + MAC_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "MAC address\n" + "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. + */ + ada.ada_type = "mac"; + ada.ada_name = name; + ada.ada_action = action; + + if (mac_str) { + ada.ada_xpath[0] = "./mac"; + ada.ada_value[0] = mac_str; + } else { + ada.ada_xpath[0] = "./any"; + ada.ada_value[0] = ""; + } + + 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). + */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='mac'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (seq_str == NULL) { + /* Use XPath to find the next sequence number. */ + sseq = acl_get_seq(vty, xpath); + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + } else + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%s']", xpath, seq_str); + + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL); + + nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action); + if (mac_str != NULL) { + nb_cli_enqueue_change(vty, "./mac", NB_OP_MODIFY, mac_str); + } else { + nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL); + } + + return nb_cli_apply_changes(vty, xpath_entry); +} + +DEFPY_YANG( + no_mac_access_list, no_mac_access_list_cmd, + "no mac access-list ACCESSLIST_MAC_NAME$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X:X:X:X:X$mac|any>", + NO_STR + MAC_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "MAC address\n" + "Match any MAC address\n") +{ + int64_t sseq; + struct acl_dup_args ada = {}; + + /* If the user provided sequence number, then just go for it. */ + if (seq_str != NULL) + return acl_remove(vty, "mac", name, seq); + + /* Otherwise, to keep compatibility, we need to figure it out. */ + ada.ada_type = "mac"; + ada.ada_name = name; + ada.ada_action = action; + + if (mac_str) { + ada.ada_xpath[0] = "./mac"; + ada.ada_value[0] = mac_str; + } else { + ada.ada_xpath[0] = "./any"; + ada.ada_value[0] = ""; + } + + if (acl_is_dup(vty->candidate_config->dnode, &ada)) + sseq = ada.ada_seq; + else + return CMD_WARNING_CONFIG_FAILED; + + return acl_remove(vty, "mac", name, sseq); +} + +DEFPY_YANG( + no_mac_access_list_all, no_mac_access_list_all_cmd, + "no mac access-list ACCESSLIST_MAC_NAME$name", + NO_STR + MAC_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='mac'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + mac_access_list_remark, mac_access_list_remark_cmd, + "mac access-list ACCESSLIST_MAC_NAME$name remark LINE...", + MAC_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) +{ + int rv; + char *remark; + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='mac'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + remark = argv_concat(argv, argc, 4); + nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark); + rv = nb_cli_apply_changes(vty, xpath); + XFREE(MTYPE_TMP, remark); + + return rv; +} + +DEFPY_YANG( + no_mac_access_list_remark, no_mac_access_list_remark_cmd, + "no mac access-list ACCESSLIST_MAC_NAME$name remark", + NO_STR + MAC_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR) +{ + char xpath[XPATH_MAXLEN]; + int rv; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='mac'][name='%s']/remark", + name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) + return acl_remove_if_empty(vty, "mac", name); + + return rv; +} + +ALIAS( + no_mac_access_list_remark, no_mac_access_list_remark_line_cmd, + "no mac access-list ACCESSLIST_MAC_NAME$name remark LINE...", + NO_STR + MAC_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) + +int access_list_cmp(const struct lyd_node *dnode1, + const struct lyd_node *dnode2) +{ + uint32_t seq1 = yang_dnode_get_uint32(dnode1, "./sequence"); + uint32_t seq2 = yang_dnode_get_uint32(dnode2, "./sequence"); + + return seq1 - seq2; +} + +void access_list_show(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + int type = yang_dnode_get_enum(dnode, "../type"); + struct prefix p; + bool is_any; + bool is_exact = false; + bool cisco_style = false; + bool cisco_extended = false; + struct in_addr addr, mask; + char macstr[PREFIX2STR_BUFFER]; + + is_any = yang_dnode_exists(dnode, "./any"); + switch (type) { + case YALT_IPV4: + if (is_any) + break; + + if (yang_dnode_exists(dnode, "./host") + || yang_dnode_exists(dnode, "./network/address") + || yang_dnode_exists(dnode, "./source-any")) { + cisco_style = true; + if (yang_dnode_exists(dnode, "./destination-host") + || yang_dnode_exists( + dnode, "./destination-network/address") + || yang_dnode_exists(dnode, "./destination-any")) + cisco_extended = true; + } else { + yang_dnode_get_prefix(&p, dnode, "./ipv4-prefix"); + is_exact = yang_dnode_get_bool(dnode, + "./ipv4-exact-match"); + } + break; + case YALT_IPV6: /* ipv6 */ + vty_out(vty, "ipv6 "); + if (is_any) + break; + + yang_dnode_get_prefix(&p, dnode, "./ipv6-prefix"); + is_exact = yang_dnode_get_bool(dnode, "./ipv6-exact-match"); + break; + case YALT_MAC: /* mac */ + vty_out(vty, "mac "); + if (is_any) + break; + + yang_dnode_get_prefix(&p, dnode, "./mac"); + break; + } + + vty_out(vty, "access-list %s seq %s %s", + yang_dnode_get_string(dnode, "../name"), + yang_dnode_get_string(dnode, "./sequence"), + yang_dnode_get_string(dnode, "./action")); + + /* Handle Cisco style access lists. */ + if (cisco_style) { + if (cisco_extended) + vty_out(vty, " ip"); + + if (yang_dnode_exists(dnode, "./network")) { + yang_dnode_get_ipv4(&addr, dnode, "./network/address"); + yang_dnode_get_ipv4(&mask, dnode, "./network/mask"); + vty_out(vty, " %pI4 %pI4", &addr, &mask); + } else if (yang_dnode_exists(dnode, "./host")) { + if (cisco_extended) + vty_out(vty, " host"); + + vty_out(vty, " %s", + yang_dnode_get_string(dnode, "./host")); + } else if (yang_dnode_exists(dnode, "./source-any")) + vty_out(vty, " any"); + + /* Not extended, exit earlier. */ + if (!cisco_extended) { + vty_out(vty, "\n"); + return; + } + + /* Handle destination address. */ + if (yang_dnode_exists(dnode, "./destination-network")) { + yang_dnode_get_ipv4(&addr, dnode, + "./destination-network/address"); + yang_dnode_get_ipv4(&mask, dnode, + "./destination-network/mask"); + vty_out(vty, " %pI4 %pI4", &addr, &mask); + } else if (yang_dnode_exists(dnode, "./destination-host")) + vty_out(vty, " host %s", + yang_dnode_get_string(dnode, + "./destination-host")); + else if (yang_dnode_exists(dnode, "./destination-any")) + vty_out(vty, " any"); + + vty_out(vty, "\n"); + return; + } + + /* Zebra style access list. */ + if (!is_any) { + /* If type is MAC don't show '/mask'. */ + if (type == 2 /* mac */) { + prefix_mac2str(&p.u.prefix_eth, macstr, sizeof(macstr)); + vty_out(vty, " %s", macstr); + } else + vty_out(vty, " %pFX", &p); + } else + vty_out(vty, " any"); + + if (is_exact) + vty_out(vty, " exact-match"); + + vty_out(vty, "\n"); +} + +void access_list_remark_show(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + int type = yang_dnode_get_enum(dnode, "../type"); + + switch (type) { + case YALT_IPV4: + break; + case YALT_IPV6: + vty_out(vty, "ipv6 "); + break; + case YALT_MAC: + vty_out(vty, "mac "); + break; + } + + vty_out(vty, "access-list %s remark %s\n", + yang_dnode_get_string(dnode, "../name"), + yang_dnode_get_string(dnode, NULL)); +} + +/* + * Prefix lists. + */ + +/** + * Remove main data structure prefix list if there are no more entries or + * remark. This fixes compatibility with old CLI and tests. + */ +static int plist_remove_if_empty(struct vty *vty, const char *iptype, + const char *name) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='%s'][name='%s']/remark", + iptype, name); + /* List is not empty if there is a remark, check that: */ + if (yang_dnode_exists(vty->candidate_config->dnode, xpath)) + return CMD_SUCCESS; + + /* Check if we have any entries: */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='%s'][name='%s']", iptype, + name); + /* + * NOTE: if the list is empty it will return the first sequence + * number: 5. + */ + if (acl_get_seq(vty, xpath) != 5) + return CMD_SUCCESS; + + /* Nobody is using this list, lets remove it. */ + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static int plist_remove(struct vty *vty, const char *iptype, const char *name, + const char *seq, const char *action, + union prefixconstptr prefix, int ge, int le) +{ + int64_t sseq; + struct plist_dup_args pda = {}; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 32]; + int rv; + + /* If the user provided sequence number, then just go for it. */ + if (seq != NULL) { + snprintf( + xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='%s'][name='%s']/entry[sequence='%s']", + iptype, name, seq); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) + return plist_remove_if_empty(vty, iptype, name); + + return rv; + } + + /* Otherwise, to keep compatibility, we need to figure it out. */ + pda.pda_type = iptype; + pda.pda_name = name; + pda.pda_action = action; + if (prefix.p) { + prefix_copy(&pda.prefix, prefix); + apply_mask(&pda.prefix); + pda.ge = ge; + pda.le = le; + } else { + pda.any = true; + } + + if (plist_is_dup(vty->candidate_config->dnode, &pda)) + sseq = pda.pda_seq; + else + return CMD_WARNING_CONFIG_FAILED; + + snprintfrr( + xpath_entry, sizeof(xpath_entry), + "/frr-filter:lib/prefix-list[type='%s'][name='%s']/entry[sequence='%" PRId64 "']", + iptype, name, sseq); + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL); + + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) + return plist_remove_if_empty(vty, iptype, name); + + return rv; +} + +DEFPY_YANG( + ip_prefix_list, ip_prefix_list_cmd, + "ip prefix-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <any|A.B.C.D/M$prefix [{ge (0-32)$ge|le (0-32)$le}]>", + IP_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + int64_t sseq; + 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. + */ + pda.pda_type = "ipv4"; + pda.pda_name = name; + pda.pda_action = action; + if (prefix_str) { + prefix_copy(&pda.prefix, prefix); + pda.ge = ge; + pda.le = le; + } else { + pda.any = true; + } + + 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). + */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (seq_str == NULL) { + /* Use XPath to find the next sequence number. */ + sseq = acl_get_seq(vty, xpath); + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + } else + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%s']", xpath, seq_str); + + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL); + + nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action); + if (prefix_str != NULL) { + nb_cli_enqueue_change(vty, "./ipv4-prefix", NB_OP_MODIFY, + prefix_str); + + if (ge_str) { + nb_cli_enqueue_change( + vty, "./ipv4-prefix-length-greater-or-equal", + NB_OP_MODIFY, ge_str); + } else { + /* + * Remove old ge if not being modified + */ + nb_cli_enqueue_change( + vty, "./ipv4-prefix-length-greater-or-equal", + NB_OP_DESTROY, NULL); + } + + if (le_str) { + nb_cli_enqueue_change( + vty, "./ipv4-prefix-length-lesser-or-equal", + NB_OP_MODIFY, le_str); + } else { + /* + * Remove old le if not being modified + */ + nb_cli_enqueue_change( + vty, "./ipv4-prefix-length-lesser-or-equal", + NB_OP_DESTROY, NULL); + } + nb_cli_enqueue_change(vty, "./any", NB_OP_DESTROY, NULL); + } else { + nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL); + } + + return nb_cli_apply_changes(vty, xpath_entry); +} + +DEFPY_YANG( + no_ip_prefix_list, no_ip_prefix_list_cmd, + "no ip prefix-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <any|A.B.C.D/M$prefix [{ge (0-32)|le (0-32)}]>", + NO_STR + IP_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return plist_remove(vty, "ipv4", name, seq_str, action, + prefix_str ? prefix : NULL, ge, le); +} + +DEFPY_YANG( + no_ip_prefix_list_seq, no_ip_prefix_list_seq_cmd, + "no ip prefix-list WORD$name seq (1-4294967295)$seq", + NO_STR + IP_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_SEQ_STR) +{ + return plist_remove(vty, "ipv4", name, seq_str, NULL, NULL, 0, 0); +} + +DEFPY_YANG( + no_ip_prefix_list_all, no_ip_prefix_list_all_cmd, + "no ip prefix-list WORD$name", + NO_STR + IP_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + ip_prefix_list_remark, ip_prefix_list_remark_cmd, + "ip prefix-list WORD$name description LINE...", + IP_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) +{ + int rv; + char *remark; + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + remark = argv_concat(argv, argc, 4); + nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark); + rv = nb_cli_apply_changes(vty, xpath); + XFREE(MTYPE_TMP, remark); + + return rv; +} + +DEFPY_YANG( + no_ip_prefix_list_remark, no_ip_prefix_list_remark_cmd, + "no ip prefix-list WORD$name description", + NO_STR + IP_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_REMARK_STR) +{ + char xpath[XPATH_MAXLEN]; + int rv; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']/remark", + name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) + return plist_remove_if_empty(vty, "ipv4", name); + + return rv; +} + +ALIAS( + no_ip_prefix_list_remark, no_ip_prefix_list_remark_line_cmd, + "no ip prefix-list WORD$name description LINE...", + NO_STR + IP_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) + +DEFPY_YANG( + ipv6_prefix_list, ipv6_prefix_list_cmd, + "ipv6 prefix-list WORD$name [seq (1-4294967295)] <deny|permit>$action <any|X:X::X:X/M$prefix [{ge (0-128)$ge|le (0-128)$le}]>", + IPV6_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "Any prefix match. Same as \"::0/0 le 128\"\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + int64_t sseq; + 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. + */ + pda.pda_type = "ipv6"; + pda.pda_name = name; + pda.pda_action = action; + if (prefix_str) { + prefix_copy(&pda.prefix, prefix); + pda.ge = ge; + pda.le = le; + } else { + pda.any = true; + } + + 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). + */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (seq_str == NULL) { + /* Use XPath to find the next sequence number. */ + sseq = acl_get_seq(vty, xpath); + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + } else + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%s']", xpath, seq_str); + + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL); + + nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action); + if (prefix_str != NULL) { + nb_cli_enqueue_change(vty, "./ipv6-prefix", NB_OP_MODIFY, + prefix_str); + + if (ge_str) { + nb_cli_enqueue_change( + vty, "./ipv6-prefix-length-greater-or-equal", + NB_OP_MODIFY, ge_str); + } else { + /* + * Remove old ge if not being modified + */ + nb_cli_enqueue_change( + vty, "./ipv6-prefix-length-greater-or-equal", + NB_OP_DESTROY, NULL); + } + + if (le_str) { + nb_cli_enqueue_change( + vty, "./ipv6-prefix-length-lesser-or-equal", + NB_OP_MODIFY, le_str); + } else { + /* + * Remove old le if not being modified + */ + nb_cli_enqueue_change( + vty, "./ipv6-prefix-length-lesser-or-equal", + NB_OP_DESTROY, NULL); + } + nb_cli_enqueue_change(vty, "./any", NB_OP_DESTROY, NULL); + } else { + nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL); + } + + return nb_cli_apply_changes(vty, xpath_entry); +} + +DEFPY_YANG( + no_ipv6_prefix_list, no_ipv6_prefix_list_cmd, + "no ipv6 prefix-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <any|X:X::X:X/M$prefix [{ge (0-128)$ge|le (0-128)$le}]>", + NO_STR + IPV6_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "Any prefix match. Same as \"::0/0 le 128\"\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return plist_remove(vty, "ipv6", name, seq_str, action, + prefix_str ? prefix : NULL, ge, le); +} + +DEFPY_YANG( + no_ipv6_prefix_list_seq, no_ipv6_prefix_list_seq_cmd, + "no ipv6 prefix-list WORD$name seq (1-4294967295)$seq", + NO_STR + IPV6_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_SEQ_STR) +{ + return plist_remove(vty, "ipv6", name, seq_str, NULL, NULL, 0, 0); +} + +DEFPY_YANG( + no_ipv6_prefix_list_all, no_ipv6_prefix_list_all_cmd, + "no ipv6 prefix-list WORD$name", + NO_STR + IPV6_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + ipv6_prefix_list_remark, ipv6_prefix_list_remark_cmd, + "ipv6 prefix-list WORD$name description LINE...", + IPV6_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) +{ + int rv; + char *remark; + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + remark = argv_concat(argv, argc, 4); + nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark); + rv = nb_cli_apply_changes(vty, xpath); + XFREE(MTYPE_TMP, remark); + + return rv; +} + +DEFPY_YANG( + no_ipv6_prefix_list_remark, no_ipv6_prefix_list_remark_cmd, + "no ipv6 prefix-list WORD$name description", + NO_STR + IPV6_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_REMARK_STR) +{ + char xpath[XPATH_MAXLEN]; + int rv; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']/remark", + name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) + return plist_remove_if_empty(vty, "ipv6", name); + + return rv; +} + +ALIAS( + no_ipv6_prefix_list_remark, no_ipv6_prefix_list_remark_line_cmd, + "no ipv6 prefix-list WORD$name description LINE...", + NO_STR + IPV6_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) + +int prefix_list_cmp(const struct lyd_node *dnode1, + const struct lyd_node *dnode2) +{ + uint32_t seq1 = yang_dnode_get_uint32(dnode1, "./sequence"); + uint32_t seq2 = yang_dnode_get_uint32(dnode2, "./sequence"); + + return seq1 - seq2; +} + +void prefix_list_show(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + int type = yang_dnode_get_enum(dnode, "../type"); + const char *ge_str = NULL, *le_str = NULL; + bool is_any; + struct prefix p; + + is_any = yang_dnode_exists(dnode, "./any"); + switch (type) { + case YPLT_IPV4: + if (!is_any) + yang_dnode_get_prefix(&p, dnode, "./ipv4-prefix"); + if (yang_dnode_exists(dnode, + "./ipv4-prefix-length-greater-or-equal")) + ge_str = yang_dnode_get_string( + dnode, "./ipv4-prefix-length-greater-or-equal"); + if (yang_dnode_exists(dnode, + "./ipv4-prefix-length-lesser-or-equal")) + le_str = yang_dnode_get_string( + dnode, "./ipv4-prefix-length-lesser-or-equal"); + + vty_out(vty, "ip "); + break; + case YPLT_IPV6: + if (!is_any) + yang_dnode_get_prefix(&p, dnode, "ipv6-prefix"); + if (yang_dnode_exists(dnode, + "./ipv6-prefix-length-greater-or-equal")) + ge_str = yang_dnode_get_string( + dnode, "./ipv6-prefix-length-greater-or-equal"); + if (yang_dnode_exists(dnode, + "./ipv6-prefix-length-lesser-or-equal")) + le_str = yang_dnode_get_string( + dnode, "./ipv6-prefix-length-lesser-or-equal"); + + vty_out(vty, "ipv6 "); + break; + } + + vty_out(vty, "prefix-list %s seq %s %s", + yang_dnode_get_string(dnode, "../name"), + yang_dnode_get_string(dnode, "./sequence"), + yang_dnode_get_string(dnode, "./action")); + + if (is_any) { + vty_out(vty, " any\n"); + return; + } + + vty_out(vty, " %pFX", &p); + if (ge_str) + vty_out(vty, " ge %s", ge_str); + if (le_str) + vty_out(vty, " le %s", le_str); + + vty_out(vty, "\n"); +} + +void prefix_list_remark_show(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + int type = yang_dnode_get_enum(dnode, "../type"); + + switch (type) { + case YPLT_IPV4: + vty_out(vty, "ip "); + break; + case YPLT_IPV6: + vty_out(vty, "ipv6 "); + break; + } + + vty_out(vty, "prefix-list %s description %s\n", + yang_dnode_get_string(dnode, "../name"), + yang_dnode_get_string(dnode, NULL)); +} + +void filter_cli_init(void) +{ + /* access-list cisco-style (legacy). */ + install_element(CONFIG_NODE, &access_list_std_cmd); + install_element(CONFIG_NODE, &no_access_list_std_cmd); + install_element(CONFIG_NODE, &access_list_ext_cmd); + install_element(CONFIG_NODE, &no_access_list_ext_cmd); + + /* access-list zebra-style. */ + install_element(CONFIG_NODE, &access_list_cmd); + install_element(CONFIG_NODE, &no_access_list_cmd); + install_element(CONFIG_NODE, &no_access_list_all_cmd); + install_element(CONFIG_NODE, &access_list_remark_cmd); + install_element(CONFIG_NODE, &no_access_list_remark_cmd); + install_element(CONFIG_NODE, &no_access_list_remark_line_cmd); + + install_element(CONFIG_NODE, &ipv6_access_list_cmd); + install_element(CONFIG_NODE, &no_ipv6_access_list_cmd); + install_element(CONFIG_NODE, &no_ipv6_access_list_all_cmd); + install_element(CONFIG_NODE, &ipv6_access_list_remark_cmd); + install_element(CONFIG_NODE, &no_ipv6_access_list_remark_cmd); + install_element(CONFIG_NODE, &no_ipv6_access_list_remark_line_cmd); + + install_element(CONFIG_NODE, &mac_access_list_cmd); + install_element(CONFIG_NODE, &no_mac_access_list_cmd); + install_element(CONFIG_NODE, &no_mac_access_list_all_cmd); + install_element(CONFIG_NODE, &mac_access_list_remark_cmd); + install_element(CONFIG_NODE, &no_mac_access_list_remark_cmd); + install_element(CONFIG_NODE, &no_mac_access_list_remark_line_cmd); + + /* prefix lists. */ + install_element(CONFIG_NODE, &ip_prefix_list_cmd); + install_element(CONFIG_NODE, &no_ip_prefix_list_cmd); + install_element(CONFIG_NODE, &no_ip_prefix_list_seq_cmd); + install_element(CONFIG_NODE, &no_ip_prefix_list_all_cmd); + install_element(CONFIG_NODE, &ip_prefix_list_remark_cmd); + install_element(CONFIG_NODE, &no_ip_prefix_list_remark_cmd); + install_element(CONFIG_NODE, &no_ip_prefix_list_remark_line_cmd); + + install_element(CONFIG_NODE, &ipv6_prefix_list_cmd); + install_element(CONFIG_NODE, &no_ipv6_prefix_list_cmd); + install_element(CONFIG_NODE, &no_ipv6_prefix_list_seq_cmd); + install_element(CONFIG_NODE, &no_ipv6_prefix_list_all_cmd); + install_element(CONFIG_NODE, &ipv6_prefix_list_remark_cmd); + install_element(CONFIG_NODE, &no_ipv6_prefix_list_remark_cmd); + install_element(CONFIG_NODE, &no_ipv6_prefix_list_remark_line_cmd); +} |