1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* Community attribute related functions.
3 * Copyright (C) 1998, 2001 Kunihiro Ishiguro
14 #include "bgpd/bgp_memory.h"
15 #include "bgpd/bgp_community.h"
16 #include "bgpd/bgp_community_alias.h"
18 /* Hash of community attribute. */
19 static struct hash
*comhash
;
21 /* Allocate a new communities value. */
22 static struct community
*community_new(void)
24 return XCALLOC(MTYPE_COMMUNITY
, sizeof(struct community
));
27 /* Free communities value. */
28 void community_free(struct community
**com
)
33 XFREE(MTYPE_COMMUNITY_VAL
, (*com
)->val
);
34 XFREE(MTYPE_COMMUNITY_STR
, (*com
)->str
);
37 json_object_free((*com
)->json
);
41 XFREE(MTYPE_COMMUNITY
, (*com
));
44 /* Add one community value to the community. */
45 void community_add_val(struct community
*com
, uint32_t val
)
48 com
->val
= XREALLOC(MTYPE_COMMUNITY_VAL
, com
->val
, com_length(com
));
51 memcpy(com_lastval(com
), &val
, sizeof(uint32_t));
54 /* Delete one community. */
55 void community_del_val(struct community
*com
, uint32_t *val
)
63 while (i
< com
->size
) {
64 if (memcmp(com
->val
+ i
, val
, sizeof(uint32_t)) == 0) {
65 c
= com
->size
- i
- 1;
68 memmove(com
->val
+ i
, com
->val
+ (i
+ 1),
74 com
->val
= XREALLOC(MTYPE_COMMUNITY_VAL
,
75 com
->val
, com_length(com
));
77 XFREE(MTYPE_COMMUNITY_VAL
, com
->val
);
85 /* Delete all communities listed in com2 from com1 */
86 struct community
*community_delete(struct community
*com1
,
87 struct community
*com2
)
91 while (i
< com2
->size
) {
92 community_del_val(com1
, com2
->val
+ i
);
99 /* Callback function from qsort(). */
100 static int community_compare(const void *a1
, const void *a2
)
105 memcpy(&v1
, a1
, sizeof(uint32_t));
106 memcpy(&v2
, a2
, sizeof(uint32_t));
117 bool community_include(struct community
*com
, uint32_t val
)
123 for (i
= 0; i
< com
->size
; i
++)
124 if (memcmp(&val
, com_nthval(com
, i
), sizeof(uint32_t)) == 0)
129 uint32_t community_val_get(struct community
*com
, int i
)
134 p
= (uint8_t *)com
->val
;
135 p
+= (i
* COMMUNITY_SIZE
);
137 memcpy(&val
, p
, sizeof(uint32_t));
142 /* Sort and uniq given community. */
143 struct community
*community_uniq_sort(struct community
*com
)
146 struct community
*new;
152 new = community_new();
155 for (i
= 0; i
< com
->size
; i
++) {
156 val
= community_val_get(com
, i
);
158 if (!community_include(new, val
))
159 community_add_val(new, val
);
162 qsort(new->val
, new->size
, sizeof(uint32_t), community_compare
);
167 /* Convert communities attribute to string.
169 For Well-known communities value, below keyword is used.
172 0xFFFF0000 "graceful-shutdown"
173 0xFFFF0001 "accept-own"
174 0xFFFF0002 "route-filter-translated-v4"
175 0xFFFF0003 "route-filter-v4"
176 0xFFFF0004 "route-filter-translated-v6"
177 0xFFFF0005 "route-filter-v6"
178 0xFFFF0006 "llgr-stale"
180 0xFFFF0008 "accept-own-nexthop"
181 0xFFFF029A "blackhole"
182 0xFFFFFF01 "no-export"
183 0xFFFFFF02 "no-advertise"
184 0xFFFFFF03 "local-AS"
187 For other values, "AS:VAL" format is used. */
188 static void set_community_string(struct community
*com
, bool make_json
,
189 bool translate_alias
)
198 json_object
*json_community_list
= NULL
;
199 json_object
*json_string
= NULL
;
205 com
->json
= json_object_new_object();
206 json_community_list
= json_object_new_array();
209 /* When communities attribute is empty. */
210 if (com
->size
== 0) {
211 str
= XMALLOC(MTYPE_COMMUNITY_STR
, 1);
215 json_object_string_add(com
->json
, "string", "");
216 json_object_object_add(com
->json
, "list",
217 json_community_list
);
223 /* Memory allocation is time consuming work. So we calculate
224 required string length first. */
227 for (i
= 0; i
< com
->size
; i
++) {
228 memcpy(&comval
, com_nthval(com
, i
), sizeof(uint32_t));
229 comval
= ntohl(comval
);
232 case COMMUNITY_INTERNET
:
233 len
+= strlen(" internet");
235 case COMMUNITY_GSHUT
:
236 len
+= strlen(" graceful-shutdown");
238 case COMMUNITY_ACCEPT_OWN
:
239 len
+= strlen(" accept-own");
241 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4
:
242 len
+= strlen(" route-filter-translated-v4");
244 case COMMUNITY_ROUTE_FILTER_v4
:
245 len
+= strlen(" route-filter-v4");
247 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6
:
248 len
+= strlen(" route-filter-translated-v6");
250 case COMMUNITY_ROUTE_FILTER_v6
:
251 len
+= strlen(" route-filter-v6");
253 case COMMUNITY_LLGR_STALE
:
254 len
+= strlen(" llgr-stale");
256 case COMMUNITY_NO_LLGR
:
257 len
+= strlen(" no-llgr");
259 case COMMUNITY_ACCEPT_OWN_NEXTHOP
:
260 len
+= strlen(" accept-own-nexthop");
262 case COMMUNITY_BLACKHOLE
:
263 len
+= strlen(" blackhole");
265 case COMMUNITY_NO_EXPORT
:
266 len
+= strlen(" no-export");
268 case COMMUNITY_NO_ADVERTISE
:
269 len
+= strlen(" no-advertise");
271 case COMMUNITY_LOCAL_AS
:
272 len
+= strlen(" local-AS");
274 case COMMUNITY_NO_PEER
:
275 len
+= strlen(" no-peer");
283 /* Allocate memory. */
284 str
= XCALLOC(MTYPE_COMMUNITY_STR
, len
);
287 /* Fill in string. */
288 for (i
= 0; i
< com
->size
; i
++) {
289 memcpy(&comval
, com_nthval(com
, i
), sizeof(uint32_t));
290 comval
= ntohl(comval
);
295 strlcat(str
, " ", len
);
298 case COMMUNITY_INTERNET
:
299 strlcat(str
, "internet", len
);
302 json_object_new_string("internet");
303 json_object_array_add(json_community_list
,
307 case COMMUNITY_GSHUT
:
308 strlcat(str
, "graceful-shutdown", len
);
310 json_string
= json_object_new_string(
312 json_object_array_add(json_community_list
,
316 case COMMUNITY_ACCEPT_OWN
:
317 strlcat(str
, "accept-own", len
);
319 json_string
= json_object_new_string(
321 json_object_array_add(json_community_list
,
325 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4
:
326 strlcat(str
, "route-filter-translated-v4", len
);
328 json_string
= json_object_new_string(
329 "routeFilterTranslatedV4");
330 json_object_array_add(json_community_list
,
334 case COMMUNITY_ROUTE_FILTER_v4
:
335 strlcat(str
, "route-filter-v4", len
);
337 json_string
= json_object_new_string(
339 json_object_array_add(json_community_list
,
343 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6
:
344 strlcat(str
, "route-filter-translated-v6", len
);
346 json_string
= json_object_new_string(
347 "routeFilterTranslatedV6");
348 json_object_array_add(json_community_list
,
352 case COMMUNITY_ROUTE_FILTER_v6
:
353 strlcat(str
, "route-filter-v6", len
);
355 json_string
= json_object_new_string(
357 json_object_array_add(json_community_list
,
361 case COMMUNITY_LLGR_STALE
:
362 strlcat(str
, "llgr-stale", len
);
364 json_string
= json_object_new_string(
366 json_object_array_add(json_community_list
,
370 case COMMUNITY_NO_LLGR
:
371 strlcat(str
, "no-llgr", len
);
373 json_string
= json_object_new_string(
375 json_object_array_add(json_community_list
,
379 case COMMUNITY_ACCEPT_OWN_NEXTHOP
:
380 strlcat(str
, "accept-own-nexthop", len
);
382 json_string
= json_object_new_string(
384 json_object_array_add(json_community_list
,
388 case COMMUNITY_BLACKHOLE
:
389 strlcat(str
, "blackhole", len
);
391 json_string
= json_object_new_string(
393 json_object_array_add(json_community_list
,
397 case COMMUNITY_NO_EXPORT
:
398 strlcat(str
, "no-export", len
);
401 json_object_new_string("noExport");
402 json_object_array_add(json_community_list
,
406 case COMMUNITY_NO_ADVERTISE
:
407 strlcat(str
, "no-advertise", len
);
410 json_object_new_string("noAdvertise");
411 json_object_array_add(json_community_list
,
415 case COMMUNITY_LOCAL_AS
:
416 strlcat(str
, "local-AS", len
);
418 json_string
= json_object_new_string("localAs");
419 json_object_array_add(json_community_list
,
423 case COMMUNITY_NO_PEER
:
424 strlcat(str
, "no-peer", len
);
426 json_string
= json_object_new_string("noPeer");
427 json_object_array_add(json_community_list
,
432 as
= (comval
>> 16) & 0xFFFF;
433 val
= comval
& 0xFFFF;
435 snprintf(buf
, sizeof(buf
), "%u:%d", as
, val
);
436 const char *com2alias
=
437 translate_alias
? bgp_community2alias(buf
)
440 strlcat(str
, com2alias
, len
);
442 json_string
= json_object_new_string(com2alias
);
443 json_object_array_add(json_community_list
,
451 json_object_string_add(com
->json
, "string", str
);
452 json_object_object_add(com
->json
, "list", json_community_list
);
457 /* Intern communities attribute. */
458 struct community
*community_intern(struct community
*com
)
460 struct community
*find
;
462 /* Assert this community structure is not interned. */
463 assert(com
->refcnt
== 0);
465 /* Lookup community hash. */
466 find
= (struct community
*)hash_get(comhash
, com
, hash_alloc_intern
);
468 /* Arguemnt com is allocated temporary. So when it is not used in
469 hash, it should be freed. */
471 community_free(&com
);
473 /* Increment refrence counter. */
478 set_community_string(find
, false, true);
483 /* Free community attribute. */
484 void community_unintern(struct community
**com
)
486 struct community
*ret
;
494 /* Pull off from hash. */
495 if ((*com
)->refcnt
== 0) {
496 /* Community value com must exist in hash. */
497 ret
= (struct community
*)hash_release(comhash
, *com
);
504 /* Create new community attribute. */
505 struct community
*community_parse(uint32_t *pnt
, unsigned short length
)
507 struct community tmp
;
508 struct community
*new;
510 /* If length is malformed return NULL. */
511 if (length
% COMMUNITY_SIZE
)
514 /* Make temporary community for hash look up. */
515 tmp
.size
= length
/ COMMUNITY_SIZE
;
518 new = community_uniq_sort(&tmp
);
520 return community_intern(new);
523 struct community
*community_dup(struct community
*com
)
525 struct community
*new;
527 new = XCALLOC(MTYPE_COMMUNITY
, sizeof(struct community
));
528 new->size
= com
->size
;
530 new->val
= XMALLOC(MTYPE_COMMUNITY_VAL
,
531 com
->size
* COMMUNITY_SIZE
);
532 memcpy(new->val
, com
->val
, com
->size
* COMMUNITY_SIZE
);
538 /* Return string representation of communities attribute. */
539 char *community_str(struct community
*com
, bool make_json
, bool translate_alias
)
544 if (make_json
&& !com
->json
&& com
->str
)
545 XFREE(MTYPE_COMMUNITY_STR
, com
->str
);
548 set_community_string(com
, make_json
, translate_alias
);
552 /* Make hash value of community attribute. This function is used by
554 unsigned int community_hash_make(const struct community
*com
)
556 uint32_t *pnt
= com
->val
;
558 return jhash2(pnt
, com
->size
, 0x43ea96c1);
561 bool community_match(const struct community
*com1
, const struct community
*com2
)
566 if (com1
== NULL
&& com2
== NULL
)
569 if (com1
== NULL
|| com2
== NULL
)
572 if (com1
->size
< com2
->size
)
575 /* Every community on com2 needs to be on com1 for this to match */
576 while (i
< com1
->size
&& j
< com2
->size
) {
577 if (memcmp(com1
->val
+ i
, com2
->val
+ j
, sizeof(uint32_t)) == 0)
588 bool community_cmp(const struct community
*com1
, const struct community
*com2
)
590 if (com1
== NULL
&& com2
== NULL
)
592 if (com1
== NULL
|| com2
== NULL
)
595 if (com1
->size
== com2
->size
)
596 if (memcmp(com1
->val
, com2
->val
, com1
->size
* COMMUNITY_SIZE
)
602 /* Add com2 to the end of com1. */
603 struct community
*community_merge(struct community
*com1
,
604 struct community
*com2
)
606 com1
->val
= XREALLOC(MTYPE_COMMUNITY_VAL
, com1
->val
,
607 (com1
->size
+ com2
->size
) * COMMUNITY_SIZE
);
609 memcpy(com1
->val
+ com1
->size
, com2
->val
, com2
->size
* COMMUNITY_SIZE
);
610 com1
->size
+= com2
->size
;
615 /* Community token enum. */
616 enum community_token
{
618 community_token_gshut
,
619 community_token_accept_own
,
620 community_token_route_filter_translated_v4
,
621 community_token_route_filter_v4
,
622 community_token_route_filter_translated_v6
,
623 community_token_route_filter_v6
,
624 community_token_llgr_stale
,
625 community_token_no_llgr
,
626 community_token_accept_own_nexthop
,
627 community_token_blackhole
,
628 community_token_no_export
,
629 community_token_no_advertise
,
630 community_token_local_as
,
631 community_token_no_peer
,
632 community_token_unknown
635 /* Helper to check if a given community is valid */
636 static bool community_valid(const char *community
)
643 frrstr_split(community
, ":", &splits
, &num
);
645 for (int i
= 0; i
< num
; i
++) {
646 if (strtoul(splits
[i
], NULL
, 10) > UINT16_MAX
)
649 if (strlen(splits
[i
]) == 0)
653 XFREE(MTYPE_TMP
, splits
[i
]);
655 XFREE(MTYPE_TMP
, splits
);
657 return (octets
< 2 || invalid
) ? false : true;
660 /* Get next community token from string. */
662 community_gettoken(const char *buf
, enum community_token
*token
, uint32_t *val
)
666 /* Skip white space. */
667 while (isspace((unsigned char)*p
))
670 /* Check the end of the line. */
674 /* Well known community string check. */
675 if (isalpha((unsigned char)*p
)) {
676 if (strncmp(p
, "internet", strlen("internet")) == 0) {
677 *val
= COMMUNITY_INTERNET
;
678 *token
= community_token_no_export
;
679 p
+= strlen("internet");
682 if (strncmp(p
, "graceful-shutdown", strlen("graceful-shutdown"))
684 *val
= COMMUNITY_GSHUT
;
685 *token
= community_token_gshut
;
686 p
+= strlen("graceful-shutdown");
689 if (strncmp(p
, "accept-own-nexthop",
690 strlen("accept-own-nexthop"))
692 *val
= COMMUNITY_ACCEPT_OWN_NEXTHOP
;
693 *token
= community_token_accept_own_nexthop
;
694 p
+= strlen("accept-own-nexthop");
697 if (strncmp(p
, "accept-own", strlen("accept-own"))
699 *val
= COMMUNITY_ACCEPT_OWN
;
700 *token
= community_token_accept_own
;
701 p
+= strlen("accept-own");
704 if (strncmp(p
, "route-filter-translated-v4",
705 strlen("route-filter-translated-v4"))
707 *val
= COMMUNITY_ROUTE_FILTER_TRANSLATED_v4
;
708 *token
= community_token_route_filter_translated_v4
;
709 p
+= strlen("route-filter-translated-v4");
712 if (strncmp(p
, "route-filter-v4", strlen("route-filter-v4"))
714 *val
= COMMUNITY_ROUTE_FILTER_v4
;
715 *token
= community_token_route_filter_v4
;
716 p
+= strlen("route-filter-v4");
719 if (strncmp(p
, "route-filter-translated-v6",
720 strlen("route-filter-translated-v6"))
722 *val
= COMMUNITY_ROUTE_FILTER_TRANSLATED_v6
;
723 *token
= community_token_route_filter_translated_v6
;
724 p
+= strlen("route-filter-translated-v6");
727 if (strncmp(p
, "route-filter-v6", strlen("route-filter-v6"))
729 *val
= COMMUNITY_ROUTE_FILTER_v6
;
730 *token
= community_token_route_filter_v6
;
731 p
+= strlen("route-filter-v6");
734 if (strncmp(p
, "llgr-stale", strlen("llgr-stale"))
736 *val
= COMMUNITY_LLGR_STALE
;
737 *token
= community_token_llgr_stale
;
738 p
+= strlen("llgr-stale");
741 if (strncmp(p
, "no-llgr", strlen("no-llgr"))
743 *val
= COMMUNITY_NO_LLGR
;
744 *token
= community_token_no_llgr
;
745 p
+= strlen("no-llgr");
748 if (strncmp(p
, "blackhole", strlen("blackhole"))
750 *val
= COMMUNITY_BLACKHOLE
;
751 *token
= community_token_blackhole
;
752 p
+= strlen("blackhole");
755 if (strncmp(p
, "no-export", strlen("no-export")) == 0) {
756 *val
= COMMUNITY_NO_EXPORT
;
757 *token
= community_token_no_export
;
758 p
+= strlen("no-export");
761 if (strncmp(p
, "no-advertise", strlen("no-advertise")) == 0) {
762 *val
= COMMUNITY_NO_ADVERTISE
;
763 *token
= community_token_no_advertise
;
764 p
+= strlen("no-advertise");
767 if (strncmp(p
, "local-AS", strlen("local-AS")) == 0) {
768 *val
= COMMUNITY_LOCAL_AS
;
769 *token
= community_token_local_as
;
770 p
+= strlen("local-AS");
773 if (strncmp(p
, "no-peer", strlen("no-peer")) == 0) {
774 *val
= COMMUNITY_NO_PEER
;
775 *token
= community_token_no_peer
;
776 p
+= strlen("no-peer");
780 /* Unknown string. */
781 *token
= community_token_unknown
;
785 /* Community value. */
786 if (isdigit((unsigned char)*p
)) {
789 uint32_t community_low
= 0;
790 uint32_t community_high
= 0;
792 if (!community_valid(p
)) {
793 *token
= community_token_unknown
;
797 while (isdigit((unsigned char)*p
) || *p
== ':') {
800 *token
= community_token_unknown
;
806 if (community_low
> UINT16_MAX
) {
808 community_token_unknown
;
812 community_high
= community_low
<< 16;
818 community_low
+= (*p
- '0');
823 *token
= community_token_unknown
;
827 *val
= community_high
+ community_low
;
828 *token
= community_token_val
;
831 *token
= community_token_unknown
;
835 /* convert string to community structure */
836 struct community
*community_str2com(const char *str
)
838 struct community
*com
= NULL
;
839 struct community
*com_sort
= NULL
;
841 enum community_token token
= community_token_unknown
;
844 str
= community_gettoken(str
, &token
, &val
);
847 case community_token_val
:
848 case community_token_gshut
:
849 case community_token_accept_own
:
850 case community_token_route_filter_translated_v4
:
851 case community_token_route_filter_v4
:
852 case community_token_route_filter_translated_v6
:
853 case community_token_route_filter_v6
:
854 case community_token_llgr_stale
:
855 case community_token_no_llgr
:
856 case community_token_accept_own_nexthop
:
857 case community_token_blackhole
:
858 case community_token_no_export
:
859 case community_token_no_advertise
:
860 case community_token_local_as
:
861 case community_token_no_peer
:
863 com
= community_new();
866 community_add_val(com
, val
);
868 case community_token_unknown
:
870 community_free(&com
);
875 com_sort
= community_uniq_sort(com
);
876 community_free(&com
);
881 /* Return communities hash entry count. */
882 unsigned long community_count(void)
884 return comhash
->count
;
887 /* Return communities hash. */
888 struct hash
*community_hash(void)
893 /* Initialize comminity related hash. */
894 void community_init(void)
897 hash_create((unsigned int (*)(const void *))community_hash_make
,
898 (bool (*)(const void *, const void *))community_cmp
,
899 "BGP Community Hash");
902 static void community_hash_free(void *data
)
904 struct community
*com
= data
;
906 community_free(&com
);
909 void community_finish(void)
911 hash_clean(comhash
, community_hash_free
);
916 static struct community
*bgp_aggr_community_lookup(
917 struct bgp_aggregate
*aggregate
,
918 struct community
*community
)
920 return hash_lookup(aggregate
->community_hash
, community
);
923 static void *bgp_aggr_community_hash_alloc(void *p
)
925 struct community
*ref
= (struct community
*)p
;
926 struct community
*community
= NULL
;
928 community
= community_dup(ref
);
932 static void bgp_aggr_community_prepare(struct hash_bucket
*hb
, void *arg
)
934 struct community
*hb_community
= hb
->data
;
935 struct community
**aggr_community
= arg
;
938 *aggr_community
= community_merge(*aggr_community
,
941 *aggr_community
= community_dup(hb_community
);
944 void bgp_aggr_community_remove(void *arg
)
946 struct community
*community
= arg
;
948 community_free(&community
);
951 void bgp_compute_aggregate_community(struct bgp_aggregate
*aggregate
,
952 struct community
*community
)
954 bgp_compute_aggregate_community_hash(aggregate
, community
);
955 bgp_compute_aggregate_community_val(aggregate
);
959 void bgp_compute_aggregate_community_hash(struct bgp_aggregate
*aggregate
,
960 struct community
*community
)
962 struct community
*aggr_community
= NULL
;
964 if ((aggregate
== NULL
) || (community
== NULL
))
967 /* Create hash if not already created.
969 if (aggregate
->community_hash
== NULL
)
970 aggregate
->community_hash
= hash_create(
971 (unsigned int (*)(const void *))community_hash_make
,
972 (bool (*)(const void *, const void *))community_cmp
,
973 "BGP Aggregator community hash");
975 aggr_community
= bgp_aggr_community_lookup(aggregate
, community
);
976 if (aggr_community
== NULL
) {
977 /* Insert community into hash.
979 aggr_community
= hash_get(aggregate
->community_hash
, community
,
980 bgp_aggr_community_hash_alloc
);
983 /* Increment reference counter.
985 aggr_community
->refcnt
++;
988 void bgp_compute_aggregate_community_val(struct bgp_aggregate
*aggregate
)
990 struct community
*commerge
= NULL
;
992 if (aggregate
== NULL
)
995 /* Re-compute aggregate's community.
997 if (aggregate
->community
)
998 community_free(&aggregate
->community
);
999 if (aggregate
->community_hash
&&
1000 aggregate
->community_hash
->count
) {
1001 hash_iterate(aggregate
->community_hash
,
1002 bgp_aggr_community_prepare
,
1003 &aggregate
->community
);
1004 commerge
= aggregate
->community
;
1005 aggregate
->community
= community_uniq_sort(commerge
);
1007 community_free(&commerge
);
1013 void bgp_remove_community_from_aggregate(struct bgp_aggregate
*aggregate
,
1014 struct community
*community
)
1016 struct community
*aggr_community
= NULL
;
1017 struct community
*ret_comm
= NULL
;
1020 || (!aggregate
->community_hash
)
1024 /* Look-up the community in the hash.
1026 aggr_community
= bgp_aggr_community_lookup(aggregate
, community
);
1027 if (aggr_community
) {
1028 aggr_community
->refcnt
--;
1030 if (aggr_community
->refcnt
== 0) {
1031 ret_comm
= hash_release(aggregate
->community_hash
,
1033 community_free(&ret_comm
);
1035 bgp_compute_aggregate_community_val(aggregate
);
1040 void bgp_remove_comm_from_aggregate_hash(struct bgp_aggregate
*aggregate
,
1041 struct community
*community
)
1044 struct community
*aggr_community
= NULL
;
1045 struct community
*ret_comm
= NULL
;
1048 || (!aggregate
->community_hash
)
1052 /* Look-up the community in the hash.
1054 aggr_community
= bgp_aggr_community_lookup(aggregate
, community
);
1055 if (aggr_community
) {
1056 aggr_community
->refcnt
--;
1058 if (aggr_community
->refcnt
== 0) {
1059 ret_comm
= hash_release(aggregate
->community_hash
,
1061 community_free(&ret_comm
);