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 XFREE(MTYPE_AS_FILTER_STR
, asfilter
->reg_str
);
99 XFREE(MTYPE_AS_FILTER
, asfilter
);
102 /* Make new AS filter. */
103 static struct as_filter
*as_filter_make(regex_t
*reg
, const char *reg_str
,
104 enum as_filter_type type
)
106 struct as_filter
*asfilter
;
108 asfilter
= as_filter_new();
110 asfilter
->type
= type
;
111 asfilter
->reg_str
= XSTRDUP(MTYPE_AS_FILTER_STR
, reg_str
);
116 static struct as_filter
*as_filter_lookup(struct as_list
*aslist
,
118 enum as_filter_type type
)
120 struct as_filter
*asfilter
;
122 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
)
123 if (strcmp(reg_str
, asfilter
->reg_str
) == 0)
128 static void as_list_filter_add(struct as_list
*aslist
,
129 struct as_filter
*asfilter
)
131 asfilter
->next
= NULL
;
132 asfilter
->prev
= aslist
->tail
;
135 aslist
->tail
->next
= asfilter
;
137 aslist
->head
= asfilter
;
138 aslist
->tail
= asfilter
;
140 /* Run hook function. */
141 if (as_list_master
.add_hook
)
142 (*as_list_master
.add_hook
)(aslist
->name
);
145 /* Lookup as_list from list of as_list by name. */
146 struct as_list
*as_list_lookup(const char *name
)
148 struct as_list
*aslist
;
153 for (aslist
= as_list_master
.num
.head
; aslist
; aslist
= aslist
->next
)
154 if (strcmp(aslist
->name
, name
) == 0)
157 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= aslist
->next
)
158 if (strcmp(aslist
->name
, name
) == 0)
164 static struct as_list
*as_list_new(void)
166 return XCALLOC(MTYPE_AS_LIST
, sizeof(struct as_list
));
169 static void as_list_free(struct as_list
*aslist
)
172 XFREE(MTYPE_AS_STR
, aslist
->name
);
175 XFREE(MTYPE_AS_LIST
, aslist
);
178 /* Insert new AS list to list of as_list. Each as_list is sorted by
180 static struct as_list
*as_list_insert(const char *name
)
184 struct as_list
*aslist
;
185 struct as_list
*point
;
186 struct as_list_list
*list
;
188 /* Allocate new access_list and copy given name. */
189 aslist
= as_list_new();
190 aslist
->name
= XSTRDUP(MTYPE_AS_STR
, name
);
191 assert(aslist
->name
);
193 /* If name is made by all digit character. We treat it as
195 for (number
= 0, i
= 0; i
< strlen(name
); i
++) {
196 if (isdigit((unsigned char)name
[i
]))
197 number
= (number
* 10) + (name
[i
] - '0');
202 /* In case of name is all digit character */
203 if (i
== strlen(name
)) {
204 aslist
->type
= ACCESS_TYPE_NUMBER
;
206 /* Set access_list to number list. */
207 list
= &as_list_master
.num
;
209 for (point
= list
->head
; point
; point
= point
->next
)
210 if (atol(point
->name
) >= number
)
213 aslist
->type
= ACCESS_TYPE_STRING
;
215 /* Set access_list to string list. */
216 list
= &as_list_master
.str
;
218 /* Set point to insertion point. */
219 for (point
= list
->head
; point
; point
= point
->next
)
220 if (strcmp(point
->name
, name
) >= 0)
224 /* In case of this is the first element of master. */
225 if (list
->head
== NULL
) {
226 list
->head
= list
->tail
= aslist
;
230 /* In case of insertion is made at the tail of access_list. */
232 aslist
->prev
= list
->tail
;
233 list
->tail
->next
= aslist
;
238 /* In case of insertion is made at the head of access_list. */
239 if (point
== list
->head
) {
240 aslist
->next
= list
->head
;
241 list
->head
->prev
= aslist
;
246 /* Insertion is made at middle of the access_list. */
247 aslist
->next
= point
;
248 aslist
->prev
= point
->prev
;
251 point
->prev
->next
= aslist
;
252 point
->prev
= aslist
;
257 static struct as_list
*as_list_get(const char *name
)
259 struct as_list
*aslist
;
261 aslist
= as_list_lookup(name
);
263 aslist
= as_list_insert(name
);
268 static const char *filter_type_str(enum as_filter_type type
)
271 case AS_FILTER_PERMIT
:
280 static void as_list_delete(struct as_list
*aslist
)
282 struct as_list_list
*list
;
283 struct as_filter
*filter
, *next
;
285 for (filter
= aslist
->head
; filter
; filter
= next
) {
287 as_filter_free(filter
);
290 if (aslist
->type
== ACCESS_TYPE_NUMBER
)
291 list
= &as_list_master
.num
;
293 list
= &as_list_master
.str
;
296 aslist
->next
->prev
= aslist
->prev
;
298 list
->tail
= aslist
->prev
;
301 aslist
->prev
->next
= aslist
->next
;
303 list
->head
= aslist
->next
;
305 as_list_free(aslist
);
308 static int as_list_empty(struct as_list
*aslist
)
310 if (aslist
->head
== NULL
&& aslist
->tail
== NULL
)
316 static void as_list_filter_delete(struct as_list
*aslist
,
317 struct as_filter
*asfilter
)
319 char *name
= XSTRDUP(MTYPE_AS_STR
, aslist
->name
);
322 asfilter
->next
->prev
= asfilter
->prev
;
324 aslist
->tail
= asfilter
->prev
;
327 asfilter
->prev
->next
= asfilter
->next
;
329 aslist
->head
= asfilter
->next
;
331 as_filter_free(asfilter
);
333 /* If access_list becomes empty delete it from access_master. */
334 if (as_list_empty(aslist
))
335 as_list_delete(aslist
);
337 /* Run hook function. */
338 if (as_list_master
.delete_hook
)
339 (*as_list_master
.delete_hook
)(name
);
340 XFREE(MTYPE_AS_STR
, name
);
343 static int as_filter_match(struct as_filter
*asfilter
, struct aspath
*aspath
)
345 if (bgp_regexec(asfilter
->reg
, aspath
) != REG_NOMATCH
)
350 /* Apply AS path filter to AS. */
351 enum as_filter_type
as_list_apply(struct as_list
*aslist
, void *object
)
353 struct as_filter
*asfilter
;
354 struct aspath
*aspath
;
356 aspath
= (struct aspath
*)object
;
359 return AS_FILTER_DENY
;
361 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
) {
362 if (as_filter_match(asfilter
, aspath
))
363 return asfilter
->type
;
365 return AS_FILTER_DENY
;
368 /* Add hook function. */
369 void as_list_add_hook(void (*func
)(char *))
371 as_list_master
.add_hook
= func
;
374 /* Delete hook function. */
375 void as_list_delete_hook(void (*func
)(const char *))
377 as_list_master
.delete_hook
= func
;
380 static int as_list_dup_check(struct as_list
*aslist
, struct as_filter
*new)
382 struct as_filter
*asfilter
;
384 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
) {
385 if (asfilter
->type
== new->type
386 && strcmp(asfilter
->reg_str
, new->reg_str
) == 0)
392 int config_bgp_aspath_validate(const char *regstr
)
394 char valid_chars
[] = "1234567890_^|[,{}() ]$*+.?-\\";
396 if (strspn(regstr
, valid_chars
) == strlen(regstr
))
402 DEFUN(as_path
, bgp_as_path_cmd
,
403 "bgp as-path access-list WORD <deny|permit> LINE...",
405 "BGP autonomous system path filter\n"
406 "Specify an access list name\n"
407 "Regular expression access list name\n"
408 "Specify packets to reject\n"
409 "Specify packets to forward\n"
410 "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
413 enum as_filter_type type
;
414 struct as_filter
*asfilter
;
415 struct as_list
*aslist
;
419 if (argv_find(argv
, argc
, "ip", &idx
)) {
420 vty_out(vty
, "This config option is deprecated and is scheduled for removal.\n");
421 vty_out(vty
, "if you are using this please migrate to the below command\n");
422 vty_out(vty
, "'bgp as-path access-list WORD <deny|permit> LINE'\n");
423 zlog_warn("Deprecated option: 'ip as-path access-list WORD <deny|permit> LINE' being used");
426 /* Retrieve access list name */
427 argv_find(argv
, argc
, "WORD", &idx
);
428 char *alname
= argv
[idx
]->arg
;
430 /* Check the filter type. */
431 type
= argv_find(argv
, argc
, "deny", &idx
) ? AS_FILTER_DENY
434 /* Check AS path regex. */
435 argv_find(argv
, argc
, "LINE", &idx
);
436 regstr
= argv_concat(argv
, argc
, idx
);
438 regex
= bgp_regcomp(regstr
);
440 vty_out(vty
, "can't compile regexp %s\n", regstr
);
441 XFREE(MTYPE_TMP
, regstr
);
442 return CMD_WARNING_CONFIG_FAILED
;
445 if (!config_bgp_aspath_validate(regstr
)) {
446 vty_out(vty
, "Invalid character in as-path access-list %s\n",
448 return CMD_WARNING_CONFIG_FAILED
;
451 asfilter
= as_filter_make(regex
, regstr
, type
);
453 XFREE(MTYPE_TMP
, regstr
);
455 /* Install new filter to the access_list. */
456 aslist
= as_list_get(alname
);
458 /* Duplicate insertion check. */;
459 if (as_list_dup_check(aslist
, asfilter
))
460 as_filter_free(asfilter
);
462 as_list_filter_add(aslist
, asfilter
);
467 #if CONFDATE > 20191005
468 CPP_NOTICE("bgpd: remove deprecated 'ip as-path access-list WORD <deny|permit> LINE' command")
470 ALIAS(as_path
, ip_as_path_cmd
,
471 "ip as-path access-list WORD <deny|permit> LINE...",
473 "BGP autonomous system path filter\n"
474 "Specify an access list name\n"
475 "Regular expression access list name\n"
476 "Specify packets to reject\n"
477 "Specify packets to forward\n"
478 "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
480 DEFUN(no_as_path
, no_bgp_as_path_cmd
,
481 "no bgp as-path access-list WORD <deny|permit> LINE...",
484 "BGP autonomous system path filter\n"
485 "Specify an access list name\n"
486 "Regular expression access list name\n"
487 "Specify packets to reject\n"
488 "Specify packets to forward\n"
489 "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
492 enum as_filter_type type
;
493 struct as_filter
*asfilter
;
494 struct as_list
*aslist
;
498 if (argv_find(argv
, argc
, "ip", &idx
)) {
499 vty_out(vty
, "This config option is deprecated, and is scheduled for removal.\n");
500 vty_out(vty
, "if you are using this please migrate to the below command\n");
501 vty_out(vty
, "'no bgp as-path access-list WORD <deny|permit> LINE'\n");
502 zlog_warn("Deprecated option: 'no ip as-path access-list WORD <deny|permit> LINE' being used");
505 argv_find(argv
, argc
, "WORD", &idx
) ? argv
[idx
]->arg
: NULL
;
507 /* Lookup AS list from AS path list. */
508 aslist
= as_list_lookup(aslistname
);
509 if (aslist
== NULL
) {
510 vty_out(vty
, "bgp as-path access-list %s doesn't exist\n",
512 return CMD_WARNING_CONFIG_FAILED
;
515 /* Check the filter type. */
516 if (argv_find(argv
, argc
, "permit", &idx
))
517 type
= AS_FILTER_PERMIT
;
518 else if (argv_find(argv
, argc
, "deny", &idx
))
519 type
= AS_FILTER_DENY
;
521 vty_out(vty
, "filter type must be [permit|deny]\n");
522 return CMD_WARNING_CONFIG_FAILED
;
525 /* Compile AS path. */
526 argv_find(argv
, argc
, "LINE", &idx
);
527 regstr
= argv_concat(argv
, argc
, idx
);
529 if (!config_bgp_aspath_validate(regstr
)) {
530 vty_out(vty
, "Invalid character in as-path access-list %s\n",
532 return CMD_WARNING_CONFIG_FAILED
;
535 regex
= bgp_regcomp(regstr
);
537 vty_out(vty
, "can't compile regexp %s\n", regstr
);
538 XFREE(MTYPE_TMP
, regstr
);
539 return CMD_WARNING_CONFIG_FAILED
;
542 /* Lookup asfilter. */
543 asfilter
= as_filter_lookup(aslist
, regstr
, type
);
545 XFREE(MTYPE_TMP
, regstr
);
546 bgp_regex_free(regex
);
548 if (asfilter
== NULL
) {
550 return CMD_WARNING_CONFIG_FAILED
;
553 as_list_filter_delete(aslist
, asfilter
);
558 ALIAS(no_as_path
, no_ip_as_path_cmd
,
559 "no ip as-path access-list WORD <deny|permit> LINE...",
561 "BGP autonomous system path filter\n"
562 "Specify an access list name\n"
563 "Regular expression access list name\n"
564 "Specify packets to reject\n"
565 "Specify packets to forward\n"
566 "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
568 DEFUN (no_as_path_all
,
569 no_bgp_as_path_all_cmd
,
570 "no bgp as-path access-list WORD",
573 "BGP autonomous system path filter\n"
574 "Specify an access list name\n"
575 "Regular expression access list name\n")
578 struct as_list
*aslist
;
581 if (argv_find(argv
, argc
, "ip", &idx
)) {
582 vty_out(vty
, "This config option is deprecated, and is scheduled for removal.\n");
583 vty_out(vty
, "if you are using this please migrate to the below command\n");
584 vty_out(vty
, "'no bgp as-path access-list WORD'\n");
585 zlog_warn("Deprecated option: `no ip as-path access-list WORD` being used");
588 aslist
= as_list_lookup(argv
[idx_word
]->arg
);
589 if (aslist
== NULL
) {
590 vty_out(vty
, "bgp as-path access-list %s doesn't exist\n",
591 argv
[idx_word
]->arg
);
592 return CMD_WARNING_CONFIG_FAILED
;
595 as_list_delete(aslist
);
597 /* Run hook function. */
598 if (as_list_master
.delete_hook
)
599 (*as_list_master
.delete_hook
)(argv
[idx_word
]->arg
);
604 ALIAS (no_as_path_all
,
605 no_ip_as_path_all_cmd
,
606 "no ip as-path access-list WORD",
609 "BGP autonomous system path filter\n"
610 "Specify an access list name\n"
611 "Regular expression access list name\n")
613 static void as_list_show(struct vty
*vty
, struct as_list
*aslist
)
615 struct as_filter
*asfilter
;
617 vty_out(vty
, "AS path access list %s\n", aslist
->name
);
619 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
) {
620 vty_out(vty
, " %s %s\n", filter_type_str(asfilter
->type
),
625 static void as_list_show_all(struct vty
*vty
)
627 struct as_list
*aslist
;
628 struct as_filter
*asfilter
;
630 for (aslist
= as_list_master
.num
.head
; aslist
; aslist
= aslist
->next
) {
631 vty_out(vty
, "AS path access list %s\n", aslist
->name
);
633 for (asfilter
= aslist
->head
; asfilter
;
634 asfilter
= asfilter
->next
) {
635 vty_out(vty
, " %s %s\n",
636 filter_type_str(asfilter
->type
),
641 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= aslist
->next
) {
642 vty_out(vty
, "AS path access list %s\n", aslist
->name
);
644 for (asfilter
= aslist
->head
; asfilter
;
645 asfilter
= asfilter
->next
) {
646 vty_out(vty
, " %s %s\n",
647 filter_type_str(asfilter
->type
),
653 DEFUN (show_as_path_access_list
,
654 show_bgp_as_path_access_list_cmd
,
655 "show bgp as-path-access-list WORD",
658 "List AS path access lists\n"
659 "AS path access list name\n")
662 struct as_list
*aslist
;
665 if (argv_find(argv
, argc
, "ip", &idx
)) {
666 vty_out(vty
, "This config option is deprecated, and is scheduled for removal.\n");
667 vty_out(vty
, "if you are using this please migrate to the below command\n");
668 vty_out(vty
, "'show bgp as-path-access-list WORD'\n");
669 zlog_warn("Deprecated option: 'show ip as-path-access-list WORD' being used");
671 aslist
= as_list_lookup(argv
[idx_word
]->arg
);
673 as_list_show(vty
, aslist
);
678 ALIAS (show_as_path_access_list
,
679 show_ip_as_path_access_list_cmd
,
680 "show ip as-path-access-list WORD",
683 "List AS path access lists\n"
684 "AS path access list name\n")
686 DEFUN (show_as_path_access_list_all
,
687 show_bgp_as_path_access_list_all_cmd
,
688 "show bgp as-path-access-list",
691 "List AS path access lists\n")
695 if (argv_find(argv
, argc
, "ip", &idx
)) {
696 vty_out(vty
, "This config option is deprecated, and is scheduled for removal.\n");
697 vty_out(vty
, "if you are using this please migrate to the below command\n");
698 vty_out(vty
, "'show bgp as-path-access-list'\n");
699 zlog_warn("Deprecated option: 'show ip as-path-access-list' being used");
701 as_list_show_all(vty
);
705 ALIAS (show_as_path_access_list_all
,
706 show_ip_as_path_access_list_all_cmd
,
707 "show ip as-path-access-list",
710 "List AS path access lists\n")
712 static int config_write_as_list(struct vty
*vty
)
714 struct as_list
*aslist
;
715 struct as_filter
*asfilter
;
718 for (aslist
= as_list_master
.num
.head
; aslist
; aslist
= aslist
->next
)
719 for (asfilter
= aslist
->head
; asfilter
;
720 asfilter
= asfilter
->next
) {
721 vty_out(vty
, "bgp as-path access-list %s %s %s\n",
722 aslist
->name
, filter_type_str(asfilter
->type
),
727 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= aslist
->next
)
728 for (asfilter
= aslist
->head
; asfilter
;
729 asfilter
= asfilter
->next
) {
730 vty_out(vty
, "bgp as-path access-list %s %s %s\n",
731 aslist
->name
, filter_type_str(asfilter
->type
),
738 static struct cmd_node as_list_node
= {AS_LIST_NODE
, "", 1};
740 /* Register functions. */
741 void bgp_filter_init(void)
743 install_node(&as_list_node
, config_write_as_list
);
745 install_element(CONFIG_NODE
, &bgp_as_path_cmd
);
746 install_element(CONFIG_NODE
, &ip_as_path_cmd
);
747 install_element(CONFIG_NODE
, &no_bgp_as_path_cmd
);
748 install_element(CONFIG_NODE
, &no_ip_as_path_cmd
);
749 install_element(CONFIG_NODE
, &no_bgp_as_path_all_cmd
);
750 install_element(CONFIG_NODE
, &no_ip_as_path_all_cmd
);
752 install_element(VIEW_NODE
, &show_bgp_as_path_access_list_cmd
);
753 install_element(VIEW_NODE
, &show_ip_as_path_access_list_cmd
);
754 install_element(VIEW_NODE
, &show_bgp_as_path_access_list_all_cmd
);
755 install_element(VIEW_NODE
, &show_ip_as_path_access_list_all_cmd
);
758 void bgp_filter_reset(void)
760 struct as_list
*aslist
;
761 struct as_list
*next
;
763 for (aslist
= as_list_master
.num
.head
; aslist
; aslist
= next
) {
765 as_list_delete(aslist
);
768 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= next
) {
770 as_list_delete(aslist
);
773 assert(as_list_master
.num
.head
== NULL
);
774 assert(as_list_master
.num
.tail
== NULL
);
776 assert(as_list_master
.str
.head
== NULL
);
777 assert(as_list_master
.str
.tail
== NULL
);