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
)
617 struct as_filter
*asfilter
;
619 vty_out(vty
, "AS path access list %s\n", aslist
->name
);
621 for (asfilter
= aslist
->head
; asfilter
; asfilter
= asfilter
->next
) {
622 vty_out(vty
, " %s %s\n", filter_type_str(asfilter
->type
),
627 static void as_list_show_all(struct vty
*vty
)
629 struct as_list
*aslist
;
630 struct as_filter
*asfilter
;
632 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= aslist
->next
) {
633 vty_out(vty
, "AS path access list %s\n", aslist
->name
);
635 for (asfilter
= aslist
->head
; asfilter
;
636 asfilter
= asfilter
->next
) {
637 vty_out(vty
, " %s %s\n",
638 filter_type_str(asfilter
->type
),
644 DEFUN (show_as_path_access_list
,
645 show_bgp_as_path_access_list_cmd
,
646 "show bgp as-path-access-list WORD",
649 "List AS path access lists\n"
650 "AS path access list name\n")
653 struct as_list
*aslist
;
655 aslist
= as_list_lookup(argv
[idx_word
]->arg
);
657 as_list_show(vty
, aslist
);
662 ALIAS (show_as_path_access_list
,
663 show_ip_as_path_access_list_cmd
,
664 "show ip as-path-access-list WORD",
667 "List AS path access lists\n"
668 "AS path access list name\n")
670 DEFUN (show_as_path_access_list_all
,
671 show_bgp_as_path_access_list_all_cmd
,
672 "show bgp as-path-access-list",
675 "List AS path access lists\n")
677 as_list_show_all(vty
);
681 ALIAS (show_as_path_access_list_all
,
682 show_ip_as_path_access_list_all_cmd
,
683 "show ip as-path-access-list",
686 "List AS path access lists\n")
688 static int config_write_as_list(struct vty
*vty
)
690 struct as_list
*aslist
;
691 struct as_filter
*asfilter
;
694 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= aslist
->next
)
695 for (asfilter
= aslist
->head
; asfilter
;
696 asfilter
= asfilter
->next
) {
698 "bgp as-path access-list %s seq %" PRId64
700 aslist
->name
, asfilter
->seq
,
701 filter_type_str(asfilter
->type
),
708 static int config_write_as_list(struct vty
*vty
);
709 static struct cmd_node as_list_node
= {
711 .node
= AS_LIST_NODE
,
713 .config_write
= config_write_as_list
,
716 /* Register functions. */
717 void bgp_filter_init(void)
719 install_node(&as_list_node
);
721 install_element(CONFIG_NODE
, &bgp_as_path_cmd
);
722 install_element(CONFIG_NODE
, &no_bgp_as_path_cmd
);
723 install_element(CONFIG_NODE
, &no_bgp_as_path_all_cmd
);
725 install_element(VIEW_NODE
, &show_bgp_as_path_access_list_cmd
);
726 install_element(VIEW_NODE
, &show_ip_as_path_access_list_cmd
);
727 install_element(VIEW_NODE
, &show_bgp_as_path_access_list_all_cmd
);
728 install_element(VIEW_NODE
, &show_ip_as_path_access_list_all_cmd
);
731 void bgp_filter_reset(void)
733 struct as_list
*aslist
;
734 struct as_list
*next
;
736 for (aslist
= as_list_master
.str
.head
; aslist
; aslist
= next
) {
738 as_list_delete(aslist
);
741 assert(as_list_master
.str
.head
== NULL
);
742 assert(as_list_master
.str
.tail
== NULL
);