]> git.proxmox.com Git - mirror_frr.git/blob - lib/filter.c
*: auto-convert to SPDX License IDs
[mirror_frr.git] / lib / filter.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* Route filtering function.
3 * Copyright (C) 1998, 1999 Kunihiro Ishiguro
4 */
5
6 #include <zebra.h>
7
8 #include "prefix.h"
9 #include "filter.h"
10 #include "memory.h"
11 #include "command.h"
12 #include "sockunion.h"
13 #include "buffer.h"
14 #include "log.h"
15 #include "routemap.h"
16 #include "libfrr.h"
17 #include "northbound_cli.h"
18 #include "json.h"
19
20 DEFINE_MTYPE_STATIC(LIB, ACCESS_LIST, "Access List");
21 DEFINE_MTYPE_STATIC(LIB, ACCESS_LIST_STR, "Access List Str");
22 DEFINE_MTYPE_STATIC(LIB, ACCESS_FILTER, "Access Filter");
23
24 /* Static structure for mac access_list's master. */
25 static struct access_master access_master_mac = {
26 {NULL, NULL},
27 NULL,
28 NULL,
29 };
30
31 /* Static structure for IPv4 access_list's master. */
32 static struct access_master access_master_ipv4 = {
33 {NULL, NULL},
34 NULL,
35 NULL,
36 };
37
38 /* Static structure for IPv6 access_list's master. */
39 static struct access_master access_master_ipv6 = {
40 {NULL, NULL},
41 NULL,
42 NULL,
43 };
44
45 static struct access_master *access_master_get(afi_t afi)
46 {
47 if (afi == AFI_IP)
48 return &access_master_ipv4;
49 else if (afi == AFI_IP6)
50 return &access_master_ipv6;
51 else if (afi == AFI_L2VPN)
52 return &access_master_mac;
53 return NULL;
54 }
55
56 /* Allocate new filter structure. */
57 struct filter *filter_new(void)
58 {
59 return XCALLOC(MTYPE_ACCESS_FILTER, sizeof(struct filter));
60 }
61
62 static void filter_free(struct filter *filter)
63 {
64 XFREE(MTYPE_ACCESS_FILTER, filter);
65 }
66
67 /* Return string of filter_type. */
68 static const char *filter_type_str(struct filter *filter)
69 {
70 switch (filter->type) {
71 case FILTER_PERMIT:
72 return "permit";
73 case FILTER_DENY:
74 return "deny";
75 case FILTER_DYNAMIC:
76 return "dynamic";
77 default:
78 return "";
79 }
80 }
81
82 /* If filter match to the prefix then return 1. */
83 static int filter_match_cisco(struct filter *mfilter, const struct prefix *p)
84 {
85 struct filter_cisco *filter;
86 struct in_addr mask;
87 uint32_t check_addr;
88 uint32_t check_mask;
89
90 filter = &mfilter->u.cfilter;
91 check_addr = p->u.prefix4.s_addr & ~filter->addr_mask.s_addr;
92
93 if (filter->extended) {
94 masklen2ip(p->prefixlen, &mask);
95 check_mask = mask.s_addr & ~filter->mask_mask.s_addr;
96
97 if (memcmp(&check_addr, &filter->addr.s_addr, IPV4_MAX_BYTELEN)
98 == 0
99 && memcmp(&check_mask, &filter->mask.s_addr,
100 IPV4_MAX_BYTELEN)
101 == 0)
102 return 1;
103 } else if (memcmp(&check_addr, &filter->addr.s_addr, IPV4_MAX_BYTELEN)
104 == 0)
105 return 1;
106
107 return 0;
108 }
109
110 /* If filter match to the prefix then return 1. */
111 static int filter_match_zebra(struct filter *mfilter, const struct prefix *p)
112 {
113 struct filter_zebra *filter = NULL;
114
115 filter = &mfilter->u.zfilter;
116
117 if (filter->prefix.family == p->family) {
118 if (filter->exact) {
119 if (filter->prefix.prefixlen == p->prefixlen)
120 return prefix_match(&filter->prefix, p);
121 else
122 return 0;
123 } else
124 return prefix_match(&filter->prefix, p);
125 } else
126 return 0;
127 }
128
129 /* Allocate new access list structure. */
130 static struct access_list *access_list_new(void)
131 {
132 return XCALLOC(MTYPE_ACCESS_LIST, sizeof(struct access_list));
133 }
134
135 /* Free allocated access_list. */
136 static void access_list_free(struct access_list *access)
137 {
138 XFREE(MTYPE_ACCESS_LIST, access);
139 }
140
141 /* Delete access_list from access_master and free it. */
142 void access_list_delete(struct access_list *access)
143 {
144 struct filter *filter;
145 struct filter *next;
146 struct access_list_list *list;
147 struct access_master *master;
148
149 for (filter = access->head; filter; filter = next) {
150 next = filter->next;
151 filter_free(filter);
152 }
153
154 master = access->master;
155
156 list = &master->str;
157
158 if (access->next)
159 access->next->prev = access->prev;
160 else
161 list->tail = access->prev;
162
163 if (access->prev)
164 access->prev->next = access->next;
165 else
166 list->head = access->next;
167
168 route_map_notify_dependencies(access->name, RMAP_EVENT_FILTER_DELETED);
169
170 if (master->delete_hook)
171 master->delete_hook(access);
172
173 XFREE(MTYPE_ACCESS_LIST_STR, access->name);
174
175 XFREE(MTYPE_TMP, access->remark);
176
177 access_list_free(access);
178 }
179
180 /* Insert new access list to list of access_list. Each access_list
181 is sorted by the name. */
182 static struct access_list *access_list_insert(afi_t afi, const char *name)
183 {
184 struct access_list *access;
185 struct access_list *point;
186 struct access_list_list *alist;
187 struct access_master *master;
188
189 master = access_master_get(afi);
190 if (master == NULL)
191 return NULL;
192
193 /* Allocate new access_list and copy given name. */
194 access = access_list_new();
195 access->name = XSTRDUP(MTYPE_ACCESS_LIST_STR, name);
196 access->master = master;
197
198 /* Set access_list to string list. */
199 alist = &master->str;
200
201 /* Set point to insertion point. */
202 for (point = alist->head; point; point = point->next)
203 if (strcmp(point->name, name) >= 0)
204 break;
205
206 /* In case of this is the first element of master. */
207 if (alist->head == NULL) {
208 alist->head = alist->tail = access;
209 return access;
210 }
211
212 /* In case of insertion is made at the tail of access_list. */
213 if (point == NULL) {
214 access->prev = alist->tail;
215 alist->tail->next = access;
216 alist->tail = access;
217 return access;
218 }
219
220 /* In case of insertion is made at the head of access_list. */
221 if (point == alist->head) {
222 access->next = alist->head;
223 alist->head->prev = access;
224 alist->head = access;
225 return access;
226 }
227
228 /* Insertion is made at middle of the access_list. */
229 access->next = point;
230 access->prev = point->prev;
231
232 if (point->prev)
233 point->prev->next = access;
234 point->prev = access;
235
236 return access;
237 }
238
239 /* Lookup access_list from list of access_list by name. */
240 struct access_list *access_list_lookup(afi_t afi, const char *name)
241 {
242 struct access_list *access;
243 struct access_master *master;
244
245 if (name == NULL)
246 return NULL;
247
248 master = access_master_get(afi);
249 if (master == NULL)
250 return NULL;
251
252 for (access = master->str.head; access; access = access->next)
253 if (strcmp(access->name, name) == 0)
254 return access;
255
256 return NULL;
257 }
258
259 /* Get access list from list of access_list. If there isn't matched
260 access_list create new one and return it. */
261 struct access_list *access_list_get(afi_t afi, const char *name)
262 {
263 struct access_list *access;
264
265 access = access_list_lookup(afi, name);
266 if (access == NULL)
267 access = access_list_insert(afi, name);
268 return access;
269 }
270
271 /* Apply access list to object (which should be struct prefix *). */
272 enum filter_type access_list_apply(struct access_list *access,
273 const void *object)
274 {
275 struct filter *filter;
276 const struct prefix *p = (const struct prefix *)object;
277
278 if (access == NULL)
279 return FILTER_DENY;
280
281 for (filter = access->head; filter; filter = filter->next) {
282 if (filter->cisco) {
283 if (filter_match_cisco(filter, p))
284 return filter->type;
285 } else {
286 if (filter_match_zebra(filter, p))
287 return filter->type;
288 }
289 }
290
291 return FILTER_DENY;
292 }
293
294 /* Add hook function. */
295 void access_list_add_hook(void (*func)(struct access_list *access))
296 {
297 access_master_ipv4.add_hook = func;
298 access_master_ipv6.add_hook = func;
299 access_master_mac.add_hook = func;
300 }
301
302 /* Delete hook function. */
303 void access_list_delete_hook(void (*func)(struct access_list *access))
304 {
305 access_master_ipv4.delete_hook = func;
306 access_master_ipv6.delete_hook = func;
307 access_master_mac.delete_hook = func;
308 }
309
310 /* Calculate new sequential number. */
311 int64_t filter_new_seq_get(struct access_list *access)
312 {
313 int64_t maxseq;
314 int64_t newseq;
315 struct filter *filter;
316
317 maxseq = 0;
318
319 for (filter = access->head; filter; filter = filter->next) {
320 if (maxseq < filter->seq)
321 maxseq = filter->seq;
322 }
323
324 newseq = ((maxseq / 5) * 5) + 5;
325
326 return (newseq > UINT_MAX) ? UINT_MAX : newseq;
327 }
328
329 /* Return access list entry which has same seq number. */
330 static struct filter *filter_seq_check(struct access_list *access,
331 int64_t seq)
332 {
333 struct filter *filter;
334
335 for (filter = access->head; filter; filter = filter->next)
336 if (filter->seq == seq)
337 return filter;
338 return NULL;
339 }
340
341 /* Delete filter from specified access_list. If there is hook
342 function execute it. */
343 void access_list_filter_delete(struct access_list *access,
344 struct filter *filter)
345 {
346 struct access_master *master;
347
348 master = access->master;
349
350 if (filter->next)
351 filter->next->prev = filter->prev;
352 else
353 access->tail = filter->prev;
354
355 if (filter->prev)
356 filter->prev->next = filter->next;
357 else
358 access->head = filter->next;
359
360 filter_free(filter);
361
362 route_map_notify_dependencies(access->name, RMAP_EVENT_FILTER_DELETED);
363 /* Run hook function. */
364 if (master->delete_hook)
365 (*master->delete_hook)(access);
366 }
367
368 /* Add new filter to the end of specified access_list. */
369 void access_list_filter_add(struct access_list *access,
370 struct filter *filter)
371 {
372 struct filter *replace;
373 struct filter *point;
374
375 /* Automatic assignment of seq no. */
376 if (filter->seq == -1)
377 filter->seq = filter_new_seq_get(access);
378
379 if (access->tail && filter->seq > access->tail->seq)
380 point = NULL;
381 else {
382 /* Is there any same seq access list filter? */
383 replace = filter_seq_check(access, filter->seq);
384 if (replace)
385 access_list_filter_delete(access, replace);
386
387 /* Check insert point. */
388 for (point = access->head; point; point = point->next)
389 if (point->seq >= filter->seq)
390 break;
391 }
392
393 /* In case of this is the first element of the list. */
394 filter->next = point;
395
396 if (point) {
397 if (point->prev)
398 point->prev->next = filter;
399 else
400 access->head = filter;
401
402 filter->prev = point->prev;
403 point->prev = filter;
404 } else {
405 if (access->tail)
406 access->tail->next = filter;
407 else
408 access->head = filter;
409
410 filter->prev = access->tail;
411 access->tail = filter;
412 }
413
414 /* Run hook function. */
415 if (access->master->add_hook)
416 (*access->master->add_hook)(access);
417 route_map_notify_dependencies(access->name, RMAP_EVENT_FILTER_ADDED);
418 }
419
420 /*
421 deny Specify packets to reject
422 permit Specify packets to forward
423 dynamic ?
424 */
425
426 /*
427 Hostname or A.B.C.D Address to match
428 any Any source host
429 host A single host address
430 */
431
432 static void config_write_access_zebra(struct vty *, struct filter *,
433 json_object *);
434 static void config_write_access_cisco(struct vty *, struct filter *,
435 json_object *);
436
437 static const char *filter_type2str(struct filter *filter)
438 {
439 if (filter->cisco) {
440 if (filter->u.cfilter.extended)
441 return "Extended";
442 else
443 return "Standard";
444 } else
445 return "Zebra";
446 }
447
448 /* show access-list command. */
449 static int filter_show(struct vty *vty, const char *name, afi_t afi,
450 bool use_json)
451 {
452 struct access_list *access;
453 struct access_master *master;
454 struct filter *mfilter;
455 struct filter_cisco *filter;
456 bool first;
457 json_object *json = NULL;
458 json_object *json_proto = NULL;
459
460 master = access_master_get(afi);
461 if (master == NULL) {
462 if (use_json)
463 vty_out(vty, "{}\n");
464 return 0;
465 }
466
467 if (use_json)
468 json = json_object_new_object();
469
470 /* Print the name of the protocol */
471 if (json) {
472 json_proto = json_object_new_object();
473 json_object_object_add(json, frr_protoname, json_proto);
474 } else
475 vty_out(vty, "%s:\n", frr_protoname);
476
477 for (access = master->str.head; access; access = access->next) {
478 json_object *json_acl = NULL;
479 json_object *json_rules = NULL;
480
481 if (name && strcmp(access->name, name) != 0)
482 continue;
483
484 first = true;
485
486 for (mfilter = access->head; mfilter; mfilter = mfilter->next) {
487 json_object *json_rule = NULL;
488
489 filter = &mfilter->u.cfilter;
490
491 if (first) {
492 const char *type = filter_type2str(mfilter);
493
494 if (json) {
495 json_acl = json_object_new_object();
496 json_object_object_add(json_proto,
497 access->name,
498 json_acl);
499
500 json_object_string_add(json_acl, "type",
501 type);
502 json_object_string_add(json_acl,
503 "addressFamily",
504 afi2str(afi));
505 json_rules = json_object_new_array();
506 json_object_object_add(
507 json_acl, "rules", json_rules);
508 } else {
509 vty_out(vty, "%s %s access list %s\n",
510 type,
511 (afi == AFI_IP)
512 ? ("IP")
513 : ((afi == AFI_IP6)
514 ? ("IPv6 ")
515 : ("MAC ")),
516 access->name);
517 }
518
519 first = false;
520 }
521
522 if (json) {
523 json_rule = json_object_new_object();
524 json_object_array_add(json_rules, json_rule);
525
526 json_object_int_add(json_rule, "sequenceNumber",
527 mfilter->seq);
528 json_object_string_add(
529 json_rule, "filterType",
530 filter_type_str(mfilter));
531 } else {
532 vty_out(vty, " seq %" PRId64, mfilter->seq);
533 vty_out(vty, " %s%s", filter_type_str(mfilter),
534 mfilter->type == FILTER_DENY ? " "
535 : "");
536 }
537
538 if (!mfilter->cisco)
539 config_write_access_zebra(vty, mfilter,
540 json_rule);
541 else if (filter->extended)
542 config_write_access_cisco(vty, mfilter,
543 json_rule);
544 else {
545 if (json) {
546 json_object_string_addf(
547 json_rule, "address", "%pI4",
548 &filter->addr);
549 json_object_string_addf(
550 json_rule, "mask", "%pI4",
551 &filter->addr_mask);
552 } else {
553 if (filter->addr_mask.s_addr
554 == 0xffffffff)
555 vty_out(vty, " any\n");
556 else {
557 vty_out(vty, " %pI4",
558 &filter->addr);
559 if (filter->addr_mask.s_addr
560 != INADDR_ANY)
561 vty_out(vty,
562 ", wildcard bits %pI4",
563 &filter->addr_mask);
564 vty_out(vty, "\n");
565 }
566 }
567 }
568 }
569 }
570
571 return vty_json(vty, json);
572 }
573
574 /* show MAC access list - this only has MAC filters for now*/
575 DEFUN (show_mac_access_list,
576 show_mac_access_list_cmd,
577 "show mac access-list",
578 SHOW_STR
579 "mac access lists\n"
580 "List mac access lists\n")
581 {
582 return filter_show(vty, NULL, AFI_L2VPN, false);
583 }
584
585 DEFUN (show_mac_access_list_name,
586 show_mac_access_list_name_cmd,
587 "show mac access-list ACCESSLIST_MAC_NAME",
588 SHOW_STR
589 "mac access lists\n"
590 "List mac access lists\n"
591 "mac address\n")
592 {
593 return filter_show(vty, argv[3]->arg, AFI_L2VPN, false);
594 }
595
596 DEFUN (show_ip_access_list,
597 show_ip_access_list_cmd,
598 "show ip access-list [json]",
599 SHOW_STR
600 IP_STR
601 "List IP access lists\n"
602 JSON_STR)
603 {
604 bool uj = use_json(argc, argv);
605 return filter_show(vty, NULL, AFI_IP, uj);
606 }
607
608 DEFUN (show_ip_access_list_name,
609 show_ip_access_list_name_cmd,
610 "show ip access-list ACCESSLIST4_NAME [json]",
611 SHOW_STR
612 IP_STR
613 "List IP access lists\n"
614 "IP access-list name\n"
615 JSON_STR)
616 {
617 bool uj = use_json(argc, argv);
618 int idx_acl = 3;
619 return filter_show(vty, argv[idx_acl]->arg, AFI_IP, uj);
620 }
621
622 DEFUN (show_ipv6_access_list,
623 show_ipv6_access_list_cmd,
624 "show ipv6 access-list [json]",
625 SHOW_STR
626 IPV6_STR
627 "List IPv6 access lists\n"
628 JSON_STR)
629 {
630 bool uj = use_json(argc, argv);
631 return filter_show(vty, NULL, AFI_IP6, uj);
632 }
633
634 DEFUN (show_ipv6_access_list_name,
635 show_ipv6_access_list_name_cmd,
636 "show ipv6 access-list ACCESSLIST6_NAME [json]",
637 SHOW_STR
638 IPV6_STR
639 "List IPv6 access lists\n"
640 "IPv6 access-list name\n"
641 JSON_STR)
642 {
643 bool uj = use_json(argc, argv);
644 int idx_word = 3;
645 return filter_show(vty, argv[idx_word]->arg, AFI_IP6, uj);
646 }
647
648 static void config_write_access_cisco(struct vty *vty, struct filter *mfilter,
649 json_object *json)
650 {
651 struct filter_cisco *filter;
652
653 filter = &mfilter->u.cfilter;
654
655 if (json) {
656 json_object_boolean_add(json, "extended", !!filter->extended);
657 json_object_string_addf(json, "sourceAddress", "%pI4",
658 &filter->addr);
659 json_object_string_addf(json, "sourceMask", "%pI4",
660 &filter->addr_mask);
661 json_object_string_addf(json, "destinationAddress", "%pI4",
662 &filter->mask);
663 json_object_string_addf(json, "destinationMask", "%pI4",
664 &filter->mask_mask);
665 } else {
666 vty_out(vty, " ip");
667 if (filter->addr_mask.s_addr == 0xffffffff)
668 vty_out(vty, " any");
669 else if (filter->addr_mask.s_addr == INADDR_ANY)
670 vty_out(vty, " host %pI4", &filter->addr);
671 else {
672 vty_out(vty, " %pI4", &filter->addr);
673 vty_out(vty, " %pI4", &filter->addr_mask);
674 }
675
676 if (filter->mask_mask.s_addr == 0xffffffff)
677 vty_out(vty, " any");
678 else if (filter->mask_mask.s_addr == INADDR_ANY)
679 vty_out(vty, " host %pI4", &filter->mask);
680 else {
681 vty_out(vty, " %pI4", &filter->mask);
682 vty_out(vty, " %pI4", &filter->mask_mask);
683 }
684 vty_out(vty, "\n");
685 }
686 }
687
688 static void config_write_access_zebra(struct vty *vty, struct filter *mfilter,
689 json_object *json)
690 {
691 struct filter_zebra *filter;
692 struct prefix *p;
693 char buf[BUFSIZ];
694
695 filter = &mfilter->u.zfilter;
696 p = &filter->prefix;
697
698 if (json) {
699 json_object_string_addf(json, "prefix", "%pFX", p);
700 json_object_boolean_add(json, "exact-match", !!filter->exact);
701 } else {
702 if (p->prefixlen == 0 && !filter->exact)
703 vty_out(vty, " any");
704 else if (p->family == AF_INET6 || p->family == AF_INET)
705 vty_out(vty, " %pFX%s", p,
706 filter->exact ? " exact-match" : "");
707 else if (p->family == AF_ETHERNET) {
708 if (p->prefixlen == 0)
709 vty_out(vty, " any");
710 else
711 vty_out(vty, " %s",
712 prefix_mac2str(&(p->u.prefix_eth), buf,
713 sizeof(buf)));
714 }
715
716 vty_out(vty, "\n");
717 }
718 }
719
720 static struct cmd_node access_mac_node = {
721 .name = "MAC access list",
722 .node = ACCESS_MAC_NODE,
723 .prompt = "",
724 };
725
726 static void access_list_reset_mac(void)
727 {
728 struct access_list *access;
729 struct access_list *next;
730 struct access_master *master;
731
732 master = access_master_get(AFI_L2VPN);
733 if (master == NULL)
734 return;
735
736 for (access = master->str.head; access; access = next) {
737 next = access->next;
738 access_list_delete(access);
739 }
740
741 assert(master->str.head == NULL);
742 assert(master->str.tail == NULL);
743 }
744
745 /* Install vty related command. */
746 static void access_list_init_mac(void)
747 {
748 install_node(&access_mac_node);
749
750 install_element(ENABLE_NODE, &show_mac_access_list_cmd);
751 install_element(ENABLE_NODE, &show_mac_access_list_name_cmd);
752 }
753
754 /* Access-list node. */
755 static int config_write_access(struct vty *vty);
756 static struct cmd_node access_node = {
757 .name = "ipv4 access list",
758 .node = ACCESS_NODE,
759 .prompt = "",
760 .config_write = config_write_access,
761 };
762
763 static int config_write_access(struct vty *vty)
764 {
765 struct lyd_node *dnode;
766 int written = 0;
767
768 dnode = yang_dnode_get(running_config->dnode, "/frr-filter:lib");
769 if (dnode) {
770 nb_cli_show_dnode_cmds(vty, dnode, false);
771 written = 1;
772 }
773
774 return written;
775 }
776
777 static void access_list_reset_ipv4(void)
778 {
779 struct access_list *access;
780 struct access_list *next;
781 struct access_master *master;
782
783 master = access_master_get(AFI_IP);
784 if (master == NULL)
785 return;
786
787 for (access = master->str.head; access; access = next) {
788 next = access->next;
789 access_list_delete(access);
790 }
791
792 assert(master->str.head == NULL);
793 assert(master->str.tail == NULL);
794 }
795
796 /* Install vty related command. */
797 static void access_list_init_ipv4(void)
798 {
799 install_node(&access_node);
800
801 install_element(ENABLE_NODE, &show_ip_access_list_cmd);
802 install_element(ENABLE_NODE, &show_ip_access_list_name_cmd);
803 }
804
805 static void access_list_autocomplete_afi(afi_t afi, vector comps,
806 struct cmd_token *token)
807 {
808 struct access_list *access;
809 struct access_list *next;
810 struct access_master *master;
811
812 master = access_master_get(afi);
813 if (master == NULL)
814 return;
815
816 for (access = master->str.head; access; access = next) {
817 next = access->next;
818 vector_set(comps, XSTRDUP(MTYPE_COMPLETION, access->name));
819 }
820 }
821
822 static struct cmd_node access_ipv6_node = {
823 .name = "ipv6 access list",
824 .node = ACCESS_IPV6_NODE,
825 .prompt = "",
826 };
827
828 static void access_list_autocomplete(vector comps, struct cmd_token *token)
829 {
830 access_list_autocomplete_afi(AFI_IP, comps, token);
831 access_list_autocomplete_afi(AFI_IP6, comps, token);
832 access_list_autocomplete_afi(AFI_L2VPN, comps, token);
833 }
834
835 static void access_list4_autocomplete(vector comps, struct cmd_token *token)
836 {
837 access_list_autocomplete_afi(AFI_IP, comps, token);
838 }
839
840 static void access_list6_autocomplete(vector comps, struct cmd_token *token)
841 {
842 access_list_autocomplete_afi(AFI_IP6, comps, token);
843 }
844
845 static void access_list_mac_autocomplete(vector comps, struct cmd_token *token)
846 {
847 access_list_autocomplete_afi(AFI_L2VPN, comps, token);
848 }
849
850 static const struct cmd_variable_handler access_list_handlers[] = {
851 {.tokenname = "ACCESSLIST_NAME",
852 .completions = access_list_autocomplete},
853 {.tokenname = "ACCESSLIST4_NAME",
854 .completions = access_list4_autocomplete},
855 {.tokenname = "ACCESSLIST6_NAME",
856 .completions = access_list6_autocomplete},
857 {.tokenname = "ACCESSLIST_MAC_NAME",
858 .completions = access_list_mac_autocomplete},
859 {.completions = NULL}};
860
861 static void access_list_reset_ipv6(void)
862 {
863 struct access_list *access;
864 struct access_list *next;
865 struct access_master *master;
866
867 master = access_master_get(AFI_IP6);
868 if (master == NULL)
869 return;
870
871 for (access = master->str.head; access; access = next) {
872 next = access->next;
873 access_list_delete(access);
874 }
875
876 assert(master->str.head == NULL);
877 assert(master->str.tail == NULL);
878 }
879
880 static void access_list_init_ipv6(void)
881 {
882 install_node(&access_ipv6_node);
883
884 install_element(ENABLE_NODE, &show_ipv6_access_list_cmd);
885 install_element(ENABLE_NODE, &show_ipv6_access_list_name_cmd);
886 }
887
888 void access_list_init(void)
889 {
890 cmd_variable_handler_register(access_list_handlers);
891
892 access_list_init_ipv4();
893 access_list_init_ipv6();
894 access_list_init_mac();
895
896 filter_cli_init();
897 }
898
899 void access_list_reset(void)
900 {
901 access_list_reset_ipv4();
902 access_list_reset_ipv6();
903 access_list_reset_mac();
904 }