1 /* AS path filter list.
2 * Copyright (C) 1999 Kunihiro Ishiguro
4 * This file is part of GNU Zebra.
6 * GNU Zebra is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
11 * GNU Zebra is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
30 #include "bgpd/bgpd.h"
31 #include "bgpd/bgp_aspath.h"
32 #include "bgpd/bgp_regex.h"
33 #include "bgpd/bgp_filter.h"
35 /* List of AS filter list. */
41 /* AS path filter master. */
42 struct as_list_master
{
43 /* List of access_list which name is number. */
44 struct as_list_list num
;
46 /* List of access_list which name is string. */
47 struct as_list_list str
;
49 /* Hook function which is executed when new access_list is added. */
50 void (*add_hook
)(char *);
52 /* Hook function which is executed when access_list is deleted. */
53 void (*delete_hook
)(const char *);
56 /* Element of AS path filter. */
58 struct as_filter
*next
;
59 struct as_filter
*prev
;
61 enum as_filter_type type
;
67 /* AS path filter list. */
71 enum access_type type
;
76 struct as_filter
*head
;
77 struct as_filter
*tail
;
80 /* as-path access-list 10 permit AS1. */
82 static struct as_list_master as_list_master
= {{NULL
, NULL
},
87 /* Allocate new AS filter. */
88 static struct as_filter
*as_filter_new(void)
90 return XCALLOC(MTYPE_AS_FILTER
, sizeof(struct as_filter
));
93 /* Free allocated AS filter. */
94 static void as_filter_free(struct as_filter
*asfilter
)
97 bgp_regex_free(asfilter
->reg
);
98 if (asfilter
->reg_str
)
99 XFREE(MTYPE_AS_FILTER_STR
, asfilter
->reg_str
);
100 XFREE(MTYPE_AS_FILTER
, asfilter
);
103 /* Make new AS filter. */
104 static struct as_filter
*as_filter_make(regex_t
*reg
, const char *reg_str
,
105 enum as_filter_type type
)
107 struct as_filter
*asfilter
;
109 asfilter
= as_filter_new();
111 asfilter
->type
= type
;
112 asfilter
->reg_str
= XSTRDUP(MTYPE_AS_FILTER_STR
, reg_str
);
117 static struct as_filter
*as_filter_lookup(struct as_list
*aslist
,
119 enum as_filter_type type
)
121 struct as_filter
*asfilter
;
123 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
)
124 if (strcmp(reg_str
, asfilter
->reg_str
) == 0)
129 static void as_list_filter_add(struct as_list
*aslist
,
130 struct as_filter
*asfilter
)
132 asfilter
->next
= NULL
;
133 asfilter
->prev
= aslist
->tail
;
136 aslist
->tail
->next
= asfilter
;
138 aslist
->head
= asfilter
;
139 aslist
->tail
= asfilter
;
141 /* Run hook function. */
142 if (as_list_master
.add_hook
)
143 (*as_list_master
.add_hook
)(aslist
->name
);
146 /* Lookup as_list from list of as_list by name. */
147 struct as_list
*as_list_lookup(const char *name
)
149 struct as_list
*aslist
;
154 for (aslist
= as_list_master
.num
.head
; aslist
; aslist
= aslist
->next
)
155 if (strcmp(aslist
->name
, name
) == 0)
158 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= aslist
->next
)
159 if (strcmp(aslist
->name
, name
) == 0)
165 static struct as_list
*as_list_new(void)
167 return XCALLOC(MTYPE_AS_LIST
, sizeof(struct as_list
));
170 static void as_list_free(struct as_list
*aslist
)
173 XFREE(MTYPE_AS_STR
, aslist
->name
);
176 XFREE(MTYPE_AS_LIST
, aslist
);
179 /* Insert new AS list to list of as_list. Each as_list is sorted by
181 static struct as_list
*as_list_insert(const char *name
)
185 struct as_list
*aslist
;
186 struct as_list
*point
;
187 struct as_list_list
*list
;
189 /* Allocate new access_list and copy given name. */
190 aslist
= as_list_new();
191 aslist
->name
= XSTRDUP(MTYPE_AS_STR
, name
);
192 assert(aslist
->name
);
194 /* If name is made by all digit character. We treat it as
196 for (number
= 0, i
= 0; i
< strlen(name
); i
++) {
197 if (isdigit((int)name
[i
]))
198 number
= (number
* 10) + (name
[i
] - '0');
203 /* In case of name is all digit character */
204 if (i
== strlen(name
)) {
205 aslist
->type
= ACCESS_TYPE_NUMBER
;
207 /* Set access_list to number list. */
208 list
= &as_list_master
.num
;
210 for (point
= list
->head
; point
; point
= point
->next
)
211 if (atol(point
->name
) >= number
)
214 aslist
->type
= ACCESS_TYPE_STRING
;
216 /* Set access_list to string list. */
217 list
= &as_list_master
.str
;
219 /* Set point to insertion point. */
220 for (point
= list
->head
; point
; point
= point
->next
)
221 if (strcmp(point
->name
, name
) >= 0)
225 /* In case of this is the first element of master. */
226 if (list
->head
== NULL
) {
227 list
->head
= list
->tail
= aslist
;
231 /* In case of insertion is made at the tail of access_list. */
233 aslist
->prev
= list
->tail
;
234 list
->tail
->next
= aslist
;
239 /* In case of insertion is made at the head of access_list. */
240 if (point
== list
->head
) {
241 aslist
->next
= list
->head
;
242 list
->head
->prev
= aslist
;
247 /* Insertion is made at middle of the access_list. */
248 aslist
->next
= point
;
249 aslist
->prev
= point
->prev
;
252 point
->prev
->next
= aslist
;
253 point
->prev
= aslist
;
258 static struct as_list
*as_list_get(const char *name
)
260 struct as_list
*aslist
;
262 aslist
= as_list_lookup(name
);
264 aslist
= as_list_insert(name
);
269 static const char *filter_type_str(enum as_filter_type type
)
272 case AS_FILTER_PERMIT
:
281 static void as_list_delete(struct as_list
*aslist
)
283 struct as_list_list
*list
;
284 struct as_filter
*filter
, *next
;
286 for (filter
= aslist
->head
; filter
; filter
= next
) {
288 as_filter_free(filter
);
291 if (aslist
->type
== ACCESS_TYPE_NUMBER
)
292 list
= &as_list_master
.num
;
294 list
= &as_list_master
.str
;
297 aslist
->next
->prev
= aslist
->prev
;
299 list
->tail
= aslist
->prev
;
302 aslist
->prev
->next
= aslist
->next
;
304 list
->head
= aslist
->next
;
306 as_list_free(aslist
);
309 static int as_list_empty(struct as_list
*aslist
)
311 if (aslist
->head
== NULL
&& aslist
->tail
== NULL
)
317 static void as_list_filter_delete(struct as_list
*aslist
,
318 struct as_filter
*asfilter
)
320 char *name
= XSTRDUP(MTYPE_AS_STR
, aslist
->name
);
323 asfilter
->next
->prev
= asfilter
->prev
;
325 aslist
->tail
= asfilter
->prev
;
328 asfilter
->prev
->next
= asfilter
->next
;
330 aslist
->head
= asfilter
->next
;
332 as_filter_free(asfilter
);
334 /* If access_list becomes empty delete it from access_master. */
335 if (as_list_empty(aslist
))
336 as_list_delete(aslist
);
338 /* Run hook function. */
339 if (as_list_master
.delete_hook
)
340 (*as_list_master
.delete_hook
)(name
);
342 XFREE(MTYPE_AS_STR
, name
);
345 static int as_filter_match(struct as_filter
*asfilter
, struct aspath
*aspath
)
347 if (bgp_regexec(asfilter
->reg
, aspath
) != REG_NOMATCH
)
352 /* Apply AS path filter to AS. */
353 enum as_filter_type
as_list_apply(struct as_list
*aslist
, void *object
)
355 struct as_filter
*asfilter
;
356 struct aspath
*aspath
;
358 aspath
= (struct aspath
*)object
;
361 return AS_FILTER_DENY
;
363 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
) {
364 if (as_filter_match(asfilter
, aspath
))
365 return asfilter
->type
;
367 return AS_FILTER_DENY
;
370 /* Add hook function. */
371 void as_list_add_hook(void (*func
)(char *))
373 as_list_master
.add_hook
= func
;
376 /* Delete hook function. */
377 void as_list_delete_hook(void (*func
)(const char *))
379 as_list_master
.delete_hook
= func
;
382 static int as_list_dup_check(struct as_list
*aslist
, struct as_filter
*new)
384 struct as_filter
*asfilter
;
386 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
) {
387 if (asfilter
->type
== new->type
388 && strcmp(asfilter
->reg_str
, new->reg_str
) == 0)
394 static int config_bgp_aspath_validate(const char *regstr
)
396 char valid_chars
[] = "1234567890_^|[,{}() ]$*+.?-";
398 if (strspn(regstr
, valid_chars
) == strlen(regstr
))
404 DEFUN(as_path
, bgp_as_path_cmd
,
405 "bgp as-path access-list WORD <deny|permit> LINE...",
407 "BGP autonomous system path filter\n"
408 "Specify an access list name\n"
409 "Regular expression access list name\n"
410 "Specify packets to reject\n"
411 "Specify packets to forward\n"
412 "A regular-expression (1234567890_(^|[,{}() ]|$)) to match the BGP AS paths\n")
415 enum as_filter_type type
;
416 struct as_filter
*asfilter
;
417 struct as_list
*aslist
;
421 if (argv_find(argv
, argc
, "ip", &idx
)) {
422 vty_out(vty
, "This config option is deprecated and is scheduled for removal.\n");
423 vty_out(vty
, "if you are using this please migrate to the below command\n");
424 vty_out(vty
, "'bgp as-path access-list WORD <deny|permit> LINE'\n");
425 zlog_warn("Deprecated option: 'ip as-path access-list WORD <deny|permit> LINE' being used");
428 /* Retrieve access list name */
429 argv_find(argv
, argc
, "WORD", &idx
);
430 char *alname
= argv
[idx
]->arg
;
432 /* Check the filter type. */
433 type
= argv_find(argv
, argc
, "deny", &idx
) ? AS_FILTER_DENY
436 /* Check AS path regex. */
437 argv_find(argv
, argc
, "LINE", &idx
);
438 regstr
= argv_concat(argv
, argc
, idx
);
440 regex
= bgp_regcomp(regstr
);
442 vty_out(vty
, "can't compile regexp %s\n", regstr
);
443 XFREE(MTYPE_TMP
, regstr
);
444 return CMD_WARNING_CONFIG_FAILED
;
447 if (!config_bgp_aspath_validate(regstr
)) {
448 vty_out(vty
, "Invalid character in as-path access-list %s\n",
450 return CMD_WARNING_CONFIG_FAILED
;
453 asfilter
= as_filter_make(regex
, regstr
, type
);
455 XFREE(MTYPE_TMP
, regstr
);
457 /* Install new filter to the access_list. */
458 aslist
= as_list_get(alname
);
460 /* Duplicate insertion check. */;
461 if (as_list_dup_check(aslist
, asfilter
))
462 as_filter_free(asfilter
);
464 as_list_filter_add(aslist
, asfilter
);
469 #if CONFDATE > 20191005
470 CPP_NOTICE("bgpd: remove deprecated 'ip as-path access-list WORD <deny|permit> LINE' command")
472 ALIAS(as_path
, ip_as_path_cmd
,
473 "ip as-path access-list WORD <deny|permit> LINE...",
475 "BGP autonomous system path filter\n"
476 "Specify an access list name\n"
477 "Regular expression access list name\n"
478 "Specify packets to reject\n"
479 "Specify packets to forward\n"
480 "A regular-expression (1234567890_(^|[,{}() ]|$)) to match the BGP AS paths\n")
482 DEFUN(no_as_path
, no_bgp_as_path_cmd
,
483 "no bgp as-path access-list WORD <deny|permit> LINE...",
486 "BGP autonomous system path filter\n"
487 "Specify an access list name\n"
488 "Regular expression access list name\n"
489 "Specify packets to reject\n"
490 "Specify packets to forward\n"
491 "A regular-expression (1234567890_(^|[,{}() ]|$)) to match the BGP AS paths\n")
494 enum as_filter_type type
;
495 struct as_filter
*asfilter
;
496 struct as_list
*aslist
;
500 if (argv_find(argv
, argc
, "ip", &idx
)) {
501 vty_out(vty
, "This config option is deprecated, and is scheduled for removal.\n");
502 vty_out(vty
, "if you are using this please migrate to the below command\n");
503 vty_out(vty
, "'no bgp as-path access-list WORD <deny|permit> LINE'\n");
504 zlog_warn("Deprecated option: 'no ip as-path access-list WORD <deny|permit> LINE' being used");
507 argv_find(argv
, argc
, "WORD", &idx
) ? argv
[idx
]->arg
: NULL
;
509 /* Lookup AS list from AS path list. */
510 aslist
= as_list_lookup(aslistname
);
511 if (aslist
== NULL
) {
512 vty_out(vty
, "bgp as-path access-list %s doesn't exist\n",
514 return CMD_WARNING_CONFIG_FAILED
;
517 /* Check the filter type. */
518 if (argv_find(argv
, argc
, "permit", &idx
))
519 type
= AS_FILTER_PERMIT
;
520 else if (argv_find(argv
, argc
, "deny", &idx
))
521 type
= AS_FILTER_DENY
;
523 vty_out(vty
, "filter type must be [permit|deny]\n");
524 return CMD_WARNING_CONFIG_FAILED
;
527 /* Compile AS path. */
528 argv_find(argv
, argc
, "LINE", &idx
);
529 regstr
= argv_concat(argv
, argc
, idx
);
531 if (!config_bgp_aspath_validate(regstr
)) {
532 vty_out(vty
, "Invalid character in as-path access-list %s\n",
534 return CMD_WARNING_CONFIG_FAILED
;
537 regex
= bgp_regcomp(regstr
);
539 vty_out(vty
, "can't compile regexp %s\n", regstr
);
540 XFREE(MTYPE_TMP
, regstr
);
541 return CMD_WARNING_CONFIG_FAILED
;
544 /* Lookup asfilter. */
545 asfilter
= as_filter_lookup(aslist
, regstr
, type
);
547 XFREE(MTYPE_TMP
, regstr
);
548 bgp_regex_free(regex
);
550 if (asfilter
== NULL
) {
552 return CMD_WARNING_CONFIG_FAILED
;
555 as_list_filter_delete(aslist
, asfilter
);
560 ALIAS(no_as_path
, no_ip_as_path_cmd
,
561 "no ip as-path access-list WORD <deny|permit> LINE...",
563 "BGP autonomous system path filter\n"
564 "Specify an access list name\n"
565 "Regular expression access list name\n"
566 "Specify packets to reject\n"
567 "Specify packets to forward\n"
568 "A regular-expression (1234567890_(^|[,{}() ]|$)) to match the BGP AS paths\n")
570 DEFUN (no_as_path_all
,
571 no_bgp_as_path_all_cmd
,
572 "no bgp as-path access-list WORD",
575 "BGP autonomous system path filter\n"
576 "Specify an access list name\n"
577 "Regular expression access list name\n")
580 struct as_list
*aslist
;
583 if (argv_find(argv
, argc
, "ip", &idx
)) {
584 vty_out(vty
, "This config option is deprecated, and is scheduled for removal.\n");
585 vty_out(vty
, "if you are using this please migrate to the below command\n");
586 vty_out(vty
, "'no bgp as-path access-list WORD'\n");
587 zlog_warn("Deprecated option: `no ip as-path access-list WORD` being used");
590 aslist
= as_list_lookup(argv
[idx_word
]->arg
);
591 if (aslist
== NULL
) {
592 vty_out(vty
, "bgp as-path access-list %s doesn't exist\n",
593 argv
[idx_word
]->arg
);
594 return CMD_WARNING_CONFIG_FAILED
;
597 as_list_delete(aslist
);
599 /* Run hook function. */
600 if (as_list_master
.delete_hook
)
601 (*as_list_master
.delete_hook
)(argv
[idx_word
]->arg
);
606 ALIAS (no_as_path_all
,
607 no_ip_as_path_all_cmd
,
608 "no ip as-path access-list WORD",
611 "BGP autonomous system path filter\n"
612 "Specify an access list name\n"
613 "Regular expression access list name\n")
615 static void as_list_show(struct vty
*vty
, struct as_list
*aslist
)
617 struct as_filter
*asfilter
;
619 vty_out(vty
, "AS path access list %s\n", aslist
->name
);
621 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
) {
622 vty_out(vty
, " %s %s\n", filter_type_str(asfilter
->type
),
627 static void as_list_show_all(struct vty
*vty
)
629 struct as_list
*aslist
;
630 struct as_filter
*asfilter
;
632 for (aslist
= as_list_master
.num
.head
; aslist
; aslist
= aslist
->next
) {
633 vty_out(vty
, "AS path access list %s\n", aslist
->name
);
635 for (asfilter
= aslist
->head
; asfilter
;
636 asfilter
= asfilter
->next
) {
637 vty_out(vty
, " %s %s\n",
638 filter_type_str(asfilter
->type
),
643 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= aslist
->next
) {
644 vty_out(vty
, "AS path access list %s\n", aslist
->name
);
646 for (asfilter
= aslist
->head
; asfilter
;
647 asfilter
= asfilter
->next
) {
648 vty_out(vty
, " %s %s\n",
649 filter_type_str(asfilter
->type
),
655 DEFUN (show_as_path_access_list
,
656 show_bgp_as_path_access_list_cmd
,
657 "show bgp as-path-access-list WORD",
660 "List AS path access lists\n"
661 "AS path access list name\n")
664 struct as_list
*aslist
;
667 if (argv_find(argv
, argc
, "ip", &idx
)) {
668 vty_out(vty
, "This config option is deprecated, and is scheduled for removal.\n");
669 vty_out(vty
, "if you are using this please migrate to the below command\n");
670 vty_out(vty
, "'show bgp as-path-access-list WORD'\n");
671 zlog_warn("Deprecated option: 'show ip as-path-access-list WORD' being used");
673 aslist
= as_list_lookup(argv
[idx_word
]->arg
);
675 as_list_show(vty
, aslist
);
680 ALIAS (show_as_path_access_list
,
681 show_ip_as_path_access_list_cmd
,
682 "show ip as-path-access-list WORD",
685 "List AS path access lists\n"
686 "AS path access list name\n")
688 DEFUN (show_as_path_access_list_all
,
689 show_bgp_as_path_access_list_all_cmd
,
690 "show bgp as-path-access-list",
693 "List AS path access lists\n")
697 if (argv_find(argv
, argc
, "ip", &idx
)) {
698 vty_out(vty
, "This config option is deprecated, and is scheduled for removal.\n");
699 vty_out(vty
, "if you are using this please migrate to the below command\n");
700 vty_out(vty
, "'show bgp as-path-access-list'\n");
701 zlog_warn("Deprecated option: 'show ip as-path-access-list' being used");
703 as_list_show_all(vty
);
707 ALIAS (show_as_path_access_list_all
,
708 show_ip_as_path_access_list_all_cmd
,
709 "show ip as-path-access-list",
712 "List AS path access lists\n")
714 static int config_write_as_list(struct vty
*vty
)
716 struct as_list
*aslist
;
717 struct as_filter
*asfilter
;
720 for (aslist
= as_list_master
.num
.head
; aslist
; aslist
= aslist
->next
)
721 for (asfilter
= aslist
->head
; asfilter
;
722 asfilter
= asfilter
->next
) {
723 vty_out(vty
, "bgp as-path access-list %s %s %s\n",
724 aslist
->name
, filter_type_str(asfilter
->type
),
729 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= aslist
->next
)
730 for (asfilter
= aslist
->head
; asfilter
;
731 asfilter
= asfilter
->next
) {
732 vty_out(vty
, "bgp as-path access-list %s %s %s\n",
733 aslist
->name
, filter_type_str(asfilter
->type
),
740 static struct cmd_node as_list_node
= {AS_LIST_NODE
, "", 1};
742 /* Register functions. */
743 void bgp_filter_init(void)
745 install_node(&as_list_node
, config_write_as_list
);
747 install_element(CONFIG_NODE
, &bgp_as_path_cmd
);
748 install_element(CONFIG_NODE
, &ip_as_path_cmd
);
749 install_element(CONFIG_NODE
, &no_bgp_as_path_cmd
);
750 install_element(CONFIG_NODE
, &no_ip_as_path_cmd
);
751 install_element(CONFIG_NODE
, &no_bgp_as_path_all_cmd
);
752 install_element(CONFIG_NODE
, &no_ip_as_path_all_cmd
);
754 install_element(VIEW_NODE
, &show_bgp_as_path_access_list_cmd
);
755 install_element(VIEW_NODE
, &show_ip_as_path_access_list_cmd
);
756 install_element(VIEW_NODE
, &show_bgp_as_path_access_list_all_cmd
);
757 install_element(VIEW_NODE
, &show_ip_as_path_access_list_all_cmd
);
760 void bgp_filter_reset(void)
762 struct as_list
*aslist
;
763 struct as_list
*next
;
765 for (aslist
= as_list_master
.num
.head
; aslist
; aslist
= next
) {
767 as_list_delete(aslist
);
770 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= next
) {
772 as_list_delete(aslist
);
775 assert(as_list_master
.num
.head
== NULL
);
776 assert(as_list_master
.num
.tail
== NULL
);
778 assert(as_list_master
.str
.head
== NULL
);
779 assert(as_list_master
.str
.tail
== NULL
);