]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_filter.c
Merge pull request #5703 from ton31337/feature/limit_outgoing_prefixes
[mirror_frr.git] / bgpd / bgp_filter.c
1 /* AS path filter list.
2 * Copyright (C) 1999 Kunihiro Ishiguro
3 *
4 * This file is part of GNU Zebra.
5 *
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
9 * later version.
10 *
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.
15 *
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
19 */
20
21 #include <zebra.h>
22
23 #include "command.h"
24 #include "log.h"
25 #include "memory.h"
26 #include "buffer.h"
27 #include "queue.h"
28 #include "filter.h"
29
30 #include "bgpd/bgpd.h"
31 #include "bgpd/bgp_aspath.h"
32 #include "bgpd/bgp_regex.h"
33 #include "bgpd/bgp_filter.h"
34
35 /* List of AS filter list. */
36 struct as_list_list {
37 struct as_list *head;
38 struct as_list *tail;
39 };
40
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;
45
46 /* List of access_list which name is string. */
47 struct as_list_list str;
48
49 /* Hook function which is executed when new access_list is added. */
50 void (*add_hook)(char *);
51
52 /* Hook function which is executed when access_list is deleted. */
53 void (*delete_hook)(const char *);
54 };
55
56 /* Element of AS path filter. */
57 struct as_filter {
58 struct as_filter *next;
59 struct as_filter *prev;
60
61 enum as_filter_type type;
62
63 regex_t *reg;
64 char *reg_str;
65 };
66
67 /* AS path filter list. */
68 struct as_list {
69 char *name;
70
71 enum access_type type;
72
73 struct as_list *next;
74 struct as_list *prev;
75
76 struct as_filter *head;
77 struct as_filter *tail;
78 };
79
80 /* as-path access-list 10 permit AS1. */
81
82 static struct as_list_master as_list_master = {{NULL, NULL},
83 {NULL, NULL},
84 NULL,
85 NULL};
86
87 /* Allocate new AS filter. */
88 static struct as_filter *as_filter_new(void)
89 {
90 return XCALLOC(MTYPE_AS_FILTER, sizeof(struct as_filter));
91 }
92
93 /* Free allocated AS filter. */
94 static void as_filter_free(struct as_filter *asfilter)
95 {
96 if (asfilter->reg)
97 bgp_regex_free(asfilter->reg);
98 XFREE(MTYPE_AS_FILTER_STR, asfilter->reg_str);
99 XFREE(MTYPE_AS_FILTER, asfilter);
100 }
101
102 /* Make new AS filter. */
103 static struct as_filter *as_filter_make(regex_t *reg, const char *reg_str,
104 enum as_filter_type type)
105 {
106 struct as_filter *asfilter;
107
108 asfilter = as_filter_new();
109 asfilter->reg = reg;
110 asfilter->type = type;
111 asfilter->reg_str = XSTRDUP(MTYPE_AS_FILTER_STR, reg_str);
112
113 return asfilter;
114 }
115
116 static struct as_filter *as_filter_lookup(struct as_list *aslist,
117 const char *reg_str,
118 enum as_filter_type type)
119 {
120 struct as_filter *asfilter;
121
122 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next)
123 if (strcmp(reg_str, asfilter->reg_str) == 0)
124 return asfilter;
125 return NULL;
126 }
127
128 static void as_list_filter_add(struct as_list *aslist,
129 struct as_filter *asfilter)
130 {
131 asfilter->next = NULL;
132 asfilter->prev = aslist->tail;
133
134 if (aslist->tail)
135 aslist->tail->next = asfilter;
136 else
137 aslist->head = asfilter;
138 aslist->tail = asfilter;
139
140 /* Run hook function. */
141 if (as_list_master.add_hook)
142 (*as_list_master.add_hook)(aslist->name);
143 }
144
145 /* Lookup as_list from list of as_list by name. */
146 struct as_list *as_list_lookup(const char *name)
147 {
148 struct as_list *aslist;
149
150 if (name == NULL)
151 return NULL;
152
153 for (aslist = as_list_master.num.head; aslist; aslist = aslist->next)
154 if (strcmp(aslist->name, name) == 0)
155 return aslist;
156
157 for (aslist = as_list_master.str.head; aslist; aslist = aslist->next)
158 if (strcmp(aslist->name, name) == 0)
159 return aslist;
160
161 return NULL;
162 }
163
164 static struct as_list *as_list_new(void)
165 {
166 return XCALLOC(MTYPE_AS_LIST, sizeof(struct as_list));
167 }
168
169 static void as_list_free(struct as_list *aslist)
170 {
171 if (aslist->name) {
172 XFREE(MTYPE_AS_STR, aslist->name);
173 aslist->name = NULL;
174 }
175 XFREE(MTYPE_AS_LIST, aslist);
176 }
177
178 /* Insert new AS list to list of as_list. Each as_list is sorted by
179 the name. */
180 static struct as_list *as_list_insert(const char *name)
181 {
182 size_t i;
183 long number;
184 struct as_list *aslist;
185 struct as_list *point;
186 struct as_list_list *list;
187
188 /* Allocate new access_list and copy given name. */
189 aslist = as_list_new();
190 aslist->name = XSTRDUP(MTYPE_AS_STR, name);
191 assert(aslist->name);
192
193 /* If name is made by all digit character. We treat it as
194 number. */
195 for (number = 0, i = 0; i < strlen(name); i++) {
196 if (isdigit((unsigned char)name[i]))
197 number = (number * 10) + (name[i] - '0');
198 else
199 break;
200 }
201
202 /* In case of name is all digit character */
203 if (i == strlen(name)) {
204 aslist->type = ACCESS_TYPE_NUMBER;
205
206 /* Set access_list to number list. */
207 list = &as_list_master.num;
208
209 for (point = list->head; point; point = point->next)
210 if (atol(point->name) >= number)
211 break;
212 } else {
213 aslist->type = ACCESS_TYPE_STRING;
214
215 /* Set access_list to string list. */
216 list = &as_list_master.str;
217
218 /* Set point to insertion point. */
219 for (point = list->head; point; point = point->next)
220 if (strcmp(point->name, name) >= 0)
221 break;
222 }
223
224 /* In case of this is the first element of master. */
225 if (list->head == NULL) {
226 list->head = list->tail = aslist;
227 return aslist;
228 }
229
230 /* In case of insertion is made at the tail of access_list. */
231 if (point == NULL) {
232 aslist->prev = list->tail;
233 list->tail->next = aslist;
234 list->tail = aslist;
235 return aslist;
236 }
237
238 /* In case of insertion is made at the head of access_list. */
239 if (point == list->head) {
240 aslist->next = list->head;
241 list->head->prev = aslist;
242 list->head = aslist;
243 return aslist;
244 }
245
246 /* Insertion is made at middle of the access_list. */
247 aslist->next = point;
248 aslist->prev = point->prev;
249
250 if (point->prev)
251 point->prev->next = aslist;
252 point->prev = aslist;
253
254 return aslist;
255 }
256
257 static struct as_list *as_list_get(const char *name)
258 {
259 struct as_list *aslist;
260
261 aslist = as_list_lookup(name);
262 if (aslist == NULL)
263 aslist = as_list_insert(name);
264
265 return aslist;
266 }
267
268 static const char *filter_type_str(enum as_filter_type type)
269 {
270 switch (type) {
271 case AS_FILTER_PERMIT:
272 return "permit";
273 case AS_FILTER_DENY:
274 return "deny";
275 default:
276 return "";
277 }
278 }
279
280 static void as_list_delete(struct as_list *aslist)
281 {
282 struct as_list_list *list;
283 struct as_filter *filter, *next;
284
285 for (filter = aslist->head; filter; filter = next) {
286 next = filter->next;
287 as_filter_free(filter);
288 }
289
290 if (aslist->type == ACCESS_TYPE_NUMBER)
291 list = &as_list_master.num;
292 else
293 list = &as_list_master.str;
294
295 if (aslist->next)
296 aslist->next->prev = aslist->prev;
297 else
298 list->tail = aslist->prev;
299
300 if (aslist->prev)
301 aslist->prev->next = aslist->next;
302 else
303 list->head = aslist->next;
304
305 as_list_free(aslist);
306 }
307
308 static int as_list_empty(struct as_list *aslist)
309 {
310 if (aslist->head == NULL && aslist->tail == NULL)
311 return 1;
312 else
313 return 0;
314 }
315
316 static void as_list_filter_delete(struct as_list *aslist,
317 struct as_filter *asfilter)
318 {
319 char *name = XSTRDUP(MTYPE_AS_STR, aslist->name);
320
321 if (asfilter->next)
322 asfilter->next->prev = asfilter->prev;
323 else
324 aslist->tail = asfilter->prev;
325
326 if (asfilter->prev)
327 asfilter->prev->next = asfilter->next;
328 else
329 aslist->head = asfilter->next;
330
331 as_filter_free(asfilter);
332
333 /* If access_list becomes empty delete it from access_master. */
334 if (as_list_empty(aslist))
335 as_list_delete(aslist);
336
337 /* Run hook function. */
338 if (as_list_master.delete_hook)
339 (*as_list_master.delete_hook)(name);
340 XFREE(MTYPE_AS_STR, name);
341 }
342
343 static int as_filter_match(struct as_filter *asfilter, struct aspath *aspath)
344 {
345 if (bgp_regexec(asfilter->reg, aspath) != REG_NOMATCH)
346 return 1;
347 return 0;
348 }
349
350 /* Apply AS path filter to AS. */
351 enum as_filter_type as_list_apply(struct as_list *aslist, void *object)
352 {
353 struct as_filter *asfilter;
354 struct aspath *aspath;
355
356 aspath = (struct aspath *)object;
357
358 if (aslist == NULL)
359 return AS_FILTER_DENY;
360
361 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
362 if (as_filter_match(asfilter, aspath))
363 return asfilter->type;
364 }
365 return AS_FILTER_DENY;
366 }
367
368 /* Add hook function. */
369 void as_list_add_hook(void (*func)(char *))
370 {
371 as_list_master.add_hook = func;
372 }
373
374 /* Delete hook function. */
375 void as_list_delete_hook(void (*func)(const char *))
376 {
377 as_list_master.delete_hook = func;
378 }
379
380 static int as_list_dup_check(struct as_list *aslist, struct as_filter *new)
381 {
382 struct as_filter *asfilter;
383
384 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
385 if (asfilter->type == new->type
386 && strcmp(asfilter->reg_str, new->reg_str) == 0)
387 return 1;
388 }
389 return 0;
390 }
391
392 int config_bgp_aspath_validate(const char *regstr)
393 {
394 char valid_chars[] = "1234567890_^|[,{}() ]$*+.?-\\";
395
396 if (strspn(regstr, valid_chars) == strlen(regstr))
397 return 1;
398
399 return 0;
400 }
401
402 DEFUN(as_path, bgp_as_path_cmd,
403 "bgp as-path access-list WORD <deny|permit> LINE...",
404 BGP_STR
405 "BGP autonomous system path filter\n"
406 "Specify an access list name\n"
407 "Regular expression access list name\n"
408 "Specify packets to reject\n"
409 "Specify packets to forward\n"
410 "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
411 {
412 int idx = 0;
413 enum as_filter_type type;
414 struct as_filter *asfilter;
415 struct as_list *aslist;
416 regex_t *regex;
417 char *regstr;
418
419 /* Retrieve access list name */
420 argv_find(argv, argc, "WORD", &idx);
421 char *alname = argv[idx]->arg;
422
423 /* Check the filter type. */
424 type = argv_find(argv, argc, "deny", &idx) ? AS_FILTER_DENY
425 : AS_FILTER_PERMIT;
426
427 /* Check AS path regex. */
428 argv_find(argv, argc, "LINE", &idx);
429 regstr = argv_concat(argv, argc, idx);
430
431 regex = bgp_regcomp(regstr);
432 if (!regex) {
433 vty_out(vty, "can't compile regexp %s\n", regstr);
434 XFREE(MTYPE_TMP, regstr);
435 return CMD_WARNING_CONFIG_FAILED;
436 }
437
438 if (!config_bgp_aspath_validate(regstr)) {
439 vty_out(vty, "Invalid character in as-path access-list %s\n",
440 regstr);
441 return CMD_WARNING_CONFIG_FAILED;
442 }
443
444 asfilter = as_filter_make(regex, regstr, type);
445
446 XFREE(MTYPE_TMP, regstr);
447
448 /* Install new filter to the access_list. */
449 aslist = as_list_get(alname);
450
451 /* Duplicate insertion check. */;
452 if (as_list_dup_check(aslist, asfilter))
453 as_filter_free(asfilter);
454 else
455 as_list_filter_add(aslist, asfilter);
456
457 return CMD_SUCCESS;
458 }
459
460 DEFUN(no_as_path, no_bgp_as_path_cmd,
461 "no bgp as-path access-list WORD <deny|permit> LINE...",
462 NO_STR
463 BGP_STR
464 "BGP autonomous system path filter\n"
465 "Specify an access list name\n"
466 "Regular expression access list name\n"
467 "Specify packets to reject\n"
468 "Specify packets to forward\n"
469 "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
470 {
471 int idx = 0;
472 enum as_filter_type type;
473 struct as_filter *asfilter;
474 struct as_list *aslist;
475 char *regstr;
476 regex_t *regex;
477
478 char *aslistname =
479 argv_find(argv, argc, "WORD", &idx) ? argv[idx]->arg : NULL;
480
481 /* Lookup AS list from AS path list. */
482 aslist = as_list_lookup(aslistname);
483 if (aslist == NULL) {
484 vty_out(vty, "bgp as-path access-list %s doesn't exist\n",
485 aslistname);
486 return CMD_WARNING_CONFIG_FAILED;
487 }
488
489 /* Check the filter type. */
490 if (argv_find(argv, argc, "permit", &idx))
491 type = AS_FILTER_PERMIT;
492 else if (argv_find(argv, argc, "deny", &idx))
493 type = AS_FILTER_DENY;
494 else {
495 vty_out(vty, "filter type must be [permit|deny]\n");
496 return CMD_WARNING_CONFIG_FAILED;
497 }
498
499 /* Compile AS path. */
500 argv_find(argv, argc, "LINE", &idx);
501 regstr = argv_concat(argv, argc, idx);
502
503 if (!config_bgp_aspath_validate(regstr)) {
504 vty_out(vty, "Invalid character in as-path access-list %s\n",
505 regstr);
506 return CMD_WARNING_CONFIG_FAILED;
507 }
508
509 regex = bgp_regcomp(regstr);
510 if (!regex) {
511 vty_out(vty, "can't compile regexp %s\n", regstr);
512 XFREE(MTYPE_TMP, regstr);
513 return CMD_WARNING_CONFIG_FAILED;
514 }
515
516 /* Lookup asfilter. */
517 asfilter = as_filter_lookup(aslist, regstr, type);
518
519 XFREE(MTYPE_TMP, regstr);
520 bgp_regex_free(regex);
521
522 if (asfilter == NULL) {
523 vty_out(vty, "\n");
524 return CMD_WARNING_CONFIG_FAILED;
525 }
526
527 as_list_filter_delete(aslist, asfilter);
528
529 return CMD_SUCCESS;
530 }
531
532 DEFUN (no_as_path_all,
533 no_bgp_as_path_all_cmd,
534 "no bgp as-path access-list WORD",
535 NO_STR
536 BGP_STR
537 "BGP autonomous system path filter\n"
538 "Specify an access list name\n"
539 "Regular expression access list name\n")
540 {
541 int idx_word = 4;
542 struct as_list *aslist;
543
544 aslist = as_list_lookup(argv[idx_word]->arg);
545 if (aslist == NULL) {
546 vty_out(vty, "bgp as-path access-list %s doesn't exist\n",
547 argv[idx_word]->arg);
548 return CMD_WARNING_CONFIG_FAILED;
549 }
550
551 as_list_delete(aslist);
552
553 /* Run hook function. */
554 if (as_list_master.delete_hook)
555 (*as_list_master.delete_hook)(argv[idx_word]->arg);
556
557 return CMD_SUCCESS;
558 }
559
560 ALIAS (no_as_path_all,
561 no_ip_as_path_all_cmd,
562 "no ip as-path access-list WORD",
563 NO_STR
564 IP_STR
565 "BGP autonomous system path filter\n"
566 "Specify an access list name\n"
567 "Regular expression access list name\n")
568
569 static void as_list_show(struct vty *vty, struct as_list *aslist)
570 {
571 struct as_filter *asfilter;
572
573 vty_out(vty, "AS path access list %s\n", aslist->name);
574
575 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
576 vty_out(vty, " %s %s\n", filter_type_str(asfilter->type),
577 asfilter->reg_str);
578 }
579 }
580
581 static void as_list_show_all(struct vty *vty)
582 {
583 struct as_list *aslist;
584 struct as_filter *asfilter;
585
586 for (aslist = as_list_master.num.head; aslist; aslist = aslist->next) {
587 vty_out(vty, "AS path access list %s\n", aslist->name);
588
589 for (asfilter = aslist->head; asfilter;
590 asfilter = asfilter->next) {
591 vty_out(vty, " %s %s\n",
592 filter_type_str(asfilter->type),
593 asfilter->reg_str);
594 }
595 }
596
597 for (aslist = as_list_master.str.head; aslist; aslist = aslist->next) {
598 vty_out(vty, "AS path access list %s\n", aslist->name);
599
600 for (asfilter = aslist->head; asfilter;
601 asfilter = asfilter->next) {
602 vty_out(vty, " %s %s\n",
603 filter_type_str(asfilter->type),
604 asfilter->reg_str);
605 }
606 }
607 }
608
609 DEFUN (show_as_path_access_list,
610 show_bgp_as_path_access_list_cmd,
611 "show bgp as-path-access-list WORD",
612 SHOW_STR
613 BGP_STR
614 "List AS path access lists\n"
615 "AS path access list name\n")
616 {
617 int idx_word = 3;
618 struct as_list *aslist;
619
620 aslist = as_list_lookup(argv[idx_word]->arg);
621 if (aslist)
622 as_list_show(vty, aslist);
623
624 return CMD_SUCCESS;
625 }
626
627 ALIAS (show_as_path_access_list,
628 show_ip_as_path_access_list_cmd,
629 "show ip as-path-access-list WORD",
630 SHOW_STR
631 IP_STR
632 "List AS path access lists\n"
633 "AS path access list name\n")
634
635 DEFUN (show_as_path_access_list_all,
636 show_bgp_as_path_access_list_all_cmd,
637 "show bgp as-path-access-list",
638 SHOW_STR
639 BGP_STR
640 "List AS path access lists\n")
641 {
642 as_list_show_all(vty);
643 return CMD_SUCCESS;
644 }
645
646 ALIAS (show_as_path_access_list_all,
647 show_ip_as_path_access_list_all_cmd,
648 "show ip as-path-access-list",
649 SHOW_STR
650 IP_STR
651 "List AS path access lists\n")
652
653 static int config_write_as_list(struct vty *vty)
654 {
655 struct as_list *aslist;
656 struct as_filter *asfilter;
657 int write = 0;
658
659 for (aslist = as_list_master.num.head; aslist; aslist = aslist->next)
660 for (asfilter = aslist->head; asfilter;
661 asfilter = asfilter->next) {
662 vty_out(vty, "bgp as-path access-list %s %s %s\n",
663 aslist->name, filter_type_str(asfilter->type),
664 asfilter->reg_str);
665 write++;
666 }
667
668 for (aslist = as_list_master.str.head; aslist; aslist = aslist->next)
669 for (asfilter = aslist->head; asfilter;
670 asfilter = asfilter->next) {
671 vty_out(vty, "bgp as-path access-list %s %s %s\n",
672 aslist->name, filter_type_str(asfilter->type),
673 asfilter->reg_str);
674 write++;
675 }
676 return write;
677 }
678
679 static struct cmd_node as_list_node = {AS_LIST_NODE, "", 1};
680
681 /* Register functions. */
682 void bgp_filter_init(void)
683 {
684 install_node(&as_list_node, config_write_as_list);
685
686 install_element(CONFIG_NODE, &bgp_as_path_cmd);
687 install_element(CONFIG_NODE, &no_bgp_as_path_cmd);
688 install_element(CONFIG_NODE, &no_bgp_as_path_all_cmd);
689 install_element(CONFIG_NODE, &no_ip_as_path_all_cmd);
690
691 install_element(VIEW_NODE, &show_bgp_as_path_access_list_cmd);
692 install_element(VIEW_NODE, &show_ip_as_path_access_list_cmd);
693 install_element(VIEW_NODE, &show_bgp_as_path_access_list_all_cmd);
694 install_element(VIEW_NODE, &show_ip_as_path_access_list_all_cmd);
695 }
696
697 void bgp_filter_reset(void)
698 {
699 struct as_list *aslist;
700 struct as_list *next;
701
702 for (aslist = as_list_master.num.head; aslist; aslist = next) {
703 next = aslist->next;
704 as_list_delete(aslist);
705 }
706
707 for (aslist = as_list_master.str.head; aslist; aslist = next) {
708 next = aslist->next;
709 as_list_delete(aslist);
710 }
711
712 assert(as_list_master.num.head == NULL);
713 assert(as_list_master.num.tail == NULL);
714
715 assert(as_list_master.str.head == NULL);
716 assert(as_list_master.str.tail == NULL);
717 }