]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_filter.c
Merge pull request #8870 from anlancs/master-fix-reload-service
[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 string. */
44 struct as_list_list str;
45
46 /* Hook function which is executed when new access_list is added. */
47 void (*add_hook)(char *);
48
49 /* Hook function which is executed when access_list is deleted. */
50 void (*delete_hook)(const char *);
51 };
52
53 /* Element of AS path filter. */
54 struct as_filter {
55 struct as_filter *next;
56 struct as_filter *prev;
57
58 enum as_filter_type type;
59
60 regex_t *reg;
61 char *reg_str;
62
63 /* Sequence number. */
64 int64_t seq;
65 };
66
67 /* AS path filter list. */
68 struct as_list {
69 char *name;
70
71 struct as_list *next;
72 struct as_list *prev;
73
74 struct as_filter *head;
75 struct as_filter *tail;
76 };
77
78
79 /* Calculate new sequential number. */
80 static int64_t bgp_alist_new_seq_get(struct as_list *list)
81 {
82 int64_t maxseq;
83 int64_t newseq;
84 struct as_filter *entry;
85
86 maxseq = 0;
87
88 for (entry = list->head; entry; entry = entry->next) {
89 if (maxseq < entry->seq)
90 maxseq = entry->seq;
91 }
92
93 newseq = ((maxseq / 5) * 5) + 5;
94
95 return (newseq > UINT_MAX) ? UINT_MAX : newseq;
96 }
97
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)
100 {
101 struct as_filter *entry;
102
103 for (entry = list->head; entry; entry = entry->next)
104 if (entry->seq == seq)
105 return entry;
106
107 return NULL;
108 }
109
110 /* as-path access-list 10 permit AS1. */
111
112 static struct as_list_master as_list_master = {{NULL, NULL},
113 NULL,
114 NULL};
115
116 /* Allocate new AS filter. */
117 static struct as_filter *as_filter_new(void)
118 {
119 return XCALLOC(MTYPE_AS_FILTER, sizeof(struct as_filter));
120 }
121
122 /* Free allocated AS filter. */
123 static void as_filter_free(struct as_filter *asfilter)
124 {
125 if (asfilter->reg)
126 bgp_regex_free(asfilter->reg);
127 XFREE(MTYPE_AS_FILTER_STR, asfilter->reg_str);
128 XFREE(MTYPE_AS_FILTER, asfilter);
129 }
130
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)
134 {
135 struct as_filter *asfilter;
136
137 asfilter = as_filter_new();
138 asfilter->reg = reg;
139 asfilter->type = type;
140 asfilter->reg_str = XSTRDUP(MTYPE_AS_FILTER_STR, reg_str);
141
142 return asfilter;
143 }
144
145 static struct as_filter *as_filter_lookup(struct as_list *aslist,
146 const char *reg_str,
147 enum as_filter_type type)
148 {
149 struct as_filter *asfilter;
150
151 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next)
152 if (strcmp(reg_str, asfilter->reg_str) == 0)
153 return asfilter;
154 return NULL;
155 }
156
157 static void as_filter_entry_replace(struct as_list *list,
158 struct as_filter *replace,
159 struct as_filter *entry)
160 {
161 if (replace->next) {
162 entry->next = replace->next;
163 replace->next->prev = entry;
164 } else {
165 entry->next = NULL;
166 list->tail = entry;
167 }
168
169 if (replace->prev) {
170 entry->prev = replace->prev;
171 replace->prev->next = entry;
172 } else {
173 entry->prev = NULL;
174 list->head = entry;
175 }
176
177 as_filter_free(replace);
178 }
179
180 static void as_list_filter_add(struct as_list *aslist,
181 struct as_filter *asfilter)
182 {
183 struct as_filter *point;
184 struct as_filter *replace;
185
186 if (aslist->tail && asfilter->seq > aslist->tail->seq)
187 point = NULL;
188 else {
189 replace = bgp_aslist_seq_check(aslist, asfilter->seq);
190 if (replace) {
191 as_filter_entry_replace(aslist, replace, asfilter);
192 return;
193 }
194
195 /* Check insert point. */
196 for (point = aslist->head; point; point = point->next)
197 if (point->seq >= asfilter->seq)
198 break;
199 }
200
201 asfilter->next = point;
202
203 if (point) {
204 if (point->prev)
205 point->prev->next = asfilter;
206 else
207 aslist->head = asfilter;
208
209 asfilter->prev = point->prev;
210 point->prev = asfilter;
211 } else {
212 if (aslist->tail)
213 aslist->tail->next = asfilter;
214 else
215 aslist->head = asfilter;
216
217 asfilter->prev = aslist->tail;
218 aslist->tail = asfilter;
219 }
220
221 /* Run hook function. */
222 if (as_list_master.add_hook)
223 (*as_list_master.add_hook)(aslist->name);
224 }
225
226 /* Lookup as_list from list of as_list by name. */
227 struct as_list *as_list_lookup(const char *name)
228 {
229 struct as_list *aslist;
230
231 if (name == NULL)
232 return NULL;
233
234 for (aslist = as_list_master.str.head; aslist; aslist = aslist->next)
235 if (strcmp(aslist->name, name) == 0)
236 return aslist;
237
238 return NULL;
239 }
240
241 static struct as_list *as_list_new(void)
242 {
243 return XCALLOC(MTYPE_AS_LIST, sizeof(struct as_list));
244 }
245
246 static void as_list_free(struct as_list *aslist)
247 {
248 XFREE(MTYPE_AS_STR, aslist->name);
249 XFREE(MTYPE_AS_LIST, aslist);
250 }
251
252 /* Insert new AS list to list of as_list. Each as_list is sorted by
253 the name. */
254 static struct as_list *as_list_insert(const char *name)
255 {
256 struct as_list *aslist;
257 struct as_list *point;
258 struct as_list_list *list;
259
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);
264
265 /* Set access_list to string list. */
266 list = &as_list_master.str;
267
268 /* Set point to insertion point. */
269 for (point = list->head; point; point = point->next)
270 if (strcmp(point->name, name) >= 0)
271 break;
272
273 /* In case of this is the first element of master. */
274 if (list->head == NULL) {
275 list->head = list->tail = aslist;
276 return aslist;
277 }
278
279 /* In case of insertion is made at the tail of access_list. */
280 if (point == NULL) {
281 aslist->prev = list->tail;
282 list->tail->next = aslist;
283 list->tail = aslist;
284 return aslist;
285 }
286
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;
291 list->head = aslist;
292 return aslist;
293 }
294
295 /* Insertion is made at middle of the access_list. */
296 aslist->next = point;
297 aslist->prev = point->prev;
298
299 if (point->prev)
300 point->prev->next = aslist;
301 point->prev = aslist;
302
303 return aslist;
304 }
305
306 static struct as_list *as_list_get(const char *name)
307 {
308 struct as_list *aslist;
309
310 aslist = as_list_lookup(name);
311 if (aslist == NULL)
312 aslist = as_list_insert(name);
313
314 return aslist;
315 }
316
317 static const char *filter_type_str(enum as_filter_type type)
318 {
319 switch (type) {
320 case AS_FILTER_PERMIT:
321 return "permit";
322 case AS_FILTER_DENY:
323 return "deny";
324 default:
325 return "";
326 }
327 }
328
329 static void as_list_delete(struct as_list *aslist)
330 {
331 struct as_list_list *list;
332 struct as_filter *filter, *next;
333
334 for (filter = aslist->head; filter; filter = next) {
335 next = filter->next;
336 as_filter_free(filter);
337 }
338
339 list = &as_list_master.str;
340
341 if (aslist->next)
342 aslist->next->prev = aslist->prev;
343 else
344 list->tail = aslist->prev;
345
346 if (aslist->prev)
347 aslist->prev->next = aslist->next;
348 else
349 list->head = aslist->next;
350
351 as_list_free(aslist);
352 }
353
354 static bool as_list_empty(struct as_list *aslist)
355 {
356 return aslist->head == NULL && aslist->tail == NULL;
357 }
358
359 static void as_list_filter_delete(struct as_list *aslist,
360 struct as_filter *asfilter)
361 {
362 char *name = XSTRDUP(MTYPE_AS_STR, aslist->name);
363
364 if (asfilter->next)
365 asfilter->next->prev = asfilter->prev;
366 else
367 aslist->tail = asfilter->prev;
368
369 if (asfilter->prev)
370 asfilter->prev->next = asfilter->next;
371 else
372 aslist->head = asfilter->next;
373
374 as_filter_free(asfilter);
375
376 /* If access_list becomes empty delete it from access_master. */
377 if (as_list_empty(aslist))
378 as_list_delete(aslist);
379
380 /* Run hook function. */
381 if (as_list_master.delete_hook)
382 (*as_list_master.delete_hook)(name);
383 XFREE(MTYPE_AS_STR, name);
384 }
385
386 static bool as_filter_match(struct as_filter *asfilter, struct aspath *aspath)
387 {
388 return bgp_regexec(asfilter->reg, aspath) != REG_NOMATCH;
389 }
390
391 /* Apply AS path filter to AS. */
392 enum as_filter_type as_list_apply(struct as_list *aslist, void *object)
393 {
394 struct as_filter *asfilter;
395 struct aspath *aspath;
396
397 aspath = (struct aspath *)object;
398
399 if (aslist == NULL)
400 return AS_FILTER_DENY;
401
402 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
403 if (as_filter_match(asfilter, aspath))
404 return asfilter->type;
405 }
406 return AS_FILTER_DENY;
407 }
408
409 /* Add hook function. */
410 void as_list_add_hook(void (*func)(char *))
411 {
412 as_list_master.add_hook = func;
413 }
414
415 /* Delete hook function. */
416 void as_list_delete_hook(void (*func)(const char *))
417 {
418 as_list_master.delete_hook = func;
419 }
420
421 static bool as_list_dup_check(struct as_list *aslist, struct as_filter *new)
422 {
423 struct as_filter *asfilter;
424
425 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
426 if (asfilter->type == new->type
427 && strcmp(asfilter->reg_str, new->reg_str) == 0)
428 return true;
429 }
430 return false;
431 }
432
433 bool config_bgp_aspath_validate(const char *regstr)
434 {
435 char valid_chars[] = "1234567890_^|[,{}() ]$*+.?-\\";
436
437 if (strspn(regstr, valid_chars) == strlen(regstr))
438 return true;
439 return false;
440 }
441
442 DEFUN(as_path, bgp_as_path_cmd,
443 "bgp as-path access-list WORD [seq (0-4294967295)] <deny|permit> LINE...",
444 BGP_STR
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"
449 "Sequence number\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")
453 {
454 int idx = 0;
455 enum as_filter_type type;
456 struct as_filter *asfilter;
457 struct as_list *aslist;
458 regex_t *regex;
459 char *regstr;
460 int64_t seqnum = ASPATH_SEQ_NUMBER_AUTO;
461
462 /* Retrieve access list name */
463 argv_find(argv, argc, "WORD", &idx);
464 char *alname = argv[idx]->arg;
465
466 if (argv_find(argv, argc, "(0-4294967295)", &idx))
467 seqnum = (int64_t)atol(argv[idx]->arg);
468
469 /* Check the filter type. */
470 type = argv_find(argv, argc, "deny", &idx) ? AS_FILTER_DENY
471 : AS_FILTER_PERMIT;
472
473 /* Check AS path regex. */
474 argv_find(argv, argc, "LINE", &idx);
475 regstr = argv_concat(argv, argc, idx);
476
477 regex = bgp_regcomp(regstr);
478 if (!regex) {
479 vty_out(vty, "can't compile regexp %s\n", regstr);
480 XFREE(MTYPE_TMP, regstr);
481 return CMD_WARNING_CONFIG_FAILED;
482 }
483
484 if (!config_bgp_aspath_validate(regstr)) {
485 vty_out(vty, "Invalid character in as-path access-list %s\n",
486 regstr);
487 return CMD_WARNING_CONFIG_FAILED;
488 }
489
490 asfilter = as_filter_make(regex, regstr, type);
491
492 XFREE(MTYPE_TMP, regstr);
493
494 /* Install new filter to the access_list. */
495 aslist = as_list_get(alname);
496
497 if (seqnum == ASPATH_SEQ_NUMBER_AUTO)
498 seqnum = bgp_alist_new_seq_get(aslist);
499
500 asfilter->seq = seqnum;
501
502 /* Duplicate insertion check. */;
503 if (as_list_dup_check(aslist, asfilter))
504 as_filter_free(asfilter);
505 else
506 as_list_filter_add(aslist, asfilter);
507
508 return CMD_SUCCESS;
509 }
510
511 DEFUN(no_as_path, no_bgp_as_path_cmd,
512 "no bgp as-path access-list WORD [seq (0-4294967295)] <deny|permit> LINE...",
513 NO_STR
514 BGP_STR
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"
519 "Sequence number\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")
523 {
524 int idx = 0;
525 enum as_filter_type type;
526 struct as_filter *asfilter;
527 struct as_list *aslist;
528 char *regstr;
529 regex_t *regex;
530
531 char *aslistname =
532 argv_find(argv, argc, "WORD", &idx) ? argv[idx]->arg : NULL;
533
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",
538 aslistname);
539 return CMD_WARNING_CONFIG_FAILED;
540 }
541
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;
547 else {
548 vty_out(vty, "filter type must be [permit|deny]\n");
549 return CMD_WARNING_CONFIG_FAILED;
550 }
551
552 /* Compile AS path. */
553 argv_find(argv, argc, "LINE", &idx);
554 regstr = argv_concat(argv, argc, idx);
555
556 if (!config_bgp_aspath_validate(regstr)) {
557 vty_out(vty, "Invalid character in as-path access-list %s\n",
558 regstr);
559 return CMD_WARNING_CONFIG_FAILED;
560 }
561
562 regex = bgp_regcomp(regstr);
563 if (!regex) {
564 vty_out(vty, "can't compile regexp %s\n", regstr);
565 XFREE(MTYPE_TMP, regstr);
566 return CMD_WARNING_CONFIG_FAILED;
567 }
568
569 /* Lookup asfilter. */
570 asfilter = as_filter_lookup(aslist, regstr, type);
571
572 bgp_regex_free(regex);
573
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;
578 }
579
580 XFREE(MTYPE_TMP, regstr);
581
582 as_list_filter_delete(aslist, asfilter);
583
584 return CMD_SUCCESS;
585 }
586
587 DEFUN (no_as_path_all,
588 no_bgp_as_path_all_cmd,
589 "no bgp as-path access-list WORD",
590 NO_STR
591 BGP_STR
592 "BGP autonomous system path filter\n"
593 "Specify an access list name\n"
594 "Regular expression access list name\n")
595 {
596 int idx_word = 4;
597 struct as_list *aslist;
598
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;
604 }
605
606 as_list_delete(aslist);
607
608 /* Run hook function. */
609 if (as_list_master.delete_hook)
610 (*as_list_master.delete_hook)(argv[idx_word]->arg);
611
612 return CMD_SUCCESS;
613 }
614
615 static void as_list_show(struct vty *vty, struct as_list *aslist,
616 json_object *json)
617 {
618 struct as_filter *asfilter;
619 json_object *json_aslist = NULL;
620
621 if (json) {
622 json_aslist = json_object_new_array();
623 json_object_object_add(json, aslist->name, json_aslist);
624 } else
625 vty_out(vty, "AS path access list %s\n", aslist->name);
626
627 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
628 if (json) {
629 json_object *json_asfilter = json_object_new_object();
630
631 json_object_int_add(json_asfilter, "sequenceNumber",
632 asfilter->seq);
633 json_object_string_add(json_asfilter, "type",
634 filter_type_str(asfilter->type));
635 json_object_string_add(json_asfilter, "regExp",
636 asfilter->reg_str);
637
638 json_object_array_add(json_aslist, json_asfilter);
639 } else
640 vty_out(vty, " %s %s\n",
641 filter_type_str(asfilter->type),
642 asfilter->reg_str);
643 }
644 }
645
646 static void as_list_show_all(struct vty *vty, json_object *json)
647 {
648 struct as_list *aslist;
649
650 for (aslist = as_list_master.str.head; aslist; aslist = aslist->next)
651 as_list_show(vty, aslist, json);
652 }
653
654 DEFUN (show_as_path_access_list,
655 show_bgp_as_path_access_list_cmd,
656 "show bgp as-path-access-list WORD [json]",
657 SHOW_STR
658 BGP_STR
659 "List AS path access lists\n"
660 "AS path access list name\n"
661 JSON_STR)
662 {
663 int idx_word = 3;
664 struct as_list *aslist;
665 bool uj = use_json(argc, argv);
666 json_object *json = NULL;
667
668 if (uj)
669 json = json_object_new_object();
670
671 aslist = as_list_lookup(argv[idx_word]->arg);
672 if (aslist)
673 as_list_show(vty, aslist, json);
674
675 if (uj) {
676 vty_out(vty, "%s\n",
677 json_object_to_json_string_ext(
678 json, JSON_C_TO_STRING_PRETTY));
679 json_object_free(json);
680 }
681
682 return CMD_SUCCESS;
683 }
684
685 ALIAS (show_as_path_access_list,
686 show_ip_as_path_access_list_cmd,
687 "show ip as-path-access-list WORD [json]",
688 SHOW_STR
689 IP_STR
690 "List AS path access lists\n"
691 "AS path access list name\n"
692 JSON_STR)
693
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]",
697 SHOW_STR
698 BGP_STR
699 "List AS path access lists\n"
700 JSON_STR)
701 {
702 bool uj = use_json(argc, argv);
703 json_object *json = NULL;
704
705 if (uj)
706 json = json_object_new_object();
707
708 as_list_show_all(vty, json);
709
710 if (uj) {
711 vty_out(vty, "%s\n",
712 json_object_to_json_string_ext(
713 json, JSON_C_TO_STRING_PRETTY));
714 json_object_free(json);
715 }
716
717 return CMD_SUCCESS;
718 }
719
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]",
723 SHOW_STR
724 IP_STR
725 "List AS path access lists\n"
726 JSON_STR)
727
728 static int config_write_as_list(struct vty *vty)
729 {
730 struct as_list *aslist;
731 struct as_filter *asfilter;
732 int write = 0;
733
734 for (aslist = as_list_master.str.head; aslist; aslist = aslist->next)
735 for (asfilter = aslist->head; asfilter;
736 asfilter = asfilter->next) {
737 vty_out(vty,
738 "bgp as-path access-list %s seq %" PRId64
739 " %s %s\n",
740 aslist->name, asfilter->seq,
741 filter_type_str(asfilter->type),
742 asfilter->reg_str);
743 write++;
744 }
745 return write;
746 }
747
748 static int config_write_as_list(struct vty *vty);
749 static struct cmd_node as_list_node = {
750 .name = "as list",
751 .node = AS_LIST_NODE,
752 .prompt = "",
753 .config_write = config_write_as_list,
754 };
755
756 /* Register functions. */
757 void bgp_filter_init(void)
758 {
759 install_node(&as_list_node);
760
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);
764
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);
769 }
770
771 void bgp_filter_reset(void)
772 {
773 struct as_list *aslist;
774 struct as_list *next;
775
776 for (aslist = as_list_master.str.head; aslist; aslist = next) {
777 next = aslist->next;
778 as_list_delete(aslist);
779 }
780
781 assert(as_list_master.str.head == NULL);
782 assert(as_list_master.str.tail == NULL);
783 }