]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_filter.c
bgpd: Fix for large AS paths which are split into segments
[mirror_frr.git] / bgpd / bgp_filter.c
CommitLineData
718e3744 1/* AS path filter list.
896014f4
DL
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 */
718e3744 20
21#include <zebra.h>
22
23#include "command.h"
24#include "log.h"
25#include "memory.h"
26#include "buffer.h"
3f9c7369 27#include "queue.h"
039f3a34 28#include "filter.h"
718e3744 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. */
d62a17ae 36struct as_list_list {
37 struct as_list *head;
38 struct as_list *tail;
718e3744 39};
40
41/* AS path filter master. */
d62a17ae 42struct as_list_master {
43 /* List of access_list which name is number. */
44 struct as_list_list num;
718e3744 45
d62a17ae 46 /* List of access_list which name is string. */
47 struct as_list_list str;
718e3744 48
d62a17ae 49 /* Hook function which is executed when new access_list is added. */
50 void (*add_hook)(char *);
718e3744 51
d62a17ae 52 /* Hook function which is executed when access_list is deleted. */
53 void (*delete_hook)(const char *);
718e3744 54};
55
56/* Element of AS path filter. */
d62a17ae 57struct as_filter {
58 struct as_filter *next;
59 struct as_filter *prev;
718e3744 60
d62a17ae 61 enum as_filter_type type;
718e3744 62
d62a17ae 63 regex_t *reg;
64 char *reg_str;
718e3744 65};
66
718e3744 67/* AS path filter list. */
d62a17ae 68struct as_list {
69 char *name;
718e3744 70
d62a17ae 71 enum access_type type;
718e3744 72
d62a17ae 73 struct as_list *next;
74 struct as_list *prev;
718e3744 75
d62a17ae 76 struct as_filter *head;
77 struct as_filter *tail;
718e3744 78};
6b0655a2 79
718e3744 80/* ip as-path access-list 10 permit AS1. */
81
d62a17ae 82static struct as_list_master as_list_master = {{NULL, NULL},
83 {NULL, NULL},
84 NULL,
85 NULL};
718e3744 86
87/* Allocate new AS filter. */
d62a17ae 88static struct as_filter *as_filter_new(void)
718e3744 89{
d62a17ae 90 return XCALLOC(MTYPE_AS_FILTER, sizeof(struct as_filter));
718e3744 91}
92
93/* Free allocated AS filter. */
d62a17ae 94static void as_filter_free(struct as_filter *asfilter)
718e3744 95{
d62a17ae 96 if (asfilter->reg)
97 bgp_regex_free(asfilter->reg);
98 if (asfilter->reg_str)
99 XFREE(MTYPE_AS_FILTER_STR, asfilter->reg_str);
100 XFREE(MTYPE_AS_FILTER, asfilter);
718e3744 101}
102
103/* Make new AS filter. */
d62a17ae 104static struct as_filter *as_filter_make(regex_t *reg, const char *reg_str,
105 enum as_filter_type type)
718e3744 106{
d62a17ae 107 struct as_filter *asfilter;
718e3744 108
d62a17ae 109 asfilter = as_filter_new();
110 asfilter->reg = reg;
111 asfilter->type = type;
112 asfilter->reg_str = XSTRDUP(MTYPE_AS_FILTER_STR, reg_str);
718e3744 113
d62a17ae 114 return asfilter;
718e3744 115}
116
d62a17ae 117static struct as_filter *as_filter_lookup(struct as_list *aslist,
118 const char *reg_str,
119 enum as_filter_type type)
718e3744 120{
d62a17ae 121 struct as_filter *asfilter;
718e3744 122
d62a17ae 123 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next)
124 if (strcmp(reg_str, asfilter->reg_str) == 0)
125 return asfilter;
126 return NULL;
718e3744 127}
128
d62a17ae 129static void as_list_filter_add(struct as_list *aslist,
130 struct as_filter *asfilter)
718e3744 131{
d62a17ae 132 asfilter->next = NULL;
133 asfilter->prev = aslist->tail;
718e3744 134
d62a17ae 135 if (aslist->tail)
136 aslist->tail->next = asfilter;
137 else
138 aslist->head = asfilter;
139 aslist->tail = asfilter;
518f0eb1 140
d62a17ae 141 /* Run hook function. */
142 if (as_list_master.add_hook)
143 (*as_list_master.add_hook)(aslist->name);
718e3744 144}
145
146/* Lookup as_list from list of as_list by name. */
d62a17ae 147struct as_list *as_list_lookup(const char *name)
718e3744 148{
d62a17ae 149 struct as_list *aslist;
718e3744 150
d62a17ae 151 if (name == NULL)
152 return NULL;
718e3744 153
d62a17ae 154 for (aslist = as_list_master.num.head; aslist; aslist = aslist->next)
155 if (strcmp(aslist->name, name) == 0)
156 return aslist;
718e3744 157
d62a17ae 158 for (aslist = as_list_master.str.head; aslist; aslist = aslist->next)
159 if (strcmp(aslist->name, name) == 0)
160 return aslist;
718e3744 161
d62a17ae 162 return NULL;
718e3744 163}
164
d62a17ae 165static struct as_list *as_list_new(void)
718e3744 166{
d62a17ae 167 return XCALLOC(MTYPE_AS_LIST, sizeof(struct as_list));
718e3744 168}
169
d62a17ae 170static void as_list_free(struct as_list *aslist)
718e3744 171{
d62a17ae 172 if (aslist->name) {
173 XFREE(MTYPE_AS_STR, aslist->name);
174 aslist->name = NULL;
175 }
176 XFREE(MTYPE_AS_LIST, aslist);
718e3744 177}
178
179/* Insert new AS list to list of as_list. Each as_list is sorted by
180 the name. */
d62a17ae 181static struct as_list *as_list_insert(const char *name)
182{
183 size_t i;
184 long number;
185 struct as_list *aslist;
186 struct as_list *point;
187 struct as_list_list *list;
188
189 /* Allocate new access_list and copy given name. */
190 aslist = as_list_new();
191 aslist->name = XSTRDUP(MTYPE_AS_STR, name);
192 assert(aslist->name);
193
194 /* If name is made by all digit character. We treat it as
195 number. */
196 for (number = 0, i = 0; i < strlen(name); i++) {
197 if (isdigit((int)name[i]))
198 number = (number * 10) + (name[i] - '0');
199 else
200 break;
201 }
202
203 /* In case of name is all digit character */
204 if (i == strlen(name)) {
205 aslist->type = ACCESS_TYPE_NUMBER;
206
207 /* Set access_list to number list. */
208 list = &as_list_master.num;
209
210 for (point = list->head; point; point = point->next)
211 if (atol(point->name) >= number)
212 break;
213 } else {
214 aslist->type = ACCESS_TYPE_STRING;
215
216 /* Set access_list to string list. */
217 list = &as_list_master.str;
218
219 /* Set point to insertion point. */
220 for (point = list->head; point; point = point->next)
221 if (strcmp(point->name, name) >= 0)
222 break;
223 }
224
225 /* In case of this is the first element of master. */
226 if (list->head == NULL) {
227 list->head = list->tail = aslist;
228 return aslist;
229 }
230
231 /* In case of insertion is made at the tail of access_list. */
232 if (point == NULL) {
233 aslist->prev = list->tail;
234 list->tail->next = aslist;
235 list->tail = aslist;
236 return aslist;
237 }
238
239 /* In case of insertion is made at the head of access_list. */
240 if (point == list->head) {
241 aslist->next = list->head;
242 list->head->prev = aslist;
243 list->head = aslist;
244 return aslist;
245 }
246
247 /* Insertion is made at middle of the access_list. */
248 aslist->next = point;
249 aslist->prev = point->prev;
250
251 if (point->prev)
252 point->prev->next = aslist;
253 point->prev = aslist;
254
255 return aslist;
718e3744 256}
257
d62a17ae 258static struct as_list *as_list_get(const char *name)
718e3744 259{
d62a17ae 260 struct as_list *aslist;
718e3744 261
d62a17ae 262 aslist = as_list_lookup(name);
263 if (aslist == NULL)
264 aslist = as_list_insert(name);
718e3744 265
d62a17ae 266 return aslist;
718e3744 267}
268
d62a17ae 269static const char *filter_type_str(enum as_filter_type type)
718e3744 270{
d62a17ae 271 switch (type) {
272 case AS_FILTER_PERMIT:
273 return "permit";
274 case AS_FILTER_DENY:
275 return "deny";
276 default:
277 return "";
278 }
718e3744 279}
280
d62a17ae 281static void as_list_delete(struct as_list *aslist)
718e3744 282{
d62a17ae 283 struct as_list_list *list;
284 struct as_filter *filter, *next;
285
286 for (filter = aslist->head; filter; filter = next) {
287 next = filter->next;
288 as_filter_free(filter);
289 }
290
291 if (aslist->type == ACCESS_TYPE_NUMBER)
292 list = &as_list_master.num;
293 else
294 list = &as_list_master.str;
295
296 if (aslist->next)
297 aslist->next->prev = aslist->prev;
298 else
299 list->tail = aslist->prev;
300
301 if (aslist->prev)
302 aslist->prev->next = aslist->next;
303 else
304 list->head = aslist->next;
305
306 as_list_free(aslist);
718e3744 307}
308
d62a17ae 309static int as_list_empty(struct as_list *aslist)
718e3744 310{
d62a17ae 311 if (aslist->head == NULL && aslist->tail == NULL)
312 return 1;
313 else
314 return 0;
718e3744 315}
316
d62a17ae 317static void as_list_filter_delete(struct as_list *aslist,
318 struct as_filter *asfilter)
718e3744 319{
d62a17ae 320 char *name = XSTRDUP(MTYPE_AS_STR, aslist->name);
518f0eb1 321
d62a17ae 322 if (asfilter->next)
323 asfilter->next->prev = asfilter->prev;
324 else
325 aslist->tail = asfilter->prev;
718e3744 326
d62a17ae 327 if (asfilter->prev)
328 asfilter->prev->next = asfilter->next;
329 else
330 aslist->head = asfilter->next;
718e3744 331
d62a17ae 332 as_filter_free(asfilter);
718e3744 333
d62a17ae 334 /* If access_list becomes empty delete it from access_master. */
335 if (as_list_empty(aslist))
336 as_list_delete(aslist);
718e3744 337
d62a17ae 338 /* Run hook function. */
339 if (as_list_master.delete_hook)
340 (*as_list_master.delete_hook)(name);
341 if (name)
342 XFREE(MTYPE_AS_STR, name);
718e3744 343}
6b0655a2 344
d62a17ae 345static int as_filter_match(struct as_filter *asfilter, struct aspath *aspath)
718e3744 346{
d62a17ae 347 if (bgp_regexec(asfilter->reg, aspath) != REG_NOMATCH)
348 return 1;
349 return 0;
718e3744 350}
351
352/* Apply AS path filter to AS. */
d62a17ae 353enum as_filter_type as_list_apply(struct as_list *aslist, void *object)
718e3744 354{
d62a17ae 355 struct as_filter *asfilter;
356 struct aspath *aspath;
718e3744 357
d62a17ae 358 aspath = (struct aspath *)object;
718e3744 359
d62a17ae 360 if (aslist == NULL)
361 return AS_FILTER_DENY;
718e3744 362
d62a17ae 363 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
364 if (as_filter_match(asfilter, aspath))
365 return asfilter->type;
366 }
367 return AS_FILTER_DENY;
718e3744 368}
369
370/* Add hook function. */
d62a17ae 371void as_list_add_hook(void (*func)(char *))
718e3744 372{
d62a17ae 373 as_list_master.add_hook = func;
718e3744 374}
375
376/* Delete hook function. */
d62a17ae 377void as_list_delete_hook(void (*func)(const char *))
718e3744 378{
d62a17ae 379 as_list_master.delete_hook = func;
718e3744 380}
6b0655a2 381
d62a17ae 382static int as_list_dup_check(struct as_list *aslist, struct as_filter *new)
718e3744 383{
d62a17ae 384 struct as_filter *asfilter;
385
386 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
387 if (asfilter->type == new->type
388 && strcmp(asfilter->reg_str, new->reg_str) == 0)
389 return 1;
390 }
391 return 0;
718e3744 392}
393
4dcadbef
DW
394DEFUN (ip_as_path,
395 ip_as_path_cmd,
e961923c 396 "ip as-path access-list WORD <deny|permit> LINE...",
718e3744 397 IP_STR
398 "BGP autonomous system path filter\n"
399 "Specify an access list name\n"
400 "Regular expression access list name\n"
401 "Specify packets to reject\n"
402 "Specify packets to forward\n"
403 "A regular-expression to match the BGP AS paths\n")
404{
d62a17ae 405 int idx = 0;
406 enum as_filter_type type;
407 struct as_filter *asfilter;
408 struct as_list *aslist;
409 regex_t *regex;
410 char *regstr;
411
412 /* Retrieve access list name */
e991f75c
VJ
413 argv_find(argv, argc, "WORD", &idx);
414 char *alname = argv[idx]->arg;
d62a17ae 415
416 /* Check the filter type. */
417 type = argv_find(argv, argc, "deny", &idx) ? AS_FILTER_DENY
418 : AS_FILTER_PERMIT;
419
420 /* Check AS path regex. */
421 argv_find(argv, argc, "LINE", &idx);
422 regstr = argv_concat(argv, argc, idx);
423
424 regex = bgp_regcomp(regstr);
425 if (!regex) {
426 vty_out(vty, "can't compile regexp %s\n", regstr);
427 XFREE(MTYPE_TMP, regstr);
428 return CMD_WARNING_CONFIG_FAILED;
429 }
430
431 asfilter = as_filter_make(regex, regstr, type);
432
433 XFREE(MTYPE_TMP, regstr);
434
435 /* Install new filter to the access_list. */
436 aslist = as_list_get(alname);
437
438 /* Duplicate insertion check. */;
439 if (as_list_dup_check(aslist, asfilter))
440 as_filter_free(asfilter);
441 else
442 as_list_filter_add(aslist, asfilter);
443
444 return CMD_SUCCESS;
718e3744 445}
446
447DEFUN (no_ip_as_path,
448 no_ip_as_path_cmd,
e961923c 449 "no ip as-path access-list WORD <deny|permit> LINE...",
718e3744 450 NO_STR
451 IP_STR
452 "BGP autonomous system path filter\n"
453 "Specify an access list name\n"
454 "Regular expression access list name\n"
455 "Specify packets to reject\n"
456 "Specify packets to forward\n"
457 "A regular-expression to match the BGP AS paths\n")
458{
d62a17ae 459 int idx = 0;
460 enum as_filter_type type;
461 struct as_filter *asfilter;
462 struct as_list *aslist;
463 char *regstr;
464 regex_t *regex;
465
466 char *aslistname =
467 argv_find(argv, argc, "WORD", &idx) ? argv[idx]->arg : NULL;
468
469 /* Lookup AS list from AS path list. */
470 aslist = as_list_lookup(aslistname);
471 if (aslist == NULL) {
472 vty_out(vty, "ip as-path access-list %s doesn't exist\n",
473 aslistname);
474 return CMD_WARNING_CONFIG_FAILED;
475 }
476
477 /* Check the filter type. */
478 if (argv_find(argv, argc, "permit", &idx))
479 type = AS_FILTER_PERMIT;
480 else if (argv_find(argv, argc, "deny", &idx))
481 type = AS_FILTER_DENY;
482 else {
483 vty_out(vty, "filter type must be [permit|deny]\n");
484 return CMD_WARNING_CONFIG_FAILED;
485 }
486
487 /* Compile AS path. */
488 argv_find(argv, argc, "LINE", &idx);
489 regstr = argv_concat(argv, argc, idx);
490
491 regex = bgp_regcomp(regstr);
492 if (!regex) {
493 vty_out(vty, "can't compile regexp %s\n", regstr);
494 XFREE(MTYPE_TMP, regstr);
495 return CMD_WARNING_CONFIG_FAILED;
496 }
497
498 /* Lookup asfilter. */
499 asfilter = as_filter_lookup(aslist, regstr, type);
500
501 XFREE(MTYPE_TMP, regstr);
502 bgp_regex_free(regex);
503
504 if (asfilter == NULL) {
505 vty_out(vty, "\n");
506 return CMD_WARNING_CONFIG_FAILED;
507 }
508
509 as_list_filter_delete(aslist, asfilter);
510
511 return CMD_SUCCESS;
718e3744 512}
513
514DEFUN (no_ip_as_path_all,
515 no_ip_as_path_all_cmd,
516 "no ip as-path access-list WORD",
517 NO_STR
518 IP_STR
519 "BGP autonomous system path filter\n"
520 "Specify an access list name\n"
521 "Regular expression access list name\n")
522{
d62a17ae 523 int idx_word = 4;
524 struct as_list *aslist;
718e3744 525
d62a17ae 526 aslist = as_list_lookup(argv[idx_word]->arg);
527 if (aslist == NULL) {
528 vty_out(vty, "ip as-path access-list %s doesn't exist\n",
529 argv[idx_word]->arg);
530 return CMD_WARNING_CONFIG_FAILED;
531 }
718e3744 532
d62a17ae 533 as_list_delete(aslist);
718e3744 534
d62a17ae 535 /* Run hook function. */
536 if (as_list_master.delete_hook)
537 (*as_list_master.delete_hook)(argv[idx_word]->arg);
e0701b79 538
d62a17ae 539 return CMD_SUCCESS;
718e3744 540}
541
d62a17ae 542static void as_list_show(struct vty *vty, struct as_list *aslist)
4f991ef0 543{
d62a17ae 544 struct as_filter *asfilter;
4f991ef0 545
d62a17ae 546 vty_out(vty, "AS path access list %s\n", aslist->name);
4f991ef0 547
d62a17ae 548 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
549 vty_out(vty, " %s %s\n", filter_type_str(asfilter->type),
550 asfilter->reg_str);
551 }
4f991ef0 552}
553
d62a17ae 554static void as_list_show_all(struct vty *vty)
4f991ef0 555{
d62a17ae 556 struct as_list *aslist;
557 struct as_filter *asfilter;
4f991ef0 558
d62a17ae 559 for (aslist = as_list_master.num.head; aslist; aslist = aslist->next) {
560 vty_out(vty, "AS path access list %s\n", aslist->name);
4f991ef0 561
d62a17ae 562 for (asfilter = aslist->head; asfilter;
563 asfilter = asfilter->next) {
564 vty_out(vty, " %s %s\n",
565 filter_type_str(asfilter->type),
566 asfilter->reg_str);
567 }
4f991ef0 568 }
4f991ef0 569
d62a17ae 570 for (aslist = as_list_master.str.head; aslist; aslist = aslist->next) {
571 vty_out(vty, "AS path access list %s\n", aslist->name);
4f991ef0 572
d62a17ae 573 for (asfilter = aslist->head; asfilter;
574 asfilter = asfilter->next) {
575 vty_out(vty, " %s %s\n",
576 filter_type_str(asfilter->type),
577 asfilter->reg_str);
578 }
4f991ef0 579 }
4f991ef0 580}
581
582DEFUN (show_ip_as_path_access_list,
583 show_ip_as_path_access_list_cmd,
584 "show ip as-path-access-list WORD",
585 SHOW_STR
586 IP_STR
587 "List AS path access lists\n"
588 "AS path access list name\n")
589{
d62a17ae 590 int idx_word = 3;
591 struct as_list *aslist;
4f991ef0 592
d62a17ae 593 aslist = as_list_lookup(argv[idx_word]->arg);
594 if (aslist)
595 as_list_show(vty, aslist);
4f991ef0 596
d62a17ae 597 return CMD_SUCCESS;
4f991ef0 598}
599
600DEFUN (show_ip_as_path_access_list_all,
601 show_ip_as_path_access_list_all_cmd,
602 "show ip as-path-access-list",
603 SHOW_STR
604 IP_STR
605 "List AS path access lists\n")
606{
d62a17ae 607 as_list_show_all(vty);
608 return CMD_SUCCESS;
4f991ef0 609}
610
d62a17ae 611static int config_write_as_list(struct vty *vty)
612{
613 struct as_list *aslist;
614 struct as_filter *asfilter;
615 int write = 0;
616
617 for (aslist = as_list_master.num.head; aslist; aslist = aslist->next)
618 for (asfilter = aslist->head; asfilter;
619 asfilter = asfilter->next) {
620 vty_out(vty, "ip as-path access-list %s %s %s\n",
621 aslist->name, filter_type_str(asfilter->type),
622 asfilter->reg_str);
623 write++;
624 }
625
626 for (aslist = as_list_master.str.head; aslist; aslist = aslist->next)
627 for (asfilter = aslist->head; asfilter;
628 asfilter = asfilter->next) {
629 vty_out(vty, "ip as-path access-list %s %s %s\n",
630 aslist->name, filter_type_str(asfilter->type),
631 asfilter->reg_str);
632 write++;
633 }
634 return write;
718e3744 635}
636
d62a17ae 637static struct cmd_node as_list_node = {AS_LIST_NODE, "", 1};
718e3744 638
639/* Register functions. */
d62a17ae 640void bgp_filter_init(void)
718e3744 641{
d62a17ae 642 install_node(&as_list_node, config_write_as_list);
718e3744 643
d62a17ae 644 install_element(CONFIG_NODE, &ip_as_path_cmd);
645 install_element(CONFIG_NODE, &no_ip_as_path_cmd);
646 install_element(CONFIG_NODE, &no_ip_as_path_all_cmd);
4f991ef0 647
d62a17ae 648 install_element(VIEW_NODE, &show_ip_as_path_access_list_cmd);
649 install_element(VIEW_NODE, &show_ip_as_path_access_list_all_cmd);
718e3744 650}
228da428 651
d62a17ae 652void bgp_filter_reset(void)
228da428 653{
d62a17ae 654 struct as_list *aslist;
655 struct as_list *next;
656
657 for (aslist = as_list_master.num.head; aslist; aslist = next) {
658 next = aslist->next;
659 as_list_delete(aslist);
660 }
661
662 for (aslist = as_list_master.str.head; aslist; aslist = next) {
663 next = aslist->next;
664 as_list_delete(aslist);
665 }
666
667 assert(as_list_master.num.head == NULL);
668 assert(as_list_master.num.tail == NULL);
669
670 assert(as_list_master.str.head == NULL);
671 assert(as_list_master.str.tail == NULL);
228da428 672}