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
;
221 /* Run hook function. */
222 if (as_list_master
.add_hook
)
223 (*as_list_master
.add_hook
)(aslist
->name
);
226 /* Lookup as_list from list of as_list by name. */
227 struct as_list
*as_list_lookup(const char *name
)
229 struct as_list
*aslist
;
234 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= aslist
->next
)
235 if (strcmp(aslist
->name
, name
) == 0)
241 static struct as_list
*as_list_new(void)
243 return XCALLOC(MTYPE_AS_LIST
, sizeof(struct as_list
));
246 static void as_list_free(struct as_list
*aslist
)
248 XFREE(MTYPE_AS_STR
, aslist
->name
);
249 XFREE(MTYPE_AS_LIST
, aslist
);
252 /* Insert new AS list to list of as_list. Each as_list is sorted by
254 static struct as_list
*as_list_insert(const char *name
)
256 struct as_list
*aslist
;
257 struct as_list
*point
;
258 struct as_list_list
*list
;
260 /* Allocate new access_list and copy given name. */
261 aslist
= as_list_new();
262 aslist
->name
= XSTRDUP(MTYPE_AS_STR
, name
);
263 assert(aslist
->name
);
265 /* Set access_list to string list. */
266 list
= &as_list_master
.str
;
268 /* Set point to insertion point. */
269 for (point
= list
->head
; point
; point
= point
->next
)
270 if (strcmp(point
->name
, name
) >= 0)
273 /* In case of this is the first element of master. */
274 if (list
->head
== NULL
) {
275 list
->head
= list
->tail
= aslist
;
279 /* In case of insertion is made at the tail of access_list. */
281 aslist
->prev
= list
->tail
;
282 list
->tail
->next
= aslist
;
287 /* In case of insertion is made at the head of access_list. */
288 if (point
== list
->head
) {
289 aslist
->next
= list
->head
;
290 list
->head
->prev
= aslist
;
295 /* Insertion is made at middle of the access_list. */
296 aslist
->next
= point
;
297 aslist
->prev
= point
->prev
;
300 point
->prev
->next
= aslist
;
301 point
->prev
= aslist
;
306 static struct as_list
*as_list_get(const char *name
)
308 struct as_list
*aslist
;
310 aslist
= as_list_lookup(name
);
312 aslist
= as_list_insert(name
);
317 static const char *filter_type_str(enum as_filter_type type
)
320 case AS_FILTER_PERMIT
:
329 static void as_list_delete(struct as_list
*aslist
)
331 struct as_list_list
*list
;
332 struct as_filter
*filter
, *next
;
334 for (filter
= aslist
->head
; filter
; filter
= next
) {
336 as_filter_free(filter
);
339 list
= &as_list_master
.str
;
342 aslist
->next
->prev
= aslist
->prev
;
344 list
->tail
= aslist
->prev
;
347 aslist
->prev
->next
= aslist
->next
;
349 list
->head
= aslist
->next
;
351 as_list_free(aslist
);
354 static bool as_list_empty(struct as_list
*aslist
)
356 return aslist
->head
== NULL
&& aslist
->tail
== NULL
;
359 static void as_list_filter_delete(struct as_list
*aslist
,
360 struct as_filter
*asfilter
)
362 char *name
= XSTRDUP(MTYPE_AS_STR
, aslist
->name
);
365 asfilter
->next
->prev
= asfilter
->prev
;
367 aslist
->tail
= asfilter
->prev
;
370 asfilter
->prev
->next
= asfilter
->next
;
372 aslist
->head
= asfilter
->next
;
374 as_filter_free(asfilter
);
376 /* If access_list becomes empty delete it from access_master. */
377 if (as_list_empty(aslist
))
378 as_list_delete(aslist
);
380 /* Run hook function. */
381 if (as_list_master
.delete_hook
)
382 (*as_list_master
.delete_hook
)(name
);
383 XFREE(MTYPE_AS_STR
, name
);
386 static bool as_filter_match(struct as_filter
*asfilter
, struct aspath
*aspath
)
388 return bgp_regexec(asfilter
->reg
, aspath
) != REG_NOMATCH
;
391 /* Apply AS path filter to AS. */
392 enum as_filter_type
as_list_apply(struct as_list
*aslist
, void *object
)
394 struct as_filter
*asfilter
;
395 struct aspath
*aspath
;
397 aspath
= (struct aspath
*)object
;
400 return AS_FILTER_DENY
;
402 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
) {
403 if (as_filter_match(asfilter
, aspath
))
404 return asfilter
->type
;
406 return AS_FILTER_DENY
;
409 /* Add hook function. */
410 void as_list_add_hook(void (*func
)(char *))
412 as_list_master
.add_hook
= func
;
415 /* Delete hook function. */
416 void as_list_delete_hook(void (*func
)(const char *))
418 as_list_master
.delete_hook
= func
;
421 static bool as_list_dup_check(struct as_list
*aslist
, struct as_filter
*new)
423 struct as_filter
*asfilter
;
425 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
) {
426 if (asfilter
->type
== new->type
427 && strcmp(asfilter
->reg_str
, new->reg_str
) == 0)
433 bool config_bgp_aspath_validate(const char *regstr
)
435 char valid_chars
[] = "1234567890_^|[,{}() ]$*+.?-\\";
437 if (strspn(regstr
, valid_chars
) == strlen(regstr
))
442 DEFUN(as_path
, bgp_as_path_cmd
,
443 "bgp as-path access-list WORD [seq (0-4294967295)] <deny|permit> LINE...",
445 "BGP autonomous system path filter\n"
446 "Specify an access list name\n"
447 "Regular expression access list name\n"
448 "Sequence number of an entry\n"
450 "Specify packets to reject\n"
451 "Specify packets to forward\n"
452 "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
455 enum as_filter_type type
;
456 struct as_filter
*asfilter
;
457 struct as_list
*aslist
;
460 int64_t seqnum
= ASPATH_SEQ_NUMBER_AUTO
;
462 /* Retrieve access list name */
463 argv_find(argv
, argc
, "WORD", &idx
);
464 char *alname
= argv
[idx
]->arg
;
466 if (argv_find(argv
, argc
, "(0-4294967295)", &idx
))
467 seqnum
= (int64_t)atol(argv
[idx
]->arg
);
469 /* Check the filter type. */
470 type
= argv_find(argv
, argc
, "deny", &idx
) ? AS_FILTER_DENY
473 /* Check AS path regex. */
474 argv_find(argv
, argc
, "LINE", &idx
);
475 regstr
= argv_concat(argv
, argc
, idx
);
477 regex
= bgp_regcomp(regstr
);
479 vty_out(vty
, "can't compile regexp %s\n", regstr
);
480 XFREE(MTYPE_TMP
, regstr
);
481 return CMD_WARNING_CONFIG_FAILED
;
484 if (!config_bgp_aspath_validate(regstr
)) {
485 vty_out(vty
, "Invalid character in as-path access-list %s\n",
487 return CMD_WARNING_CONFIG_FAILED
;
490 asfilter
= as_filter_make(regex
, regstr
, type
);
492 XFREE(MTYPE_TMP
, regstr
);
494 /* Install new filter to the access_list. */
495 aslist
= as_list_get(alname
);
497 if (seqnum
== ASPATH_SEQ_NUMBER_AUTO
)
498 seqnum
= bgp_alist_new_seq_get(aslist
);
500 asfilter
->seq
= seqnum
;
502 /* Duplicate insertion check. */;
503 if (as_list_dup_check(aslist
, asfilter
))
504 as_filter_free(asfilter
);
506 as_list_filter_add(aslist
, asfilter
);
511 DEFUN(no_as_path
, no_bgp_as_path_cmd
,
512 "no bgp as-path access-list WORD [seq (0-4294967295)] <deny|permit> LINE...",
515 "BGP autonomous system path filter\n"
516 "Specify an access list name\n"
517 "Regular expression access list name\n"
518 "Sequence number of an entry\n"
520 "Specify packets to reject\n"
521 "Specify packets to forward\n"
522 "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
525 enum as_filter_type type
;
526 struct as_filter
*asfilter
;
527 struct as_list
*aslist
;
532 argv_find(argv
, argc
, "WORD", &idx
) ? argv
[idx
]->arg
: NULL
;
534 /* Lookup AS list from AS path list. */
535 aslist
= as_list_lookup(aslistname
);
536 if (aslist
== NULL
) {
537 vty_out(vty
, "bgp as-path access-list %s doesn't exist\n",
539 return CMD_WARNING_CONFIG_FAILED
;
542 /* Check the filter type. */
543 if (argv_find(argv
, argc
, "permit", &idx
))
544 type
= AS_FILTER_PERMIT
;
545 else if (argv_find(argv
, argc
, "deny", &idx
))
546 type
= AS_FILTER_DENY
;
548 vty_out(vty
, "filter type must be [permit|deny]\n");
549 return CMD_WARNING_CONFIG_FAILED
;
552 /* Compile AS path. */
553 argv_find(argv
, argc
, "LINE", &idx
);
554 regstr
= argv_concat(argv
, argc
, idx
);
556 if (!config_bgp_aspath_validate(regstr
)) {
557 vty_out(vty
, "Invalid character in as-path access-list %s\n",
559 return CMD_WARNING_CONFIG_FAILED
;
562 regex
= bgp_regcomp(regstr
);
564 vty_out(vty
, "can't compile regexp %s\n", regstr
);
565 XFREE(MTYPE_TMP
, regstr
);
566 return CMD_WARNING_CONFIG_FAILED
;
569 /* Lookup asfilter. */
570 asfilter
= as_filter_lookup(aslist
, regstr
, type
);
572 bgp_regex_free(regex
);
574 if (asfilter
== NULL
) {
575 vty_out(vty
, "Regex entered %s does not exist\n", regstr
);
576 XFREE(MTYPE_TMP
, regstr
);
577 return CMD_WARNING_CONFIG_FAILED
;
580 XFREE(MTYPE_TMP
, regstr
);
582 as_list_filter_delete(aslist
, asfilter
);
587 DEFUN (no_as_path_all
,
588 no_bgp_as_path_all_cmd
,
589 "no bgp as-path access-list WORD",
592 "BGP autonomous system path filter\n"
593 "Specify an access list name\n"
594 "Regular expression access list name\n")
597 struct as_list
*aslist
;
599 aslist
= as_list_lookup(argv
[idx_word
]->arg
);
600 if (aslist
== NULL
) {
601 vty_out(vty
, "bgp as-path access-list %s doesn't exist\n",
602 argv
[idx_word
]->arg
);
603 return CMD_WARNING_CONFIG_FAILED
;
606 as_list_delete(aslist
);
608 /* Run hook function. */
609 if (as_list_master
.delete_hook
)
610 (*as_list_master
.delete_hook
)(argv
[idx_word
]->arg
);
615 static void as_list_show(struct vty
*vty
, struct as_list
*aslist
,
618 struct as_filter
*asfilter
;
619 json_object
*json_aslist
= NULL
;
622 json_aslist
= json_object_new_array();
623 json_object_object_add(json
, aslist
->name
, json_aslist
);
625 vty_out(vty
, "AS path access list %s\n", aslist
->name
);
627 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
) {
629 json_object
*json_asfilter
= json_object_new_object();
631 json_object_int_add(json_asfilter
, "sequenceNumber",
633 json_object_string_add(json_asfilter
, "type",
634 filter_type_str(asfilter
->type
));
635 json_object_string_add(json_asfilter
, "regExp",
638 json_object_array_add(json_aslist
, json_asfilter
);
640 vty_out(vty
, " %s %s\n",
641 filter_type_str(asfilter
->type
),
646 static void as_list_show_all(struct vty
*vty
, json_object
*json
)
648 struct as_list
*aslist
;
650 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= aslist
->next
)
651 as_list_show(vty
, aslist
, json
);
654 DEFUN (show_as_path_access_list
,
655 show_bgp_as_path_access_list_cmd
,
656 "show bgp as-path-access-list WORD [json]",
659 "List AS path access lists\n"
660 "AS path access list name\n"
664 struct as_list
*aslist
;
665 bool uj
= use_json(argc
, argv
);
666 json_object
*json
= NULL
;
669 json
= json_object_new_object();
671 aslist
= as_list_lookup(argv
[idx_word
]->arg
);
673 as_list_show(vty
, aslist
, json
);
677 json_object_to_json_string_ext(
678 json
, JSON_C_TO_STRING_PRETTY
));
679 json_object_free(json
);
685 ALIAS (show_as_path_access_list
,
686 show_ip_as_path_access_list_cmd
,
687 "show ip as-path-access-list WORD [json]",
690 "List AS path access lists\n"
691 "AS path access list name\n"
694 DEFUN (show_as_path_access_list_all
,
695 show_bgp_as_path_access_list_all_cmd
,
696 "show bgp as-path-access-list [json]",
699 "List AS path access lists\n"
702 bool uj
= use_json(argc
, argv
);
703 json_object
*json
= NULL
;
706 json
= json_object_new_object();
708 as_list_show_all(vty
, json
);
712 json_object_to_json_string_ext(
713 json
, JSON_C_TO_STRING_PRETTY
));
714 json_object_free(json
);
720 ALIAS (show_as_path_access_list_all
,
721 show_ip_as_path_access_list_all_cmd
,
722 "show ip as-path-access-list [json]",
725 "List AS path access lists\n"
728 static int config_write_as_list(struct vty
*vty
)
730 struct as_list
*aslist
;
731 struct as_filter
*asfilter
;
734 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= aslist
->next
)
735 for (asfilter
= aslist
->head
; asfilter
;
736 asfilter
= asfilter
->next
) {
738 "bgp as-path access-list %s seq %" PRId64
740 aslist
->name
, asfilter
->seq
,
741 filter_type_str(asfilter
->type
),
748 static int config_write_as_list(struct vty
*vty
);
749 static struct cmd_node as_list_node
= {
751 .node
= AS_LIST_NODE
,
753 .config_write
= config_write_as_list
,
756 /* Register functions. */
757 void bgp_filter_init(void)
759 install_node(&as_list_node
);
761 install_element(CONFIG_NODE
, &bgp_as_path_cmd
);
762 install_element(CONFIG_NODE
, &no_bgp_as_path_cmd
);
763 install_element(CONFIG_NODE
, &no_bgp_as_path_all_cmd
);
765 install_element(VIEW_NODE
, &show_bgp_as_path_access_list_cmd
);
766 install_element(VIEW_NODE
, &show_ip_as_path_access_list_cmd
);
767 install_element(VIEW_NODE
, &show_bgp_as_path_access_list_all_cmd
);
768 install_element(VIEW_NODE
, &show_ip_as_path_access_list_all_cmd
);
771 void bgp_filter_reset(void)
773 struct as_list
*aslist
;
774 struct as_list
*next
;
776 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= next
) {
778 as_list_delete(aslist
);
781 assert(as_list_master
.str
.head
== NULL
);
782 assert(as_list_master
.str
.tail
== NULL
);