diff options
author | paul <paul> | 2002-12-13 20:15:29 +0000 |
---|---|---|
committer | paul <paul> | 2002-12-13 20:15:29 +0000 |
commit | 718e3744195351130f4ce7dbe0613f4b3e23df93 (patch) | |
tree | bac2ad39971cd43f31241ef123bd4e470f695ac9 /bgpd/bgp_clist.c |
Initial revision
Diffstat (limited to 'bgpd/bgp_clist.c')
-rw-r--r-- | bgpd/bgp_clist.c | 905 |
1 files changed, 905 insertions, 0 deletions
diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c new file mode 100644 index 00000000..0b6a2e8c --- /dev/null +++ b/bgpd/bgp_clist.c @@ -0,0 +1,905 @@ +/* BGP community-list and extcommunity-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 <zebra.h> + +#include "command.h" +#include "prefix.h" +#include "memory.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_community.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_regex.h" +#include "bgpd/bgp_clist.h" + +/* Lookup master structure for community-list or + extcommunity-list. */ +struct community_list_master * +community_list_master_lookup (struct community_list_handler *ch, int style) +{ + if (ch) + switch (style) + { + case COMMUNITY_LIST_STANDARD: + case COMMUNITY_LIST_EXPANDED: + case COMMUNITY_LIST_AUTO: + return &ch->community_list; + break; + case EXTCOMMUNITY_LIST_STANDARD: + case EXTCOMMUNITY_LIST_EXPANDED: + case EXTCOMMUNITY_LIST_AUTO: + return &ch->extcommunity_list; + } + return NULL; +} + +/* Allocate a new community list entry. */ +struct community_entry * +community_entry_new () +{ + struct community_entry *new; + + new = XMALLOC (MTYPE_COMMUNITY_LIST_ENTRY, sizeof (struct community_entry)); + memset (new, 0, sizeof (struct community_entry)); + return new; +} + +/* Free community list entry. */ +void +community_entry_free (struct community_entry *entry) +{ + switch (entry->style) + { + case COMMUNITY_LIST_STANDARD: + if (entry->u.com) + community_free (entry->u.com); + break; + case EXTCOMMUNITY_LIST_STANDARD: + /* In case of standard extcommunity-list, configuration string + is made by ecommunity_ecom2str(). */ + if (entry->config) + XFREE (MTYPE_ECOMMUNITY_STR, entry->config); + if (entry->u.ecom) + ecommunity_free (entry->u.ecom); + break; + case COMMUNITY_LIST_EXPANDED: + case EXTCOMMUNITY_LIST_EXPANDED: + if (entry->config) + XFREE (MTYPE_COMMUNITY_LIST_CONFIG, entry->config); + if (entry->reg) + bgp_regex_free (entry->reg); + default: + break; + } + XFREE (MTYPE_COMMUNITY_LIST_ENTRY, entry); +} + +/* Allocate a new community-list. */ +struct community_list * +community_list_new () +{ + struct community_list *new; + + new = XMALLOC (MTYPE_COMMUNITY_LIST, sizeof (struct community_list)); + memset (new, 0, sizeof (struct community_list)); + return new; +} + +/* Free community-list. */ +void +community_list_free (struct community_list *list) +{ + if (list->name) + XFREE (MTYPE_COMMUNITY_LIST_NAME, list->name); + XFREE (MTYPE_COMMUNITY_LIST, list); +} + +struct community_list * +community_list_insert (struct community_list_handler *ch, + char *name, int style) +{ + int i; + long number; + struct community_list *new; + struct community_list *point; + struct community_list_list *list; + struct community_list_master *cm; + + /* Lookup community-list master. */ + cm = community_list_master_lookup (ch, style); + if (! cm) + return NULL; + + /* Allocate new community_list and copy given name. */ + new = community_list_new (); + new->name = XSTRDUP (MTYPE_COMMUNITY_LIST_NAME, 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)) + { + new->sort = COMMUNITY_LIST_NUMBER; + + /* Set access_list to number list. */ + list = &cm->num; + + for (point = list->head; point; point = point->next) + if (atol (point->name) >= number) + break; + } + else + { + new->sort = COMMUNITY_LIST_STRING; + + /* Set access_list to string list. */ + list = &cm->str; + + /* Set point to insertion point. */ + for (point = list->head; point; point = point->next) + if (strcmp (point->name, name) >= 0) + break; + } + + /* Link to upper list. */ + new->parent = list; + + /* In case of this is the first element of master. */ + if (list->head == NULL) + { + list->head = list->tail = new; + return new; + } + + /* In case of insertion is made at the tail of access_list. */ + if (point == NULL) + { + new->prev = list->tail; + list->tail->next = new; + list->tail = new; + return new; + } + + /* In case of insertion is made at the head of access_list. */ + if (point == list->head) + { + new->next = list->head; + list->head->prev = new; + list->head = new; + return new; + } + + /* Insertion is made at middle of the access_list. */ + new->next = point; + new->prev = point->prev; + + if (point->prev) + point->prev->next = new; + point->prev = new; + + return new; +} + +struct community_list * +community_list_lookup (struct community_list_handler *ch, + char *name, int style) +{ + struct community_list *list; + struct community_list_master *cm; + + if (! name) + return NULL; + + cm = community_list_master_lookup (ch, style); + if (! cm) + return NULL; + + for (list = cm->num.head; list; list = list->next) + if (strcmp (list->name, name) == 0) + return list; + for (list = cm->str.head; list; list = list->next) + if (strcmp (list->name, name) == 0) + return list; + + return NULL; +} + +struct community_list * +community_list_get (struct community_list_handler *ch, char *name, int style) +{ + struct community_list *list; + + list = community_list_lookup (ch, name, style); + if (! list) + list = community_list_insert (ch, name, style); + return list; +} + +void +community_list_delete (struct community_list *list) +{ + struct community_list_list *clist; + struct community_entry *entry, *next; + + for (entry = list->head; entry; entry = next) + { + next = entry->next; + community_entry_free (entry); + } + + clist = list->parent; + + if (list->next) + list->next->prev = list->prev; + else + clist->tail = list->prev; + + if (list->prev) + list->prev->next = list->next; + else + clist->head = list->next; + + community_list_free (list); +} + +int +community_list_empty_p (struct community_list *list) +{ + return (list->head == NULL && list->tail == NULL) ? 1 : 0; +} + +/* Add community-list entry to the list. */ +static void +community_list_entry_add (struct community_list *list, + struct community_entry *entry) +{ + entry->next = NULL; + entry->prev = list->tail; + + if (list->tail) + list->tail->next = entry; + else + list->head = entry; + list->tail = entry; +} + +/* Delete community-list entry from the list. */ +static void +community_list_entry_delete (struct community_list *list, + struct community_entry *entry, int style) +{ + if (entry->next) + entry->next->prev = entry->prev; + else + list->tail = entry->prev; + + if (entry->prev) + entry->prev->next = entry->next; + else + list->head = entry->next; + + community_entry_free (entry); + + if (community_list_empty_p (list)) + community_list_delete (list); +} + +/* Lookup community-list entry from the list. */ +static struct community_entry * +community_list_entry_lookup (struct community_list *list, void *arg, + int direct) +{ + struct community_entry *entry; + + for (entry = list->head; entry; entry = entry->next) + { + switch (entry->style) + { + case COMMUNITY_LIST_STANDARD: + if (community_cmp (entry->u.com, arg)) + return entry; + break; + case EXTCOMMUNITY_LIST_STANDARD: + if (ecommunity_cmp (entry->u.ecom, arg)) + return entry; + break; + case COMMUNITY_LIST_EXPANDED: + case EXTCOMMUNITY_LIST_EXPANDED: + if (strcmp (entry->config, arg) == 0) + return entry; + break; + default: + break; + } + } + return NULL; +} + +/* Internal function to perform regular expression match for community + attribute. */ +static int +community_regexp_match (struct community *com, regex_t *reg) +{ + char *str; + + /* When there is no communities attribute it is treated as empty + string. */ + if (com == NULL || com->size == 0) + str = ""; + else + str = community_str (com); + + /* Regular expression match. */ + if (regexec (reg, str, 0, NULL, 0) == 0) + return 1; + + /* No match. */ + return 0; +} + +/* Delete community attribute using regular expression match. Return + modified communites attribute. */ +static struct community * +community_regexp_delete (struct community *com, regex_t *reg) +{ + int i; + u_int32_t comval; + /* Maximum is "65535:65535" + '\0'. */ + char c[12]; + char *str; + + if (! com) + return NULL; + + i = 0; + while (i < com->size) + { + memcpy (&comval, com_nthval (com, i), sizeof (u_int32_t)); + comval = ntohl (comval); + + switch (comval) + { + case COMMUNITY_INTERNET: + str = "internet"; + break; + case COMMUNITY_NO_EXPORT: + str = "no-export"; + break; + case COMMUNITY_NO_ADVERTISE: + str = "no-advertise"; + break; + case COMMUNITY_LOCAL_AS: + str = "local-AS"; + break; + default: + sprintf (c, "%d:%d", (comval >> 16) & 0xFFFF, comval & 0xFFFF); + str = c; + break; + } + + if (regexec (reg, str, 0, NULL, 0) == 0) + community_del_val (com, com_nthval (com, i)); + else + i++; + } + return com; +} + +/* When given community attribute matches to the community-list return + 1 else return 0. */ +int +community_list_match (struct community *com, struct community_list *list) +{ + struct community_entry *entry; + + for (entry = list->head; entry; entry = entry->next) + { + if (entry->any) + return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + + if (entry->style == COMMUNITY_LIST_STANDARD) + { + if (community_include (entry->u.com, COMMUNITY_INTERNET)) + return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + + if (community_match (com, entry->u.com)) + return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + } + else if (entry->style == COMMUNITY_LIST_EXPANDED) + { + if (community_regexp_match (com, entry->reg)) + return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + } + } + return 0; +} + +/* Perform exact matching. In case of expanded community-list, do + same thing as community_list_match(). */ +int +community_list_exact_match (struct community *com, struct community_list *list) +{ + struct community_entry *entry; + + for (entry = list->head; entry; entry = entry->next) + { + if (entry->any) + return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + + if (entry->style == COMMUNITY_LIST_STANDARD) + { + if (community_include (entry->u.com, COMMUNITY_INTERNET)) + return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + + if (community_cmp (com, entry->u.com)) + return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + } + else if (entry->style == COMMUNITY_LIST_EXPANDED) + { + if (community_regexp_match (com, entry->reg)) + return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + } + } + return 0; +} + +/* Delete all permitted communities in the list from com1 */ +struct community * +community_list_match_delete (struct community *com, + struct community_list *list) +{ + struct community_entry *entry; + + for (entry = list->head; entry; entry = entry->next) + { + if (entry->any && entry->direct == COMMUNITY_PERMIT) + { + /* This is a tricky part. Currently only + route_set_community_delete() uses this function. In the + function com->size is zero, it free the community + structure. */ + com->size = 0; + return com; + } + + if (entry->style == COMMUNITY_LIST_STANDARD) + { + if (entry->direct == COMMUNITY_PERMIT) + community_delete (com, entry->u.com); + } + else if (entry->style == COMMUNITY_LIST_EXPANDED) + { + if (entry->direct == COMMUNITY_PERMIT) + community_regexp_delete (com, entry->reg); + } + } + return com; +} + +/* To avoid duplicated entry in the community-list, this function + compares specified entry to existing entry. */ +int +community_list_dup_check (struct community_list *list, + struct community_entry *new) +{ + struct community_entry *entry; + + for (entry = list->head; entry; entry = entry->next) + { + if (entry->style != new->style) + continue; + + if (entry->direct != new->direct) + continue; + + if (entry->any != new->any) + continue; + + if (entry->any) + return 1; + + switch (entry->style) + { + case COMMUNITY_LIST_STANDARD: + if (community_cmp (entry->u.com, new->u.com)) + return 1; + break; + case EXTCOMMUNITY_LIST_STANDARD: + if (ecommunity_cmp (entry->u.ecom, new->u.ecom)) + return 1; + break; + case COMMUNITY_LIST_EXPANDED: + case EXTCOMMUNITY_LIST_EXPANDED: + if (strcmp (entry->config, new->config) == 0) + return 1; + break; + default: + break; + } + } + return 0; +} + +/* Set community-list. */ +int +community_list_set (struct community_list_handler *ch, + char *name, char *str, int direct, int style) +{ + struct community_entry *entry; + struct community_list *list; + struct community *com; + regex_t *regex; + + entry = NULL; + + /* Get community list. */ + list = community_list_get (ch, name, style); + + /* When community-list already has entry, new entry should have same + style. If you want to have mixed style community-list, you can + comment out this check. */ + if (! community_list_empty_p (list)) + { + struct community_entry *first; + + first = list->head; + + if (style == COMMUNITY_LIST_AUTO) + style = first->style; + else if (style != first->style) + { + return (first->style == COMMUNITY_LIST_STANDARD + ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT + : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT); + } + } + + /* When str is NULL, it is matches any. */ + if (! str) + { + entry = community_entry_new (); + entry->direct = direct; + entry->any = 1; + if (style == COMMUNITY_LIST_AUTO) + entry->style = COMMUNITY_LIST_STANDARD; + else + entry->style = style; + } + else + { + /* Standard community-list parse. String must be converted into + community structure without problem. */ + if (style == COMMUNITY_LIST_STANDARD || style == COMMUNITY_LIST_AUTO) + { + com = community_str2com (str); + if (com) + { + entry = community_entry_new (); + entry->u.com = com; + entry->direct = direct; + entry->style = COMMUNITY_LIST_STANDARD; + } + else if (style == COMMUNITY_LIST_STANDARD) + return COMMUNITY_LIST_ERR_MALFORMED_VAL; + + /* We can't convert string into communities value. When + community-list type is auto, fall dawn to regular expression + match. */ + } + + /* Expanded community-list parse. String may include regular + expression. */ + if (! entry && (style == COMMUNITY_LIST_EXPANDED + || style == COMMUNITY_LIST_AUTO)) + { + regex = bgp_regcomp (str); + if (regex) + { + entry = community_entry_new (); + entry->reg = regex; + entry->config = XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG, str); + entry->direct = direct; + entry->style = COMMUNITY_LIST_EXPANDED; + } + else + return COMMUNITY_LIST_ERR_MALFORMED_VAL; + } + } + + /* Do not put duplicated community entry. */ + if (community_list_dup_check (list, entry)) + community_entry_free (entry); + else + community_list_entry_add (list, entry); + + return 0; +} + +/* Unset community-list. When str is NULL, delete all of + community-list entry belongs to the specified name. */ +int +community_list_unset (struct community_list_handler *ch, + char *name, char *str, int direct, int style) +{ + struct community_entry *entry; + struct community_list *list; + struct community *com; + regex_t *regex; + + entry = NULL; + + /* Lookup community list. */ + list = community_list_lookup (ch, name, style); + if (list == NULL) + return COMMUNITY_LIST_ERR_CANT_FIND_LIST; + + /* Delete all of entry belongs to this community-list. */ + if (! str) + { + community_list_delete (list); + return 0; + } + + /* Community list string is specified. Lookup entry from community + list. */ + if (style == COMMUNITY_LIST_STANDARD || style == COMMUNITY_LIST_AUTO) + { + com = community_str2com (str); + if (com) + { + entry = community_list_entry_lookup (list, com, direct); + community_free (com); + } + else if (style == COMMUNITY_LIST_STANDARD) + return COMMUNITY_LIST_ERR_MALFORMED_VAL; + + /* If we can't convert string into community and community-list + type is auto, fall dawn to expanded community-list. */ + } + + /* Expanded community-list parse. String may include regular + expression. */ + if (! entry + && (style == COMMUNITY_LIST_EXPANDED || style == COMMUNITY_LIST_AUTO)) + { + regex = bgp_regcomp (str); + if (regex) + { + entry = community_list_entry_lookup (list, str, direct); + bgp_regex_free (regex); + } + else + return COMMUNITY_LIST_ERR_MALFORMED_VAL; + } + + if (! entry) + return COMMUNITY_LIST_ERR_CANT_FIND_LIST; + + community_list_entry_delete (list, entry, style); + + return 0; +} + +/* Set extcommunity-list. */ +int +extcommunity_list_set (struct community_list_handler *ch, + char *name, char *str, int direct, int style) +{ + struct community_entry *entry; + struct community_list *list; + struct ecommunity *ecom; + regex_t *regex; + + entry = NULL; + + /* Get community list. */ + list = community_list_get (ch, name, style); + + /* When community-list already has entry, new entry should have same + style. If you want to have mixed style community-list, you can + comment out this check. */ + if (! community_list_empty_p (list)) + { + struct community_entry *first; + + first = list->head; + + if (style == EXTCOMMUNITY_LIST_AUTO) + style = first->style; + else if (style != first->style) + { + return (first->style == EXTCOMMUNITY_LIST_STANDARD + ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT + : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT); + } + } + + /* When str is NULL, it is matches any. */ + if (! str) + { + entry = community_entry_new (); + entry->direct = direct; + entry->any = 1; + if (style == EXTCOMMUNITY_LIST_AUTO) + entry->style = EXTCOMMUNITY_LIST_STANDARD; + else + entry->style = style; + } + else + { + /* Standard extcommunity-list parse. String is converted into + ecommunity structure. */ + if (style == EXTCOMMUNITY_LIST_STANDARD + || style == EXTCOMMUNITY_LIST_AUTO) + { + /* Type is unknown. String includes keyword. */ + ecom = ecommunity_str2com (str, 0, 1); + if (ecom) + { + entry = community_entry_new (); + entry->config + = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_COMMUNITY_LIST); + ecom->str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_DISPLAY); + entry->u.ecom = ecom; + entry->direct = direct; + entry->style = EXTCOMMUNITY_LIST_STANDARD; + } + else if (style == EXTCOMMUNITY_LIST_STANDARD) + return COMMUNITY_LIST_ERR_MALFORMED_VAL; + + /* We can't convert string into communities value. When + community-list type is auto, fall dawn to regular expression + match. */ + } + + /* Expanded extcommunity-list parse. String may include regular + expression. */ + if (! entry && (style == EXTCOMMUNITY_LIST_EXPANDED + || style == EXTCOMMUNITY_LIST_AUTO)) + { + regex = bgp_regcomp (str); + if (regex) + { + entry = community_entry_new (); + entry->reg = regex; + entry->config = XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG, str); + entry->direct = direct; + entry->style = EXTCOMMUNITY_LIST_EXPANDED; + } + else + return COMMUNITY_LIST_ERR_MALFORMED_VAL; + } + } + + /* Do not put duplicated community entry. */ + if (community_list_dup_check (list, entry)) + community_entry_free (entry); + else + community_list_entry_add (list, entry); + + return 0; +} + +/* Unset extcommunity-list. When str is NULL, delete all of + extcommunity-list entry belongs to the specified name. */ +int +extcommunity_list_unset (struct community_list_handler *ch, + char *name, char *str, int direct, int style) +{ + struct community_entry *entry; + struct community_list *list; + struct ecommunity *ecom = NULL; + regex_t *regex; + + entry = NULL; + + /* Lookup extcommunity list. */ + list = community_list_lookup (ch, name, style); + if (list == NULL) + return COMMUNITY_LIST_ERR_CANT_FIND_LIST; + + /* Delete all of entry belongs to this extcommunity-list. */ + if (! str) + { + community_list_delete (list); + return 0; + } + + /* Community list string is specified. Lookup entry from community + list. */ + if (style == EXTCOMMUNITY_LIST_STANDARD || style == EXTCOMMUNITY_LIST_AUTO) + { + ecom = ecommunity_str2com (str, 0, 1); + if (ecom) + { + entry = community_list_entry_lookup (list, ecom, direct); + ecommunity_free (ecom); + } + else if (style == COMMUNITY_LIST_STANDARD) + return COMMUNITY_LIST_ERR_MALFORMED_VAL; + + /* If we can't convert string into community and community-list + type is auto, fall dawn to expanded community-list. */ + } + + /* Expanded community-list parse. String may include regular + expression. */ + if (! entry + && (style == COMMUNITY_LIST_EXPANDED || style == COMMUNITY_LIST_AUTO)) + { + regex = bgp_regcomp (str); + if (regex) + { + entry = community_list_entry_lookup (list, str, direct); + bgp_regex_free (regex); + } + else + return COMMUNITY_LIST_ERR_MALFORMED_VAL; + } + + if (! entry) + return COMMUNITY_LIST_ERR_CANT_FIND_LIST; + + community_list_entry_delete (list, entry, style); + + return 0; +} + +/* Initializa community-list. Return community-list handler. */ +struct community_list_handler * +community_list_init () +{ + struct community_list_handler *ch; + ch = XCALLOC (MTYPE_COMMUNITY_LIST_HANDLER, + sizeof (struct community_list_handler)); + return ch; +} + +/* Terminate community-list. */ +void +community_list_terminate (struct community_list_handler *ch) +{ + struct community_list_master *cm; + struct community_list *list; + + cm = &ch->community_list; + while ((list = cm->num.head) != NULL) + community_list_delete (list); + while ((list = cm->str.head) != NULL) + community_list_delete (list); + + cm = &ch->extcommunity_list; + while ((list = cm->num.head) != NULL) + community_list_delete (list); + while ((list = cm->str.head) != NULL) + community_list_delete (list); + + XFREE (MTYPE_COMMUNITY_LIST_HANDLER, ch); +} |