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
27 #include "bgpd/bgp_memory.h"
28 #include "bgpd/bgp_community.h"
30 /* Hash of community attribute. */
31 static struct hash
*comhash
;
33 /* Allocate a new communities value. */
34 static struct community
*community_new(void)
36 return (struct community
*)XCALLOC(MTYPE_COMMUNITY
,
37 sizeof(struct community
));
40 /* Free communities value. */
41 void community_free(struct community
*com
)
44 XFREE(MTYPE_COMMUNITY_VAL
, com
->val
);
46 XFREE(MTYPE_COMMUNITY_STR
, com
->str
);
49 json_object_free(com
->json
);
53 XFREE(MTYPE_COMMUNITY
, com
);
56 /* Add one community value to the community. */
57 static void community_add_val(struct community
*com
, u_int32_t val
)
61 com
->val
= XREALLOC(MTYPE_COMMUNITY_VAL
, com
->val
,
64 com
->val
= XMALLOC(MTYPE_COMMUNITY_VAL
, com_length(com
));
67 memcpy(com_lastval(com
), &val
, sizeof(u_int32_t
));
70 /* Delete one community. */
71 void community_del_val(struct community
*com
, u_int32_t
*val
)
79 while (i
< com
->size
) {
80 if (memcmp(com
->val
+ i
, val
, sizeof(u_int32_t
)) == 0) {
81 c
= com
->size
- i
- 1;
84 memmove(com
->val
+ i
, com
->val
+ (i
+ 1),
90 com
->val
= XREALLOC(MTYPE_COMMUNITY_VAL
,
91 com
->val
, com_length(com
));
93 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(u_int32_t
));
123 memcpy(&v2
, a2
, sizeof(u_int32_t
));
134 int community_include(struct community
*com
, u_int32_t val
)
140 for (i
= 0; i
< com
->size
; i
++)
141 if (memcmp(&val
, com_nthval(com
, i
), sizeof(u_int32_t
)) == 0)
147 u_int32_t
community_val_get(struct community
*com
, int i
)
152 p
= (u_char
*)com
->val
;
155 memcpy(&val
, p
, sizeof(u_int32_t
));
160 /* Sort and uniq given community. */
161 struct community
*community_uniq_sort(struct community
*com
)
164 struct community
*new;
170 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
)
207 json_object
*json_community_list
= NULL
;
208 json_object
*json_string
= NULL
;
213 com
->json
= json_object_new_object();
214 json_community_list
= json_object_new_array();
216 /* When communities attribute is empty. */
217 if (com
->size
== 0) {
218 str
= XMALLOC(MTYPE_COMMUNITY_STR
, 1);
221 json_object_string_add(com
->json
, "string", "");
222 json_object_object_add(com
->json
, "list", json_community_list
);
227 /* Memory allocation is time consuming work. So we calculate
228 required string length first. */
231 for (i
= 0; i
< com
->size
; i
++) {
232 memcpy(&comval
, com_nthval(com
, i
), sizeof(u_int32_t
));
233 comval
= ntohl(comval
);
236 case COMMUNITY_INTERNET
:
237 len
+= strlen(" internet");
239 case COMMUNITY_NO_EXPORT
:
240 len
+= strlen(" no-export");
242 case COMMUNITY_NO_ADVERTISE
:
243 len
+= strlen(" no-advertise");
245 case COMMUNITY_LOCAL_AS
:
246 len
+= strlen(" local-AS");
248 case COMMUNITY_GSHUT
:
249 len
+= strlen(" graceful-shutdown");
252 len
+= strlen(" 65536:65535");
257 /* Allocate memory. */
258 str
= pnt
= XMALLOC(MTYPE_COMMUNITY_STR
, len
);
261 /* Fill in string. */
262 for (i
= 0; i
< com
->size
; i
++) {
263 memcpy(&comval
, com_nthval(com
, i
), sizeof(u_int32_t
));
264 comval
= ntohl(comval
);
272 case COMMUNITY_INTERNET
:
273 strcpy(pnt
, "internet");
274 pnt
+= strlen("internet");
275 json_string
= json_object_new_string("internet");
276 json_object_array_add(json_community_list
, json_string
);
278 case COMMUNITY_NO_EXPORT
:
279 strcpy(pnt
, "no-export");
280 pnt
+= strlen("no-export");
281 json_string
= json_object_new_string("noExport");
282 json_object_array_add(json_community_list
, json_string
);
284 case COMMUNITY_NO_ADVERTISE
:
285 strcpy(pnt
, "no-advertise");
286 pnt
+= strlen("no-advertise");
287 json_string
= json_object_new_string("noAdvertise");
288 json_object_array_add(json_community_list
, json_string
);
290 case COMMUNITY_LOCAL_AS
:
291 strcpy(pnt
, "local-AS");
292 pnt
+= strlen("local-AS");
293 json_string
= json_object_new_string("localAs");
294 json_object_array_add(json_community_list
, json_string
);
296 case COMMUNITY_GSHUT
:
297 strcpy(pnt
, "graceful-shutdown");
298 pnt
+= strlen("graceful-shutdown");
299 json_string
= json_object_new_string("gracefulShutdown");
300 json_object_array_add(json_community_list
, json_string
);
303 as
= (comval
>> 16) & 0xFFFF;
304 val
= comval
& 0xFFFF;
305 sprintf(pnt
, "%u:%d", as
, val
);
306 json_string
= json_object_new_string(pnt
);
307 json_object_array_add(json_community_list
, json_string
);
314 json_object_string_add(com
->json
, "string", str
);
315 json_object_object_add(com
->json
, "list", json_community_list
);
319 /* Intern communities attribute. */
320 struct community
*community_intern(struct community
*com
)
322 struct community
*find
;
324 /* Assert this community structure is not interned. */
325 assert(com
->refcnt
== 0);
327 /* Lookup community hash. */
328 find
= (struct community
*)hash_get(comhash
, com
, hash_alloc_intern
);
330 /* Arguemnt com is allocated temporary. So when it is not used in
331 hash, it should be freed. */
335 /* Increment refrence counter. */
340 set_community_string(find
);
345 /* Free community attribute. */
346 void community_unintern(struct community
**com
)
348 struct community
*ret
;
353 /* Pull off from hash. */
354 if ((*com
)->refcnt
== 0) {
355 /* Community value com must exist in hash. */
356 ret
= (struct community
*)hash_release(comhash
, *com
);
359 community_free(*com
);
364 /* Create new community attribute. */
365 struct community
*community_parse(u_int32_t
*pnt
, u_short length
)
367 struct community tmp
;
368 struct community
*new;
370 /* If length is malformed return NULL. */
374 /* Make temporary community for hash look up. */
375 tmp
.size
= length
/ 4;
378 new = community_uniq_sort(&tmp
);
380 return community_intern(new);
383 struct community
*community_dup(struct community
*com
)
385 struct community
*new;
387 new = XCALLOC(MTYPE_COMMUNITY
, sizeof(struct community
));
388 new->size
= com
->size
;
390 new->val
= XMALLOC(MTYPE_COMMUNITY_VAL
, com
->size
* 4);
391 memcpy(new->val
, com
->val
, com
->size
* 4);
397 /* Retrun string representation of communities attribute. */
398 char *community_str(struct community
*com
)
404 set_community_string(com
);
408 /* Make hash value of community attribute. This function is used by
410 unsigned int community_hash_make(struct community
*com
)
412 unsigned char *pnt
= (unsigned char *)com
->val
;
413 int size
= com
->size
* 4;
414 unsigned int key
= 0;
417 for (c
= 0; c
< size
; c
+= 4) {
427 int community_match(const struct community
*com1
, const struct community
*com2
)
432 if (com1
== NULL
&& com2
== NULL
)
435 if (com1
== NULL
|| com2
== NULL
)
438 if (com1
->size
< com2
->size
)
441 /* Every community on com2 needs to be on com1 for this to match */
442 while (i
< com1
->size
&& j
< com2
->size
) {
443 if (memcmp(com1
->val
+ i
, com2
->val
+ j
, sizeof(u_int32_t
))
455 /* If two aspath have same value then return 1 else return 0. This
456 function is used by hash package. */
457 int community_cmp(const struct community
*com1
, const struct community
*com2
)
459 if (com1
== NULL
&& com2
== NULL
)
461 if (com1
== NULL
|| com2
== NULL
)
464 if (com1
->size
== com2
->size
)
465 if (memcmp(com1
->val
, com2
->val
, com1
->size
* 4) == 0)
470 /* Add com2 to the end of com1. */
471 struct community
*community_merge(struct community
*com1
,
472 struct community
*com2
)
475 com1
->val
= XREALLOC(MTYPE_COMMUNITY_VAL
, com1
->val
,
476 (com1
->size
+ com2
->size
) * 4);
478 com1
->val
= XMALLOC(MTYPE_COMMUNITY_VAL
,
479 (com1
->size
+ com2
->size
) * 4);
481 memcpy(com1
->val
+ com1
->size
, com2
->val
, com2
->size
* 4);
482 com1
->size
+= com2
->size
;
487 /* Community token enum. */
488 enum community_token
{
490 community_token_no_export
,
491 community_token_no_advertise
,
492 community_token_local_as
,
493 community_token_gshut
,
494 community_token_unknown
497 /* Get next community token from string. */
499 community_gettoken(const char *buf
, enum community_token
*token
, u_int32_t
*val
)
503 /* Skip white space. */
504 while (isspace((int)*p
))
507 /* Check the end of the line. */
511 /* Well known community string check. */
512 if (isalpha((int)*p
)) {
513 if (strncmp(p
, "internet", strlen("internet")) == 0) {
514 *val
= COMMUNITY_INTERNET
;
515 *token
= community_token_no_export
;
516 p
+= strlen("internet");
519 if (strncmp(p
, "no-export", strlen("no-export")) == 0) {
520 *val
= COMMUNITY_NO_EXPORT
;
521 *token
= community_token_no_export
;
522 p
+= strlen("no-export");
525 if (strncmp(p
, "no-advertise", strlen("no-advertise")) == 0) {
526 *val
= COMMUNITY_NO_ADVERTISE
;
527 *token
= community_token_no_advertise
;
528 p
+= strlen("no-advertise");
531 if (strncmp(p
, "local-AS", strlen("local-AS")) == 0) {
532 *val
= COMMUNITY_LOCAL_AS
;
533 *token
= community_token_local_as
;
534 p
+= strlen("local-AS");
537 if (strncmp(p
, "graceful-shutdown", strlen("graceful-shutdown")) == 0) {
538 *val
= COMMUNITY_GSHUT
;
539 *token
= community_token_gshut
;
540 p
+= strlen("graceful-shutdown");
544 /* Unknown string. */
545 *token
= community_token_unknown
;
549 /* Community value. */
550 if (isdigit((int)*p
)) {
553 u_int32_t community_low
= 0;
554 u_int32_t community_high
= 0;
556 while (isdigit((int)*p
) || *p
== ':') {
559 *token
= community_token_unknown
;
565 if (community_low
> UINT16_MAX
) {
567 community_token_unknown
;
571 community_high
= community_low
<< 16;
577 community_low
+= (*p
- '0');
582 *token
= community_token_unknown
;
586 if (community_low
> UINT16_MAX
) {
587 *token
= community_token_unknown
;
591 *val
= community_high
+ community_low
;
592 *token
= community_token_val
;
595 *token
= community_token_unknown
;
599 /* convert string to community structure */
600 struct community
*community_str2com(const char *str
)
602 struct community
*com
= NULL
;
603 struct community
*com_sort
= NULL
;
605 enum community_token token
= community_token_unknown
;
608 str
= community_gettoken(str
, &token
, &val
);
611 case community_token_val
:
612 case community_token_no_export
:
613 case community_token_no_advertise
:
614 case community_token_local_as
:
615 case community_token_gshut
:
617 com
= community_new();
620 community_add_val(com
, val
);
622 case community_token_unknown
:
633 com_sort
= community_uniq_sort(com
);
639 /* Return communities hash entry count. */
640 unsigned long community_count(void)
642 return comhash
->count
;
645 /* Return communities hash. */
646 struct hash
*community_hash(void)
651 /* Initialize comminity related hash. */
652 void community_init(void)
654 comhash
= hash_create(
655 (unsigned int (*)(void *))community_hash_make
,
656 (int (*)(const void *, const void *))community_cmp
, NULL
);
659 void community_finish(void)