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