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) (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. */
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. */
99static struct as_filter *
100as_filter_new (void)
101{
102  return XCALLOC (MTYPE_AS_FILTER, sizeof (struct as_filter));
103}
104
105/* Free allocated AS filter. */
106static void
107as_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. */
117static struct as_filter *
118as_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
130static struct as_filter *
131as_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
142static void
143as_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. */
156struct as_list *
157as_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
175static struct as_list *
176as_list_new (void)
177{
178  return XCALLOC (MTYPE_AS_LIST, sizeof (struct as_list));
179}
180
181static void
182as_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. */
194static struct as_list *
195as_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
279static struct as_list *
280as_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
297static const char *
298filter_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
311static void
312as_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
341static int
342as_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
350static void
351as_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
374static int
375as_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. */
383enum as_filter_type
384as_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. */
403void
404as_list_add_hook (void (*func) (void))
405{
406  as_list_master.add_hook = func;
407}
408
409/* Delete hook function. */
410void
411as_list_delete_hook (void (*func) (void))
412{
413  as_list_master.delete_hook = func;
414}
415
416static int
417as_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
430DEFUN (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
485DEFUN (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
552DEFUN (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
580static void
581as_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
594static void
595as_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
623DEFUN (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
640DEFUN (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
651static int
652config_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
680static struct cmd_node as_list_node =
681{
682  AS_LIST_NODE,
683  "",
684  1
685};
686
687/* Register functions. */
688void
689bgp_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
703void
704bgp_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}
727