diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c --- a/bgpd/bgp_community.c +++ b/bgpd/bgp_community.c @@ -320,18 +320,30 @@ community_intern (struct community *com) void community_unintern (struct community *com) { - struct community *ret; + struct community *hashed; if (com->refcnt) com->refcnt--; - /* Pull off from hash. */ if (com->refcnt == 0) { - /* Community value com must exist in hash. */ - ret = (struct community *) hash_release (comhash, com); - assert (ret != NULL); - + /* For sake of simplicity in routemap code, we allow + * unintern to be called on communities which need not have been + * interned. Cost is double hash lookup in some cases. + */ + + /* Is there a hash reference? */ + if (hash_lookup (comhash, com)) + { + hashed = (struct community *) hash_release (comhash, com); + assert (hashed != NULL); + + /* com could match value of hashed, but not be same + * struct + */ + if (hashed != com) + community_free (hashed); + } community_free (com); } } diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -1195,7 +1195,7 @@ route_set_community (void *rule, struct struct community *new = NULL; struct community *old; struct community *merge; - + if (type == RMAP_BGP) { rcs = rule; @@ -1215,13 +1215,17 @@ route_set_community (void *rule, struct if (rcs->additive && old) { merge = community_merge (community_dup (old), rcs->com); + community_unintern (old); new = community_uniq_sort (merge); community_free (merge); } else new = community_dup (rcs->com); - - attr->community = new; + + /* we rely on fact that new is the result of a dup and hence + * community_intern will free it. + */ + attr->community = community_intern (new); attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES); }