1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* AS path filter list.
3 * Copyright (C) 1999 Kunihiro Ishiguro
15 #include "bgpd/bgpd.h"
16 #include "bgpd/bgp_aspath.h"
17 #include "bgpd/bgp_regex.h"
18 #include "bgpd/bgp_filter.h"
20 /* List of AS filter list. */
26 /* AS path filter master. */
27 struct as_list_master
{
28 /* List of access_list which name is string. */
29 struct as_list_list str
;
31 /* Hook function which is executed when new access_list is added. */
32 void (*add_hook
)(char *);
34 /* Hook function which is executed when access_list is deleted. */
35 void (*delete_hook
)(const char *);
38 /* Element of AS path filter. */
40 struct as_filter
*next
;
41 struct as_filter
*prev
;
43 enum as_filter_type type
;
48 /* Sequence number. */
52 /* AS path filter list. */
59 struct as_filter
*head
;
60 struct as_filter
*tail
;
64 /* Calculate new sequential number. */
65 static int64_t bgp_alist_new_seq_get(struct as_list
*list
)
69 struct as_filter
*entry
;
73 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
74 if (maxseq
< entry
->seq
)
78 newseq
= ((maxseq
/ 5) * 5) + 5;
80 return (newseq
> UINT_MAX
) ? UINT_MAX
: newseq
;
83 /* Return as-list entry which has same seq number. */
84 static struct as_filter
*bgp_aslist_seq_check(struct as_list
*list
, int64_t seq
)
86 struct as_filter
*entry
;
88 for (entry
= list
->head
; entry
; entry
= entry
->next
)
89 if (entry
->seq
== seq
)
95 /* as-path access-list 10 permit AS1. */
97 static struct as_list_master as_list_master
= {{NULL
, NULL
},
101 /* Allocate new AS filter. */
102 static struct as_filter
*as_filter_new(void)
104 return XCALLOC(MTYPE_AS_FILTER
, sizeof(struct as_filter
));
107 /* Free allocated AS filter. */
108 static void as_filter_free(struct as_filter
*asfilter
)
111 bgp_regex_free(asfilter
->reg
);
112 XFREE(MTYPE_AS_FILTER_STR
, asfilter
->reg_str
);
113 XFREE(MTYPE_AS_FILTER
, asfilter
);
116 /* Make new AS filter. */
117 static struct as_filter
*as_filter_make(regex_t
*reg
, const char *reg_str
,
118 enum as_filter_type type
)
120 struct as_filter
*asfilter
;
122 asfilter
= as_filter_new();
124 asfilter
->type
= type
;
125 asfilter
->reg_str
= XSTRDUP(MTYPE_AS_FILTER_STR
, reg_str
);
130 static struct as_filter
*as_filter_lookup(struct as_list
*aslist
,
132 enum as_filter_type type
)
134 struct as_filter
*asfilter
;
136 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
)
137 if (strcmp(reg_str
, asfilter
->reg_str
) == 0)
142 static void as_filter_entry_replace(struct as_list
*list
,
143 struct as_filter
*replace
,
144 struct as_filter
*entry
)
147 entry
->next
= replace
->next
;
148 replace
->next
->prev
= entry
;
155 entry
->prev
= replace
->prev
;
156 replace
->prev
->next
= entry
;
162 as_filter_free(replace
);
165 static void as_list_filter_add(struct as_list
*aslist
,
166 struct as_filter
*asfilter
)
168 struct as_filter
*point
;
169 struct as_filter
*replace
;
171 if (aslist
->tail
&& asfilter
->seq
> aslist
->tail
->seq
)
174 replace
= bgp_aslist_seq_check(aslist
, asfilter
->seq
);
176 as_filter_entry_replace(aslist
, replace
, asfilter
);
180 /* Check insert point. */
181 for (point
= aslist
->head
; point
; point
= point
->next
)
182 if (point
->seq
>= asfilter
->seq
)
186 asfilter
->next
= point
;
190 point
->prev
->next
= asfilter
;
192 aslist
->head
= asfilter
;
194 asfilter
->prev
= point
->prev
;
195 point
->prev
= asfilter
;
198 aslist
->tail
->next
= asfilter
;
200 aslist
->head
= asfilter
;
202 asfilter
->prev
= aslist
->tail
;
203 aslist
->tail
= asfilter
;
207 /* Run hook function. */
208 if (as_list_master
.add_hook
)
209 (*as_list_master
.add_hook
)(aslist
->name
);
212 /* Lookup as_list from list of as_list by name. */
213 struct as_list
*as_list_lookup(const char *name
)
215 struct as_list
*aslist
;
220 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= aslist
->next
)
221 if (strcmp(aslist
->name
, name
) == 0)
227 static struct as_list
*as_list_new(void)
229 return XCALLOC(MTYPE_AS_LIST
, sizeof(struct as_list
));
232 static void as_list_free(struct as_list
*aslist
)
234 XFREE(MTYPE_AS_STR
, aslist
->name
);
235 XFREE(MTYPE_AS_LIST
, aslist
);
238 /* Insert new AS list to list of as_list. Each as_list is sorted by
240 static struct as_list
*as_list_insert(const char *name
)
242 struct as_list
*aslist
;
243 struct as_list
*point
;
244 struct as_list_list
*list
;
246 /* Allocate new access_list and copy given name. */
247 aslist
= as_list_new();
248 aslist
->name
= XSTRDUP(MTYPE_AS_STR
, name
);
249 assert(aslist
->name
);
251 /* Set access_list to string list. */
252 list
= &as_list_master
.str
;
254 /* Set point to insertion point. */
255 for (point
= list
->head
; point
; point
= point
->next
)
256 if (strcmp(point
->name
, name
) >= 0)
259 /* In case of this is the first element of master. */
260 if (list
->head
== NULL
) {
261 list
->head
= list
->tail
= aslist
;
265 /* In case of insertion is made at the tail of access_list. */
267 aslist
->prev
= list
->tail
;
268 list
->tail
->next
= aslist
;
273 /* In case of insertion is made at the head of access_list. */
274 if (point
== list
->head
) {
275 aslist
->next
= list
->head
;
276 list
->head
->prev
= aslist
;
281 /* Insertion is made at middle of the access_list. */
282 aslist
->next
= point
;
283 aslist
->prev
= point
->prev
;
286 point
->prev
->next
= aslist
;
287 point
->prev
= aslist
;
292 static struct as_list
*as_list_get(const char *name
)
294 struct as_list
*aslist
;
296 aslist
= as_list_lookup(name
);
298 aslist
= as_list_insert(name
);
303 static const char *filter_type_str(enum as_filter_type type
)
306 case AS_FILTER_PERMIT
:
315 static void as_list_delete(struct as_list
*aslist
)
317 struct as_list_list
*list
;
318 struct as_filter
*filter
, *next
;
320 for (filter
= aslist
->head
; filter
; filter
= next
) {
322 as_filter_free(filter
);
325 list
= &as_list_master
.str
;
328 aslist
->next
->prev
= aslist
->prev
;
330 list
->tail
= aslist
->prev
;
333 aslist
->prev
->next
= aslist
->next
;
335 list
->head
= aslist
->next
;
337 as_list_free(aslist
);
340 static bool as_list_empty(struct as_list
*aslist
)
342 return aslist
->head
== NULL
&& aslist
->tail
== NULL
;
345 static void as_list_filter_delete(struct as_list
*aslist
,
346 struct as_filter
*asfilter
)
348 char *name
= XSTRDUP(MTYPE_AS_STR
, aslist
->name
);
351 asfilter
->next
->prev
= asfilter
->prev
;
353 aslist
->tail
= asfilter
->prev
;
356 asfilter
->prev
->next
= asfilter
->next
;
358 aslist
->head
= asfilter
->next
;
360 as_filter_free(asfilter
);
362 /* If access_list becomes empty delete it from access_master. */
363 if (as_list_empty(aslist
))
364 as_list_delete(aslist
);
366 /* Run hook function. */
367 if (as_list_master
.delete_hook
)
368 (*as_list_master
.delete_hook
)(name
);
369 XFREE(MTYPE_AS_STR
, name
);
372 static bool as_filter_match(struct as_filter
*asfilter
, struct aspath
*aspath
)
374 return bgp_regexec(asfilter
->reg
, aspath
) != REG_NOMATCH
;
377 /* Apply AS path filter to AS. */
378 enum as_filter_type
as_list_apply(struct as_list
*aslist
, void *object
)
380 struct as_filter
*asfilter
;
381 struct aspath
*aspath
;
383 aspath
= (struct aspath
*)object
;
386 return AS_FILTER_DENY
;
388 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
) {
389 if (as_filter_match(asfilter
, aspath
))
390 return asfilter
->type
;
392 return AS_FILTER_DENY
;
395 /* Add hook function. */
396 void as_list_add_hook(void (*func
)(char *))
398 as_list_master
.add_hook
= func
;
401 /* Delete hook function. */
402 void as_list_delete_hook(void (*func
)(const char *))
404 as_list_master
.delete_hook
= func
;
407 static bool as_list_dup_check(struct as_list
*aslist
, struct as_filter
*new)
409 struct as_filter
*asfilter
;
411 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
) {
412 if (asfilter
->type
== new->type
413 && strcmp(asfilter
->reg_str
, new->reg_str
) == 0)
419 bool config_bgp_aspath_validate(const char *regstr
)
421 char valid_chars
[] = "1234567890_^|[,{}() ]$*+.?-\\";
423 if (strspn(regstr
, valid_chars
) == strlen(regstr
))
428 DEFUN(as_path
, bgp_as_path_cmd
,
429 "bgp as-path access-list AS_PATH_FILTER_NAME [seq (0-4294967295)] <deny|permit> LINE...",
431 "BGP autonomous system path filter\n"
432 "Specify an access list name\n"
433 "Regular expression access list name\n"
434 "Sequence number of an entry\n"
436 "Specify packets to reject\n"
437 "Specify packets to forward\n"
438 "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
441 enum as_filter_type type
;
442 struct as_filter
*asfilter
;
443 struct as_list
*aslist
;
446 int64_t seqnum
= ASPATH_SEQ_NUMBER_AUTO
;
448 /* Retrieve access list name */
449 argv_find(argv
, argc
, "AS_PATH_FILTER_NAME", &idx
);
450 char *alname
= argv
[idx
]->arg
;
452 if (argv_find(argv
, argc
, "(0-4294967295)", &idx
))
453 seqnum
= (int64_t)atol(argv
[idx
]->arg
);
455 /* Check the filter type. */
456 type
= argv_find(argv
, argc
, "deny", &idx
) ? AS_FILTER_DENY
459 /* Check AS path regex. */
460 argv_find(argv
, argc
, "LINE", &idx
);
461 regstr
= argv_concat(argv
, argc
, idx
);
463 regex
= bgp_regcomp(regstr
);
465 vty_out(vty
, "can't compile regexp %s\n", regstr
);
466 XFREE(MTYPE_TMP
, regstr
);
467 return CMD_WARNING_CONFIG_FAILED
;
470 if (!config_bgp_aspath_validate(regstr
)) {
471 vty_out(vty
, "Invalid character in as-path access-list %s\n",
473 XFREE(MTYPE_TMP
, regstr
);
474 return CMD_WARNING_CONFIG_FAILED
;
477 asfilter
= as_filter_make(regex
, regstr
, type
);
479 XFREE(MTYPE_TMP
, regstr
);
481 /* Install new filter to the access_list. */
482 aslist
= as_list_get(alname
);
484 if (seqnum
== ASPATH_SEQ_NUMBER_AUTO
)
485 seqnum
= bgp_alist_new_seq_get(aslist
);
487 asfilter
->seq
= seqnum
;
489 /* Duplicate insertion check. */;
490 if (as_list_dup_check(aslist
, asfilter
))
491 as_filter_free(asfilter
);
493 as_list_filter_add(aslist
, asfilter
);
498 DEFUN(no_as_path
, no_bgp_as_path_cmd
,
499 "no bgp as-path access-list AS_PATH_FILTER_NAME [seq (0-4294967295)] <deny|permit> LINE...",
502 "BGP autonomous system path filter\n"
503 "Specify an access list name\n"
504 "Regular expression access list name\n"
505 "Sequence number of an entry\n"
507 "Specify packets to reject\n"
508 "Specify packets to forward\n"
509 "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
512 enum as_filter_type type
;
513 struct as_filter
*asfilter
;
514 struct as_list
*aslist
;
519 argv_find(argv
, argc
, "AS_PATH_FILTER_NAME", &idx
) ? argv
[idx
]->arg
: NULL
;
521 /* Lookup AS list from AS path list. */
522 aslist
= as_list_lookup(aslistname
);
523 if (aslist
== NULL
) {
524 vty_out(vty
, "bgp as-path access-list %s doesn't exist\n",
526 return CMD_WARNING_CONFIG_FAILED
;
529 /* Check the filter type. */
530 if (argv_find(argv
, argc
, "permit", &idx
))
531 type
= AS_FILTER_PERMIT
;
532 else if (argv_find(argv
, argc
, "deny", &idx
))
533 type
= AS_FILTER_DENY
;
535 vty_out(vty
, "filter type must be [permit|deny]\n");
536 return CMD_WARNING_CONFIG_FAILED
;
539 /* Compile AS path. */
540 argv_find(argv
, argc
, "LINE", &idx
);
541 regstr
= argv_concat(argv
, argc
, idx
);
543 if (!config_bgp_aspath_validate(regstr
)) {
544 vty_out(vty
, "Invalid character in as-path access-list %s\n",
546 return CMD_WARNING_CONFIG_FAILED
;
549 regex
= bgp_regcomp(regstr
);
551 vty_out(vty
, "can't compile regexp %s\n", regstr
);
552 XFREE(MTYPE_TMP
, regstr
);
553 return CMD_WARNING_CONFIG_FAILED
;
556 /* Lookup asfilter. */
557 asfilter
= as_filter_lookup(aslist
, regstr
, type
);
559 bgp_regex_free(regex
);
561 if (asfilter
== NULL
) {
562 vty_out(vty
, "Regex entered %s does not exist\n", regstr
);
563 XFREE(MTYPE_TMP
, regstr
);
564 return CMD_WARNING_CONFIG_FAILED
;
567 XFREE(MTYPE_TMP
, regstr
);
569 as_list_filter_delete(aslist
, asfilter
);
574 DEFUN (no_as_path_all
,
575 no_bgp_as_path_all_cmd
,
576 "no bgp as-path access-list AS_PATH_FILTER_NAME",
579 "BGP autonomous system path filter\n"
580 "Specify an access list name\n"
581 "Regular expression access list name\n")
584 struct as_list
*aslist
;
586 aslist
= as_list_lookup(argv
[idx_word
]->arg
);
587 if (aslist
== NULL
) {
588 vty_out(vty
, "bgp as-path access-list %s doesn't exist\n",
589 argv
[idx_word
]->arg
);
590 return CMD_WARNING_CONFIG_FAILED
;
593 as_list_delete(aslist
);
595 /* Run hook function. */
596 if (as_list_master
.delete_hook
)
597 (*as_list_master
.delete_hook
)(argv
[idx_word
]->arg
);
602 static void as_list_show(struct vty
*vty
, struct as_list
*aslist
,
605 struct as_filter
*asfilter
;
606 json_object
*json_aslist
= NULL
;
609 json_aslist
= json_object_new_array();
610 json_object_object_add(json
, aslist
->name
, json_aslist
);
612 vty_out(vty
, "AS path access list %s\n", aslist
->name
);
614 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
) {
616 json_object
*json_asfilter
= json_object_new_object();
618 json_object_int_add(json_asfilter
, "sequenceNumber",
620 json_object_string_add(json_asfilter
, "type",
621 filter_type_str(asfilter
->type
));
622 json_object_string_add(json_asfilter
, "regExp",
625 json_object_array_add(json_aslist
, json_asfilter
);
627 vty_out(vty
, " %s %s\n",
628 filter_type_str(asfilter
->type
),
633 static void as_list_show_all(struct vty
*vty
, json_object
*json
)
635 struct as_list
*aslist
;
637 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= aslist
->next
)
638 as_list_show(vty
, aslist
, json
);
641 DEFUN (show_as_path_access_list
,
642 show_bgp_as_path_access_list_cmd
,
643 "show bgp as-path-access-list AS_PATH_FILTER_NAME [json]",
646 "List AS path access lists\n"
647 "AS path access list name\n"
651 struct as_list
*aslist
;
652 bool uj
= use_json(argc
, argv
);
653 json_object
*json
= NULL
;
656 json
= json_object_new_object();
658 aslist
= as_list_lookup(argv
[idx_word
]->arg
);
660 as_list_show(vty
, aslist
, json
);
668 ALIAS (show_as_path_access_list
,
669 show_ip_as_path_access_list_cmd
,
670 "show ip as-path-access-list AS_PATH_FILTER_NAME [json]",
673 "List AS path access lists\n"
674 "AS path access list name\n"
677 DEFUN (show_as_path_access_list_all
,
678 show_bgp_as_path_access_list_all_cmd
,
679 "show bgp as-path-access-list [json]",
682 "List AS path access lists\n"
685 bool uj
= use_json(argc
, argv
);
686 json_object
*json
= NULL
;
689 json
= json_object_new_object();
691 as_list_show_all(vty
, json
);
699 ALIAS (show_as_path_access_list_all
,
700 show_ip_as_path_access_list_all_cmd
,
701 "show ip as-path-access-list [json]",
704 "List AS path access lists\n"
707 static int config_write_as_list(struct vty
*vty
)
709 struct as_list
*aslist
;
710 struct as_filter
*asfilter
;
713 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= aslist
->next
)
714 for (asfilter
= aslist
->head
; asfilter
;
715 asfilter
= asfilter
->next
) {
717 "bgp as-path access-list %s seq %" PRId64
719 aslist
->name
, asfilter
->seq
,
720 filter_type_str(asfilter
->type
),
727 static int config_write_as_list(struct vty
*vty
);
728 static struct cmd_node as_list_node
= {
730 .node
= AS_LIST_NODE
,
732 .config_write
= config_write_as_list
,
735 static void bgp_aspath_filter_cmd_completion(vector comps
,
736 struct cmd_token
*token
)
738 struct as_list
*aslist
;
740 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= aslist
->next
)
741 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, aslist
->name
));
744 static const struct cmd_variable_handler aspath_filter_handlers
[] = {
745 {.tokenname
= "AS_PATH_FILTER_NAME",
746 .completions
= bgp_aspath_filter_cmd_completion
},
747 {.completions
= NULL
}};
749 /* Register functions. */
750 void bgp_filter_init(void)
752 install_node(&as_list_node
);
754 install_element(CONFIG_NODE
, &bgp_as_path_cmd
);
755 install_element(CONFIG_NODE
, &no_bgp_as_path_cmd
);
756 install_element(CONFIG_NODE
, &no_bgp_as_path_all_cmd
);
758 install_element(VIEW_NODE
, &show_bgp_as_path_access_list_cmd
);
759 install_element(VIEW_NODE
, &show_ip_as_path_access_list_cmd
);
760 install_element(VIEW_NODE
, &show_bgp_as_path_access_list_all_cmd
);
761 install_element(VIEW_NODE
, &show_ip_as_path_access_list_all_cmd
);
763 cmd_variable_handler_register(aspath_filter_handlers
);
766 void bgp_filter_reset(void)
768 struct as_list
*aslist
;
769 struct as_list
*next
;
771 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= next
) {
773 as_list_delete(aslist
);
776 assert(as_list_master
.str
.head
== NULL
);
777 assert(as_list_master
.str
.tail
== NULL
);