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
;
66 /* Sequence number. */
70 /* AS path filter list. */
74 enum access_type type
;
79 struct as_filter
*head
;
80 struct as_filter
*tail
;
84 /* Calculate new sequential number. */
85 static int64_t bgp_alist_new_seq_get(struct as_list
*list
)
89 struct as_filter
*entry
;
93 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
94 if (maxseq
< entry
->seq
)
98 newseq
= ((maxseq
/ 5) * 5) + 5;
100 return (newseq
> UINT_MAX
) ? UINT_MAX
: newseq
;
103 /* Return as-list entry which has same seq number. */
104 static struct as_filter
*bgp_aslist_seq_check(struct as_list
*list
, int64_t seq
)
106 struct as_filter
*entry
;
108 for (entry
= list
->head
; entry
; entry
= entry
->next
)
109 if (entry
->seq
== seq
)
115 /* as-path access-list 10 permit AS1. */
117 static struct as_list_master as_list_master
= {{NULL
, NULL
},
122 /* Allocate new AS filter. */
123 static struct as_filter
*as_filter_new(void)
125 return XCALLOC(MTYPE_AS_FILTER
, sizeof(struct as_filter
));
128 /* Free allocated AS filter. */
129 static void as_filter_free(struct as_filter
*asfilter
)
132 bgp_regex_free(asfilter
->reg
);
133 XFREE(MTYPE_AS_FILTER_STR
, asfilter
->reg_str
);
134 XFREE(MTYPE_AS_FILTER
, asfilter
);
137 /* Make new AS filter. */
138 static struct as_filter
*as_filter_make(regex_t
*reg
, const char *reg_str
,
139 enum as_filter_type type
)
141 struct as_filter
*asfilter
;
143 asfilter
= as_filter_new();
145 asfilter
->type
= type
;
146 asfilter
->reg_str
= XSTRDUP(MTYPE_AS_FILTER_STR
, reg_str
);
151 static struct as_filter
*as_filter_lookup(struct as_list
*aslist
,
153 enum as_filter_type type
)
155 struct as_filter
*asfilter
;
157 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
)
158 if (strcmp(reg_str
, asfilter
->reg_str
) == 0)
163 static void as_filter_entry_replace(struct as_list
*list
,
164 struct as_filter
*replace
,
165 struct as_filter
*entry
)
168 entry
->next
= replace
->next
;
169 replace
->next
->prev
= entry
;
176 entry
->prev
= replace
->prev
;
177 replace
->prev
->next
= entry
;
183 as_filter_free(replace
);
186 static void as_list_filter_add(struct as_list
*aslist
,
187 struct as_filter
*asfilter
)
189 struct as_filter
*point
;
190 struct as_filter
*replace
;
192 if (aslist
->tail
&& asfilter
->seq
> aslist
->tail
->seq
)
195 replace
= bgp_aslist_seq_check(aslist
, asfilter
->seq
);
197 as_filter_entry_replace(aslist
, replace
, asfilter
);
201 /* Check insert point. */
202 for (point
= aslist
->head
; point
; point
= point
->next
)
203 if (point
->seq
>= asfilter
->seq
)
207 asfilter
->next
= point
;
211 point
->prev
->next
= asfilter
;
213 aslist
->head
= asfilter
;
215 asfilter
->prev
= point
->prev
;
216 point
->prev
= asfilter
;
219 aslist
->tail
->next
= asfilter
;
221 aslist
->head
= asfilter
;
223 asfilter
->prev
= aslist
->tail
;
224 aslist
->tail
= asfilter
;
227 /* Run hook function. */
228 if (as_list_master
.add_hook
)
229 (*as_list_master
.add_hook
)(aslist
->name
);
232 /* Lookup as_list from list of as_list by name. */
233 struct as_list
*as_list_lookup(const char *name
)
235 struct as_list
*aslist
;
240 for (aslist
= as_list_master
.num
.head
; aslist
; aslist
= aslist
->next
)
241 if (strcmp(aslist
->name
, name
) == 0)
244 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= aslist
->next
)
245 if (strcmp(aslist
->name
, name
) == 0)
251 static struct as_list
*as_list_new(void)
253 return XCALLOC(MTYPE_AS_LIST
, sizeof(struct as_list
));
256 static void as_list_free(struct as_list
*aslist
)
258 XFREE(MTYPE_AS_STR
, aslist
->name
);
259 XFREE(MTYPE_AS_LIST
, aslist
);
262 /* Insert new AS list to list of as_list. Each as_list is sorted by
264 static struct as_list
*as_list_insert(const char *name
)
268 struct as_list
*aslist
;
269 struct as_list
*point
;
270 struct as_list_list
*list
;
272 /* Allocate new access_list and copy given name. */
273 aslist
= as_list_new();
274 aslist
->name
= XSTRDUP(MTYPE_AS_STR
, name
);
275 assert(aslist
->name
);
277 /* If name is made by all digit character. We treat it as
279 for (number
= 0, i
= 0; i
< strlen(name
); i
++) {
280 if (isdigit((unsigned char)name
[i
]))
281 number
= (number
* 10) + (name
[i
] - '0');
286 /* In case of name is all digit character */
287 if (i
== strlen(name
)) {
288 aslist
->type
= ACCESS_TYPE_NUMBER
;
290 /* Set access_list to number list. */
291 list
= &as_list_master
.num
;
293 for (point
= list
->head
; point
; point
= point
->next
)
294 if (atol(point
->name
) >= number
)
297 aslist
->type
= ACCESS_TYPE_STRING
;
299 /* Set access_list to string list. */
300 list
= &as_list_master
.str
;
302 /* Set point to insertion point. */
303 for (point
= list
->head
; point
; point
= point
->next
)
304 if (strcmp(point
->name
, name
) >= 0)
308 /* In case of this is the first element of master. */
309 if (list
->head
== NULL
) {
310 list
->head
= list
->tail
= aslist
;
314 /* In case of insertion is made at the tail of access_list. */
316 aslist
->prev
= list
->tail
;
317 list
->tail
->next
= aslist
;
322 /* In case of insertion is made at the head of access_list. */
323 if (point
== list
->head
) {
324 aslist
->next
= list
->head
;
325 list
->head
->prev
= aslist
;
330 /* Insertion is made at middle of the access_list. */
331 aslist
->next
= point
;
332 aslist
->prev
= point
->prev
;
335 point
->prev
->next
= aslist
;
336 point
->prev
= aslist
;
341 static struct as_list
*as_list_get(const char *name
)
343 struct as_list
*aslist
;
345 aslist
= as_list_lookup(name
);
347 aslist
= as_list_insert(name
);
352 static const char *filter_type_str(enum as_filter_type type
)
355 case AS_FILTER_PERMIT
:
364 static void as_list_delete(struct as_list
*aslist
)
366 struct as_list_list
*list
;
367 struct as_filter
*filter
, *next
;
369 for (filter
= aslist
->head
; filter
; filter
= next
) {
371 as_filter_free(filter
);
374 if (aslist
->type
== ACCESS_TYPE_NUMBER
)
375 list
= &as_list_master
.num
;
377 list
= &as_list_master
.str
;
380 aslist
->next
->prev
= aslist
->prev
;
382 list
->tail
= aslist
->prev
;
385 aslist
->prev
->next
= aslist
->next
;
387 list
->head
= aslist
->next
;
389 as_list_free(aslist
);
392 static bool as_list_empty(struct as_list
*aslist
)
394 return aslist
->head
== NULL
&& aslist
->tail
== NULL
;
397 static void as_list_filter_delete(struct as_list
*aslist
,
398 struct as_filter
*asfilter
)
400 char *name
= XSTRDUP(MTYPE_AS_STR
, aslist
->name
);
403 asfilter
->next
->prev
= asfilter
->prev
;
405 aslist
->tail
= asfilter
->prev
;
408 asfilter
->prev
->next
= asfilter
->next
;
410 aslist
->head
= asfilter
->next
;
412 as_filter_free(asfilter
);
414 /* If access_list becomes empty delete it from access_master. */
415 if (as_list_empty(aslist
))
416 as_list_delete(aslist
);
418 /* Run hook function. */
419 if (as_list_master
.delete_hook
)
420 (*as_list_master
.delete_hook
)(name
);
421 XFREE(MTYPE_AS_STR
, name
);
424 static bool as_filter_match(struct as_filter
*asfilter
, struct aspath
*aspath
)
426 return bgp_regexec(asfilter
->reg
, aspath
) != REG_NOMATCH
;
429 /* Apply AS path filter to AS. */
430 enum as_filter_type
as_list_apply(struct as_list
*aslist
, void *object
)
432 struct as_filter
*asfilter
;
433 struct aspath
*aspath
;
435 aspath
= (struct aspath
*)object
;
438 return AS_FILTER_DENY
;
440 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
) {
441 if (as_filter_match(asfilter
, aspath
))
442 return asfilter
->type
;
444 return AS_FILTER_DENY
;
447 /* Add hook function. */
448 void as_list_add_hook(void (*func
)(char *))
450 as_list_master
.add_hook
= func
;
453 /* Delete hook function. */
454 void as_list_delete_hook(void (*func
)(const char *))
456 as_list_master
.delete_hook
= func
;
459 static bool as_list_dup_check(struct as_list
*aslist
, struct as_filter
*new)
461 struct as_filter
*asfilter
;
463 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
) {
464 if (asfilter
->type
== new->type
465 && strcmp(asfilter
->reg_str
, new->reg_str
) == 0)
471 bool config_bgp_aspath_validate(const char *regstr
)
473 char valid_chars
[] = "1234567890_^|[,{}() ]$*+.?-\\";
475 if (strspn(regstr
, valid_chars
) == strlen(regstr
))
480 DEFUN(as_path
, bgp_as_path_cmd
,
481 "bgp as-path access-list WORD [seq (0-4294967295)] <deny|permit> LINE...",
483 "BGP autonomous system path filter\n"
484 "Specify an access list name\n"
485 "Regular expression access list name\n"
486 "Sequence number of an entry\n"
488 "Specify packets to reject\n"
489 "Specify packets to forward\n"
490 "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
493 enum as_filter_type type
;
494 struct as_filter
*asfilter
;
495 struct as_list
*aslist
;
498 int64_t seqnum
= ASPATH_SEQ_NUMBER_AUTO
;
500 /* Retrieve access list name */
501 argv_find(argv
, argc
, "WORD", &idx
);
502 char *alname
= argv
[idx
]->arg
;
504 if (argv_find(argv
, argc
, "(0-4294967295)", &idx
))
505 seqnum
= (int64_t)atol(argv
[idx
]->arg
);
507 /* Check the filter type. */
508 type
= argv_find(argv
, argc
, "deny", &idx
) ? AS_FILTER_DENY
511 /* Check AS path regex. */
512 argv_find(argv
, argc
, "LINE", &idx
);
513 regstr
= argv_concat(argv
, argc
, idx
);
515 regex
= bgp_regcomp(regstr
);
517 vty_out(vty
, "can't compile regexp %s\n", regstr
);
518 XFREE(MTYPE_TMP
, regstr
);
519 return CMD_WARNING_CONFIG_FAILED
;
522 if (!config_bgp_aspath_validate(regstr
)) {
523 vty_out(vty
, "Invalid character in as-path access-list %s\n",
525 return CMD_WARNING_CONFIG_FAILED
;
528 asfilter
= as_filter_make(regex
, regstr
, type
);
530 XFREE(MTYPE_TMP
, regstr
);
532 /* Install new filter to the access_list. */
533 aslist
= as_list_get(alname
);
535 if (seqnum
== ASPATH_SEQ_NUMBER_AUTO
)
536 seqnum
= bgp_alist_new_seq_get(aslist
);
538 asfilter
->seq
= seqnum
;
540 /* Duplicate insertion check. */;
541 if (as_list_dup_check(aslist
, asfilter
))
542 as_filter_free(asfilter
);
544 as_list_filter_add(aslist
, asfilter
);
549 DEFUN(no_as_path
, no_bgp_as_path_cmd
,
550 "no bgp as-path access-list WORD [seq (0-4294967295)] <deny|permit> LINE...",
553 "BGP autonomous system path filter\n"
554 "Specify an access list name\n"
555 "Regular expression access list name\n"
556 "Sequence number of an entry\n"
558 "Specify packets to reject\n"
559 "Specify packets to forward\n"
560 "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
563 enum as_filter_type type
;
564 struct as_filter
*asfilter
;
565 struct as_list
*aslist
;
570 argv_find(argv
, argc
, "WORD", &idx
) ? argv
[idx
]->arg
: NULL
;
572 /* Lookup AS list from AS path list. */
573 aslist
= as_list_lookup(aslistname
);
574 if (aslist
== NULL
) {
575 vty_out(vty
, "bgp as-path access-list %s doesn't exist\n",
577 return CMD_WARNING_CONFIG_FAILED
;
580 /* Check the filter type. */
581 if (argv_find(argv
, argc
, "permit", &idx
))
582 type
= AS_FILTER_PERMIT
;
583 else if (argv_find(argv
, argc
, "deny", &idx
))
584 type
= AS_FILTER_DENY
;
586 vty_out(vty
, "filter type must be [permit|deny]\n");
587 return CMD_WARNING_CONFIG_FAILED
;
590 /* Compile AS path. */
591 argv_find(argv
, argc
, "LINE", &idx
);
592 regstr
= argv_concat(argv
, argc
, idx
);
594 if (!config_bgp_aspath_validate(regstr
)) {
595 vty_out(vty
, "Invalid character in as-path access-list %s\n",
597 return CMD_WARNING_CONFIG_FAILED
;
600 regex
= bgp_regcomp(regstr
);
602 vty_out(vty
, "can't compile regexp %s\n", regstr
);
603 XFREE(MTYPE_TMP
, regstr
);
604 return CMD_WARNING_CONFIG_FAILED
;
607 /* Lookup asfilter. */
608 asfilter
= as_filter_lookup(aslist
, regstr
, type
);
610 bgp_regex_free(regex
);
612 if (asfilter
== NULL
) {
613 vty_out(vty
, "Regex entered %s does not exist\n", regstr
);
614 XFREE(MTYPE_TMP
, regstr
);
615 return CMD_WARNING_CONFIG_FAILED
;
618 XFREE(MTYPE_TMP
, regstr
);
620 as_list_filter_delete(aslist
, asfilter
);
625 DEFUN (no_as_path_all
,
626 no_bgp_as_path_all_cmd
,
627 "no bgp as-path access-list WORD",
630 "BGP autonomous system path filter\n"
631 "Specify an access list name\n"
632 "Regular expression access list name\n")
635 struct as_list
*aslist
;
637 aslist
= as_list_lookup(argv
[idx_word
]->arg
);
638 if (aslist
== NULL
) {
639 vty_out(vty
, "bgp as-path access-list %s doesn't exist\n",
640 argv
[idx_word
]->arg
);
641 return CMD_WARNING_CONFIG_FAILED
;
644 as_list_delete(aslist
);
646 /* Run hook function. */
647 if (as_list_master
.delete_hook
)
648 (*as_list_master
.delete_hook
)(argv
[idx_word
]->arg
);
653 static void as_list_show(struct vty
*vty
, struct as_list
*aslist
)
655 struct as_filter
*asfilter
;
657 vty_out(vty
, "AS path access list %s\n", aslist
->name
);
659 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
) {
660 vty_out(vty
, " %s %s\n", filter_type_str(asfilter
->type
),
665 static void as_list_show_all(struct vty
*vty
)
667 struct as_list
*aslist
;
668 struct as_filter
*asfilter
;
670 for (aslist
= as_list_master
.num
.head
; aslist
; aslist
= aslist
->next
) {
671 vty_out(vty
, "AS path access list %s\n", aslist
->name
);
673 for (asfilter
= aslist
->head
; asfilter
;
674 asfilter
= asfilter
->next
) {
675 vty_out(vty
, " %s %s\n",
676 filter_type_str(asfilter
->type
),
681 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= aslist
->next
) {
682 vty_out(vty
, "AS path access list %s\n", aslist
->name
);
684 for (asfilter
= aslist
->head
; asfilter
;
685 asfilter
= asfilter
->next
) {
686 vty_out(vty
, " %s %s\n",
687 filter_type_str(asfilter
->type
),
693 DEFUN (show_as_path_access_list
,
694 show_bgp_as_path_access_list_cmd
,
695 "show bgp as-path-access-list WORD",
698 "List AS path access lists\n"
699 "AS path access list name\n")
702 struct as_list
*aslist
;
704 aslist
= as_list_lookup(argv
[idx_word
]->arg
);
706 as_list_show(vty
, aslist
);
711 ALIAS (show_as_path_access_list
,
712 show_ip_as_path_access_list_cmd
,
713 "show ip as-path-access-list WORD",
716 "List AS path access lists\n"
717 "AS path access list name\n")
719 DEFUN (show_as_path_access_list_all
,
720 show_bgp_as_path_access_list_all_cmd
,
721 "show bgp as-path-access-list",
724 "List AS path access lists\n")
726 as_list_show_all(vty
);
730 ALIAS (show_as_path_access_list_all
,
731 show_ip_as_path_access_list_all_cmd
,
732 "show ip as-path-access-list",
735 "List AS path access lists\n")
737 static int config_write_as_list(struct vty
*vty
)
739 struct as_list
*aslist
;
740 struct as_filter
*asfilter
;
743 for (aslist
= as_list_master
.num
.head
; aslist
; aslist
= aslist
->next
)
744 for (asfilter
= aslist
->head
; asfilter
;
745 asfilter
= asfilter
->next
) {
747 "bgp as-path access-list %s seq %" PRId64
749 aslist
->name
, asfilter
->seq
,
750 filter_type_str(asfilter
->type
),
755 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= aslist
->next
)
756 for (asfilter
= aslist
->head
; asfilter
;
757 asfilter
= asfilter
->next
) {
759 "bgp as-path access-list %s seq %" PRId64
761 aslist
->name
, asfilter
->seq
,
762 filter_type_str(asfilter
->type
),
769 static int config_write_as_list(struct vty
*vty
);
770 static struct cmd_node as_list_node
= {
772 .node
= AS_LIST_NODE
,
774 .config_write
= config_write_as_list
,
777 /* Register functions. */
778 void bgp_filter_init(void)
780 install_node(&as_list_node
);
782 install_element(CONFIG_NODE
, &bgp_as_path_cmd
);
783 install_element(CONFIG_NODE
, &no_bgp_as_path_cmd
);
784 install_element(CONFIG_NODE
, &no_bgp_as_path_all_cmd
);
786 install_element(VIEW_NODE
, &show_bgp_as_path_access_list_cmd
);
787 install_element(VIEW_NODE
, &show_ip_as_path_access_list_cmd
);
788 install_element(VIEW_NODE
, &show_bgp_as_path_access_list_all_cmd
);
789 install_element(VIEW_NODE
, &show_ip_as_path_access_list_all_cmd
);
792 void bgp_filter_reset(void)
794 struct as_list
*aslist
;
795 struct as_list
*next
;
797 for (aslist
= as_list_master
.num
.head
; aslist
; aslist
= next
) {
799 as_list_delete(aslist
);
802 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= next
) {
804 as_list_delete(aslist
);
807 assert(as_list_master
.num
.head
== NULL
);
808 assert(as_list_master
.num
.tail
== NULL
);
810 assert(as_list_master
.str
.head
== NULL
);
811 assert(as_list_master
.str
.tail
== NULL
);