1 /* Community attribute related functions.
2 * Copyright (C) 1998, 2001 Kunihiro Ishiguro
4 * This file is part of GNU Zebra.
6 * GNU Zebra is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
11 * GNU Zebra is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 #include "bgpd/bgp_memory.h"
29 #include "bgpd/bgp_community.h"
31 /* Hash of community attribute. */
32 static struct hash
*comhash
;
34 /* Allocate a new communities value. */
35 static struct community
*community_new(void)
37 return XCALLOC(MTYPE_COMMUNITY
, sizeof(struct community
));
40 /* Free communities value. */
41 void community_free(struct community
**com
)
46 XFREE(MTYPE_COMMUNITY_VAL
, (*com
)->val
);
47 XFREE(MTYPE_COMMUNITY_STR
, (*com
)->str
);
50 json_object_free((*com
)->json
);
54 XFREE(MTYPE_COMMUNITY
, (*com
));
57 /* Add one community value to the community. */
58 static void community_add_val(struct community
*com
, uint32_t val
)
62 com
->val
= XREALLOC(MTYPE_COMMUNITY_VAL
, com
->val
,
65 com
->val
= XMALLOC(MTYPE_COMMUNITY_VAL
, com_length(com
));
68 memcpy(com_lastval(com
), &val
, sizeof(uint32_t));
71 /* Delete one community. */
72 void community_del_val(struct community
*com
, uint32_t *val
)
80 while (i
< com
->size
) {
81 if (memcmp(com
->val
+ i
, val
, sizeof(uint32_t)) == 0) {
82 c
= com
->size
- i
- 1;
85 memmove(com
->val
+ i
, com
->val
+ (i
+ 1),
91 com
->val
= XREALLOC(MTYPE_COMMUNITY_VAL
,
92 com
->val
, com_length(com
));
94 XFREE(MTYPE_COMMUNITY_VAL
, com
->val
);
102 /* Delete all communities listed in com2 from com1 */
103 struct community
*community_delete(struct community
*com1
,
104 struct community
*com2
)
108 while (i
< com2
->size
) {
109 community_del_val(com1
, com2
->val
+ i
);
116 /* Callback function from qsort(). */
117 static int community_compare(const void *a1
, const void *a2
)
122 memcpy(&v1
, a1
, sizeof(uint32_t));
123 memcpy(&v2
, a2
, sizeof(uint32_t));
134 bool community_include(struct community
*com
, uint32_t val
)
140 for (i
= 0; i
< com
->size
; i
++)
141 if (memcmp(&val
, com_nthval(com
, i
), sizeof(uint32_t)) == 0)
146 uint32_t community_val_get(struct community
*com
, int i
)
151 p
= (uint8_t *)com
->val
;
152 p
+= (i
* COMMUNITY_SIZE
);
154 memcpy(&val
, p
, sizeof(uint32_t));
159 /* Sort and uniq given community. */
160 struct community
*community_uniq_sort(struct community
*com
)
163 struct community
*new;
169 new = community_new();
172 for (i
= 0; i
< com
->size
; i
++) {
173 val
= community_val_get(com
, i
);
175 if (!community_include(new, val
))
176 community_add_val(new, val
);
179 qsort(new->val
, new->size
, sizeof(uint32_t), community_compare
);
184 /* Convert communities attribute to string.
186 For Well-known communities value, below keyword is used.
189 0xFFFF0000 "graceful-shutdown"
190 0xFFFF0001 "accept-own"
191 0xFFFF0002 "route-filter-translated-v4"
192 0xFFFF0003 "route-filter-v4"
193 0xFFFF0004 "route-filter-translated-v6"
194 0xFFFF0005 "route-filter-v6"
195 0xFFFF0006 "llgr-stale"
197 0xFFFF0008 "accept-own-nexthop"
198 0xFFFF029A "blackhole"
199 0xFFFFFF01 "no-export"
200 0xFFFFFF02 "no-advertise"
201 0xFFFFFF03 "local-AS"
204 For other values, "AS:VAL" format is used. */
205 static void set_community_string(struct community
*com
, bool make_json
)
214 json_object
*json_community_list
= NULL
;
215 json_object
*json_string
= NULL
;
221 com
->json
= json_object_new_object();
222 json_community_list
= json_object_new_array();
225 /* When communities attribute is empty. */
226 if (com
->size
== 0) {
227 str
= XMALLOC(MTYPE_COMMUNITY_STR
, 1);
231 json_object_string_add(com
->json
, "string", "");
232 json_object_object_add(com
->json
, "list",
233 json_community_list
);
239 /* Memory allocation is time consuming work. So we calculate
240 required string length first. */
243 for (i
= 0; i
< com
->size
; i
++) {
244 memcpy(&comval
, com_nthval(com
, i
), sizeof(uint32_t));
245 comval
= ntohl(comval
);
248 case COMMUNITY_INTERNET
:
249 len
+= strlen(" internet");
251 case COMMUNITY_GSHUT
:
252 len
+= strlen(" graceful-shutdown");
254 case COMMUNITY_ACCEPT_OWN
:
255 len
+= strlen(" accept-own");
257 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4
:
258 len
+= strlen(" route-filter-translated-v4");
260 case COMMUNITY_ROUTE_FILTER_v4
:
261 len
+= strlen(" route-filter-v4");
263 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6
:
264 len
+= strlen(" route-filter-translated-v6");
266 case COMMUNITY_ROUTE_FILTER_v6
:
267 len
+= strlen(" route-filter-v6");
269 case COMMUNITY_LLGR_STALE
:
270 len
+= strlen(" llgr-stale");
272 case COMMUNITY_NO_LLGR
:
273 len
+= strlen(" no-llgr");
275 case COMMUNITY_ACCEPT_OWN_NEXTHOP
:
276 len
+= strlen(" accept-own-nexthop");
278 case COMMUNITY_BLACKHOLE
:
279 len
+= strlen(" blackhole");
281 case COMMUNITY_NO_EXPORT
:
282 len
+= strlen(" no-export");
284 case COMMUNITY_NO_ADVERTISE
:
285 len
+= strlen(" no-advertise");
287 case COMMUNITY_LOCAL_AS
:
288 len
+= strlen(" local-AS");
290 case COMMUNITY_NO_PEER
:
291 len
+= strlen(" no-peer");
294 len
+= strlen(" 65536:65535");
299 /* Allocate memory. */
300 str
= XCALLOC(MTYPE_COMMUNITY_STR
, len
);
303 /* Fill in string. */
304 for (i
= 0; i
< com
->size
; i
++) {
305 memcpy(&comval
, com_nthval(com
, i
), sizeof(uint32_t));
306 comval
= ntohl(comval
);
311 strlcat(str
, " ", len
);
314 case COMMUNITY_INTERNET
:
315 strlcat(str
, "internet", len
);
318 json_object_new_string("internet");
319 json_object_array_add(json_community_list
,
323 case COMMUNITY_GSHUT
:
324 strlcat(str
, "graceful-shutdown", len
);
326 json_string
= json_object_new_string(
328 json_object_array_add(json_community_list
,
332 case COMMUNITY_ACCEPT_OWN
:
333 strlcat(str
, "accept-own", len
);
335 json_string
= json_object_new_string(
337 json_object_array_add(json_community_list
,
341 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4
:
342 strlcat(str
, "route-filter-translated-v4", len
);
344 json_string
= json_object_new_string(
345 "routeFilterTranslatedV4");
346 json_object_array_add(json_community_list
,
350 case COMMUNITY_ROUTE_FILTER_v4
:
351 strlcat(str
, "route-filter-v4", len
);
353 json_string
= json_object_new_string(
355 json_object_array_add(json_community_list
,
359 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6
:
360 strlcat(str
, "route-filter-translated-v6", len
);
362 json_string
= json_object_new_string(
363 "routeFilterTranslatedV6");
364 json_object_array_add(json_community_list
,
368 case COMMUNITY_ROUTE_FILTER_v6
:
369 strlcat(str
, "route-filter-v6", len
);
371 json_string
= json_object_new_string(
373 json_object_array_add(json_community_list
,
377 case COMMUNITY_LLGR_STALE
:
378 strlcat(str
, "llgr-stale", len
);
380 json_string
= json_object_new_string(
382 json_object_array_add(json_community_list
,
386 case COMMUNITY_NO_LLGR
:
387 strlcat(str
, "no-llgr", len
);
389 json_string
= json_object_new_string(
391 json_object_array_add(json_community_list
,
395 case COMMUNITY_ACCEPT_OWN_NEXTHOP
:
396 strlcat(str
, "accept-own-nexthop", len
);
398 json_string
= json_object_new_string(
400 json_object_array_add(json_community_list
,
404 case COMMUNITY_BLACKHOLE
:
405 strlcat(str
, "blackhole", len
);
407 json_string
= json_object_new_string(
409 json_object_array_add(json_community_list
,
413 case COMMUNITY_NO_EXPORT
:
414 strlcat(str
, "no-export", len
);
417 json_object_new_string("noExport");
418 json_object_array_add(json_community_list
,
422 case COMMUNITY_NO_ADVERTISE
:
423 strlcat(str
, "no-advertise", len
);
426 json_object_new_string("noAdvertise");
427 json_object_array_add(json_community_list
,
431 case COMMUNITY_LOCAL_AS
:
432 strlcat(str
, "local-AS", len
);
434 json_string
= json_object_new_string("localAs");
435 json_object_array_add(json_community_list
,
439 case COMMUNITY_NO_PEER
:
440 strlcat(str
, "no-peer", len
);
442 json_string
= json_object_new_string("noPeer");
443 json_object_array_add(json_community_list
,
448 as
= (comval
>> 16) & 0xFFFF;
449 val
= comval
& 0xFFFF;
451 snprintf(buf
, sizeof(buf
), "%u:%d", as
, val
);
452 strlcat(str
, buf
, len
);
454 json_string
= json_object_new_string(buf
);
455 json_object_array_add(json_community_list
,
463 json_object_string_add(com
->json
, "string", str
);
464 json_object_object_add(com
->json
, "list", json_community_list
);
469 /* Intern communities attribute. */
470 struct community
*community_intern(struct community
*com
)
472 struct community
*find
;
474 /* Assert this community structure is not interned. */
475 assert(com
->refcnt
== 0);
477 /* Lookup community hash. */
478 find
= (struct community
*)hash_get(comhash
, com
, hash_alloc_intern
);
480 /* Arguemnt com is allocated temporary. So when it is not used in
481 hash, it should be freed. */
483 community_free(&com
);
485 /* Increment refrence counter. */
490 set_community_string(find
, false);
495 /* Free community attribute. */
496 void community_unintern(struct community
**com
)
498 struct community
*ret
;
503 /* Pull off from hash. */
504 if ((*com
)->refcnt
== 0) {
505 /* Community value com must exist in hash. */
506 ret
= (struct community
*)hash_release(comhash
, *com
);
513 /* Create new community attribute. */
514 struct community
*community_parse(uint32_t *pnt
, unsigned short length
)
516 struct community tmp
;
517 struct community
*new;
519 /* If length is malformed return NULL. */
520 if (length
% COMMUNITY_SIZE
)
523 /* Make temporary community for hash look up. */
524 tmp
.size
= length
/ COMMUNITY_SIZE
;
527 new = community_uniq_sort(&tmp
);
529 return community_intern(new);
532 struct community
*community_dup(struct community
*com
)
534 struct community
*new;
536 new = XCALLOC(MTYPE_COMMUNITY
, sizeof(struct community
));
537 new->size
= com
->size
;
539 new->val
= XMALLOC(MTYPE_COMMUNITY_VAL
,
540 com
->size
* COMMUNITY_SIZE
);
541 memcpy(new->val
, com
->val
, com
->size
* COMMUNITY_SIZE
);
547 /* Retrun string representation of communities attribute. */
548 char *community_str(struct community
*com
, bool make_json
)
553 if (make_json
&& !com
->json
&& com
->str
)
554 XFREE(MTYPE_COMMUNITY_STR
, com
->str
);
557 set_community_string(com
, make_json
);
561 /* Make hash value of community attribute. This function is used by
563 unsigned int community_hash_make(const struct community
*com
)
565 uint32_t *pnt
= com
->val
;
567 return jhash2(pnt
, com
->size
, 0x43ea96c1);
570 bool community_match(const struct community
*com1
, const struct community
*com2
)
575 if (com1
== NULL
&& com2
== NULL
)
578 if (com1
== NULL
|| com2
== NULL
)
581 if (com1
->size
< com2
->size
)
584 /* Every community on com2 needs to be on com1 for this to match */
585 while (i
< com1
->size
&& j
< com2
->size
) {
586 if (memcmp(com1
->val
+ i
, com2
->val
+ j
, sizeof(uint32_t)) == 0)
597 /* If two aspath have same value then return 1 else return 0. This
598 function is used by hash package. */
599 bool community_cmp(const struct community
*com1
, const struct community
*com2
)
601 if (com1
== NULL
&& com2
== NULL
)
603 if (com1
== NULL
|| com2
== NULL
)
606 if (com1
->size
== com2
->size
)
607 if (memcmp(com1
->val
, com2
->val
, com1
->size
* COMMUNITY_SIZE
)
613 /* Add com2 to the end of com1. */
614 struct community
*community_merge(struct community
*com1
,
615 struct community
*com2
)
619 XREALLOC(MTYPE_COMMUNITY_VAL
, com1
->val
,
620 (com1
->size
+ com2
->size
) * COMMUNITY_SIZE
);
622 com1
->val
= XMALLOC(MTYPE_COMMUNITY_VAL
,
623 (com1
->size
+ com2
->size
) * COMMUNITY_SIZE
);
625 memcpy(com1
->val
+ com1
->size
, com2
->val
, com2
->size
* COMMUNITY_SIZE
);
626 com1
->size
+= com2
->size
;
631 /* Community token enum. */
632 enum community_token
{
634 community_token_gshut
,
635 community_token_accept_own
,
636 community_token_route_filter_translated_v4
,
637 community_token_route_filter_v4
,
638 community_token_route_filter_translated_v6
,
639 community_token_route_filter_v6
,
640 community_token_llgr_stale
,
641 community_token_no_llgr
,
642 community_token_accept_own_nexthop
,
643 community_token_blackhole
,
644 community_token_no_export
,
645 community_token_no_advertise
,
646 community_token_local_as
,
647 community_token_no_peer
,
648 community_token_unknown
651 /* Get next community token from string. */
653 community_gettoken(const char *buf
, enum community_token
*token
, uint32_t *val
)
657 /* Skip white space. */
658 while (isspace((unsigned char)*p
))
661 /* Check the end of the line. */
665 /* Well known community string check. */
666 if (isalpha((unsigned char)*p
)) {
667 if (strncmp(p
, "internet", strlen("internet")) == 0) {
668 *val
= COMMUNITY_INTERNET
;
669 *token
= community_token_no_export
;
670 p
+= strlen("internet");
673 if (strncmp(p
, "graceful-shutdown", strlen("graceful-shutdown"))
675 *val
= COMMUNITY_GSHUT
;
676 *token
= community_token_gshut
;
677 p
+= strlen("graceful-shutdown");
680 if (strncmp(p
, "accept-own-nexthop",
681 strlen("accept-own-nexthop"))
683 *val
= COMMUNITY_ACCEPT_OWN_NEXTHOP
;
684 *token
= community_token_accept_own_nexthop
;
685 p
+= strlen("accept-own-nexthop");
688 if (strncmp(p
, "accept-own", strlen("accept-own"))
690 *val
= COMMUNITY_ACCEPT_OWN
;
691 *token
= community_token_accept_own
;
692 p
+= strlen("accept-own");
695 if (strncmp(p
, "route-filter-translated-v4",
696 strlen("route-filter-translated-v4"))
698 *val
= COMMUNITY_ROUTE_FILTER_TRANSLATED_v4
;
699 *token
= community_token_route_filter_translated_v4
;
700 p
+= strlen("route-filter-translated-v4");
703 if (strncmp(p
, "route-filter-v4", strlen("route-filter-v4"))
705 *val
= COMMUNITY_ROUTE_FILTER_v4
;
706 *token
= community_token_route_filter_v4
;
707 p
+= strlen("route-filter-v4");
710 if (strncmp(p
, "route-filter-translated-v6",
711 strlen("route-filter-translated-v6"))
713 *val
= COMMUNITY_ROUTE_FILTER_TRANSLATED_v6
;
714 *token
= community_token_route_filter_translated_v6
;
715 p
+= strlen("route-filter-translated-v6");
718 if (strncmp(p
, "route-filter-v6", strlen("route-filter-v6"))
720 *val
= COMMUNITY_ROUTE_FILTER_v6
;
721 *token
= community_token_route_filter_v6
;
722 p
+= strlen("route-filter-v6");
725 if (strncmp(p
, "llgr-stale", strlen("llgr-stale"))
727 *val
= COMMUNITY_LLGR_STALE
;
728 *token
= community_token_llgr_stale
;
729 p
+= strlen("llgr-stale");
732 if (strncmp(p
, "no-llgr", strlen("no-llgr"))
734 *val
= COMMUNITY_NO_LLGR
;
735 *token
= community_token_no_llgr
;
736 p
+= strlen("no-llgr");
739 if (strncmp(p
, "blackhole", strlen("blackhole"))
741 *val
= COMMUNITY_BLACKHOLE
;
742 *token
= community_token_blackhole
;
743 p
+= strlen("blackhole");
746 if (strncmp(p
, "no-export", strlen("no-export")) == 0) {
747 *val
= COMMUNITY_NO_EXPORT
;
748 *token
= community_token_no_export
;
749 p
+= strlen("no-export");
752 if (strncmp(p
, "no-advertise", strlen("no-advertise")) == 0) {
753 *val
= COMMUNITY_NO_ADVERTISE
;
754 *token
= community_token_no_advertise
;
755 p
+= strlen("no-advertise");
758 if (strncmp(p
, "local-AS", strlen("local-AS")) == 0) {
759 *val
= COMMUNITY_LOCAL_AS
;
760 *token
= community_token_local_as
;
761 p
+= strlen("local-AS");
764 if (strncmp(p
, "no-peer", strlen("no-peer")) == 0) {
765 *val
= COMMUNITY_NO_PEER
;
766 *token
= community_token_no_peer
;
767 p
+= strlen("no-peer");
771 /* Unknown string. */
772 *token
= community_token_unknown
;
776 /* Community value. */
777 if (isdigit((unsigned char)*p
)) {
780 uint32_t community_low
= 0;
781 uint32_t community_high
= 0;
783 while (isdigit((unsigned char)*p
) || *p
== ':') {
786 *token
= community_token_unknown
;
792 if (community_low
> UINT16_MAX
) {
794 community_token_unknown
;
798 community_high
= community_low
<< 16;
804 community_low
+= (*p
- '0');
809 *token
= community_token_unknown
;
813 if (community_low
> UINT16_MAX
) {
814 *token
= community_token_unknown
;
818 *val
= community_high
+ community_low
;
819 *token
= community_token_val
;
822 *token
= community_token_unknown
;
826 /* convert string to community structure */
827 struct community
*community_str2com(const char *str
)
829 struct community
*com
= NULL
;
830 struct community
*com_sort
= NULL
;
832 enum community_token token
= community_token_unknown
;
835 str
= community_gettoken(str
, &token
, &val
);
838 case community_token_val
:
839 case community_token_gshut
:
840 case community_token_accept_own
:
841 case community_token_route_filter_translated_v4
:
842 case community_token_route_filter_v4
:
843 case community_token_route_filter_translated_v6
:
844 case community_token_route_filter_v6
:
845 case community_token_llgr_stale
:
846 case community_token_no_llgr
:
847 case community_token_accept_own_nexthop
:
848 case community_token_blackhole
:
849 case community_token_no_export
:
850 case community_token_no_advertise
:
851 case community_token_local_as
:
852 case community_token_no_peer
:
854 com
= community_new();
857 community_add_val(com
, val
);
859 case community_token_unknown
:
861 community_free(&com
);
866 com_sort
= community_uniq_sort(com
);
867 community_free(&com
);
872 /* Return communities hash entry count. */
873 unsigned long community_count(void)
875 return comhash
->count
;
878 /* Return communities hash. */
879 struct hash
*community_hash(void)
884 /* Initialize comminity related hash. */
885 void community_init(void)
888 hash_create((unsigned int (*)(const void *))community_hash_make
,
889 (bool (*)(const void *, const void *))community_cmp
,
890 "BGP Community Hash");
893 void community_finish(void)
899 static struct community
*bgp_aggr_community_lookup(
900 struct bgp_aggregate
*aggregate
,
901 struct community
*community
)
903 return hash_lookup(aggregate
->community_hash
, community
);
906 static void *bgp_aggr_communty_hash_alloc(void *p
)
908 struct community
*ref
= (struct community
*)p
;
909 struct community
*community
= NULL
;
911 community
= community_dup(ref
);
915 static void bgp_aggr_community_prepare(struct hash_bucket
*hb
, void *arg
)
917 struct community
*hb_community
= hb
->data
;
918 struct community
**aggr_community
= arg
;
921 *aggr_community
= community_merge(*aggr_community
,
924 *aggr_community
= community_dup(hb_community
);
927 void bgp_aggr_community_remove(void *arg
)
929 struct community
*community
= arg
;
931 community_free(&community
);
934 void bgp_compute_aggregate_community(struct bgp_aggregate
*aggregate
,
935 struct community
*community
)
937 bgp_compute_aggregate_community_hash(aggregate
, community
);
938 bgp_compute_aggregate_community_val(aggregate
);
942 void bgp_compute_aggregate_community_hash(struct bgp_aggregate
*aggregate
,
943 struct community
*community
)
945 struct community
*aggr_community
= NULL
;
947 if ((aggregate
== NULL
) || (community
== NULL
))
950 /* Create hash if not already created.
952 if (aggregate
->community_hash
== NULL
)
953 aggregate
->community_hash
= hash_create(
954 (unsigned int (*)(const void *))community_hash_make
,
955 (bool (*)(const void *, const void *))community_cmp
,
956 "BGP Aggregator community hash");
958 aggr_community
= bgp_aggr_community_lookup(aggregate
, community
);
959 if (aggr_community
== NULL
) {
960 /* Insert community into hash.
962 aggr_community
= hash_get(aggregate
->community_hash
, community
,
963 bgp_aggr_communty_hash_alloc
);
966 /* Increment reference counter.
968 aggr_community
->refcnt
++;
971 void bgp_compute_aggregate_community_val(struct bgp_aggregate
*aggregate
)
973 struct community
*commerge
= NULL
;
975 if (aggregate
== NULL
)
978 /* Re-compute aggregate's community.
980 if (aggregate
->community
)
981 community_free(&aggregate
->community
);
982 if (aggregate
->community_hash
&&
983 aggregate
->community_hash
->count
) {
984 hash_iterate(aggregate
->community_hash
,
985 bgp_aggr_community_prepare
,
986 &aggregate
->community
);
987 commerge
= aggregate
->community
;
988 aggregate
->community
= community_uniq_sort(commerge
);
990 community_free(&commerge
);
996 void bgp_remove_community_from_aggregate(struct bgp_aggregate
*aggregate
,
997 struct community
*community
)
999 struct community
*aggr_community
= NULL
;
1000 struct community
*ret_comm
= NULL
;
1003 || (!aggregate
->community_hash
)
1007 /* Look-up the community in the hash.
1009 aggr_community
= bgp_aggr_community_lookup(aggregate
, community
);
1010 if (aggr_community
) {
1011 aggr_community
->refcnt
--;
1013 if (aggr_community
->refcnt
== 0) {
1014 ret_comm
= hash_release(aggregate
->community_hash
,
1016 community_free(&ret_comm
);
1018 bgp_compute_aggregate_community_val(aggregate
);
1023 void bgp_remove_comm_from_aggregate_hash(struct bgp_aggregate
*aggregate
,
1024 struct community
*community
)
1027 struct community
*aggr_community
= NULL
;
1028 struct community
*ret_comm
= NULL
;
1031 || (!aggregate
->community_hash
)
1035 /* Look-up the community in the hash.
1037 aggr_community
= bgp_aggr_community_lookup(aggregate
, community
);
1038 if (aggr_community
) {
1039 aggr_community
->refcnt
--;
1041 if (aggr_community
->refcnt
== 0) {
1042 ret_comm
= hash_release(aggregate
->community_hash
,
1044 community_free(&ret_comm
);