]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_filter.c
Merge pull request #6247 from FRRouting/nb_conversions
[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 bool as_list_empty(struct as_list *aslist)
306 {
307 return aslist->head == NULL && aslist->tail == NULL;
308 }
309
310 static void as_list_filter_delete(struct as_list *aslist,
311 struct as_filter *asfilter)
312 {
313 char *name = XSTRDUP(MTYPE_AS_STR, aslist->name);
314
315 if (asfilter->next)
316 asfilter->next->prev = asfilter->prev;
317 else
318 aslist->tail = asfilter->prev;
319
320 if (asfilter->prev)
321 asfilter->prev->next = asfilter->next;
322 else
323 aslist->head = asfilter->next;
324
325 as_filter_free(asfilter);
326
327 /* If access_list becomes empty delete it from access_master. */
328 if (as_list_empty(aslist))
329 as_list_delete(aslist);
330
331 /* Run hook function. */
332 if (as_list_master.delete_hook)
333 (*as_list_master.delete_hook)(name);
334 XFREE(MTYPE_AS_STR, name);
335 }
336
337 static bool as_filter_match(struct as_filter *asfilter, struct aspath *aspath)
338 {
339 return bgp_regexec(asfilter->reg, aspath) != REG_NOMATCH;
340 }
341
342 /* Apply AS path filter to AS. */
343 enum as_filter_type as_list_apply(struct as_list *aslist, void *object)
344 {
345 struct as_filter *asfilter;
346 struct aspath *aspath;
347
348 aspath = (struct aspath *)object;
349
350 if (aslist == NULL)
351 return AS_FILTER_DENY;
352
353 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
354 if (as_filter_match(asfilter, aspath))
355 return asfilter->type;
356 }
357 return AS_FILTER_DENY;
358 }
359
360 /* Add hook function. */
361 void as_list_add_hook(void (*func)(char *))
362 {
363 as_list_master.add_hook = func;
364 }
365
366 /* Delete hook function. */
367 void as_list_delete_hook(void (*func)(const char *))
368 {
369 as_list_master.delete_hook = func;
370 }
371
372 static bool as_list_dup_check(struct as_list *aslist, struct as_filter *new)
373 {
374 struct as_filter *asfilter;
375
376 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
377 if (asfilter->type == new->type
378 && strcmp(asfilter->reg_str, new->reg_str) == 0)
379 return true;
380 }
381 return false;
382 }
383
384 bool config_bgp_aspath_validate(const char *regstr)
385 {
386 char valid_chars[] = "1234567890_^|[,{}() ]$*+.?-\\";
387
388 if (strspn(regstr, valid_chars) == strlen(regstr))
389 return true;
390 return false;
391 }
392
393 DEFUN(as_path, bgp_as_path_cmd,
394 "bgp as-path access-list WORD <deny|permit> LINE...",
395 BGP_STR
396 "BGP autonomous system path filter\n"
397 "Specify an access list name\n"
398 "Regular expression access list name\n"
399 "Specify packets to reject\n"
400 "Specify packets to forward\n"
401 "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
402 {
403 int idx = 0;
404 enum as_filter_type type;
405 struct as_filter *asfilter;
406 struct as_list *aslist;
407 regex_t *regex;
408 char *regstr;
409
410 /* Retrieve access list name */
411 argv_find(argv, argc, "WORD", &idx);
412 char *alname = argv[idx]->arg;
413
414 /* Check the filter type. */
415 type = argv_find(argv, argc, "deny", &idx) ? AS_FILTER_DENY
416 : AS_FILTER_PERMIT;
417
418 /* Check AS path regex. */
419 argv_find(argv, argc, "LINE", &idx);
420 regstr = argv_concat(argv, argc, idx);
421
422 regex = bgp_regcomp(regstr);
423 if (!regex) {
424 vty_out(vty, "can't compile regexp %s\n", regstr);
425 XFREE(MTYPE_TMP, regstr);
426 return CMD_WARNING_CONFIG_FAILED;
427 }
428
429 if (!config_bgp_aspath_validate(regstr)) {
430 vty_out(vty, "Invalid character in as-path access-list %s\n",
431 regstr);
432 return CMD_WARNING_CONFIG_FAILED;
433 }
434
435 asfilter = as_filter_make(regex, regstr, type);
436
437 XFREE(MTYPE_TMP, regstr);
438
439 /* Install new filter to the access_list. */
440 aslist = as_list_get(alname);
441
442 /* Duplicate insertion check. */;
443 if (as_list_dup_check(aslist, asfilter))
444 as_filter_free(asfilter);
445 else
446 as_list_filter_add(aslist, asfilter);
447
448 return CMD_SUCCESS;
449 }
450
451 DEFUN(no_as_path, no_bgp_as_path_cmd,
452 "no bgp as-path access-list WORD <deny|permit> LINE...",
453 NO_STR
454 BGP_STR
455 "BGP autonomous system path filter\n"
456 "Specify an access list name\n"
457 "Regular expression access list name\n"
458 "Specify packets to reject\n"
459 "Specify packets to forward\n"
460 "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
461 {
462 int idx = 0;
463 enum as_filter_type type;
464 struct as_filter *asfilter;
465 struct as_list *aslist;
466 char *regstr;
467 regex_t *regex;
468
469 char *aslistname =
470 argv_find(argv, argc, "WORD", &idx) ? argv[idx]->arg : NULL;
471
472 /* Lookup AS list from AS path list. */
473 aslist = as_list_lookup(aslistname);
474 if (aslist == NULL) {
475 vty_out(vty, "bgp as-path access-list %s doesn't exist\n",
476 aslistname);
477 return CMD_WARNING_CONFIG_FAILED;
478 }
479
480 /* Check the filter type. */
481 if (argv_find(argv, argc, "permit", &idx))
482 type = AS_FILTER_PERMIT;
483 else if (argv_find(argv, argc, "deny", &idx))
484 type = AS_FILTER_DENY;
485 else {
486 vty_out(vty, "filter type must be [permit|deny]\n");
487 return CMD_WARNING_CONFIG_FAILED;
488 }
489
490 /* Compile AS path. */
491 argv_find(argv, argc, "LINE", &idx);
492 regstr = argv_concat(argv, argc, idx);
493
494 if (!config_bgp_aspath_validate(regstr)) {
495 vty_out(vty, "Invalid character in as-path access-list %s\n",
496 regstr);
497 return CMD_WARNING_CONFIG_FAILED;
498 }
499
500 regex = bgp_regcomp(regstr);
501 if (!regex) {
502 vty_out(vty, "can't compile regexp %s\n", regstr);
503 XFREE(MTYPE_TMP, regstr);
504 return CMD_WARNING_CONFIG_FAILED;
505 }
506
507 /* Lookup asfilter. */
508 asfilter = as_filter_lookup(aslist, regstr, type);
509
510 XFREE(MTYPE_TMP, regstr);
511 bgp_regex_free(regex);
512
513 if (asfilter == NULL) {
514 vty_out(vty, "\n");
515 return CMD_WARNING_CONFIG_FAILED;
516 }
517
518 as_list_filter_delete(aslist, asfilter);
519
520 return CMD_SUCCESS;
521 }
522
523 DEFUN (no_as_path_all,
524 no_bgp_as_path_all_cmd,
525 "no bgp as-path access-list WORD",
526 NO_STR
527 BGP_STR
528 "BGP autonomous system path filter\n"
529 "Specify an access list name\n"
530 "Regular expression access list name\n")
531 {
532 int idx_word = 4;
533 struct as_list *aslist;
534
535 aslist = as_list_lookup(argv[idx_word]->arg);
536 if (aslist == NULL) {
537 vty_out(vty, "bgp as-path access-list %s doesn't exist\n",
538 argv[idx_word]->arg);
539 return CMD_WARNING_CONFIG_FAILED;
540 }
541
542 as_list_delete(aslist);
543
544 /* Run hook function. */
545 if (as_list_master.delete_hook)
546 (*as_list_master.delete_hook)(argv[idx_word]->arg);
547
548 return CMD_SUCCESS;
549 }
550
551 ALIAS (no_as_path_all,
552 no_ip_as_path_all_cmd,
553 "no ip as-path access-list WORD",
554 NO_STR
555 IP_STR
556 "BGP autonomous system path filter\n"
557 "Specify an access list name\n"
558 "Regular expression access list name\n")
559
560 static void as_list_show(struct vty *vty, struct as_list *aslist)
561 {
562 struct as_filter *asfilter;
563
564 vty_out(vty, "AS path access list %s\n", aslist->name);
565
566 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
567 vty_out(vty, " %s %s\n", filter_type_str(asfilter->type),
568 asfilter->reg_str);
569 }
570 }
571
572 static void as_list_show_all(struct vty *vty)
573 {
574 struct as_list *aslist;
575 struct as_filter *asfilter;
576
577 for (aslist = as_list_master.num.head; aslist; aslist = aslist->next) {
578 vty_out(vty, "AS path access list %s\n", aslist->name);
579
580 for (asfilter = aslist->head; asfilter;
581 asfilter = asfilter->next) {
582 vty_out(vty, " %s %s\n",
583 filter_type_str(asfilter->type),
584 asfilter->reg_str);
585 }
586 }
587
588 for (aslist = as_list_master.str.head; aslist; aslist = aslist->next) {
589 vty_out(vty, "AS path access list %s\n", aslist->name);
590
591 for (asfilter = aslist->head; asfilter;
592 asfilter = asfilter->next) {
593 vty_out(vty, " %s %s\n",
594 filter_type_str(asfilter->type),
595 asfilter->reg_str);
596 }
597 }
598 }
599
600 DEFUN (show_as_path_access_list,
601 show_bgp_as_path_access_list_cmd,
602 "show bgp as-path-access-list WORD",
603 SHOW_STR
604 BGP_STR
605 "List AS path access lists\n"
606 "AS path access list name\n")
607 {
608 int idx_word = 3;
609 struct as_list *aslist;
610
611 aslist = as_list_lookup(argv[idx_word]->arg);
612 if (aslist)
613 as_list_show(vty, aslist);
614
615 return CMD_SUCCESS;
616 }
617
618 ALIAS (show_as_path_access_list,
619 show_ip_as_path_access_list_cmd,
620 "show ip as-path-access-list WORD",
621 SHOW_STR
622 IP_STR
623 "List AS path access lists\n"
624 "AS path access list name\n")
625
626 DEFUN (show_as_path_access_list_all,
627 show_bgp_as_path_access_list_all_cmd,
628 "show bgp as-path-access-list",
629 SHOW_STR
630 BGP_STR
631 "List AS path access lists\n")
632 {
633 as_list_show_all(vty);
634 return CMD_SUCCESS;
635 }
636
637 ALIAS (show_as_path_access_list_all,
638 show_ip_as_path_access_list_all_cmd,
639 "show ip as-path-access-list",
640 SHOW_STR
641 IP_STR
642 "List AS path access lists\n")
643
644 static int config_write_as_list(struct vty *vty)
645 {
646 struct as_list *aslist;
647 struct as_filter *asfilter;
648 int write = 0;
649
650 for (aslist = as_list_master.num.head; aslist; aslist = aslist->next)
651 for (asfilter = aslist->head; asfilter;
652 asfilter = asfilter->next) {
653 vty_out(vty, "bgp as-path access-list %s %s %s\n",
654 aslist->name, filter_type_str(asfilter->type),
655 asfilter->reg_str);
656 write++;
657 }
658
659 for (aslist = as_list_master.str.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 return write;
668 }
669
670 static int config_write_as_list(struct vty *vty);
671 static struct cmd_node as_list_node = {
672 .name = "as list",
673 .node = AS_LIST_NODE,
674 .prompt = "",
675 .config_write = config_write_as_list,
676 };
677
678 /* Register functions. */
679 void bgp_filter_init(void)
680 {
681 install_node(&as_list_node);
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 }