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