]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_filter.c
bgpd: really remove the `no ip as-path...` command
[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 static void as_list_show(struct vty *vty, struct as_list *aslist)
552 {
553 struct as_filter *asfilter;
554
555 vty_out(vty, "AS path access list %s\n", aslist->name);
556
557 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
558 vty_out(vty, " %s %s\n", filter_type_str(asfilter->type),
559 asfilter->reg_str);
560 }
561 }
562
563 static void as_list_show_all(struct vty *vty)
564 {
565 struct as_list *aslist;
566 struct as_filter *asfilter;
567
568 for (aslist = as_list_master.num.head; aslist; aslist = aslist->next) {
569 vty_out(vty, "AS path access list %s\n", aslist->name);
570
571 for (asfilter = aslist->head; asfilter;
572 asfilter = asfilter->next) {
573 vty_out(vty, " %s %s\n",
574 filter_type_str(asfilter->type),
575 asfilter->reg_str);
576 }
577 }
578
579 for (aslist = as_list_master.str.head; aslist; aslist = aslist->next) {
580 vty_out(vty, "AS path access list %s\n", aslist->name);
581
582 for (asfilter = aslist->head; asfilter;
583 asfilter = asfilter->next) {
584 vty_out(vty, " %s %s\n",
585 filter_type_str(asfilter->type),
586 asfilter->reg_str);
587 }
588 }
589 }
590
591 DEFUN (show_as_path_access_list,
592 show_bgp_as_path_access_list_cmd,
593 "show bgp as-path-access-list WORD",
594 SHOW_STR
595 BGP_STR
596 "List AS path access lists\n"
597 "AS path access list name\n")
598 {
599 int idx_word = 3;
600 struct as_list *aslist;
601
602 aslist = as_list_lookup(argv[idx_word]->arg);
603 if (aslist)
604 as_list_show(vty, aslist);
605
606 return CMD_SUCCESS;
607 }
608
609 ALIAS (show_as_path_access_list,
610 show_ip_as_path_access_list_cmd,
611 "show ip as-path-access-list WORD",
612 SHOW_STR
613 IP_STR
614 "List AS path access lists\n"
615 "AS path access list name\n")
616
617 DEFUN (show_as_path_access_list_all,
618 show_bgp_as_path_access_list_all_cmd,
619 "show bgp as-path-access-list",
620 SHOW_STR
621 BGP_STR
622 "List AS path access lists\n")
623 {
624 as_list_show_all(vty);
625 return CMD_SUCCESS;
626 }
627
628 ALIAS (show_as_path_access_list_all,
629 show_ip_as_path_access_list_all_cmd,
630 "show ip as-path-access-list",
631 SHOW_STR
632 IP_STR
633 "List AS path access lists\n")
634
635 static int config_write_as_list(struct vty *vty)
636 {
637 struct as_list *aslist;
638 struct as_filter *asfilter;
639 int write = 0;
640
641 for (aslist = as_list_master.num.head; aslist; aslist = aslist->next)
642 for (asfilter = aslist->head; asfilter;
643 asfilter = asfilter->next) {
644 vty_out(vty, "bgp as-path access-list %s %s %s\n",
645 aslist->name, filter_type_str(asfilter->type),
646 asfilter->reg_str);
647 write++;
648 }
649
650 for (aslist = as_list_master.str.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 return write;
659 }
660
661 static int config_write_as_list(struct vty *vty);
662 static struct cmd_node as_list_node = {
663 .name = "as list",
664 .node = AS_LIST_NODE,
665 .prompt = "",
666 .config_write = config_write_as_list,
667 };
668
669 /* Register functions. */
670 void bgp_filter_init(void)
671 {
672 install_node(&as_list_node);
673
674 install_element(CONFIG_NODE, &bgp_as_path_cmd);
675 install_element(CONFIG_NODE, &no_bgp_as_path_cmd);
676 install_element(CONFIG_NODE, &no_bgp_as_path_all_cmd);
677
678 install_element(VIEW_NODE, &show_bgp_as_path_access_list_cmd);
679 install_element(VIEW_NODE, &show_ip_as_path_access_list_cmd);
680 install_element(VIEW_NODE, &show_bgp_as_path_access_list_all_cmd);
681 install_element(VIEW_NODE, &show_ip_as_path_access_list_all_cmd);
682 }
683
684 void bgp_filter_reset(void)
685 {
686 struct as_list *aslist;
687 struct as_list *next;
688
689 for (aslist = as_list_master.num.head; aslist; aslist = next) {
690 next = aslist->next;
691 as_list_delete(aslist);
692 }
693
694 for (aslist = as_list_master.str.head; aslist; aslist = next) {
695 next = aslist->next;
696 as_list_delete(aslist);
697 }
698
699 assert(as_list_master.num.head == NULL);
700 assert(as_list_master.num.tail == NULL);
701
702 assert(as_list_master.str.head == NULL);
703 assert(as_list_master.str.tail == NULL);
704 }