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 (struct community
*)XCALLOC(MTYPE_COMMUNITY
,
38 sizeof(struct community
));
41 /* Free communities value. */
42 void community_free(struct community
**com
)
45 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
);
103 /* Delete all communities listed in com2 from com1 */
104 struct community
*community_delete(struct community
*com1
,
105 struct community
*com2
)
109 while (i
< com2
->size
) {
110 community_del_val(com1
, com2
->val
+ i
);
117 /* Callback function from qsort(). */
118 static int community_compare(const void *a1
, const void *a2
)
123 memcpy(&v1
, a1
, sizeof(uint32_t));
124 memcpy(&v2
, a2
, sizeof(uint32_t));
135 int community_include(struct community
*com
, uint32_t val
)
141 for (i
= 0; i
< com
->size
; i
++)
142 if (memcmp(&val
, com_nthval(com
, i
), sizeof(uint32_t)) == 0)
148 uint32_t community_val_get(struct community
*com
, int i
)
153 p
= (uint8_t *)com
->val
;
156 memcpy(&val
, p
, sizeof(uint32_t));
161 /* Sort and uniq given community. */
162 struct community
*community_uniq_sort(struct community
*com
)
165 struct community
*new;
171 new = community_new();
174 for (i
= 0; i
< com
->size
; i
++) {
175 val
= community_val_get(com
, i
);
177 if (!community_include(new, val
))
178 community_add_val(new, val
);
181 qsort(new->val
, new->size
, sizeof(uint32_t), community_compare
);
186 /* Convert communities attribute to string.
188 For Well-known communities value, below keyword is used.
191 0xFFFF0000 "graceful-shutdown"
192 0xFFFF0001 "accept-own"
193 0xFFFF0002 "route-filter-translated-v4"
194 0xFFFF0003 "route-filter-v4"
195 0xFFFF0004 "route-filter-translated-v6"
196 0xFFFF0005 "route-filter-v6"
197 0xFFFF0006 "llgr-stale"
199 0xFFFF0008 "accept-own-nexthop"
200 0xFFFF029A "blackhole"
201 0xFFFFFF01 "no-export"
202 0xFFFFFF02 "no-advertise"
203 0xFFFFFF03 "local-AS"
206 For other values, "AS:VAL" format is used. */
207 static void set_community_string(struct community
*com
, bool make_json
)
217 json_object
*json_community_list
= NULL
;
218 json_object
*json_string
= NULL
;
224 com
->json
= json_object_new_object();
225 json_community_list
= json_object_new_array();
228 /* When communities attribute is empty. */
229 if (com
->size
== 0) {
230 str
= XMALLOC(MTYPE_COMMUNITY_STR
, 1);
234 json_object_string_add(com
->json
, "string", "");
235 json_object_object_add(com
->json
, "list",
236 json_community_list
);
242 /* Memory allocation is time consuming work. So we calculate
243 required string length first. */
246 for (i
= 0; i
< com
->size
; i
++) {
247 memcpy(&comval
, com_nthval(com
, i
), sizeof(uint32_t));
248 comval
= ntohl(comval
);
251 case COMMUNITY_INTERNET
:
252 len
+= strlen(" internet");
254 case COMMUNITY_GSHUT
:
255 len
+= strlen(" graceful-shutdown");
257 case COMMUNITY_ACCEPT_OWN
:
258 len
+= strlen(" accept-own");
260 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4
:
261 len
+= strlen(" route-filter-translated-v4");
263 case COMMUNITY_ROUTE_FILTER_v4
:
264 len
+= strlen(" route-filter-v4");
266 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6
:
267 len
+= strlen(" route-filter-translated-v6");
269 case COMMUNITY_ROUTE_FILTER_v6
:
270 len
+= strlen(" route-filter-v6");
272 case COMMUNITY_LLGR_STALE
:
273 len
+= strlen(" llgr-stale");
275 case COMMUNITY_NO_LLGR
:
276 len
+= strlen(" no-llgr");
278 case COMMUNITY_ACCEPT_OWN_NEXTHOP
:
279 len
+= strlen(" accept-own-nexthop");
281 case COMMUNITY_BLACKHOLE
:
282 len
+= strlen(" blackhole");
284 case COMMUNITY_NO_EXPORT
:
285 len
+= strlen(" no-export");
287 case COMMUNITY_NO_ADVERTISE
:
288 len
+= strlen(" no-advertise");
290 case COMMUNITY_LOCAL_AS
:
291 len
+= strlen(" local-AS");
293 case COMMUNITY_NO_PEER
:
294 len
+= strlen(" no-peer");
297 len
+= strlen(" 65536:65535");
302 /* Allocate memory. */
303 str
= pnt
= XMALLOC(MTYPE_COMMUNITY_STR
, len
);
306 /* Fill in string. */
307 for (i
= 0; i
< com
->size
; i
++) {
308 memcpy(&comval
, com_nthval(com
, i
), sizeof(uint32_t));
309 comval
= ntohl(comval
);
317 case COMMUNITY_INTERNET
:
318 strcpy(pnt
, "internet");
319 pnt
+= strlen("internet");
322 json_object_new_string("internet");
323 json_object_array_add(json_community_list
,
327 case COMMUNITY_GSHUT
:
328 strcpy(pnt
, "graceful-shutdown");
329 pnt
+= strlen("graceful-shutdown");
331 json_string
= json_object_new_string(
333 json_object_array_add(json_community_list
,
337 case COMMUNITY_ACCEPT_OWN
:
338 strcpy(pnt
, "accept-own");
339 pnt
+= strlen("accept-own");
341 json_string
= json_object_new_string(
343 json_object_array_add(json_community_list
,
347 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4
:
348 strcpy(pnt
, "route-filter-translated-v4");
349 pnt
+= strlen("route-filter-translated-v4");
351 json_string
= json_object_new_string(
352 "routeFilterTranslatedV4");
353 json_object_array_add(json_community_list
,
357 case COMMUNITY_ROUTE_FILTER_v4
:
358 strcpy(pnt
, "route-filter-v4");
359 pnt
+= strlen("route-filter-v4");
361 json_string
= json_object_new_string(
363 json_object_array_add(json_community_list
,
367 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6
:
368 strcpy(pnt
, "route-filter-translated-v6");
369 pnt
+= strlen("route-filter-translated-v6");
371 json_string
= json_object_new_string(
372 "routeFilterTranslatedV6");
373 json_object_array_add(json_community_list
,
377 case COMMUNITY_ROUTE_FILTER_v6
:
378 strcpy(pnt
, "route-filter-v6");
379 pnt
+= strlen("route-filter-v6");
381 json_string
= json_object_new_string(
383 json_object_array_add(json_community_list
,
387 case COMMUNITY_LLGR_STALE
:
388 strcpy(pnt
, "llgr-stale");
389 pnt
+= strlen("llgr-stale");
391 json_string
= json_object_new_string(
393 json_object_array_add(json_community_list
,
397 case COMMUNITY_NO_LLGR
:
398 strcpy(pnt
, "no-llgr");
399 pnt
+= strlen("no-llgr");
401 json_string
= json_object_new_string(
403 json_object_array_add(json_community_list
,
407 case COMMUNITY_ACCEPT_OWN_NEXTHOP
:
408 strcpy(pnt
, "accept-own-nexthop");
409 pnt
+= strlen("accept-own-nexthop");
411 json_string
= json_object_new_string(
413 json_object_array_add(json_community_list
,
417 case COMMUNITY_BLACKHOLE
:
418 strcpy(pnt
, "blackhole");
419 pnt
+= strlen("blackhole");
421 json_string
= json_object_new_string(
423 json_object_array_add(json_community_list
,
427 case COMMUNITY_NO_EXPORT
:
428 strcpy(pnt
, "no-export");
429 pnt
+= strlen("no-export");
432 json_object_new_string("noExport");
433 json_object_array_add(json_community_list
,
437 case COMMUNITY_NO_ADVERTISE
:
438 strcpy(pnt
, "no-advertise");
439 pnt
+= strlen("no-advertise");
442 json_object_new_string("noAdvertise");
443 json_object_array_add(json_community_list
,
447 case COMMUNITY_LOCAL_AS
:
448 strcpy(pnt
, "local-AS");
449 pnt
+= strlen("local-AS");
451 json_string
= json_object_new_string("localAs");
452 json_object_array_add(json_community_list
,
456 case COMMUNITY_NO_PEER
:
457 strcpy(pnt
, "no-peer");
458 pnt
+= strlen("no-peer");
460 json_string
= json_object_new_string("noPeer");
461 json_object_array_add(json_community_list
,
466 as
= (comval
>> 16) & 0xFFFF;
467 val
= comval
& 0xFFFF;
468 sprintf(pnt
, "%u:%d", as
, val
);
470 json_string
= json_object_new_string(pnt
);
471 json_object_array_add(json_community_list
,
481 json_object_string_add(com
->json
, "string", str
);
482 json_object_object_add(com
->json
, "list", json_community_list
);
487 /* Intern communities attribute. */
488 struct community
*community_intern(struct community
*com
)
490 struct community
*find
;
492 /* Assert this community structure is not interned. */
493 assert(com
->refcnt
== 0);
495 /* Lookup community hash. */
496 find
= (struct community
*)hash_get(comhash
, com
, hash_alloc_intern
);
498 /* Arguemnt com is allocated temporary. So when it is not used in
499 hash, it should be freed. */
501 community_free(&com
);
503 /* Increment refrence counter. */
508 set_community_string(find
, false);
513 /* Free community attribute. */
514 void community_unintern(struct community
**com
)
516 struct community
*ret
;
521 /* Pull off from hash. */
522 if ((*com
)->refcnt
== 0) {
523 /* Community value com must exist in hash. */
524 ret
= (struct community
*)hash_release(comhash
, *com
);
531 /* Create new community attribute. */
532 struct community
*community_parse(uint32_t *pnt
, unsigned short length
)
534 struct community tmp
;
535 struct community
*new;
537 /* If length is malformed return NULL. */
541 /* Make temporary community for hash look up. */
542 tmp
.size
= length
/ 4;
545 new = community_uniq_sort(&tmp
);
547 return community_intern(new);
550 struct community
*community_dup(struct community
*com
)
552 struct community
*new;
554 new = XCALLOC(MTYPE_COMMUNITY
, sizeof(struct community
));
555 new->size
= com
->size
;
557 new->val
= XMALLOC(MTYPE_COMMUNITY_VAL
, com
->size
* 4);
558 memcpy(new->val
, com
->val
, com
->size
* 4);
564 /* Retrun string representation of communities attribute. */
565 char *community_str(struct community
*com
, bool make_json
)
570 if (make_json
&& !com
->json
&& com
->str
)
571 XFREE(MTYPE_COMMUNITY_STR
, com
->str
);
574 set_community_string(com
, make_json
);
578 /* Make hash value of community attribute. This function is used by
580 unsigned int community_hash_make(struct community
*com
)
582 uint32_t *pnt
= (uint32_t *)com
->val
;
584 return jhash2(pnt
, com
->size
, 0x43ea96c1);
587 int community_match(const struct community
*com1
, const struct community
*com2
)
592 if (com1
== NULL
&& com2
== NULL
)
595 if (com1
== NULL
|| com2
== NULL
)
598 if (com1
->size
< com2
->size
)
601 /* Every community on com2 needs to be on com1 for this to match */
602 while (i
< com1
->size
&& j
< com2
->size
) {
603 if (memcmp(com1
->val
+ i
, com2
->val
+ j
, sizeof(uint32_t)) == 0)
614 /* If two aspath have same value then return 1 else return 0. This
615 function is used by hash package. */
616 bool community_cmp(const struct community
*com1
, const struct community
*com2
)
618 if (com1
== NULL
&& com2
== NULL
)
620 if (com1
== NULL
|| com2
== NULL
)
623 if (com1
->size
== com2
->size
)
624 if (memcmp(com1
->val
, com2
->val
, com1
->size
* 4) == 0)
629 /* Add com2 to the end of com1. */
630 struct community
*community_merge(struct community
*com1
,
631 struct community
*com2
)
634 com1
->val
= XREALLOC(MTYPE_COMMUNITY_VAL
, com1
->val
,
635 (com1
->size
+ com2
->size
) * 4);
637 com1
->val
= XMALLOC(MTYPE_COMMUNITY_VAL
,
638 (com1
->size
+ com2
->size
) * 4);
640 memcpy(com1
->val
+ com1
->size
, com2
->val
, com2
->size
* 4);
641 com1
->size
+= com2
->size
;
646 /* Community token enum. */
647 enum community_token
{
649 community_token_gshut
,
650 community_token_accept_own
,
651 community_token_route_filter_translated_v4
,
652 community_token_route_filter_v4
,
653 community_token_route_filter_translated_v6
,
654 community_token_route_filter_v6
,
655 community_token_llgr_stale
,
656 community_token_no_llgr
,
657 community_token_accept_own_nexthop
,
658 community_token_blackhole
,
659 community_token_no_export
,
660 community_token_no_advertise
,
661 community_token_local_as
,
662 community_token_no_peer
,
663 community_token_unknown
666 /* Get next community token from string. */
668 community_gettoken(const char *buf
, enum community_token
*token
, uint32_t *val
)
672 /* Skip white space. */
673 while (isspace((int)*p
))
676 /* Check the end of the line. */
680 /* Well known community string check. */
681 if (isalpha((int)*p
)) {
682 if (strncmp(p
, "internet", strlen("internet")) == 0) {
683 *val
= COMMUNITY_INTERNET
;
684 *token
= community_token_no_export
;
685 p
+= strlen("internet");
688 if (strncmp(p
, "graceful-shutdown", strlen("graceful-shutdown"))
690 *val
= COMMUNITY_GSHUT
;
691 *token
= community_token_gshut
;
692 p
+= strlen("graceful-shutdown");
695 if (strncmp(p
, "accept-own", strlen("accept-own"))
697 *val
= COMMUNITY_ACCEPT_OWN
;
698 *token
= community_token_accept_own
;
699 p
+= strlen("accept-own");
702 if (strncmp(p
, "route-filter-translated-v4",
703 strlen("route-filter-translated-v4"))
705 *val
= COMMUNITY_ROUTE_FILTER_TRANSLATED_v4
;
706 *token
= community_token_route_filter_translated_v4
;
707 p
+= strlen("route-filter-translated-v4");
710 if (strncmp(p
, "route-filter-v4", strlen("route-filter-v4"))
712 *val
= COMMUNITY_ROUTE_FILTER_v4
;
713 *token
= community_token_route_filter_v4
;
714 p
+= strlen("route-filter-v4");
717 if (strncmp(p
, "route-filter-translated-v6",
718 strlen("route-filter-translated-v6"))
720 *val
= COMMUNITY_ROUTE_FILTER_TRANSLATED_v6
;
721 *token
= community_token_route_filter_translated_v6
;
722 p
+= strlen("route-filter-translated-v6");
725 if (strncmp(p
, "route-filter-v6", strlen("route-filter-v6"))
727 *val
= COMMUNITY_ROUTE_FILTER_v6
;
728 *token
= community_token_route_filter_v6
;
729 p
+= strlen("route-filter-v6");
732 if (strncmp(p
, "llgr-stale", strlen("llgr-stale"))
734 *val
= COMMUNITY_LLGR_STALE
;
735 *token
= community_token_llgr_stale
;
736 p
+= strlen("llgr-stale");
739 if (strncmp(p
, "no-llgr", strlen("no-llgr"))
741 *val
= COMMUNITY_NO_LLGR
;
742 *token
= community_token_no_llgr
;
743 p
+= strlen("no-llgr");
746 if (strncmp(p
, "accept-own-nexthop",
747 strlen("accept-own-nexthop"))
749 *val
= COMMUNITY_ACCEPT_OWN_NEXTHOP
;
750 *token
= community_token_accept_own_nexthop
;
751 p
+= strlen("accept-own-nexthop");
754 if (strncmp(p
, "blackhole", strlen("blackhole"))
756 *val
= COMMUNITY_BLACKHOLE
;
757 *token
= community_token_blackhole
;
758 p
+= strlen("blackhole");
761 if (strncmp(p
, "no-export", strlen("no-export")) == 0) {
762 *val
= COMMUNITY_NO_EXPORT
;
763 *token
= community_token_no_export
;
764 p
+= strlen("no-export");
767 if (strncmp(p
, "no-advertise", strlen("no-advertise")) == 0) {
768 *val
= COMMUNITY_NO_ADVERTISE
;
769 *token
= community_token_no_advertise
;
770 p
+= strlen("no-advertise");
773 if (strncmp(p
, "local-AS", strlen("local-AS")) == 0) {
774 *val
= COMMUNITY_LOCAL_AS
;
775 *token
= community_token_local_as
;
776 p
+= strlen("local-AS");
779 if (strncmp(p
, "no-peer", strlen("no-peer")) == 0) {
780 *val
= COMMUNITY_NO_PEER
;
781 *token
= community_token_no_peer
;
782 p
+= strlen("no-peer");
786 /* Unknown string. */
787 *token
= community_token_unknown
;
791 /* Community value. */
792 if (isdigit((int)*p
)) {
795 uint32_t community_low
= 0;
796 uint32_t community_high
= 0;
798 while (isdigit((int)*p
) || *p
== ':') {
801 *token
= community_token_unknown
;
807 if (community_low
> UINT16_MAX
) {
809 community_token_unknown
;
813 community_high
= community_low
<< 16;
819 community_low
+= (*p
- '0');
824 *token
= community_token_unknown
;
828 if (community_low
> UINT16_MAX
) {
829 *token
= community_token_unknown
;
833 *val
= community_high
+ community_low
;
834 *token
= community_token_val
;
837 *token
= community_token_unknown
;
841 /* convert string to community structure */
842 struct community
*community_str2com(const char *str
)
844 struct community
*com
= NULL
;
845 struct community
*com_sort
= NULL
;
847 enum community_token token
= community_token_unknown
;
850 str
= community_gettoken(str
, &token
, &val
);
853 case community_token_val
:
854 case community_token_gshut
:
855 case community_token_accept_own
:
856 case community_token_route_filter_translated_v4
:
857 case community_token_route_filter_v4
:
858 case community_token_route_filter_translated_v6
:
859 case community_token_route_filter_v6
:
860 case community_token_llgr_stale
:
861 case community_token_no_llgr
:
862 case community_token_accept_own_nexthop
:
863 case community_token_blackhole
:
864 case community_token_no_export
:
865 case community_token_no_advertise
:
866 case community_token_local_as
:
867 case community_token_no_peer
:
869 com
= community_new();
872 community_add_val(com
, val
);
874 case community_token_unknown
:
876 community_free(&com
);
881 com_sort
= community_uniq_sort(com
);
882 community_free(&com
);
887 /* Return communities hash entry count. */
888 unsigned long community_count(void)
890 return comhash
->count
;
893 /* Return communities hash. */
894 struct hash
*community_hash(void)
899 /* Initialize comminity related hash. */
900 void community_init(void)
903 hash_create((unsigned int (*)(void *))community_hash_make
,
904 (bool (*)(const void *, const void *))community_cmp
,
905 "BGP Community Hash");
908 void community_finish(void)