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