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
, u_int32_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(u_int32_t
));
71 /* Delete one community. */
72 void community_del_val(struct community
*com
, u_int32_t
*val
)
80 while (i
< com
->size
) {
81 if (memcmp(com
->val
+ i
, val
, sizeof(u_int32_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(u_int32_t
));
124 memcpy(&v2
, a2
, sizeof(u_int32_t
));
135 int community_include(struct community
*com
, u_int32_t val
)
141 for (i
= 0; i
< com
->size
; i
++)
142 if (memcmp(&val
, com_nthval(com
, i
), sizeof(u_int32_t
)) == 0)
148 u_int32_t
community_val_get(struct community
*com
, int i
)
153 p
= (u_char
*)com
->val
;
156 memcpy(&val
, p
, sizeof(u_int32_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(u_int32_t
), community_compare
);
186 /* Convert communities attribute to string.
188 For Well-known communities value, below keyword is used.
191 0xFFFFFF01 "no-export"
192 0xFFFFFF02 "no-advertise"
193 0xFFFFFF03 "local-AS"
194 0xFFFF0000 "graceful-shutdown"
196 For other values, "AS:VAL" format is used. */
197 static void set_community_string(struct community
*com
, bool make_json
)
207 json_object
*json_community_list
= NULL
;
208 json_object
*json_string
= NULL
;
214 com
->json
= json_object_new_object();
215 json_community_list
= json_object_new_array();
218 /* When communities attribute is empty. */
219 if (com
->size
== 0) {
220 str
= XMALLOC(MTYPE_COMMUNITY_STR
, 1);
224 json_object_string_add(com
->json
, "string", "");
225 json_object_object_add(com
->json
, "list", json_community_list
);
231 /* Memory allocation is time consuming work. So we calculate
232 required string length first. */
235 for (i
= 0; i
< com
->size
; i
++) {
236 memcpy(&comval
, com_nthval(com
, i
), sizeof(u_int32_t
));
237 comval
= ntohl(comval
);
240 case COMMUNITY_INTERNET
:
241 len
+= strlen(" internet");
243 case COMMUNITY_NO_EXPORT
:
244 len
+= strlen(" no-export");
246 case COMMUNITY_NO_ADVERTISE
:
247 len
+= strlen(" no-advertise");
249 case COMMUNITY_LOCAL_AS
:
250 len
+= strlen(" local-AS");
252 case COMMUNITY_GSHUT
:
253 len
+= strlen(" graceful-shutdown");
256 len
+= strlen(" 65536:65535");
261 /* Allocate memory. */
262 str
= pnt
= XMALLOC(MTYPE_COMMUNITY_STR
, len
);
265 /* Fill in string. */
266 for (i
= 0; i
< com
->size
; i
++) {
267 memcpy(&comval
, com_nthval(com
, i
), sizeof(u_int32_t
));
268 comval
= ntohl(comval
);
276 case COMMUNITY_INTERNET
:
277 strcpy(pnt
, "internet");
278 pnt
+= strlen("internet");
280 json_string
= json_object_new_string("internet");
281 json_object_array_add(json_community_list
, json_string
);
284 case COMMUNITY_NO_EXPORT
:
285 strcpy(pnt
, "no-export");
286 pnt
+= strlen("no-export");
288 json_string
= json_object_new_string("noExport");
289 json_object_array_add(json_community_list
, json_string
);
292 case COMMUNITY_NO_ADVERTISE
:
293 strcpy(pnt
, "no-advertise");
294 pnt
+= strlen("no-advertise");
296 json_string
= json_object_new_string("noAdvertise");
297 json_object_array_add(json_community_list
, json_string
);
300 case COMMUNITY_LOCAL_AS
:
301 strcpy(pnt
, "local-AS");
302 pnt
+= strlen("local-AS");
304 json_string
= json_object_new_string("localAs");
305 json_object_array_add(json_community_list
, json_string
);
308 case COMMUNITY_GSHUT
:
309 strcpy(pnt
, "graceful-shutdown");
310 pnt
+= strlen("graceful-shutdown");
312 json_string
= json_object_new_string("gracefulShutdown");
313 json_object_array_add(json_community_list
, json_string
);
317 as
= (comval
>> 16) & 0xFFFF;
318 val
= comval
& 0xFFFF;
319 sprintf(pnt
, "%u:%d", as
, val
);
321 json_string
= json_object_new_string(pnt
);
322 json_object_array_add(json_community_list
, json_string
);
331 json_object_string_add(com
->json
, "string", str
);
332 json_object_object_add(com
->json
, "list", json_community_list
);
337 /* Intern communities attribute. */
338 struct community
*community_intern(struct community
*com
)
340 struct community
*find
;
342 /* Assert this community structure is not interned. */
343 assert(com
->refcnt
== 0);
345 /* Lookup community hash. */
346 find
= (struct community
*)hash_get(comhash
, com
, hash_alloc_intern
);
348 /* Arguemnt com is allocated temporary. So when it is not used in
349 hash, it should be freed. */
353 /* Increment refrence counter. */
358 set_community_string(find
, false);
363 /* Free community attribute. */
364 void community_unintern(struct community
**com
)
366 struct community
*ret
;
371 /* Pull off from hash. */
372 if ((*com
)->refcnt
== 0) {
373 /* Community value com must exist in hash. */
374 ret
= (struct community
*)hash_release(comhash
, *com
);
377 community_free(*com
);
382 /* Create new community attribute. */
383 struct community
*community_parse(u_int32_t
*pnt
, u_short length
)
385 struct community tmp
;
386 struct community
*new;
388 /* If length is malformed return NULL. */
392 /* Make temporary community for hash look up. */
393 tmp
.size
= length
/ 4;
396 new = community_uniq_sort(&tmp
);
398 return community_intern(new);
401 struct community
*community_dup(struct community
*com
)
403 struct community
*new;
405 new = XCALLOC(MTYPE_COMMUNITY
, sizeof(struct community
));
406 new->size
= com
->size
;
408 new->val
= XMALLOC(MTYPE_COMMUNITY_VAL
, com
->size
* 4);
409 memcpy(new->val
, com
->val
, com
->size
* 4);
415 /* Retrun string representation of communities attribute. */
416 char *community_str(struct community
*com
, bool make_json
)
421 if (make_json
&& !com
->json
&& com
->str
)
422 XFREE(MTYPE_COMMUNITY_STR
, com
->str
);
425 set_community_string(com
, make_json
);
429 /* Make hash value of community attribute. This function is used by
431 unsigned int community_hash_make(struct community
*com
)
433 u_int32_t
*pnt
= (u_int32_t
*)com
->val
;
435 return jhash2(pnt
, com
->size
, 0x43ea96c1);
438 int community_match(const struct community
*com1
, const struct community
*com2
)
443 if (com1
== NULL
&& com2
== NULL
)
446 if (com1
== NULL
|| com2
== NULL
)
449 if (com1
->size
< com2
->size
)
452 /* Every community on com2 needs to be on com1 for this to match */
453 while (i
< com1
->size
&& j
< com2
->size
) {
454 if (memcmp(com1
->val
+ i
, com2
->val
+ j
, sizeof(u_int32_t
))
466 /* If two aspath have same value then return 1 else return 0. This
467 function is used by hash package. */
468 int community_cmp(const struct community
*com1
, const struct community
*com2
)
470 if (com1
== NULL
&& com2
== NULL
)
472 if (com1
== NULL
|| com2
== NULL
)
475 if (com1
->size
== com2
->size
)
476 if (memcmp(com1
->val
, com2
->val
, com1
->size
* 4) == 0)
481 /* Add com2 to the end of com1. */
482 struct community
*community_merge(struct community
*com1
,
483 struct community
*com2
)
486 com1
->val
= XREALLOC(MTYPE_COMMUNITY_VAL
, com1
->val
,
487 (com1
->size
+ com2
->size
) * 4);
489 com1
->val
= XMALLOC(MTYPE_COMMUNITY_VAL
,
490 (com1
->size
+ com2
->size
) * 4);
492 memcpy(com1
->val
+ com1
->size
, com2
->val
, com2
->size
* 4);
493 com1
->size
+= com2
->size
;
498 /* Community token enum. */
499 enum community_token
{
501 community_token_no_export
,
502 community_token_no_advertise
,
503 community_token_local_as
,
504 community_token_gshut
,
505 community_token_unknown
508 /* Get next community token from string. */
510 community_gettoken(const char *buf
, enum community_token
*token
, u_int32_t
*val
)
514 /* Skip white space. */
515 while (isspace((int)*p
))
518 /* Check the end of the line. */
522 /* Well known community string check. */
523 if (isalpha((int)*p
)) {
524 if (strncmp(p
, "internet", strlen("internet")) == 0) {
525 *val
= COMMUNITY_INTERNET
;
526 *token
= community_token_no_export
;
527 p
+= strlen("internet");
530 if (strncmp(p
, "no-export", strlen("no-export")) == 0) {
531 *val
= COMMUNITY_NO_EXPORT
;
532 *token
= community_token_no_export
;
533 p
+= strlen("no-export");
536 if (strncmp(p
, "no-advertise", strlen("no-advertise")) == 0) {
537 *val
= COMMUNITY_NO_ADVERTISE
;
538 *token
= community_token_no_advertise
;
539 p
+= strlen("no-advertise");
542 if (strncmp(p
, "local-AS", strlen("local-AS")) == 0) {
543 *val
= COMMUNITY_LOCAL_AS
;
544 *token
= community_token_local_as
;
545 p
+= strlen("local-AS");
548 if (strncmp(p
, "graceful-shutdown", strlen("graceful-shutdown")) == 0) {
549 *val
= COMMUNITY_GSHUT
;
550 *token
= community_token_gshut
;
551 p
+= strlen("graceful-shutdown");
555 /* Unknown string. */
556 *token
= community_token_unknown
;
560 /* Community value. */
561 if (isdigit((int)*p
)) {
564 u_int32_t community_low
= 0;
565 u_int32_t community_high
= 0;
567 while (isdigit((int)*p
) || *p
== ':') {
570 *token
= community_token_unknown
;
576 if (community_low
> UINT16_MAX
) {
578 community_token_unknown
;
582 community_high
= community_low
<< 16;
588 community_low
+= (*p
- '0');
593 *token
= community_token_unknown
;
597 if (community_low
> UINT16_MAX
) {
598 *token
= community_token_unknown
;
602 *val
= community_high
+ community_low
;
603 *token
= community_token_val
;
606 *token
= community_token_unknown
;
610 /* convert string to community structure */
611 struct community
*community_str2com(const char *str
)
613 struct community
*com
= NULL
;
614 struct community
*com_sort
= NULL
;
616 enum community_token token
= community_token_unknown
;
619 str
= community_gettoken(str
, &token
, &val
);
622 case community_token_val
:
623 case community_token_no_export
:
624 case community_token_no_advertise
:
625 case community_token_local_as
:
626 case community_token_gshut
:
628 com
= community_new();
631 community_add_val(com
, val
);
633 case community_token_unknown
:
644 com_sort
= community_uniq_sort(com
);
650 /* Return communities hash entry count. */
651 unsigned long community_count(void)
653 return comhash
->count
;
656 /* Return communities hash. */
657 struct hash
*community_hash(void)
662 /* Initialize comminity related hash. */
663 void community_init(void)
665 comhash
= hash_create(
666 (unsigned int (*)(void *))community_hash_make
,
667 (int (*)(const void *, const void *))community_cmp
,
668 "BGP Community Hash");
671 void community_finish(void)