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