#include "queue.h"
#include "filter.h"
#include "stream.h"
+#include "jhash.h"
+#include "frrstr.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_community.h"
#include "bgpd/bgp_regex.h"
#include "bgpd/bgp_clist.h"
+static uint32_t bgp_clist_hash_key_community_list(void *data)
+{
+ struct community_list *cl = data;
+
+ if (cl->name_hash)
+ return cl->name_hash;
+
+ cl->name_hash = bgp_clist_hash_key(cl->name);
+ return cl->name_hash;
+}
+
+static bool bgp_clist_hash_cmp_community_list(const void *a1, const void *a2)
+{
+ const struct community_list *cl1 = a1;
+ const struct community_list *cl2 = a2;
+
+ if (cl1->name_hash != cl2->name_hash)
+ return false;
+
+ if (strcmp(cl1->name, cl2->name) == 0)
+ return true;
+
+ return false;
+}
+
/* Lookup master structure for community-list or
extcommunity-list. */
struct community_list_master *
switch (entry->style) {
case COMMUNITY_LIST_STANDARD:
if (entry->u.com)
- community_free(entry->u.com);
+ community_free(&entry->u.com);
break;
case LARGE_COMMUNITY_LIST_STANDARD:
if (entry->u.lcom)
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);
+ XFREE(MTYPE_ECOMMUNITY_STR, entry->config);
if (entry->u.ecom)
ecommunity_free(&entry->u.ecom);
break;
case COMMUNITY_LIST_EXPANDED:
case EXTCOMMUNITY_LIST_EXPANDED:
case LARGE_COMMUNITY_LIST_EXPANDED:
- if (entry->config)
- XFREE(MTYPE_COMMUNITY_LIST_CONFIG, entry->config);
+ XFREE(MTYPE_COMMUNITY_LIST_CONFIG, entry->config);
if (entry->reg)
bgp_regex_free(entry->reg);
default:
/* Free community-list. */
static void community_list_free(struct community_list *list)
{
- if (list->name)
- XFREE(MTYPE_COMMUNITY_LIST_NAME, list->name);
+ XFREE(MTYPE_COMMUNITY_LIST_NAME, list->name);
XFREE(MTYPE_COMMUNITY_LIST, list);
}
/* Allocate new community_list and copy given name. */
new = community_list_new();
new->name = XSTRDUP(MTYPE_COMMUNITY_LIST_NAME, name);
+ new->name_hash = bgp_clist_hash_key_community_list(new);
+
+ /* Save for later */
+ hash_get(cm->hash, new, hash_alloc_intern);
/* If name is made by all digit character. We treat it as
number. */
}
struct community_list *community_list_lookup(struct community_list_handler *ch,
- const char *name, int master)
+ const char *name,
+ uint32_t name_hash,
+ int master)
{
- struct community_list *list;
+ struct community_list lookup;
struct community_list_master *cm;
if (!name)
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;
+ lookup.name = (char *)name;
+ lookup.name_hash = name_hash;
+ return hash_get(cm->hash, &lookup, NULL);
}
static struct community_list *
{
struct community_list *list;
- list = community_list_lookup(ch, name, master);
+ list = community_list_lookup(ch, name, 0, master);
if (!list)
list = community_list_insert(ch, name, master);
return list;
}
-static void community_list_delete(struct community_list *list)
+static void community_list_delete(struct community_list_master *cm,
+ struct community_list *list)
{
struct community_list_list *clist;
struct community_entry *entry, *next;
else
clist->head = list->next;
+ hash_release(cm->hash, list);
community_list_free(list);
}
}
/* Delete community-list entry from the list. */
-static void community_list_entry_delete(struct community_list *list,
+static void community_list_entry_delete(struct community_list_master *cm,
+ struct community_list *list,
struct community_entry *entry)
{
if (entry->next)
community_entry_free(entry);
if (community_list_empty_p(list))
- community_list_delete(list);
+ community_list_delete(cm, list);
}
/* Lookup community-list entry from the list. */
static char *community_str_get(struct community *com, int i)
{
- int len;
uint32_t comval;
uint16_t as;
uint16_t val;
char *str;
- char *pnt;
memcpy(&comval, com_nthval(com, i), sizeof(uint32_t));
comval = ntohl(comval);
switch (comval) {
case COMMUNITY_INTERNET:
- len = strlen(" internet");
- break;
- case COMMUNITY_GSHUT:
- len = strlen(" graceful-shutdown");
- break;
- case COMMUNITY_ACCEPT_OWN:
- len = strlen(" accept-own");
- break;
- case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4:
- len = strlen(" route-filter-translated-v4");
- break;
- case COMMUNITY_ROUTE_FILTER_v4:
- len = strlen(" route-filter-v4");
- break;
- case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6:
- len = strlen(" route-filter-translated-v6");
- break;
- case COMMUNITY_ROUTE_FILTER_v6:
- len = strlen(" route-filter-v6");
- break;
- case COMMUNITY_LLGR_STALE:
- len = strlen(" llgr-stale");
- break;
- case COMMUNITY_NO_LLGR:
- len = strlen(" no-llgr");
- break;
- case COMMUNITY_ACCEPT_OWN_NEXTHOP:
- len = strlen(" accept-own-nexthop");
- break;
- case COMMUNITY_BLACKHOLE:
- len = strlen(" blackhole");
- 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;
- case COMMUNITY_NO_PEER:
- len = strlen(" no-peer");
- 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");
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "internet");
break;
case COMMUNITY_GSHUT:
- strcpy(pnt, "graceful-shutdown");
- pnt += strlen("graceful-shutdown");
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "graceful-shutdown");
break;
case COMMUNITY_ACCEPT_OWN:
- strcpy(pnt, "accept-own");
- pnt += strlen("accept-own");
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "accept-own");
break;
case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4:
- strcpy(pnt, "route-filter-translated-v4");
- pnt += strlen("route-filter-translated-v4");
+ str = XSTRDUP(MTYPE_COMMUNITY_STR,
+ "route-filter-translated-v4");
break;
case COMMUNITY_ROUTE_FILTER_v4:
- strcpy(pnt, "route-filter-v4");
- pnt += strlen("route-filter-v4");
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "route-filter-v4");
break;
case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6:
- strcpy(pnt, "route-filter-translated-v6");
- pnt += strlen("route-filter-translated-v6");
+ str = XSTRDUP(MTYPE_COMMUNITY_STR,
+ "route-filter-translated-v6");
break;
case COMMUNITY_ROUTE_FILTER_v6:
- strcpy(pnt, "route-filter-v6");
- pnt += strlen("route-filter-v6");
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "route-filter-v6");
break;
case COMMUNITY_LLGR_STALE:
- strcpy(pnt, "llgr-stale");
- pnt += strlen("llgr-stale");
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "llgr-stale");
break;
case COMMUNITY_NO_LLGR:
- strcpy(pnt, "no-llgr");
- pnt += strlen("no-llgr");
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "no-llgr");
break;
case COMMUNITY_ACCEPT_OWN_NEXTHOP:
- strcpy(pnt, "accept-own-nexthop");
- pnt += strlen("accept-own-nexthop");
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "accept-own-nexthop");
break;
case COMMUNITY_BLACKHOLE:
- strcpy(pnt, "blackhole");
- pnt += strlen("blackhole");
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "blackhole");
break;
case COMMUNITY_NO_EXPORT:
- strcpy(pnt, "no-export");
- pnt += strlen("no-export");
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "no-export");
break;
case COMMUNITY_NO_ADVERTISE:
- strcpy(pnt, "no-advertise");
- pnt += strlen("no-advertise");
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "no-advertise");
break;
case COMMUNITY_LOCAL_AS:
- strcpy(pnt, "local-AS");
- pnt += strlen("local-AS");
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "local-AS");
break;
case COMMUNITY_NO_PEER:
- strcpy(pnt, "no-peer");
- pnt += strlen("no-peer");
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "no-peer");
break;
default:
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "65536:65535");
as = (comval >> 16) & 0xFFFF;
val = comval & 0xFFFF;
- sprintf(pnt, "%u:%d", as, val);
- pnt += strlen(pnt);
+ snprintf(str, strlen(str), "%u:%d", as, val);
break;
}
- *pnt = '\0';
-
return str;
}
/* 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]);
+ val = htonl(val);
community_del_val(com, &val);
}
int community_list_unset(struct community_list_handler *ch, const char *name,
const char *str, int direct, int style)
{
+ struct community_list_master *cm = NULL;
struct community_entry *entry = NULL;
struct community_list *list;
struct community *com = NULL;
/* Lookup community list. */
- list = community_list_lookup(ch, name, COMMUNITY_LIST_MASTER);
+ list = community_list_lookup(ch, name, 0, COMMUNITY_LIST_MASTER);
if (list == NULL)
return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
+ cm = community_list_master_lookup(ch, COMMUNITY_LIST_MASTER);
/* Delete all of entry belongs to this community-list. */
if (!str) {
- community_list_delete(list);
+ community_list_delete(cm, list);
route_map_notify_dependencies(name, RMAP_EVENT_CLIST_DELETED);
return 0;
}
if (com) {
entry = community_list_entry_lookup(list, com, direct);
- community_free(com);
+ community_free(&com);
} else
entry = community_list_entry_lookup(list, str, direct);
if (!entry)
return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
- community_list_entry_delete(list, entry);
+ community_list_entry_delete(cm, list, entry);
route_map_notify_dependencies(name, RMAP_EVENT_CLIST_DELETED);
return 0;
return lcom;
}
+/* Helper to check if every octet do not exceed UINT_MAX */
+static int lcommunity_list_valid(const char *community)
+{
+ int octets = 0;
+ char **splits;
+ int num;
+
+ frrstr_split(community, ":", &splits, &num);
+
+ for (int i = 0; i < num; i++) {
+ if (strtoul(splits[i], NULL, 10) > UINT_MAX)
+ return 0;
+
+ if (strlen(splits[i]) == 0)
+ return 0;
+
+ octets++;
+ XFREE(MTYPE_TMP, splits[i]);
+ }
+ XFREE(MTYPE_TMP, splits);
+
+ if (octets < 3)
+ return 0;
+
+ return 1;
+}
+
/* Set lcommunity-list. */
int lcommunity_list_set(struct community_list_handler *ch, const char *name,
const char *str, int direct, int style)
}
if (str) {
+ if (!lcommunity_list_valid(str))
+ return COMMUNITY_LIST_ERR_MALFORMED_VAL;
+
if (style == LARGE_COMMUNITY_LIST_STANDARD)
lcom = lcommunity_str2com(str);
else
/* Do not put duplicated community entry. */
if (community_list_dup_check(list, entry))
community_entry_free(entry);
- else
+ else {
community_list_entry_add(list, entry);
+ route_map_notify_dependencies(name, RMAP_EVENT_LLIST_ADDED);
+ }
return 0;
}
int lcommunity_list_unset(struct community_list_handler *ch, const char *name,
const char *str, int direct, int style)
{
+ struct community_list_master *cm = NULL;
struct community_entry *entry = NULL;
struct community_list *list;
struct lcommunity *lcom = NULL;
regex_t *regex = NULL;
/* Lookup community list. */
- list = community_list_lookup(ch, name, LARGE_COMMUNITY_LIST_MASTER);
+ list = community_list_lookup(ch, name, 0, LARGE_COMMUNITY_LIST_MASTER);
if (list == NULL)
return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
+ cm = community_list_master_lookup(ch, LARGE_COMMUNITY_LIST_MASTER);
/* Delete all of entry belongs to this community-list. */
if (!str) {
- community_list_delete(list);
+ community_list_delete(cm, list);
+ route_map_notify_dependencies(name, RMAP_EVENT_LLIST_DELETED);
return 0;
}
if (!entry)
return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
- community_list_entry_delete(list, entry);
+ community_list_entry_delete(cm, list, entry);
+ route_map_notify_dependencies(name, RMAP_EVENT_LLIST_DELETED);
return 0;
}
int extcommunity_list_unset(struct community_list_handler *ch, const char *name,
const char *str, int direct, int style)
{
+ struct community_list_master *cm = NULL;
struct community_entry *entry = NULL;
struct community_list *list;
struct ecommunity *ecom = NULL;
/* Lookup extcommunity list. */
- list = community_list_lookup(ch, name, EXTCOMMUNITY_LIST_MASTER);
+ list = community_list_lookup(ch, name, 0, EXTCOMMUNITY_LIST_MASTER);
if (list == NULL)
return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
+ cm = community_list_master_lookup(ch, EXTCOMMUNITY_LIST_MASTER);
/* Delete all of entry belongs to this extcommunity-list. */
if (!str) {
- community_list_delete(list);
+ community_list_delete(cm, list);
route_map_notify_dependencies(name, RMAP_EVENT_ECLIST_DELETED);
return 0;
}
if (!entry)
return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
- community_list_entry_delete(list, entry);
+ community_list_entry_delete(cm, list, entry);
route_map_notify_dependencies(name, RMAP_EVENT_ECLIST_DELETED);
return 0;
struct community_list_handler *ch;
ch = XCALLOC(MTYPE_COMMUNITY_LIST_HANDLER,
sizeof(struct community_list_handler));
+
+ ch->community_list.hash =
+ hash_create_size(4, bgp_clist_hash_key_community_list,
+ bgp_clist_hash_cmp_community_list,
+ "Community List Number Quick Lookup");
+
+ ch->extcommunity_list.hash =
+ hash_create_size(4, bgp_clist_hash_key_community_list,
+ bgp_clist_hash_cmp_community_list,
+ "Extended Community List Quick Lookup");
+
+ ch->lcommunity_list.hash =
+ hash_create_size(4, bgp_clist_hash_key_community_list,
+ bgp_clist_hash_cmp_community_list,
+ "Large Community List Quick Lookup");
+
return ch;
}
cm = &ch->community_list;
while ((list = cm->num.head) != NULL)
- community_list_delete(list);
+ community_list_delete(cm, list);
while ((list = cm->str.head) != NULL)
- community_list_delete(list);
+ community_list_delete(cm, list);
+ hash_free(cm->hash);
cm = &ch->lcommunity_list;
while ((list = cm->num.head) != NULL)
- community_list_delete(list);
+ community_list_delete(cm, list);
while ((list = cm->str.head) != NULL)
- community_list_delete(list);
+ community_list_delete(cm, list);
+ hash_free(cm->hash);
cm = &ch->extcommunity_list;
while ((list = cm->num.head) != NULL)
- community_list_delete(list);
+ community_list_delete(cm, list);
while ((list = cm->str.head) != NULL)
- community_list_delete(list);
+ community_list_delete(cm, list);
+ hash_free(cm->hash);
XFREE(MTYPE_COMMUNITY_LIST_HANDLER, ch);
}