]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_filter.c
doc: Add `show ipv6 rpf X:X::X:X` command to docs
[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 goto hook;
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 hook:
222 /* Run hook function. */
223 if (as_list_master.add_hook)
224 (*as_list_master.add_hook)(aslist->name);
225 }
226
227 /* Lookup as_list from list of as_list by name. */
228 struct as_list *as_list_lookup(const char *name)
229 {
230 struct as_list *aslist;
231
232 if (name == NULL)
233 return NULL;
234
235 for (aslist = as_list_master.str.head; aslist; aslist = aslist->next)
236 if (strcmp(aslist->name, name) == 0)
237 return aslist;
238
239 return NULL;
240 }
241
242 static struct as_list *as_list_new(void)
243 {
244 return XCALLOC(MTYPE_AS_LIST, sizeof(struct as_list));
245 }
246
247 static void as_list_free(struct as_list *aslist)
248 {
249 XFREE(MTYPE_AS_STR, aslist->name);
250 XFREE(MTYPE_AS_LIST, aslist);
251 }
252
253 /* Insert new AS list to list of as_list. Each as_list is sorted by
254 the name. */
255 static struct as_list *as_list_insert(const char *name)
256 {
257 struct as_list *aslist;
258 struct as_list *point;
259 struct as_list_list *list;
260
261 /* Allocate new access_list and copy given name. */
262 aslist = as_list_new();
263 aslist->name = XSTRDUP(MTYPE_AS_STR, name);
264 assert(aslist->name);
265
266 /* Set access_list to string list. */
267 list = &as_list_master.str;
268
269 /* Set point to insertion point. */
270 for (point = list->head; point; point = point->next)
271 if (strcmp(point->name, name) >= 0)
272 break;
273
274 /* In case of this is the first element of master. */
275 if (list->head == NULL) {
276 list->head = list->tail = aslist;
277 return aslist;
278 }
279
280 /* In case of insertion is made at the tail of access_list. */
281 if (point == NULL) {
282 aslist->prev = list->tail;
283 list->tail->next = aslist;
284 list->tail = aslist;
285 return aslist;
286 }
287
288 /* In case of insertion is made at the head of access_list. */
289 if (point == list->head) {
290 aslist->next = list->head;
291 list->head->prev = aslist;
292 list->head = aslist;
293 return aslist;
294 }
295
296 /* Insertion is made at middle of the access_list. */
297 aslist->next = point;
298 aslist->prev = point->prev;
299
300 if (point->prev)
301 point->prev->next = aslist;
302 point->prev = aslist;
303
304 return aslist;
305 }
306
307 static struct as_list *as_list_get(const char *name)
308 {
309 struct as_list *aslist;
310
311 aslist = as_list_lookup(name);
312 if (aslist == NULL)
313 aslist = as_list_insert(name);
314
315 return aslist;
316 }
317
318 static const char *filter_type_str(enum as_filter_type type)
319 {
320 switch (type) {
321 case AS_FILTER_PERMIT:
322 return "permit";
323 case AS_FILTER_DENY:
324 return "deny";
325 default:
326 return "";
327 }
328 }
329
330 static void as_list_delete(struct as_list *aslist)
331 {
332 struct as_list_list *list;
333 struct as_filter *filter, *next;
334
335 for (filter = aslist->head; filter; filter = next) {
336 next = filter->next;
337 as_filter_free(filter);
338 }
339
340 list = &as_list_master.str;
341
342 if (aslist->next)
343 aslist->next->prev = aslist->prev;
344 else
345 list->tail = aslist->prev;
346
347 if (aslist->prev)
348 aslist->prev->next = aslist->next;
349 else
350 list->head = aslist->next;
351
352 as_list_free(aslist);
353 }
354
355 static bool as_list_empty(struct as_list *aslist)
356 {
357 return aslist->head == NULL && aslist->tail == NULL;
358 }
359
360 static void as_list_filter_delete(struct as_list *aslist,
361 struct as_filter *asfilter)
362 {
363 char *name = XSTRDUP(MTYPE_AS_STR, aslist->name);
364
365 if (asfilter->next)
366 asfilter->next->prev = asfilter->prev;
367 else
368 aslist->tail = asfilter->prev;
369
370 if (asfilter->prev)
371 asfilter->prev->next = asfilter->next;
372 else
373 aslist->head = asfilter->next;
374
375 as_filter_free(asfilter);
376
377 /* If access_list becomes empty delete it from access_master. */
378 if (as_list_empty(aslist))
379 as_list_delete(aslist);
380
381 /* Run hook function. */
382 if (as_list_master.delete_hook)
383 (*as_list_master.delete_hook)(name);
384 XFREE(MTYPE_AS_STR, name);
385 }
386
387 static bool as_filter_match(struct as_filter *asfilter, struct aspath *aspath)
388 {
389 return bgp_regexec(asfilter->reg, aspath) != REG_NOMATCH;
390 }
391
392 /* Apply AS path filter to AS. */
393 enum as_filter_type as_list_apply(struct as_list *aslist, void *object)
394 {
395 struct as_filter *asfilter;
396 struct aspath *aspath;
397
398 aspath = (struct aspath *)object;
399
400 if (aslist == NULL)
401 return AS_FILTER_DENY;
402
403 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
404 if (as_filter_match(asfilter, aspath))
405 return asfilter->type;
406 }
407 return AS_FILTER_DENY;
408 }
409
410 /* Add hook function. */
411 void as_list_add_hook(void (*func)(char *))
412 {
413 as_list_master.add_hook = func;
414 }
415
416 /* Delete hook function. */
417 void as_list_delete_hook(void (*func)(const char *))
418 {
419 as_list_master.delete_hook = func;
420 }
421
422 static bool as_list_dup_check(struct as_list *aslist, struct as_filter *new)
423 {
424 struct as_filter *asfilter;
425
426 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
427 if (asfilter->type == new->type
428 && strcmp(asfilter->reg_str, new->reg_str) == 0)
429 return true;
430 }
431 return false;
432 }
433
434 bool config_bgp_aspath_validate(const char *regstr)
435 {
436 char valid_chars[] = "1234567890_^|[,{}() ]$*+.?-\\";
437
438 if (strspn(regstr, valid_chars) == strlen(regstr))
439 return true;
440 return false;
441 }
442
443 DEFUN(as_path, bgp_as_path_cmd,
444 "bgp as-path access-list AS_PATH_FILTER_NAME [seq (0-4294967295)] <deny|permit> LINE...",
445 BGP_STR
446 "BGP autonomous system path filter\n"
447 "Specify an access list name\n"
448 "Regular expression access list name\n"
449 "Sequence number of an entry\n"
450 "Sequence number\n"
451 "Specify packets to reject\n"
452 "Specify packets to forward\n"
453 "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
454 {
455 int idx = 0;
456 enum as_filter_type type;
457 struct as_filter *asfilter;
458 struct as_list *aslist;
459 regex_t *regex;
460 char *regstr;
461 int64_t seqnum = ASPATH_SEQ_NUMBER_AUTO;
462
463 /* Retrieve access list name */
464 argv_find(argv, argc, "AS_PATH_FILTER_NAME", &idx);
465 char *alname = argv[idx]->arg;
466
467 if (argv_find(argv, argc, "(0-4294967295)", &idx))
468 seqnum = (int64_t)atol(argv[idx]->arg);
469
470 /* Check the filter type. */
471 type = argv_find(argv, argc, "deny", &idx) ? AS_FILTER_DENY
472 : AS_FILTER_PERMIT;
473
474 /* Check AS path regex. */
475 argv_find(argv, argc, "LINE", &idx);
476 regstr = argv_concat(argv, argc, idx);
477
478 regex = bgp_regcomp(regstr);
479 if (!regex) {
480 vty_out(vty, "can't compile regexp %s\n", regstr);
481 XFREE(MTYPE_TMP, regstr);
482 return CMD_WARNING_CONFIG_FAILED;
483 }
484
485 if (!config_bgp_aspath_validate(regstr)) {
486 vty_out(vty, "Invalid character in as-path access-list %s\n",
487 regstr);
488 XFREE(MTYPE_TMP, regstr);
489 return CMD_WARNING_CONFIG_FAILED;
490 }
491
492 asfilter = as_filter_make(regex, regstr, type);
493
494 XFREE(MTYPE_TMP, regstr);
495
496 /* Install new filter to the access_list. */
497 aslist = as_list_get(alname);
498
499 if (seqnum == ASPATH_SEQ_NUMBER_AUTO)
500 seqnum = bgp_alist_new_seq_get(aslist);
501
502 asfilter->seq = seqnum;
503
504 /* Duplicate insertion check. */;
505 if (as_list_dup_check(aslist, asfilter))
506 as_filter_free(asfilter);
507 else
508 as_list_filter_add(aslist, asfilter);
509
510 return CMD_SUCCESS;
511 }
512
513 DEFUN(no_as_path, no_bgp_as_path_cmd,
514 "no bgp as-path access-list AS_PATH_FILTER_NAME [seq (0-4294967295)] <deny|permit> LINE...",
515 NO_STR
516 BGP_STR
517 "BGP autonomous system path filter\n"
518 "Specify an access list name\n"
519 "Regular expression access list name\n"
520 "Sequence number of an entry\n"
521 "Sequence number\n"
522 "Specify packets to reject\n"
523 "Specify packets to forward\n"
524 "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
525 {
526 int idx = 0;
527 enum as_filter_type type;
528 struct as_filter *asfilter;
529 struct as_list *aslist;
530 char *regstr;
531 regex_t *regex;
532
533 char *aslistname =
534 argv_find(argv, argc, "AS_PATH_FILTER_NAME", &idx) ? argv[idx]->arg : NULL;
535
536 /* Lookup AS list from AS path list. */
537 aslist = as_list_lookup(aslistname);
538 if (aslist == NULL) {
539 vty_out(vty, "bgp as-path access-list %s doesn't exist\n",
540 aslistname);
541 return CMD_WARNING_CONFIG_FAILED;
542 }
543
544 /* Check the filter type. */
545 if (argv_find(argv, argc, "permit", &idx))
546 type = AS_FILTER_PERMIT;
547 else if (argv_find(argv, argc, "deny", &idx))
548 type = AS_FILTER_DENY;
549 else {
550 vty_out(vty, "filter type must be [permit|deny]\n");
551 return CMD_WARNING_CONFIG_FAILED;
552 }
553
554 /* Compile AS path. */
555 argv_find(argv, argc, "LINE", &idx);
556 regstr = argv_concat(argv, argc, idx);
557
558 if (!config_bgp_aspath_validate(regstr)) {
559 vty_out(vty, "Invalid character in as-path access-list %s\n",
560 regstr);
561 return CMD_WARNING_CONFIG_FAILED;
562 }
563
564 regex = bgp_regcomp(regstr);
565 if (!regex) {
566 vty_out(vty, "can't compile regexp %s\n", regstr);
567 XFREE(MTYPE_TMP, regstr);
568 return CMD_WARNING_CONFIG_FAILED;
569 }
570
571 /* Lookup asfilter. */
572 asfilter = as_filter_lookup(aslist, regstr, type);
573
574 bgp_regex_free(regex);
575
576 if (asfilter == NULL) {
577 vty_out(vty, "Regex entered %s does not exist\n", regstr);
578 XFREE(MTYPE_TMP, regstr);
579 return CMD_WARNING_CONFIG_FAILED;
580 }
581
582 XFREE(MTYPE_TMP, regstr);
583
584 as_list_filter_delete(aslist, asfilter);
585
586 return CMD_SUCCESS;
587 }
588
589 DEFUN (no_as_path_all,
590 no_bgp_as_path_all_cmd,
591 "no bgp as-path access-list AS_PATH_FILTER_NAME",
592 NO_STR
593 BGP_STR
594 "BGP autonomous system path filter\n"
595 "Specify an access list name\n"
596 "Regular expression access list name\n")
597 {
598 int idx_word = 4;
599 struct as_list *aslist;
600
601 aslist = as_list_lookup(argv[idx_word]->arg);
602 if (aslist == NULL) {
603 vty_out(vty, "bgp as-path access-list %s doesn't exist\n",
604 argv[idx_word]->arg);
605 return CMD_WARNING_CONFIG_FAILED;
606 }
607
608 as_list_delete(aslist);
609
610 /* Run hook function. */
611 if (as_list_master.delete_hook)
612 (*as_list_master.delete_hook)(argv[idx_word]->arg);
613
614 return CMD_SUCCESS;
615 }
616
617 static void as_list_show(struct vty *vty, struct as_list *aslist,
618 json_object *json)
619 {
620 struct as_filter *asfilter;
621 json_object *json_aslist = NULL;
622
623 if (json) {
624 json_aslist = json_object_new_array();
625 json_object_object_add(json, aslist->name, json_aslist);
626 } else
627 vty_out(vty, "AS path access list %s\n", aslist->name);
628
629 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
630 if (json) {
631 json_object *json_asfilter = json_object_new_object();
632
633 json_object_int_add(json_asfilter, "sequenceNumber",
634 asfilter->seq);
635 json_object_string_add(json_asfilter, "type",
636 filter_type_str(asfilter->type));
637 json_object_string_add(json_asfilter, "regExp",
638 asfilter->reg_str);
639
640 json_object_array_add(json_aslist, json_asfilter);
641 } else
642 vty_out(vty, " %s %s\n",
643 filter_type_str(asfilter->type),
644 asfilter->reg_str);
645 }
646 }
647
648 static void as_list_show_all(struct vty *vty, json_object *json)
649 {
650 struct as_list *aslist;
651
652 for (aslist = as_list_master.str.head; aslist; aslist = aslist->next)
653 as_list_show(vty, aslist, json);
654 }
655
656 DEFUN (show_as_path_access_list,
657 show_bgp_as_path_access_list_cmd,
658 "show bgp as-path-access-list AS_PATH_FILTER_NAME [json]",
659 SHOW_STR
660 BGP_STR
661 "List AS path access lists\n"
662 "AS path access list name\n"
663 JSON_STR)
664 {
665 int idx_word = 3;
666 struct as_list *aslist;
667 bool uj = use_json(argc, argv);
668 json_object *json = NULL;
669
670 if (uj)
671 json = json_object_new_object();
672
673 aslist = as_list_lookup(argv[idx_word]->arg);
674 if (aslist)
675 as_list_show(vty, aslist, json);
676
677 if (uj)
678 vty_json(vty, json);
679
680 return CMD_SUCCESS;
681 }
682
683 ALIAS (show_as_path_access_list,
684 show_ip_as_path_access_list_cmd,
685 "show ip as-path-access-list AS_PATH_FILTER_NAME [json]",
686 SHOW_STR
687 IP_STR
688 "List AS path access lists\n"
689 "AS path access list name\n"
690 JSON_STR)
691
692 DEFUN (show_as_path_access_list_all,
693 show_bgp_as_path_access_list_all_cmd,
694 "show bgp as-path-access-list [json]",
695 SHOW_STR
696 BGP_STR
697 "List AS path access lists\n"
698 JSON_STR)
699 {
700 bool uj = use_json(argc, argv);
701 json_object *json = NULL;
702
703 if (uj)
704 json = json_object_new_object();
705
706 as_list_show_all(vty, json);
707
708 if (uj)
709 vty_json(vty, json);
710
711 return CMD_SUCCESS;
712 }
713
714 ALIAS (show_as_path_access_list_all,
715 show_ip_as_path_access_list_all_cmd,
716 "show ip as-path-access-list [json]",
717 SHOW_STR
718 IP_STR
719 "List AS path access lists\n"
720 JSON_STR)
721
722 static int config_write_as_list(struct vty *vty)
723 {
724 struct as_list *aslist;
725 struct as_filter *asfilter;
726 int write = 0;
727
728 for (aslist = as_list_master.str.head; aslist; aslist = aslist->next)
729 for (asfilter = aslist->head; asfilter;
730 asfilter = asfilter->next) {
731 vty_out(vty,
732 "bgp as-path access-list %s seq %" PRId64
733 " %s %s\n",
734 aslist->name, asfilter->seq,
735 filter_type_str(asfilter->type),
736 asfilter->reg_str);
737 write++;
738 }
739 return write;
740 }
741
742 static int config_write_as_list(struct vty *vty);
743 static struct cmd_node as_list_node = {
744 .name = "as list",
745 .node = AS_LIST_NODE,
746 .prompt = "",
747 .config_write = config_write_as_list,
748 };
749
750 static void bgp_aspath_filter_cmd_completion(vector comps,
751 struct cmd_token *token)
752 {
753 struct as_list *aslist;
754
755 for (aslist = as_list_master.str.head; aslist; aslist = aslist->next)
756 vector_set(comps, XSTRDUP(MTYPE_COMPLETION, aslist->name));
757 }
758
759 static const struct cmd_variable_handler aspath_filter_handlers[] = {
760 {.tokenname = "AS_PATH_FILTER_NAME",
761 .completions = bgp_aspath_filter_cmd_completion},
762 {.completions = NULL}};
763
764 /* Register functions. */
765 void bgp_filter_init(void)
766 {
767 install_node(&as_list_node);
768
769 install_element(CONFIG_NODE, &bgp_as_path_cmd);
770 install_element(CONFIG_NODE, &no_bgp_as_path_cmd);
771 install_element(CONFIG_NODE, &no_bgp_as_path_all_cmd);
772
773 install_element(VIEW_NODE, &show_bgp_as_path_access_list_cmd);
774 install_element(VIEW_NODE, &show_ip_as_path_access_list_cmd);
775 install_element(VIEW_NODE, &show_bgp_as_path_access_list_all_cmd);
776 install_element(VIEW_NODE, &show_ip_as_path_access_list_all_cmd);
777
778 cmd_variable_handler_register(aspath_filter_handlers);
779 }
780
781 void bgp_filter_reset(void)
782 {
783 struct as_list *aslist;
784 struct as_list *next;
785
786 for (aslist = as_list_master.str.head; aslist; aslist = next) {
787 next = aslist->next;
788 as_list_delete(aslist);
789 }
790
791 assert(as_list_master.str.head == NULL);
792 assert(as_list_master.str.tail == NULL);
793 }