From: Donald Sharp Date: Wed, 20 May 2015 01:04:23 +0000 (-0700) Subject: set community delete stops as soon as it hits a community-list entry with a deny X-Git-Tag: frr-2.0-rc1~1389 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=5cbea2885debe7547cf266d09df48ed3b4729aea;p=matthieu%2Ffrr.git set community delete stops as soon as it hits a community-list entry with a deny --- diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index 33444c46f3..321e1c0874 100644 --- a/bgpd/bgp_clist.c +++ b/bgpd/bgp_clist.c @@ -331,6 +331,94 @@ community_list_entry_lookup (struct community_list *list, const void *arg, return NULL; } +static char * +community_str_get (struct community *com, int i) +{ + int len; + u_int32_t comval; + u_int16_t as; + u_int16_t val; + char *str; + char *pnt; + + memcpy (&comval, com_nthval (com, i), sizeof (u_int32_t)); + comval = ntohl (comval); + + switch (comval) + { + case COMMUNITY_INTERNET: + len = strlen (" internet"); + break; + case COMMUNITY_NO_EXPORT: + len = strlen (" no-export"); + break; + case COMMUNITY_NO_ADVERTISE: + len = strlen (" no-advertise"); + break; + case COMMUNITY_LOCAL_AS: + len = strlen (" local-AS"); + break; + default: + len = strlen (" 65536:65535"); + break; + } + + /* Allocate memory. */ + str = pnt = XMALLOC (MTYPE_COMMUNITY_STR, len); + + switch (comval) + { + case COMMUNITY_INTERNET: + strcpy (pnt, "internet"); + pnt += strlen ("internet"); + break; + case COMMUNITY_NO_EXPORT: + strcpy (pnt, "no-export"); + pnt += strlen ("no-export"); + break; + case COMMUNITY_NO_ADVERTISE: + strcpy (pnt, "no-advertise"); + pnt += strlen ("no-advertise"); + break; + case COMMUNITY_LOCAL_AS: + strcpy (pnt, "local-AS"); + pnt += strlen ("local-AS"); + break; + default: + as = (comval >> 16) & 0xFFFF; + val = comval & 0xFFFF; + sprintf (pnt, "%u:%d", as, val); + pnt += strlen (pnt); + break; + } + + *pnt = '\0'; + + return str; +} + +/* Internal function to perform regular expression match for + * * a single community. */ +static int +community_regexp_include (regex_t * reg, struct community *com, int i) +{ + const 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_get (com, i); + + /* Regular expression match. */ + if (regexec (reg, str, 0, NULL, 0) == 0) + return 1; + + /* No match. */ + return 0; +} + /* Internal function to perform regular expression match for community attribute. */ static int @@ -510,41 +598,63 @@ community_list_match_delete (struct community *com, struct community_list *list) { struct community_entry *entry; + u_int32_t val; + u_int32_t com_index_to_delete[com->size]; + int delete_index = 0; + int i; - for (entry = list->head; entry; entry = entry->next) + /* Loop over each community value and evaluate each against the + * community-list. If we need to delete a community value add its index to + * com_index_to_delete. + */ + for (i = 0; i < com->size; i++) { - if (entry->any) + val = community_val_get (com, i); + + for (entry = list->head; entry; entry = entry->next) { - if (entry->direct == COMMUNITY_PERMIT) + if (entry->any) { - /* 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; + if (entry->direct == COMMUNITY_PERMIT) + { + com_index_to_delete[delete_index] = i; + delete_index++; + } + break; } - return com; - } - if ((entry->style == COMMUNITY_LIST_STANDARD) - && (community_include (entry->u.com, COMMUNITY_INTERNET) - || community_match (com, entry->u.com) )) - { + else if ((entry->style == COMMUNITY_LIST_STANDARD) + && (community_include (entry->u.com, COMMUNITY_INTERNET) + || community_include (entry->u.com, val) )) + { if (entry->direct == COMMUNITY_PERMIT) - community_delete (com, entry->u.com); - else - break; - } - else if ((entry->style == COMMUNITY_LIST_EXPANDED) - && community_regexp_match (com, entry->reg)) - { - if (entry->direct == COMMUNITY_PERMIT) - community_regexp_delete (com, entry->reg); - else - break; - } + { + com_index_to_delete[delete_index] = i; + delete_index++; + } + break; + } + + else if ((entry->style == COMMUNITY_LIST_EXPANDED) + && community_regexp_include (entry->reg, com, i)) + { + if (entry->direct == COMMUNITY_PERMIT) + { + com_index_to_delete[delete_index] = i; + delete_index++; + } + break; + } + } + } + + /* Delete all of the communities we flagged for deletion */ + for (i = delete_index-1; i >= 0; i--) + { + val = community_val_get (com, com_index_to_delete[i]); + community_del_val (com, &val); } + return com; } diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c index 1bd2dd84e4..f1997bd9ce 100644 --- a/bgpd/bgp_community.c +++ b/bgpd/bgp_community.c @@ -144,7 +144,7 @@ community_include (struct community *com, u_int32_t val) return 0; } -static u_int32_t +u_int32_t community_val_get (struct community *com, int i) { u_char *p; diff --git a/bgpd/bgp_community.h b/bgpd/bgp_community.h index 9e483770e9..c73dab3045 100644 --- a/bgpd/bgp_community.h +++ b/bgpd/bgp_community.h @@ -70,5 +70,6 @@ extern int community_include (struct community *, u_int32_t); extern void community_del_val (struct community *, u_int32_t *); extern unsigned long community_count (void); extern struct hash *community_hash (void); +extern u_int32_t community_val_get (struct community *com, int i); #endif /* _QUAGGA_BGP_COMMUNITY_H */