/* AS path filter list. Copyright (C) 1999 Kunihiro Ishiguro This file is part of GNU Zebra. GNU Zebra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNU Zebra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Zebra; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "command.h" #include "log.h" #include "memory.h" #include "buffer.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_regex.h" #include "bgpd/bgp_filter.h" /* List of AS filter list. */ struct as_list_list { struct as_list *head; struct as_list *tail; }; /* AS path filter master. */ struct as_list_master { /* List of access_list which name is number. */ struct as_list_list num; /* List of access_list which name is string. */ struct as_list_list str; /* Hook function which is executed when new access_list is added. */ void (*add_hook) (void); /* Hook function which is executed when access_list is deleted. */ void (*delete_hook) (void); }; /* Element of AS path filter. */ struct as_filter { struct as_filter *next; struct as_filter *prev; enum as_filter_type type; regex_t *reg; char *reg_str; }; enum as_list_type { ACCESS_TYPE_STRING, ACCESS_TYPE_NUMBER }; /* AS path filter list. */ struct as_list { char *name; enum as_list_type type; struct as_list *next; struct as_list *prev; struct as_filter *head; struct as_filter *tail; }; /* ip as-path access-list 10 permit AS1. */ static struct as_list_master as_list_master = { {NULL, NULL}, {NULL, NULL}, NULL, NULL }; /* Allocate new AS filter. */ static struct as_filter * as_filter_new (void) { return XCALLOC (MTYPE_AS_FILTER, sizeof (struct as_filter)); } /* Free allocated AS filter. */ static void as_filter_free (struct as_filter *asfilter) { if (asfilter->reg) bgp_regex_free (asfilter->reg); if (asfilter->reg_str) XFREE (MTYPE_AS_FILTER_STR, asfilter->reg_str); XFREE (MTYPE_AS_FILTER, asfilter); } /* Make new AS filter. */ static struct as_filter * as_filter_make (regex_t *reg, const char *reg_str, enum as_filter_type type) { struct as_filter *asfilter; asfilter = as_filter_new (); asfilter->reg = reg; asfilter->type = type; asfilter->reg_str = XSTRDUP (MTYPE_AS_FILTER_STR, reg_str); return asfilter; } static struct as_filter * as_filter_lookup (struct as_list *aslist, const char *reg_str, enum as_filter_type type) { struct as_filter *asfilter; for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) if (strcmp (reg_str, asfilter->reg_str) == 0) return asfilter; return NULL; } static void as_list_filter_add (struct as_list *aslist, struct as_filter *asfilter) { asfilter->next = NULL; asfilter->prev = aslist->tail; if (aslist->tail) aslist->tail->next = asfilter; else aslist->head = asfilter; aslist->tail = asfilter; } /* Lookup as_list from list of as_list by name. */ struct as_list * as_list_lookup (const char *name) { struct as_list *aslist; if (name == NULL) return NULL; for (aslist = as_list_master.num.head; aslist; aslist = aslist->next) if (strcmp (aslist->name, name) == 0) return aslist; for (aslist = as_list_master.str.head; aslist; aslist = aslist->next) if (strcmp (aslist->name, name) == 0) return aslist; return NULL; } static struct as_list * as_list_new (void) { return XCALLOC (MTYPE_AS_LIST, sizeof (struct as_list)); } static void as_list_free (struct as_list *aslist) { if (aslist->name) { free (aslist->name); aslist->name = NULL; } XFREE (MTYPE_AS_LIST, aslist); } /* Insert new AS list to list of as_list. Each as_list is sorted by the name. */ static struct as_list * as_list_insert (const char *name) { size_t i; long number; struct as_list *aslist; struct as_list *point; struct as_list_list *list; /* Allocate new access_list and copy given name. */ aslist = as_list_new (); aslist->name = strdup (name); assert (aslist->name); /* If name is made by all digit character. We treat it as number. */ for (number = 0, i = 0; i < strlen (name); i++) { if (isdigit ((int) name[i])) number = (number * 10) + (name[i] - '0'); else break; } /* In case of name is all digit character */ if (i == strlen (name)) { aslist->type = ACCESS_TYPE_NUMBER; /* Set access_list to number list. */ list = &as_list_master.num; for (point = list->head; point; point = point->next) if (atol (point->name) >= number) break; } else { aslist->type = ACCESS_TYPE_STRING; /* Set access_list to string list. */ list = &as_list_master.str; /* Set point to insertion point. */ for (point = list->head; point; point = point->next) if (strcmp (point->name, name) >= 0) break; } /* In case of this is the first element of master. */ if (list->head == NULL) { list->head = list->tail = aslist; return aslist; } /* In case of insertion is made at the tail of access_list. */ if (point == NULL) { aslist->prev = list->tail; list->tail->next = aslist; list->tail = aslist; return aslist; } /* In case of insertion is made at the head of access_list. */ if (point == list->head) { aslist->next = list->head; list->head->prev = aslist; list->head = aslist; return aslist; } /* Insertion is made at middle of the access_list. */ aslist->next = point; aslist->prev = point->prev; if (point->prev) point->prev->next = aslist; point->prev = aslist; return aslist; } static struct as_list * as_list_get (const char *name) { struct as_list *aslist; aslist = as_list_lookup (name); if (aslist == NULL) { aslist = as_list_insert (name); /* Run hook function. */ if (as_list_master.add_hook) (*as_list_master.add_hook) (); } return aslist; } static const char * filter_type_str (enum as_filter_type type) { switch (type) { case AS_FILTER_PERMIT: return "permit"; case AS_FILTER_DENY: return "deny"; default: return ""; } } static void as_list_delete (struct as_list *aslist) { struct as_list_list *list; struct as_filter *filter, *next; for (filter = aslist->head; filter; filter = next) { next = filter->next; as_filter_free (filter); } if (aslist->type == ACCESS_TYPE_NUMBER) list = &as_list_master.num; else list = &as_list_master.str; if (aslist->next) aslist->next->prev = aslist->prev; else list->tail = aslist->prev; if (aslist->prev) aslist->prev->next = aslist->next; else list->head = aslist->next; as_list_free (aslist); } static int as_list_empty (struct as_list *aslist) { if (aslist->head == NULL && aslist->tail == NULL) return 1; else return 0; } static void as_list_filter_delete (struct as_list *aslist, struct as_filter *asfilter) { if (asfilter->next) asfilter->next->prev = asfilter->prev; else aslist->tail = asfilter->prev; if (asfilter->prev) asfilter->prev->next = asfilter->next; else aslist->head = asfilter->next; as_filter_free (asfilter); /* If access_list becomes empty delete it from access_master. */ if (as_list_empty (aslist)) as_list_delete (aslist); /* Run hook function. */ if (as_list_master.delete_hook) (*as_list_master.delete_hook) (); } static int as_filter_match (struct as_filter *asfilter, struct aspath *aspath) { if (bgp_regexec (asfilter->reg, aspath) != REG_NOMATCH) return 1; return 0; } /* Apply AS path filter to AS. */ enum as_filter_type as_list_apply (struct as_list *aslist, void *object) { struct as_filter *asfilter; struct aspath *aspath; aspath = (struct aspath *) object; if (aslist == NULL) return AS_FILTER_DENY; for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) { if (as_filter_match (asfilter, aspath)) return asfilter->type; } return AS_FILTER_DENY; } /* Add hook function. */ void as_list_add_hook (void (*func) (void)) { as_list_master.add_hook = func; } /* Delete hook function. */ void as_list_delete_hook (void (*func) (void)) { as_list_master.delete_hook = func; } static int as_list_dup_check (struct as_list *aslist, struct as_filter *new) { struct as_filter *asfilter; for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) { if (asfilter->type == new->type && strcmp (asfilter->reg_str, new->reg_str) == 0) return 1; } return 0; } DEFUN (ip_as_path, ip_as_path_cmd, "ip as-path access-list WORD (deny|permit) .LINE", IP_STR "BGP autonomous system path filter\n" "Specify an access list name\n" "Regular expression access list name\n" "Specify packets to reject\n" "Specify packets to forward\n" "A regular-expression to match the BGP AS paths\n") { enum as_filter_type type; struct as_filter *asfilter; struct as_list *aslist; regex_t *regex; char *regstr; /* Check the filter type. */ if (strncmp (argv[1], "p", 1) == 0) type = AS_FILTER_PERMIT; else if (strncmp (argv[1], "d", 1) == 0) type = AS_FILTER_DENY; else { vty_out (vty, "filter type must be [permit|deny]%s", VTY_NEWLINE); return CMD_WARNING; } /* Check AS path regex. */ regstr = argv_concat(argv, argc, 2); regex = bgp_regcomp (regstr); if (!regex) { XFREE (MTYPE_TMP, regstr); vty_out (vty, "can't compile regexp %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } asfilter = as_filter_make (regex, regstr, type); XFREE (MTYPE_TMP, regstr); /* Install new filter to the access_list. */ aslist = as_list_get (argv[0]); /* Duplicate insertion check. */; if (as_list_dup_check (aslist, asfilter)) as_filter_free (asfilter); else as_list_filter_add (aslist, asfilter); return CMD_SUCCESS; } DEFUN (no_ip_as_path, no_ip_as_path_cmd, "no ip as-path access-list WORD (deny|permit) .LINE", NO_STR IP_STR "BGP autonomous system path filter\n" "Specify an access list name\n" "Regular expression access list name\n" "Specify packets to reject\n" "Specify packets to forward\n" "A regular-expression to match the BGP AS paths\n") { enum as_filter_type type; struct as_filter *asfilter; struct as_list *aslist; char *regstr; regex_t *regex; /* Lookup AS list from AS path list. */ aslist = as_list_lookup (argv[0]); if (aslist == NULL) { vty_out (vty, "ip as-path access-list %s doesn't exist%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } /* Check the filter type. */ if (strncmp (argv[1], "p", 1) == 0) type = AS_FILTER_PERMIT; else if (strncmp (argv[1], "d", 1) == 0) type = AS_FILTER_DENY; else { vty_out (vty, "filter type must be [permit|deny]%s", VTY_NEWLINE); return CMD_WARNING; } /* Compile AS path. */ regstr = argv_concat(argv, argc, 2); regex = bgp_regcomp (regstr); if (!regex) { XFREE (MTYPE_TMP, regstr); vty_out (vty, "can't compile regexp %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } /* Lookup asfilter. */ asfilter = as_filter_lookup (aslist, regstr, type); XFREE (MTYPE_TMP, regstr); bgp_regex_free (regex); if (asfilter == NULL) { vty_out (vty, "%s", VTY_NEWLINE); return CMD_WARNING; } as_list_filter_delete (aslist, asfilter); return CMD_SUCCESS; } DEFUN (no_ip_as_path_all, no_ip_as_path_all_cmd, "no ip as-path access-list WORD", NO_STR IP_STR "BGP autonomous system path filter\n" "Specify an access list name\n" "Regular expression access list name\n") { struct as_list *aslist; aslist = as_list_lookup (argv[0]); if (aslist == NULL) { vty_out (vty, "ip as-path access-list %s doesn't exist%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } as_list_delete (aslist); /* Run hook function. */ if (as_list_master.delete_hook) (*as_list_master.delete_hook) (); return CMD_SUCCESS; } static void as_list_show (struct vty *vty, struct as_list *aslist) { struct as_filter *asfilter; vty_out (vty, "AS path access list %s%s", aslist->name, VTY_NEWLINE); for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) { vty_out (vty, " %s %s%s", filter_type_str (asfilter->type), asfilter->reg_str, VTY_NEWLINE); } } static void as_list_show_all (struct vty *vty) { struct as_list *aslist; struct as_filter *asfilter; for (aslist = as_list_master.num.head; aslist; aslist = aslist->next) { vty_out (vty, "AS path access list %s%s", aslist->name, VTY_NEWLINE); for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) { vty_out (vty, " %s %s%s", filter_type_str (asfilter->type), asfilter->reg_str, VTY_NEWLINE); } } for (aslist = as_list_master.str.head; aslist; aslist = aslist->next) { vty_out (vty, "AS path access list %s%s", aslist->name, VTY_NEWLINE); for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) { vty_out (vty, " %s %s%s", filter_type_str (asfilter->type), asfilter->reg_str, VTY_NEWLINE); } } } DEFUN (show_ip_as_path_access_list, show_ip_as_path_access_list_cmd, "show ip as-path-access-list WORD", SHOW_STR IP_STR "List AS path access lists\n" "AS path access list name\n") { struct as_list *aslist; aslist = as_list_lookup (argv[0]); if (aslist) as_list_show (vty, aslist); return CMD_SUCCESS; } DEFUN (show_ip_as_path_access_list_all, show_ip_as_path_access_list_all_cmd, "show ip as-path-access-list", SHOW_STR IP_STR "List AS path access lists\n") { as_list_show_all (vty); return CMD_SUCCESS; } static int config_write_as_list (struct vty *vty) { struct as_list *aslist; struct as_filter *asfilter; int write = 0; for (aslist = as_list_master.num.head; aslist; aslist = aslist->next) for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) { vty_out (vty, "ip as-path access-list %s %s %s%s", aslist->name, filter_type_str (asfilter->type), asfilter->reg_str, VTY_NEWLINE); write++; } for (aslist = as_list_master.str.head; aslist; aslist = aslist->next) for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) { vty_out (vty, "ip as-path access-list %s %s %s%s", aslist->name, filter_type_str (asfilter->type), asfilter->reg_str, VTY_NEWLINE); write++; } return write; } static struct cmd_node as_list_node = { AS_LIST_NODE, "", 1 }; /* Register functions. */ void bgp_filter_init (void) { install_node (&as_list_node, config_write_as_list); install_element (CONFIG_NODE, &ip_as_path_cmd); install_element (CONFIG_NODE, &no_ip_as_path_cmd); install_element (CONFIG_NODE, &no_ip_as_path_all_cmd); install_element (VIEW_NODE, &show_ip_as_path_access_list_cmd); install_element (VIEW_NODE, &show_ip_as_path_access_list_all_cmd); install_element (ENABLE_NODE, &show_ip_as_path_access_list_cmd); install_element (ENABLE_NODE, &show_ip_as_path_access_list_all_cmd); } void bgp_filter_reset (void) { struct as_list *aslist; struct as_list *next; for (aslist = as_list_master.num.head; aslist; aslist = next) { next = aslist->next; as_list_delete (aslist); } for (aslist = as_list_master.str.head; aslist; aslist = next) { next = aslist->next; as_list_delete (aslist); } assert (as_list_master.num.head == NULL); assert (as_list_master.num.tail == NULL); assert (as_list_master.str.head == NULL); assert (as_list_master.str.tail == NULL); }