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 string. */
44 struct as_list_list str
;
46 /* Hook function which is executed when new access_list is added. */
47 void (*add_hook
)(char *);
49 /* Hook function which is executed when access_list is deleted. */
50 void (*delete_hook
)(const char *);
53 /* Element of AS path filter. */
55 struct as_filter
*next
;
56 struct as_filter
*prev
;
58 enum as_filter_type type
;
63 /* Sequence number. */
67 /* AS path filter list. */
74 struct as_filter
*head
;
75 struct as_filter
*tail
;
79 /* Calculate new sequential number. */
80 static int64_t bgp_alist_new_seq_get(struct as_list
*list
)
84 struct as_filter
*entry
;
88 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
89 if (maxseq
< entry
->seq
)
93 newseq
= ((maxseq
/ 5) * 5) + 5;
95 return (newseq
> UINT_MAX
) ? UINT_MAX
: newseq
;
98 /* Return as-list entry which has same seq number. */
99 static struct as_filter
*bgp_aslist_seq_check(struct as_list
*list
, int64_t seq
)
101 struct as_filter
*entry
;
103 for (entry
= list
->head
; entry
; entry
= entry
->next
)
104 if (entry
->seq
== seq
)
110 /* as-path access-list 10 permit AS1. */
112 static struct as_list_master as_list_master
= {{NULL
, NULL
},
116 /* Allocate new AS filter. */
117 static struct as_filter
*as_filter_new(void)
119 return XCALLOC(MTYPE_AS_FILTER
, sizeof(struct as_filter
));
122 /* Free allocated AS filter. */
123 static void as_filter_free(struct as_filter
*asfilter
)
126 bgp_regex_free(asfilter
->reg
);
127 XFREE(MTYPE_AS_FILTER_STR
, asfilter
->reg_str
);
128 XFREE(MTYPE_AS_FILTER
, asfilter
);
131 /* Make new AS filter. */
132 static struct as_filter
*as_filter_make(regex_t
*reg
, const char *reg_str
,
133 enum as_filter_type type
)
135 struct as_filter
*asfilter
;
137 asfilter
= as_filter_new();
139 asfilter
->type
= type
;
140 asfilter
->reg_str
= XSTRDUP(MTYPE_AS_FILTER_STR
, reg_str
);
145 static struct as_filter
*as_filter_lookup(struct as_list
*aslist
,
147 enum as_filter_type type
)
149 struct as_filter
*asfilter
;
151 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
)
152 if (strcmp(reg_str
, asfilter
->reg_str
) == 0)
157 static void as_filter_entry_replace(struct as_list
*list
,
158 struct as_filter
*replace
,
159 struct as_filter
*entry
)
162 entry
->next
= replace
->next
;
163 replace
->next
->prev
= entry
;
170 entry
->prev
= replace
->prev
;
171 replace
->prev
->next
= entry
;
177 as_filter_free(replace
);
180 static void as_list_filter_add(struct as_list
*aslist
,
181 struct as_filter
*asfilter
)
183 struct as_filter
*point
;
184 struct as_filter
*replace
;
186 if (aslist
->tail
&& asfilter
->seq
> aslist
->tail
->seq
)
189 replace
= bgp_aslist_seq_check(aslist
, asfilter
->seq
);
191 as_filter_entry_replace(aslist
, replace
, asfilter
);
195 /* Check insert point. */
196 for (point
= aslist
->head
; point
; point
= point
->next
)
197 if (point
->seq
>= asfilter
->seq
)
201 asfilter
->next
= point
;
205 point
->prev
->next
= asfilter
;
207 aslist
->head
= asfilter
;
209 asfilter
->prev
= point
->prev
;
210 point
->prev
= asfilter
;
213 aslist
->tail
->next
= asfilter
;
215 aslist
->head
= asfilter
;
217 asfilter
->prev
= aslist
->tail
;
218 aslist
->tail
= asfilter
;
222 /* Run hook function. */
223 if (as_list_master
.add_hook
)
224 (*as_list_master
.add_hook
)(aslist
->name
);
227 /* Lookup as_list from list of as_list by name. */
228 struct as_list
*as_list_lookup(const char *name
)
230 struct as_list
*aslist
;
235 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= aslist
->next
)
236 if (strcmp(aslist
->name
, name
) == 0)
242 static struct as_list
*as_list_new(void)
244 return XCALLOC(MTYPE_AS_LIST
, sizeof(struct as_list
));
247 static void as_list_free(struct as_list
*aslist
)
249 XFREE(MTYPE_AS_STR
, aslist
->name
);
250 XFREE(MTYPE_AS_LIST
, aslist
);
253 /* Insert new AS list to list of as_list. Each as_list is sorted by
255 static struct as_list
*as_list_insert(const char *name
)
257 struct as_list
*aslist
;
258 struct as_list
*point
;
259 struct as_list_list
*list
;
261 /* Allocate new access_list and copy given name. */
262 aslist
= as_list_new();
263 aslist
->name
= XSTRDUP(MTYPE_AS_STR
, name
);
264 assert(aslist
->name
);
266 /* Set access_list to string list. */
267 list
= &as_list_master
.str
;
269 /* Set point to insertion point. */
270 for (point
= list
->head
; point
; point
= point
->next
)
271 if (strcmp(point
->name
, name
) >= 0)
274 /* In case of this is the first element of master. */
275 if (list
->head
== NULL
) {
276 list
->head
= list
->tail
= aslist
;
280 /* In case of insertion is made at the tail of access_list. */
282 aslist
->prev
= list
->tail
;
283 list
->tail
->next
= aslist
;
288 /* In case of insertion is made at the head of access_list. */
289 if (point
== list
->head
) {
290 aslist
->next
= list
->head
;
291 list
->head
->prev
= aslist
;
296 /* Insertion is made at middle of the access_list. */
297 aslist
->next
= point
;
298 aslist
->prev
= point
->prev
;
301 point
->prev
->next
= aslist
;
302 point
->prev
= aslist
;
307 static struct as_list
*as_list_get(const char *name
)
309 struct as_list
*aslist
;
311 aslist
= as_list_lookup(name
);
313 aslist
= as_list_insert(name
);
318 static const char *filter_type_str(enum as_filter_type type
)
321 case AS_FILTER_PERMIT
:
330 static void as_list_delete(struct as_list
*aslist
)
332 struct as_list_list
*list
;
333 struct as_filter
*filter
, *next
;
335 for (filter
= aslist
->head
; filter
; filter
= next
) {
337 as_filter_free(filter
);
340 list
= &as_list_master
.str
;
343 aslist
->next
->prev
= aslist
->prev
;
345 list
->tail
= aslist
->prev
;
348 aslist
->prev
->next
= aslist
->next
;
350 list
->head
= aslist
->next
;
352 as_list_free(aslist
);
355 static bool as_list_empty(struct as_list
*aslist
)
357 return aslist
->head
== NULL
&& aslist
->tail
== NULL
;
360 static void as_list_filter_delete(struct as_list
*aslist
,
361 struct as_filter
*asfilter
)
363 char *name
= XSTRDUP(MTYPE_AS_STR
, aslist
->name
);
366 asfilter
->next
->prev
= asfilter
->prev
;
368 aslist
->tail
= asfilter
->prev
;
371 asfilter
->prev
->next
= asfilter
->next
;
373 aslist
->head
= asfilter
->next
;
375 as_filter_free(asfilter
);
377 /* If access_list becomes empty delete it from access_master. */
378 if (as_list_empty(aslist
))
379 as_list_delete(aslist
);
381 /* Run hook function. */
382 if (as_list_master
.delete_hook
)
383 (*as_list_master
.delete_hook
)(name
);
384 XFREE(MTYPE_AS_STR
, name
);
387 static bool as_filter_match(struct as_filter
*asfilter
, struct aspath
*aspath
)
389 return bgp_regexec(asfilter
->reg
, aspath
) != REG_NOMATCH
;
392 /* Apply AS path filter to AS. */
393 enum as_filter_type
as_list_apply(struct as_list
*aslist
, void *object
)
395 struct as_filter
*asfilter
;
396 struct aspath
*aspath
;
398 aspath
= (struct aspath
*)object
;
401 return AS_FILTER_DENY
;
403 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
) {
404 if (as_filter_match(asfilter
, aspath
))
405 return asfilter
->type
;
407 return AS_FILTER_DENY
;
410 /* Add hook function. */
411 void as_list_add_hook(void (*func
)(char *))
413 as_list_master
.add_hook
= func
;
416 /* Delete hook function. */
417 void as_list_delete_hook(void (*func
)(const char *))
419 as_list_master
.delete_hook
= func
;
422 static bool as_list_dup_check(struct as_list
*aslist
, struct as_filter
*new)
424 struct as_filter
*asfilter
;
426 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
) {
427 if (asfilter
->type
== new->type
428 && strcmp(asfilter
->reg_str
, new->reg_str
) == 0)
434 bool config_bgp_aspath_validate(const char *regstr
)
436 char valid_chars
[] = "1234567890_^|[,{}() ]$*+.?-\\";
438 if (strspn(regstr
, valid_chars
) == strlen(regstr
))
443 DEFUN(as_path
, bgp_as_path_cmd
,
444 "bgp as-path access-list AS_PATH_FILTER_NAME [seq (0-4294967295)] <deny|permit> LINE...",
446 "BGP autonomous system path filter\n"
447 "Specify an access list name\n"
448 "Regular expression access list name\n"
449 "Sequence number of an entry\n"
451 "Specify packets to reject\n"
452 "Specify packets to forward\n"
453 "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
456 enum as_filter_type type
;
457 struct as_filter
*asfilter
;
458 struct as_list
*aslist
;
461 int64_t seqnum
= ASPATH_SEQ_NUMBER_AUTO
;
463 /* Retrieve access list name */
464 argv_find(argv
, argc
, "AS_PATH_FILTER_NAME", &idx
);
465 char *alname
= argv
[idx
]->arg
;
467 if (argv_find(argv
, argc
, "(0-4294967295)", &idx
))
468 seqnum
= (int64_t)atol(argv
[idx
]->arg
);
470 /* Check the filter type. */
471 type
= argv_find(argv
, argc
, "deny", &idx
) ? AS_FILTER_DENY
474 /* Check AS path regex. */
475 argv_find(argv
, argc
, "LINE", &idx
);
476 regstr
= argv_concat(argv
, argc
, idx
);
478 regex
= bgp_regcomp(regstr
);
480 vty_out(vty
, "can't compile regexp %s\n", regstr
);
481 XFREE(MTYPE_TMP
, regstr
);
482 return CMD_WARNING_CONFIG_FAILED
;
485 if (!config_bgp_aspath_validate(regstr
)) {
486 vty_out(vty
, "Invalid character in as-path access-list %s\n",
488 XFREE(MTYPE_TMP
, regstr
);
489 return CMD_WARNING_CONFIG_FAILED
;
492 asfilter
= as_filter_make(regex
, regstr
, type
);
494 XFREE(MTYPE_TMP
, regstr
);
496 /* Install new filter to the access_list. */
497 aslist
= as_list_get(alname
);
499 if (seqnum
== ASPATH_SEQ_NUMBER_AUTO
)
500 seqnum
= bgp_alist_new_seq_get(aslist
);
502 asfilter
->seq
= seqnum
;
504 /* Duplicate insertion check. */;
505 if (as_list_dup_check(aslist
, asfilter
))
506 as_filter_free(asfilter
);
508 as_list_filter_add(aslist
, asfilter
);
513 DEFUN(no_as_path
, no_bgp_as_path_cmd
,
514 "no bgp as-path access-list AS_PATH_FILTER_NAME [seq (0-4294967295)] <deny|permit> LINE...",
517 "BGP autonomous system path filter\n"
518 "Specify an access list name\n"
519 "Regular expression access list name\n"
520 "Sequence number of an entry\n"
522 "Specify packets to reject\n"
523 "Specify packets to forward\n"
524 "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
527 enum as_filter_type type
;
528 struct as_filter
*asfilter
;
529 struct as_list
*aslist
;
534 argv_find(argv
, argc
, "AS_PATH_FILTER_NAME", &idx
) ? argv
[idx
]->arg
: NULL
;
536 /* Lookup AS list from AS path list. */
537 aslist
= as_list_lookup(aslistname
);
538 if (aslist
== NULL
) {
539 vty_out(vty
, "bgp as-path access-list %s doesn't exist\n",
541 return CMD_WARNING_CONFIG_FAILED
;
544 /* Check the filter type. */
545 if (argv_find(argv
, argc
, "permit", &idx
))
546 type
= AS_FILTER_PERMIT
;
547 else if (argv_find(argv
, argc
, "deny", &idx
))
548 type
= AS_FILTER_DENY
;
550 vty_out(vty
, "filter type must be [permit|deny]\n");
551 return CMD_WARNING_CONFIG_FAILED
;
554 /* Compile AS path. */
555 argv_find(argv
, argc
, "LINE", &idx
);
556 regstr
= argv_concat(argv
, argc
, idx
);
558 if (!config_bgp_aspath_validate(regstr
)) {
559 vty_out(vty
, "Invalid character in as-path access-list %s\n",
561 return CMD_WARNING_CONFIG_FAILED
;
564 regex
= bgp_regcomp(regstr
);
566 vty_out(vty
, "can't compile regexp %s\n", regstr
);
567 XFREE(MTYPE_TMP
, regstr
);
568 return CMD_WARNING_CONFIG_FAILED
;
571 /* Lookup asfilter. */
572 asfilter
= as_filter_lookup(aslist
, regstr
, type
);
574 bgp_regex_free(regex
);
576 if (asfilter
== NULL
) {
577 vty_out(vty
, "Regex entered %s does not exist\n", regstr
);
578 XFREE(MTYPE_TMP
, regstr
);
579 return CMD_WARNING_CONFIG_FAILED
;
582 XFREE(MTYPE_TMP
, regstr
);
584 as_list_filter_delete(aslist
, asfilter
);
589 DEFUN (no_as_path_all
,
590 no_bgp_as_path_all_cmd
,
591 "no bgp as-path access-list AS_PATH_FILTER_NAME",
594 "BGP autonomous system path filter\n"
595 "Specify an access list name\n"
596 "Regular expression access list name\n")
599 struct as_list
*aslist
;
601 aslist
= as_list_lookup(argv
[idx_word
]->arg
);
602 if (aslist
== NULL
) {
603 vty_out(vty
, "bgp as-path access-list %s doesn't exist\n",
604 argv
[idx_word
]->arg
);
605 return CMD_WARNING_CONFIG_FAILED
;
608 as_list_delete(aslist
);
610 /* Run hook function. */
611 if (as_list_master
.delete_hook
)
612 (*as_list_master
.delete_hook
)(argv
[idx_word
]->arg
);
617 static void as_list_show(struct vty
*vty
, struct as_list
*aslist
,
620 struct as_filter
*asfilter
;
621 json_object
*json_aslist
= NULL
;
624 json_aslist
= json_object_new_array();
625 json_object_object_add(json
, aslist
->name
, json_aslist
);
627 vty_out(vty
, "AS path access list %s\n", aslist
->name
);
629 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
) {
631 json_object
*json_asfilter
= json_object_new_object();
633 json_object_int_add(json_asfilter
, "sequenceNumber",
635 json_object_string_add(json_asfilter
, "type",
636 filter_type_str(asfilter
->type
));
637 json_object_string_add(json_asfilter
, "regExp",
640 json_object_array_add(json_aslist
, json_asfilter
);
642 vty_out(vty
, " %s %s\n",
643 filter_type_str(asfilter
->type
),
648 static void as_list_show_all(struct vty
*vty
, json_object
*json
)
650 struct as_list
*aslist
;
652 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= aslist
->next
)
653 as_list_show(vty
, aslist
, json
);
656 DEFUN (show_as_path_access_list
,
657 show_bgp_as_path_access_list_cmd
,
658 "show bgp as-path-access-list AS_PATH_FILTER_NAME [json]",
661 "List AS path access lists\n"
662 "AS path access list name\n"
666 struct as_list
*aslist
;
667 bool uj
= use_json(argc
, argv
);
668 json_object
*json
= NULL
;
671 json
= json_object_new_object();
673 aslist
= as_list_lookup(argv
[idx_word
]->arg
);
675 as_list_show(vty
, aslist
, json
);
683 ALIAS (show_as_path_access_list
,
684 show_ip_as_path_access_list_cmd
,
685 "show ip as-path-access-list AS_PATH_FILTER_NAME [json]",
688 "List AS path access lists\n"
689 "AS path access list name\n"
692 DEFUN (show_as_path_access_list_all
,
693 show_bgp_as_path_access_list_all_cmd
,
694 "show bgp as-path-access-list [json]",
697 "List AS path access lists\n"
700 bool uj
= use_json(argc
, argv
);
701 json_object
*json
= NULL
;
704 json
= json_object_new_object();
706 as_list_show_all(vty
, json
);
714 ALIAS (show_as_path_access_list_all
,
715 show_ip_as_path_access_list_all_cmd
,
716 "show ip as-path-access-list [json]",
719 "List AS path access lists\n"
722 static int config_write_as_list(struct vty
*vty
)
724 struct as_list
*aslist
;
725 struct as_filter
*asfilter
;
728 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= aslist
->next
)
729 for (asfilter
= aslist
->head
; asfilter
;
730 asfilter
= asfilter
->next
) {
732 "bgp as-path access-list %s seq %" PRId64
734 aslist
->name
, asfilter
->seq
,
735 filter_type_str(asfilter
->type
),
742 static int config_write_as_list(struct vty
*vty
);
743 static struct cmd_node as_list_node
= {
745 .node
= AS_LIST_NODE
,
747 .config_write
= config_write_as_list
,
750 static void bgp_aspath_filter_cmd_completion(vector comps
,
751 struct cmd_token
*token
)
753 struct as_list
*aslist
;
755 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= aslist
->next
)
756 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, aslist
->name
));
759 static const struct cmd_variable_handler aspath_filter_handlers
[] = {
760 {.tokenname
= "AS_PATH_FILTER_NAME",
761 .completions
= bgp_aspath_filter_cmd_completion
},
762 {.completions
= NULL
}};
764 /* Register functions. */
765 void bgp_filter_init(void)
767 install_node(&as_list_node
);
769 install_element(CONFIG_NODE
, &bgp_as_path_cmd
);
770 install_element(CONFIG_NODE
, &no_bgp_as_path_cmd
);
771 install_element(CONFIG_NODE
, &no_bgp_as_path_all_cmd
);
773 install_element(VIEW_NODE
, &show_bgp_as_path_access_list_cmd
);
774 install_element(VIEW_NODE
, &show_ip_as_path_access_list_cmd
);
775 install_element(VIEW_NODE
, &show_bgp_as_path_access_list_all_cmd
);
776 install_element(VIEW_NODE
, &show_ip_as_path_access_list_all_cmd
);
778 cmd_variable_handler_register(aspath_filter_handlers
);
781 void bgp_filter_reset(void)
783 struct as_list
*aslist
;
784 struct as_list
*next
;
786 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= next
) {
788 as_list_delete(aslist
);
791 assert(as_list_master
.str
.head
== NULL
);
792 assert(as_list_master
.str
.tail
== NULL
);