1/* AS path filter list.
2   Copyright (C) 1999 Kunihiro Ishiguro
3
4This file is part of GNU Zebra.
5
6GNU Zebra is free software; you can redistribute it and/or modify it
7under the terms of the GNU General Public License as published by the
8Free Software Foundation; either version 2, or (at your option) any
9later version.
10
11GNU Zebra is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Zebra; see the file COPYING.  If not, write to the Free
18Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
1902111-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. */
34struct as_list_list
35{
36  struct as_list *head;
37  struct as_list *tail;
38};
39
40/* AS path filter master. */
41struct 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) ();
51
52  /* Hook function which is executed when access_list is deleted. */
53  void (*delete_hook) ();
54};
55
56/* Element of AS path filter. */
57struct 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
68enum as_list_type
69{
70  ACCESS_TYPE_STRING,
71  ACCESS_TYPE_NUMBER
72};
73
74/* AS path filter list. */
75struct 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
88/* ip as-path access-list 10 permit AS1. */
89
90static 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. */
99struct as_filter *
100as_filter_new ()
101{
102  struct as_filter *new;
103
104  new = XMALLOC (MTYPE_AS_FILTER, sizeof (struct as_filter));
105  memset (new, 0, sizeof (struct as_filter));
106  return new;
107}
108
109/* Free allocated AS filter. */
110void
111as_filter_free (struct as_filter *asfilter)
112{
113  if (asfilter->reg)
114    bgp_regex_free (asfilter->reg);
115  if (asfilter->reg_str)
116    XFREE (MTYPE_AS_FILTER_STR, asfilter->reg_str);
117  XFREE (MTYPE_AS_FILTER, asfilter);
118}
119
120/* Make new AS filter. */
121struct as_filter *
122as_filter_make (regex_t *reg, char *reg_str, enum as_filter_type type)
123{
124  struct as_filter *asfilter;
125
126  asfilter = as_filter_new ();
127  asfilter->reg = reg;
128  asfilter->type = type;
129  asfilter->reg_str = XSTRDUP (MTYPE_AS_FILTER_STR, reg_str);
130
131  return asfilter;
132}
133
134struct as_filter *
135as_filter_lookup (struct as_list *aslist, char *reg_str,
136		  enum as_filter_type type)
137{
138  struct as_filter *asfilter;
139
140  for (asfilter = aslist->head; asfilter; asfilter = asfilter->next)
141    if (strcmp (reg_str, asfilter->reg_str) == 0)
142      return asfilter;
143  return NULL;
144}
145
146void
147as_list_filter_add (struct as_list *aslist, struct as_filter *asfilter)
148{
149  asfilter->next = NULL;
150  asfilter->prev = aslist->tail;
151
152  if (aslist->tail)
153    aslist->tail->next = asfilter;
154  else
155    aslist->head = asfilter;
156  aslist->tail = asfilter;
157}
158
159/* Lookup as_list from list of as_list by name. */
160struct as_list *
161as_list_lookup (char *name)
162{
163  struct as_list *aslist;
164
165  if (name == NULL)
166    return NULL;
167
168  for (aslist = as_list_master.num.head; aslist; aslist = aslist->next)
169    if (strcmp (aslist->name, name) == 0)
170      return aslist;
171
172  for (aslist = as_list_master.str.head; aslist; aslist = aslist->next)
173    if (strcmp (aslist->name, name) == 0)
174      return aslist;
175
176  return NULL;
177}
178
179struct as_list *
180as_list_new ()
181{
182  struct as_list *new;
183
184  new = XMALLOC (MTYPE_AS_LIST, sizeof (struct as_list));
185  memset (new, 0, sizeof (struct as_list));
186  return new;
187}
188
189void
190as_list_free (struct as_list *aslist)
191{
192  XFREE (MTYPE_AS_LIST, aslist);
193}
194
195/* Insert new AS list to list of as_list.  Each as_list is sorted by
196   the name. */
197struct as_list *
198as_list_insert (char *name)
199{
200  int i;
201  long number;
202  struct as_list *aslist;
203  struct as_list *point;
204  struct as_list_list *list;
205
206  /* Allocate new access_list and copy given name. */
207  aslist = as_list_new ();
208  aslist->name = strdup (name);
209
210  /* If name is made by all digit character.  We treat it as
211     number. */
212  for (number = 0, i = 0; i < strlen (name); i++)
213    {
214      if (isdigit ((int) name[i]))
215	number = (number * 10) + (name[i] - '0');
216      else
217	break;
218    }
219
220  /* In case of name is all digit character */
221  if (i == strlen (name))
222    {
223      aslist->type = ACCESS_TYPE_NUMBER;
224
225      /* Set access_list to number list. */
226      list = &as_list_master.num;
227
228      for (point = list->head; point; point = point->next)
229	if (atol (point->name) >= number)
230	  break;
231    }
232  else
233    {
234      aslist->type = ACCESS_TYPE_STRING;
235
236      /* Set access_list to string list. */
237      list = &as_list_master.str;
238
239      /* Set point to insertion point. */
240      for (point = list->head; point; point = point->next)
241	if (strcmp (point->name, name) >= 0)
242	  break;
243    }
244
245  /* In case of this is the first element of master. */
246  if (list->head == NULL)
247    {
248      list->head = list->tail = aslist;
249      return aslist;
250    }
251
252  /* In case of insertion is made at the tail of access_list. */
253  if (point == NULL)
254    {
255      aslist->prev = list->tail;
256      list->tail->next = aslist;
257      list->tail = aslist;
258      return aslist;
259    }
260
261  /* In case of insertion is made at the head of access_list. */
262  if (point == list->head)
263    {
264      aslist->next = list->head;
265      list->head->prev = aslist;
266      list->head = aslist;
267      return aslist;
268    }
269
270  /* Insertion is made at middle of the access_list. */
271  aslist->next = point;
272  aslist->prev = point->prev;
273
274  if (point->prev)
275    point->prev->next = aslist;
276  point->prev = aslist;
277
278  return aslist;
279}
280
281struct as_list *
282as_list_get (char *name)
283{
284  struct as_list *aslist;
285
286  aslist = as_list_lookup (name);
287  if (aslist == NULL)
288    {
289      aslist = as_list_insert (name);
290
291      /* Run hook function. */
292      if (as_list_master.add_hook)
293	(*as_list_master.add_hook) ();
294    }
295
296  return aslist;
297}
298
299static char *
300filter_type_str (enum as_filter_type type)
301{
302  switch (type)
303    {
304    case AS_FILTER_PERMIT:
305      return "permit";
306      break;
307    case AS_FILTER_DENY:
308      return "deny";
309      break;
310    default:
311      return "";
312      break;
313    }
314}
315
316void
317as_list_delete (struct as_list *aslist)
318{
319  struct as_list_list *list;
320  struct as_filter *filter, *next;
321
322  for (filter = aslist->head; filter; filter = next)
323    {
324      next = filter->next;
325      as_filter_free (filter);
326    }
327
328  if (aslist->type == ACCESS_TYPE_NUMBER)
329    list = &as_list_master.num;
330  else
331    list = &as_list_master.str;
332
333  if (aslist->next)
334    aslist->next->prev = aslist->prev;
335  else
336    list->tail = aslist->prev;
337
338  if (aslist->prev)
339    aslist->prev->next = aslist->next;
340  else
341    list->head = aslist->next;
342
343  as_list_free (aslist);
344}
345
346static int
347as_list_empty (struct as_list *aslist)
348{
349  if (aslist->head == NULL && aslist->tail == NULL)
350    return 1;
351  else
352    return 0;
353}
354
355void
356as_list_filter_delete (struct as_list *aslist, struct as_filter *asfilter)
357{
358  if (asfilter->next)
359    asfilter->next->prev = asfilter->prev;
360  else
361    aslist->tail = asfilter->prev;
362
363  if (asfilter->prev)
364    asfilter->prev->next = asfilter->next;
365  else
366    aslist->head = asfilter->next;
367
368  as_filter_free (asfilter);
369
370  /* If access_list becomes empty delete it from access_master. */
371  if (as_list_empty (aslist))
372    as_list_delete (aslist);
373
374  /* Run hook function. */
375  if (as_list_master.delete_hook)
376    (*as_list_master.delete_hook) ();
377}
378
379static int
380as_filter_match (struct as_filter *asfilter, struct aspath *aspath)
381{
382  if (bgp_regexec (asfilter->reg, aspath) != REG_NOMATCH)
383    return 1;
384  return 0;
385}
386
387/* Apply AS path filter to AS. */
388enum as_filter_type
389as_list_apply (struct as_list *aslist, void *object)
390{
391  struct as_filter *asfilter;
392  struct aspath *aspath;
393
394  aspath = (struct aspath *) object;
395
396  if (aslist == NULL)
397    return AS_FILTER_DENY;
398
399  for (asfilter = aslist->head; asfilter; asfilter = asfilter->next)
400    {
401      if (as_filter_match (asfilter, aspath))
402	return asfilter->type;
403    }
404  return AS_FILTER_DENY;
405}
406
407/* Add hook function. */
408void
409as_list_add_hook (void (*func) ())
410{
411  as_list_master.add_hook = func;
412}
413
414/* Delete hook function. */
415void
416as_list_delete_hook (void (*func) ())
417{
418  as_list_master.delete_hook = func;
419}
420
421int
422as_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    {
428      if (asfilter->type == new->type
429	  && strcmp (asfilter->reg_str, new->reg_str) == 0)
430	return 1;
431    }
432  return 0;
433}
434
435DEFUN (ip_as_path, ip_as_path_cmd,
436       "ip as-path access-list WORD (deny|permit) .LINE",
437       IP_STR
438       "BGP autonomous system path filter\n"
439       "Specify an access list name\n"
440       "Regular expression access list name\n"
441       "Specify packets to reject\n"
442       "Specify packets to forward\n"
443       "A regular-expression to match the BGP AS paths\n")
444{
445  enum as_filter_type type;
446  struct as_filter *asfilter;
447  struct as_list *aslist;
448  regex_t *regex;
449  struct buffer *b;
450  int i;
451  char *regstr;
452  int first = 0;
453
454  /* Check the filter type. */
455  if (strncmp (argv[1], "p", 1) == 0)
456    type = AS_FILTER_PERMIT;
457  else if (strncmp (argv[1], "d", 1) == 0)
458    type = AS_FILTER_DENY;
459  else
460    {
461      vty_out (vty, "filter type must be [permit|deny]%s", VTY_NEWLINE);
462      return CMD_WARNING;
463    }
464
465  /* Check AS path regex. */
466  b = buffer_new (1024);
467  for (i = 2; i < argc; i++)
468    {
469      if (first)
470	buffer_putc (b, ' ');
471      else
472	first = 1;
473
474      buffer_putstr (b, argv[i]);
475    }
476  buffer_putc (b, '\0');
477
478  regstr = buffer_getstr (b);
479  buffer_free (b);
480
481  regex = bgp_regcomp (regstr);
482  if (!regex)
483    {
484      free (regstr);
485      vty_out (vty, "can't compile regexp %s%s", argv[0],
486	       VTY_NEWLINE);
487      return CMD_WARNING;
488    }
489
490  asfilter = as_filter_make (regex, regstr, type);
491
492  free (regstr);
493
494  /* Install new filter to the access_list. */
495  aslist = as_list_get (argv[0]);
496
497  /* Duplicate insertion check. */;
498  if (as_list_dup_check (aslist, asfilter))
499    as_filter_free (asfilter);
500  else
501    as_list_filter_add (aslist, asfilter);
502
503  return CMD_SUCCESS;
504}
505
506DEFUN (no_ip_as_path,
507       no_ip_as_path_cmd,
508       "no ip as-path access-list WORD (deny|permit) .LINE",
509       NO_STR
510       IP_STR
511       "BGP autonomous system path filter\n"
512       "Specify an access list name\n"
513       "Regular expression access list name\n"
514       "Specify packets to reject\n"
515       "Specify packets to forward\n"
516       "A regular-expression to match the BGP AS paths\n")
517{
518  enum as_filter_type type;
519  struct as_filter *asfilter;
520  struct as_list *aslist;
521  struct buffer *b;
522  int i;
523  int first = 0;
524  char *regstr;
525  regex_t *regex;
526
527  /* Lookup AS list from AS path list. */
528  aslist = as_list_lookup (argv[0]);
529  if (aslist == NULL)
530    {
531      vty_out (vty, "ip as-path access-list %s doesn't exist%s", argv[0],
532	       VTY_NEWLINE);
533      return CMD_WARNING;
534    }
535
536  /* Check the filter type. */
537  if (strncmp (argv[1], "p", 1) == 0)
538    type = AS_FILTER_PERMIT;
539  else if (strncmp (argv[1], "d", 1) == 0)
540    type = AS_FILTER_DENY;
541  else
542    {
543      vty_out (vty, "filter type must be [permit|deny]%s", VTY_NEWLINE);
544      return CMD_WARNING;
545    }
546
547  /* Compile AS path. */
548  b = buffer_new (1024);
549  for (i = 2; i < argc; i++)
550    {
551      if (first)
552	buffer_putc (b, ' ');
553      else
554	first = 1;
555
556      buffer_putstr (b, argv[i]);
557    }
558  buffer_putc (b, '\0');
559
560  regstr = buffer_getstr (b);
561  buffer_free (b);
562
563  regex = bgp_regcomp (regstr);
564  if (!regex)
565    {
566      free (regstr);
567      vty_out (vty, "can't compile regexp %s%s", argv[0],
568	       VTY_NEWLINE);
569      return CMD_WARNING;
570    }
571
572  /* Lookup asfilter. */
573  asfilter = as_filter_lookup (aslist, regstr, type);
574
575  free (regstr);
576  bgp_regex_free (regex);
577
578  if (asfilter == NULL)
579    {
580      vty_out (vty, "%s", VTY_NEWLINE);
581      return CMD_WARNING;
582    }
583
584  as_list_filter_delete (aslist, asfilter);
585
586  return CMD_SUCCESS;
587}
588
589DEFUN (no_ip_as_path_all,
590       no_ip_as_path_all_cmd,
591       "no ip as-path access-list WORD",
592       NO_STR
593       IP_STR
594       "BGP autonomous system path filter\n"
595       "Specify an access list name\n"
596       "Regular expression access list name\n")
597{
598  struct as_list *aslist;
599
600  aslist = as_list_lookup (argv[0]);
601  if (aslist == NULL)
602    {
603      vty_out (vty, "ip as-path access-list %s doesn't exist%s", argv[0],
604	       VTY_NEWLINE);
605      return CMD_WARNING;
606    }
607
608  as_list_delete (aslist);
609
610  return CMD_SUCCESS;
611}
612
613int
614config_write_as_list (struct vty *vty)
615{
616  struct as_list *aslist;
617  struct as_filter *asfilter;
618  int write = 0;
619
620  for (aslist = as_list_master.num.head; aslist; aslist = aslist->next)
621    for (asfilter = aslist->head; asfilter; asfilter = asfilter->next)
622      {
623	vty_out (vty, "ip as-path access-list %s %s %s%s",
624		 aslist->name, filter_type_str (asfilter->type),
625		 asfilter->reg_str,
626		 VTY_NEWLINE);
627	write++;
628      }
629
630  for (aslist = as_list_master.str.head; aslist; aslist = aslist->next)
631    for (asfilter = aslist->head; asfilter; asfilter = asfilter->next)
632      {
633	vty_out (vty, "ip as-path access-list %s %s %s%s",
634		 aslist->name, filter_type_str (asfilter->type),
635		 asfilter->reg_str,
636		 VTY_NEWLINE);
637	write++;
638      }
639  return write;
640}
641
642struct cmd_node as_list_node =
643{
644  AS_LIST_NODE,
645  "",
646  1
647};
648
649/* Register functions. */
650void
651bgp_filter_init ()
652{
653  install_node (&as_list_node, config_write_as_list);
654
655  install_element (CONFIG_NODE, &ip_as_path_cmd);
656  install_element (CONFIG_NODE, &no_ip_as_path_cmd);
657  install_element (CONFIG_NODE, &no_ip_as_path_all_cmd);
658}
659