summaryrefslogtreecommitdiffstats
path: root/lib/filter.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/filter.c919
1 files changed, 919 insertions, 0 deletions
diff --git a/lib/filter.c b/lib/filter.c
new file mode 100644
index 0000000..fc4b578
--- /dev/null
+++ b/lib/filter.c
@@ -0,0 +1,919 @@
+/* Route filtering function.
+ * Copyright (C) 1998, 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra 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; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "filter.h"
+#include "memory.h"
+#include "command.h"
+#include "sockunion.h"
+#include "buffer.h"
+#include "log.h"
+#include "routemap.h"
+#include "libfrr.h"
+#include "northbound_cli.h"
+#include "json.h"
+
+DEFINE_MTYPE_STATIC(LIB, ACCESS_LIST, "Access List");
+DEFINE_MTYPE_STATIC(LIB, ACCESS_LIST_STR, "Access List Str");
+DEFINE_MTYPE_STATIC(LIB, ACCESS_FILTER, "Access Filter");
+
+/* Static structure for mac access_list's master. */
+static struct access_master access_master_mac = {
+ {NULL, NULL},
+ NULL,
+ NULL,
+};
+
+/* Static structure for IPv4 access_list's master. */
+static struct access_master access_master_ipv4 = {
+ {NULL, NULL},
+ NULL,
+ NULL,
+};
+
+/* Static structure for IPv6 access_list's master. */
+static struct access_master access_master_ipv6 = {
+ {NULL, NULL},
+ NULL,
+ NULL,
+};
+
+static struct access_master *access_master_get(afi_t afi)
+{
+ if (afi == AFI_IP)
+ return &access_master_ipv4;
+ else if (afi == AFI_IP6)
+ return &access_master_ipv6;
+ else if (afi == AFI_L2VPN)
+ return &access_master_mac;
+ return NULL;
+}
+
+/* Allocate new filter structure. */
+struct filter *filter_new(void)
+{
+ return XCALLOC(MTYPE_ACCESS_FILTER, sizeof(struct filter));
+}
+
+static void filter_free(struct filter *filter)
+{
+ XFREE(MTYPE_ACCESS_FILTER, filter);
+}
+
+/* Return string of filter_type. */
+static const char *filter_type_str(struct filter *filter)
+{
+ switch (filter->type) {
+ case FILTER_PERMIT:
+ return "permit";
+ case FILTER_DENY:
+ return "deny";
+ case FILTER_DYNAMIC:
+ return "dynamic";
+ default:
+ return "";
+ }
+}
+
+/* If filter match to the prefix then return 1. */
+static int filter_match_cisco(struct filter *mfilter, const struct prefix *p)
+{
+ struct filter_cisco *filter;
+ struct in_addr mask;
+ uint32_t check_addr;
+ uint32_t check_mask;
+
+ filter = &mfilter->u.cfilter;
+ check_addr = p->u.prefix4.s_addr & ~filter->addr_mask.s_addr;
+
+ if (filter->extended) {
+ masklen2ip(p->prefixlen, &mask);
+ check_mask = mask.s_addr & ~filter->mask_mask.s_addr;
+
+ if (memcmp(&check_addr, &filter->addr.s_addr, IPV4_MAX_BYTELEN)
+ == 0
+ && memcmp(&check_mask, &filter->mask.s_addr,
+ IPV4_MAX_BYTELEN)
+ == 0)
+ return 1;
+ } else if (memcmp(&check_addr, &filter->addr.s_addr, IPV4_MAX_BYTELEN)
+ == 0)
+ return 1;
+
+ return 0;
+}
+
+/* If filter match to the prefix then return 1. */
+static int filter_match_zebra(struct filter *mfilter, const struct prefix *p)
+{
+ struct filter_zebra *filter = NULL;
+
+ filter = &mfilter->u.zfilter;
+
+ if (filter->prefix.family == p->family) {
+ if (filter->exact) {
+ if (filter->prefix.prefixlen == p->prefixlen)
+ return prefix_match(&filter->prefix, p);
+ else
+ return 0;
+ } else
+ return prefix_match(&filter->prefix, p);
+ } else
+ return 0;
+}
+
+/* Allocate new access list structure. */
+static struct access_list *access_list_new(void)
+{
+ return XCALLOC(MTYPE_ACCESS_LIST, sizeof(struct access_list));
+}
+
+/* Free allocated access_list. */
+static void access_list_free(struct access_list *access)
+{
+ XFREE(MTYPE_ACCESS_LIST, access);
+}
+
+/* Delete access_list from access_master and free it. */
+void access_list_delete(struct access_list *access)
+{
+ struct filter *filter;
+ struct filter *next;
+ struct access_list_list *list;
+ struct access_master *master;
+
+ for (filter = access->head; filter; filter = next) {
+ next = filter->next;
+ filter_free(filter);
+ }
+
+ master = access->master;
+
+ list = &master->str;
+
+ if (access->next)
+ access->next->prev = access->prev;
+ else
+ list->tail = access->prev;
+
+ if (access->prev)
+ access->prev->next = access->next;
+ else
+ list->head = access->next;
+
+ route_map_notify_dependencies(access->name, RMAP_EVENT_FILTER_DELETED);
+
+ if (master->delete_hook)
+ master->delete_hook(access);
+
+ XFREE(MTYPE_ACCESS_LIST_STR, access->name);
+
+ XFREE(MTYPE_TMP, access->remark);
+
+ access_list_free(access);
+}
+
+/* Insert new access list to list of access_list. Each access_list
+ is sorted by the name. */
+static struct access_list *access_list_insert(afi_t afi, const char *name)
+{
+ struct access_list *access;
+ struct access_list *point;
+ struct access_list_list *alist;
+ struct access_master *master;
+
+ master = access_master_get(afi);
+ if (master == NULL)
+ return NULL;
+
+ /* Allocate new access_list and copy given name. */
+ access = access_list_new();
+ access->name = XSTRDUP(MTYPE_ACCESS_LIST_STR, name);
+ access->master = master;
+
+ /* Set access_list to string list. */
+ alist = &master->str;
+
+ /* Set point to insertion point. */
+ for (point = alist->head; point; point = point->next)
+ if (strcmp(point->name, name) >= 0)
+ break;
+
+ /* In case of this is the first element of master. */
+ if (alist->head == NULL) {
+ alist->head = alist->tail = access;
+ return access;
+ }
+
+ /* In case of insertion is made at the tail of access_list. */
+ if (point == NULL) {
+ access->prev = alist->tail;
+ alist->tail->next = access;
+ alist->tail = access;
+ return access;
+ }
+
+ /* In case of insertion is made at the head of access_list. */
+ if (point == alist->head) {
+ access->next = alist->head;
+ alist->head->prev = access;
+ alist->head = access;
+ return access;
+ }
+
+ /* Insertion is made at middle of the access_list. */
+ access->next = point;
+ access->prev = point->prev;
+
+ if (point->prev)
+ point->prev->next = access;
+ point->prev = access;
+
+ return access;
+}
+
+/* Lookup access_list from list of access_list by name. */
+struct access_list *access_list_lookup(afi_t afi, const char *name)
+{
+ struct access_list *access;
+ struct access_master *master;
+
+ if (name == NULL)
+ return NULL;
+
+ master = access_master_get(afi);
+ if (master == NULL)
+ return NULL;
+
+ for (access = master->str.head; access; access = access->next)
+ if (strcmp(access->name, name) == 0)
+ return access;
+
+ return NULL;
+}
+
+/* Get access list from list of access_list. If there isn't matched
+ access_list create new one and return it. */
+struct access_list *access_list_get(afi_t afi, const char *name)
+{
+ struct access_list *access;
+
+ access = access_list_lookup(afi, name);
+ if (access == NULL)
+ access = access_list_insert(afi, name);
+ return access;
+}
+
+/* Apply access list to object (which should be struct prefix *). */
+enum filter_type access_list_apply(struct access_list *access,
+ const void *object)
+{
+ struct filter *filter;
+ const struct prefix *p = (const struct prefix *)object;
+
+ if (access == NULL)
+ return FILTER_DENY;
+
+ for (filter = access->head; filter; filter = filter->next) {
+ if (filter->cisco) {
+ if (filter_match_cisco(filter, p))
+ return filter->type;
+ } else {
+ if (filter_match_zebra(filter, p))
+ return filter->type;
+ }
+ }
+
+ return FILTER_DENY;
+}
+
+/* Add hook function. */
+void access_list_add_hook(void (*func)(struct access_list *access))
+{
+ access_master_ipv4.add_hook = func;
+ access_master_ipv6.add_hook = func;
+ access_master_mac.add_hook = func;
+}
+
+/* Delete hook function. */
+void access_list_delete_hook(void (*func)(struct access_list *access))
+{
+ access_master_ipv4.delete_hook = func;
+ access_master_ipv6.delete_hook = func;
+ access_master_mac.delete_hook = func;
+}
+
+/* Calculate new sequential number. */
+int64_t filter_new_seq_get(struct access_list *access)
+{
+ int64_t maxseq;
+ int64_t newseq;
+ struct filter *filter;
+
+ maxseq = 0;
+
+ for (filter = access->head; filter; filter = filter->next) {
+ if (maxseq < filter->seq)
+ maxseq = filter->seq;
+ }
+
+ newseq = ((maxseq / 5) * 5) + 5;
+
+ return (newseq > UINT_MAX) ? UINT_MAX : newseq;
+}
+
+/* Return access list entry which has same seq number. */
+static struct filter *filter_seq_check(struct access_list *access,
+ int64_t seq)
+{
+ struct filter *filter;
+
+ for (filter = access->head; filter; filter = filter->next)
+ if (filter->seq == seq)
+ return filter;
+ return NULL;
+}
+
+/* Delete filter from specified access_list. If there is hook
+ function execute it. */
+void access_list_filter_delete(struct access_list *access,
+ struct filter *filter)
+{
+ struct access_master *master;
+
+ master = access->master;
+
+ if (filter->next)
+ filter->next->prev = filter->prev;
+ else
+ access->tail = filter->prev;
+
+ if (filter->prev)
+ filter->prev->next = filter->next;
+ else
+ access->head = filter->next;
+
+ filter_free(filter);
+
+ route_map_notify_dependencies(access->name, RMAP_EVENT_FILTER_DELETED);
+ /* Run hook function. */
+ if (master->delete_hook)
+ (*master->delete_hook)(access);
+}
+
+/* Add new filter to the end of specified access_list. */
+void access_list_filter_add(struct access_list *access,
+ struct filter *filter)
+{
+ struct filter *replace;
+ struct filter *point;
+
+ /* Automatic assignment of seq no. */
+ if (filter->seq == -1)
+ filter->seq = filter_new_seq_get(access);
+
+ if (access->tail && filter->seq > access->tail->seq)
+ point = NULL;
+ else {
+ /* Is there any same seq access list filter? */
+ replace = filter_seq_check(access, filter->seq);
+ if (replace)
+ access_list_filter_delete(access, replace);
+
+ /* Check insert point. */
+ for (point = access->head; point; point = point->next)
+ if (point->seq >= filter->seq)
+ break;
+ }
+
+ /* In case of this is the first element of the list. */
+ filter->next = point;
+
+ if (point) {
+ if (point->prev)
+ point->prev->next = filter;
+ else
+ access->head = filter;
+
+ filter->prev = point->prev;
+ point->prev = filter;
+ } else {
+ if (access->tail)
+ access->tail->next = filter;
+ else
+ access->head = filter;
+
+ filter->prev = access->tail;
+ access->tail = filter;
+ }
+
+ /* Run hook function. */
+ if (access->master->add_hook)
+ (*access->master->add_hook)(access);
+ route_map_notify_dependencies(access->name, RMAP_EVENT_FILTER_ADDED);
+}
+
+/*
+ deny Specify packets to reject
+ permit Specify packets to forward
+ dynamic ?
+*/
+
+/*
+ Hostname or A.B.C.D Address to match
+ any Any source host
+ host A single host address
+*/
+
+static void config_write_access_zebra(struct vty *, struct filter *,
+ json_object *);
+static void config_write_access_cisco(struct vty *, struct filter *,
+ json_object *);
+
+static const char *filter_type2str(struct filter *filter)
+{
+ if (filter->cisco) {
+ if (filter->u.cfilter.extended)
+ return "Extended";
+ else
+ return "Standard";
+ } else
+ return "Zebra";
+}
+
+/* show access-list command. */
+static int filter_show(struct vty *vty, const char *name, afi_t afi,
+ bool use_json)
+{
+ struct access_list *access;
+ struct access_master *master;
+ struct filter *mfilter;
+ struct filter_cisco *filter;
+ bool first;
+ json_object *json = NULL;
+ json_object *json_proto = NULL;
+
+ master = access_master_get(afi);
+ if (master == NULL) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return 0;
+ }
+
+ if (use_json)
+ json = json_object_new_object();
+
+ /* Print the name of the protocol */
+ if (json) {
+ json_proto = json_object_new_object();
+ json_object_object_add(json, frr_protoname, json_proto);
+ } else
+ vty_out(vty, "%s:\n", frr_protoname);
+
+ for (access = master->str.head; access; access = access->next) {
+ json_object *json_acl = NULL;
+ json_object *json_rules = NULL;
+
+ if (name && strcmp(access->name, name) != 0)
+ continue;
+
+ first = true;
+
+ for (mfilter = access->head; mfilter; mfilter = mfilter->next) {
+ json_object *json_rule = NULL;
+
+ filter = &mfilter->u.cfilter;
+
+ if (first) {
+ const char *type = filter_type2str(mfilter);
+
+ if (json) {
+ json_acl = json_object_new_object();
+ json_object_object_add(json_proto,
+ access->name,
+ json_acl);
+
+ json_object_string_add(json_acl, "type",
+ type);
+ json_object_string_add(json_acl,
+ "addressFamily",
+ afi2str(afi));
+ json_rules = json_object_new_array();
+ json_object_object_add(
+ json_acl, "rules", json_rules);
+ } else {
+ vty_out(vty, "%s %s access list %s\n",
+ type,
+ (afi == AFI_IP)
+ ? ("IP")
+ : ((afi == AFI_IP6)
+ ? ("IPv6 ")
+ : ("MAC ")),
+ access->name);
+ }
+
+ first = false;
+ }
+
+ if (json) {
+ json_rule = json_object_new_object();
+ json_object_array_add(json_rules, json_rule);
+
+ json_object_int_add(json_rule, "sequenceNumber",
+ mfilter->seq);
+ json_object_string_add(
+ json_rule, "filterType",
+ filter_type_str(mfilter));
+ } else {
+ vty_out(vty, " seq %" PRId64, mfilter->seq);
+ vty_out(vty, " %s%s", filter_type_str(mfilter),
+ mfilter->type == FILTER_DENY ? " "
+ : "");
+ }
+
+ if (!mfilter->cisco)
+ config_write_access_zebra(vty, mfilter,
+ json_rule);
+ else if (filter->extended)
+ config_write_access_cisco(vty, mfilter,
+ json_rule);
+ else {
+ if (json) {
+ json_object_string_addf(
+ json_rule, "address", "%pI4",
+ &filter->addr);
+ json_object_string_addf(
+ json_rule, "mask", "%pI4",
+ &filter->addr_mask);
+ } else {
+ if (filter->addr_mask.s_addr
+ == 0xffffffff)
+ vty_out(vty, " any\n");
+ else {
+ vty_out(vty, " %pI4",
+ &filter->addr);
+ if (filter->addr_mask.s_addr
+ != INADDR_ANY)
+ vty_out(vty,
+ ", wildcard bits %pI4",
+ &filter->addr_mask);
+ vty_out(vty, "\n");
+ }
+ }
+ }
+ }
+ }
+
+ return vty_json(vty, json);
+}
+
+/* show MAC access list - this only has MAC filters for now*/
+DEFUN (show_mac_access_list,
+ show_mac_access_list_cmd,
+ "show mac access-list",
+ SHOW_STR
+ "mac access lists\n"
+ "List mac access lists\n")
+{
+ return filter_show(vty, NULL, AFI_L2VPN, false);
+}
+
+DEFUN (show_mac_access_list_name,
+ show_mac_access_list_name_cmd,
+ "show mac access-list ACCESSLIST_MAC_NAME",
+ SHOW_STR
+ "mac access lists\n"
+ "List mac access lists\n"
+ "mac address\n")
+{
+ return filter_show(vty, argv[3]->arg, AFI_L2VPN, false);
+}
+
+DEFUN (show_ip_access_list,
+ show_ip_access_list_cmd,
+ "show ip access-list [json]",
+ SHOW_STR
+ IP_STR
+ "List IP access lists\n"
+ JSON_STR)
+{
+ bool uj = use_json(argc, argv);
+ return filter_show(vty, NULL, AFI_IP, uj);
+}
+
+DEFUN (show_ip_access_list_name,
+ show_ip_access_list_name_cmd,
+ "show ip access-list ACCESSLIST4_NAME [json]",
+ SHOW_STR
+ IP_STR
+ "List IP access lists\n"
+ "IP access-list name\n"
+ JSON_STR)
+{
+ bool uj = use_json(argc, argv);
+ int idx_acl = 3;
+ return filter_show(vty, argv[idx_acl]->arg, AFI_IP, uj);
+}
+
+DEFUN (show_ipv6_access_list,
+ show_ipv6_access_list_cmd,
+ "show ipv6 access-list [json]",
+ SHOW_STR
+ IPV6_STR
+ "List IPv6 access lists\n"
+ JSON_STR)
+{
+ bool uj = use_json(argc, argv);
+ return filter_show(vty, NULL, AFI_IP6, uj);
+}
+
+DEFUN (show_ipv6_access_list_name,
+ show_ipv6_access_list_name_cmd,
+ "show ipv6 access-list ACCESSLIST6_NAME [json]",
+ SHOW_STR
+ IPV6_STR
+ "List IPv6 access lists\n"
+ "IPv6 access-list name\n"
+ JSON_STR)
+{
+ bool uj = use_json(argc, argv);
+ int idx_word = 3;
+ return filter_show(vty, argv[idx_word]->arg, AFI_IP6, uj);
+}
+
+static void config_write_access_cisco(struct vty *vty, struct filter *mfilter,
+ json_object *json)
+{
+ struct filter_cisco *filter;
+
+ filter = &mfilter->u.cfilter;
+
+ if (json) {
+ json_object_boolean_add(json, "extended", !!filter->extended);
+ json_object_string_addf(json, "sourceAddress", "%pI4",
+ &filter->addr);
+ json_object_string_addf(json, "sourceMask", "%pI4",
+ &filter->addr_mask);
+ json_object_string_addf(json, "destinationAddress", "%pI4",
+ &filter->mask);
+ json_object_string_addf(json, "destinationMask", "%pI4",
+ &filter->mask_mask);
+ } else {
+ vty_out(vty, " ip");
+ if (filter->addr_mask.s_addr == 0xffffffff)
+ vty_out(vty, " any");
+ else if (filter->addr_mask.s_addr == INADDR_ANY)
+ vty_out(vty, " host %pI4", &filter->addr);
+ else {
+ vty_out(vty, " %pI4", &filter->addr);
+ vty_out(vty, " %pI4", &filter->addr_mask);
+ }
+
+ if (filter->mask_mask.s_addr == 0xffffffff)
+ vty_out(vty, " any");
+ else if (filter->mask_mask.s_addr == INADDR_ANY)
+ vty_out(vty, " host %pI4", &filter->mask);
+ else {
+ vty_out(vty, " %pI4", &filter->mask);
+ vty_out(vty, " %pI4", &filter->mask_mask);
+ }
+ vty_out(vty, "\n");
+ }
+}
+
+static void config_write_access_zebra(struct vty *vty, struct filter *mfilter,
+ json_object *json)
+{
+ struct filter_zebra *filter;
+ struct prefix *p;
+ char buf[BUFSIZ];
+
+ filter = &mfilter->u.zfilter;
+ p = &filter->prefix;
+
+ if (json) {
+ json_object_string_addf(json, "prefix", "%pFX", p);
+ json_object_boolean_add(json, "exact-match", !!filter->exact);
+ } else {
+ if (p->prefixlen == 0 && !filter->exact)
+ vty_out(vty, " any");
+ else if (p->family == AF_INET6 || p->family == AF_INET)
+ vty_out(vty, " %pFX%s", p,
+ filter->exact ? " exact-match" : "");
+ else if (p->family == AF_ETHERNET) {
+ if (p->prefixlen == 0)
+ vty_out(vty, " any");
+ else
+ vty_out(vty, " %s",
+ prefix_mac2str(&(p->u.prefix_eth), buf,
+ sizeof(buf)));
+ }
+
+ vty_out(vty, "\n");
+ }
+}
+
+static struct cmd_node access_mac_node = {
+ .name = "MAC access list",
+ .node = ACCESS_MAC_NODE,
+ .prompt = "",
+};
+
+static void access_list_reset_mac(void)
+{
+ struct access_list *access;
+ struct access_list *next;
+ struct access_master *master;
+
+ master = access_master_get(AFI_L2VPN);
+ if (master == NULL)
+ return;
+
+ for (access = master->str.head; access; access = next) {
+ next = access->next;
+ access_list_delete(access);
+ }
+
+ assert(master->str.head == NULL);
+ assert(master->str.tail == NULL);
+}
+
+/* Install vty related command. */
+static void access_list_init_mac(void)
+{
+ install_node(&access_mac_node);
+
+ install_element(ENABLE_NODE, &show_mac_access_list_cmd);
+ install_element(ENABLE_NODE, &show_mac_access_list_name_cmd);
+}
+
+/* Access-list node. */
+static int config_write_access(struct vty *vty);
+static struct cmd_node access_node = {
+ .name = "ipv4 access list",
+ .node = ACCESS_NODE,
+ .prompt = "",
+ .config_write = config_write_access,
+};
+
+static int config_write_access(struct vty *vty)
+{
+ struct lyd_node *dnode;
+ int written = 0;
+
+ dnode = yang_dnode_get(running_config->dnode, "/frr-filter:lib");
+ if (dnode) {
+ nb_cli_show_dnode_cmds(vty, dnode, false);
+ written = 1;
+ }
+
+ return written;
+}
+
+static void access_list_reset_ipv4(void)
+{
+ struct access_list *access;
+ struct access_list *next;
+ struct access_master *master;
+
+ master = access_master_get(AFI_IP);
+ if (master == NULL)
+ return;
+
+ for (access = master->str.head; access; access = next) {
+ next = access->next;
+ access_list_delete(access);
+ }
+
+ assert(master->str.head == NULL);
+ assert(master->str.tail == NULL);
+}
+
+/* Install vty related command. */
+static void access_list_init_ipv4(void)
+{
+ install_node(&access_node);
+
+ install_element(ENABLE_NODE, &show_ip_access_list_cmd);
+ install_element(ENABLE_NODE, &show_ip_access_list_name_cmd);
+}
+
+static void access_list_autocomplete_afi(afi_t afi, vector comps,
+ struct cmd_token *token)
+{
+ struct access_list *access;
+ struct access_list *next;
+ struct access_master *master;
+
+ master = access_master_get(afi);
+ if (master == NULL)
+ return;
+
+ for (access = master->str.head; access; access = next) {
+ next = access->next;
+ vector_set(comps, XSTRDUP(MTYPE_COMPLETION, access->name));
+ }
+}
+
+static struct cmd_node access_ipv6_node = {
+ .name = "ipv6 access list",
+ .node = ACCESS_IPV6_NODE,
+ .prompt = "",
+};
+
+static void access_list_autocomplete(vector comps, struct cmd_token *token)
+{
+ access_list_autocomplete_afi(AFI_IP, comps, token);
+ access_list_autocomplete_afi(AFI_IP6, comps, token);
+ access_list_autocomplete_afi(AFI_L2VPN, comps, token);
+}
+
+static void access_list4_autocomplete(vector comps, struct cmd_token *token)
+{
+ access_list_autocomplete_afi(AFI_IP, comps, token);
+}
+
+static void access_list6_autocomplete(vector comps, struct cmd_token *token)
+{
+ access_list_autocomplete_afi(AFI_IP6, comps, token);
+}
+
+static void access_list_mac_autocomplete(vector comps, struct cmd_token *token)
+{
+ access_list_autocomplete_afi(AFI_L2VPN, comps, token);
+}
+
+static const struct cmd_variable_handler access_list_handlers[] = {
+ {.tokenname = "ACCESSLIST_NAME",
+ .completions = access_list_autocomplete},
+ {.tokenname = "ACCESSLIST4_NAME",
+ .completions = access_list4_autocomplete},
+ {.tokenname = "ACCESSLIST6_NAME",
+ .completions = access_list6_autocomplete},
+ {.tokenname = "ACCESSLIST_MAC_NAME",
+ .completions = access_list_mac_autocomplete},
+ {.completions = NULL}};
+
+static void access_list_reset_ipv6(void)
+{
+ struct access_list *access;
+ struct access_list *next;
+ struct access_master *master;
+
+ master = access_master_get(AFI_IP6);
+ if (master == NULL)
+ return;
+
+ for (access = master->str.head; access; access = next) {
+ next = access->next;
+ access_list_delete(access);
+ }
+
+ assert(master->str.head == NULL);
+ assert(master->str.tail == NULL);
+}
+
+static void access_list_init_ipv6(void)
+{
+ install_node(&access_ipv6_node);
+
+ install_element(ENABLE_NODE, &show_ipv6_access_list_cmd);
+ install_element(ENABLE_NODE, &show_ipv6_access_list_name_cmd);
+}
+
+void access_list_init(void)
+{
+ cmd_variable_handler_register(access_list_handlers);
+
+ access_list_init_ipv4();
+ access_list_init_ipv6();
+ access_list_init_mac();
+
+ filter_cli_init();
+}
+
+void access_list_reset(void)
+{
+ access_list_reset_ipv4();
+ access_list_reset_ipv6();
+ access_list_reset_mac();
+}