1 /* BGP Large Communities Attribute
3 * Copyright (C) 2016 Keyur Patel <keyur@arrcus.com>
5 * This file is part of FRRouting (FRR).
7 * FRR is free software; you can redistribute it and/or modify it under the
8 * terms of the GNU General Public License as published by the Free Software
9 * Foundation; either version 2, or (at your option) any later version.
11 * FRR is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
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
31 #include "bgpd/bgpd.h"
32 #include "bgpd/bgp_lcommunity.h"
33 #include "bgpd/bgp_community_alias.h"
34 #include "bgpd/bgp_aspath.h"
36 /* Hash of community attribute. */
37 static struct hash
*lcomhash
;
39 /* Allocate a new lcommunities. */
40 static struct lcommunity
*lcommunity_new(void)
42 return XCALLOC(MTYPE_LCOMMUNITY
, sizeof(struct lcommunity
));
45 /* Allocate lcommunities. */
46 void lcommunity_free(struct lcommunity
**lcom
)
51 XFREE(MTYPE_LCOMMUNITY_VAL
, (*lcom
)->val
);
52 XFREE(MTYPE_LCOMMUNITY_STR
, (*lcom
)->str
);
54 json_object_free((*lcom
)->json
);
55 XFREE(MTYPE_LCOMMUNITY
, *lcom
);
58 static void lcommunity_hash_free(struct lcommunity
*lcom
)
60 lcommunity_free(&lcom
);
63 /* Add a new Large Communities value to Large Communities
64 Attribute structure. When the value is already exists in the
65 structure, we don't add the value. Newly added value is sorted by
66 numerical order. When the value is added to the structure return 1
68 static bool lcommunity_add_val(struct lcommunity
*lcom
,
69 struct lcommunity_val
*lval
)
75 /* When this is fist value, just add it. */
76 if (lcom
->val
== NULL
) {
78 lcom
->val
= XMALLOC(MTYPE_LCOMMUNITY_VAL
, lcom_length(lcom
));
79 memcpy(lcom
->val
, lval
->val
, LCOMMUNITY_SIZE
);
83 /* If the value already exists in the structure return 0. */
85 for (p
= lcom
->val
; c
< lcom
->size
; p
+= LCOMMUNITY_SIZE
, c
++) {
86 ret
= memcmp(p
, lval
->val
, LCOMMUNITY_SIZE
);
93 /* Add the value to the structure with numerical sorting. */
96 XREALLOC(MTYPE_LCOMMUNITY_VAL
, lcom
->val
, lcom_length(lcom
));
98 memmove(lcom
->val
+ (c
+ 1) * LCOMMUNITY_SIZE
,
99 lcom
->val
+ c
* LCOMMUNITY_SIZE
,
100 (lcom
->size
- 1 - c
) * LCOMMUNITY_SIZE
);
101 memcpy(lcom
->val
+ c
* LCOMMUNITY_SIZE
, lval
->val
, LCOMMUNITY_SIZE
);
106 /* This function takes pointer to Large Communites strucutre then
107 create a new Large Communities structure by uniq and sort each
108 Large Communities value. */
109 struct lcommunity
*lcommunity_uniq_sort(struct lcommunity
*lcom
)
112 struct lcommunity
*new;
113 struct lcommunity_val
*lval
;
118 new = lcommunity_new();
120 for (i
= 0; i
< lcom
->size
; i
++) {
121 lval
= (struct lcommunity_val
*)(lcom
->val
122 + (i
* LCOMMUNITY_SIZE
));
123 lcommunity_add_val(new, lval
);
128 /* Parse Large Communites Attribute in BGP packet. */
129 struct lcommunity
*lcommunity_parse(uint8_t *pnt
, unsigned short length
)
131 struct lcommunity tmp
;
132 struct lcommunity
*new;
135 if (length
% LCOMMUNITY_SIZE
)
138 /* Prepare tmporary structure for making a new Large Communities
140 tmp
.size
= length
/ LCOMMUNITY_SIZE
;
143 /* Create a new Large Communities Attribute by uniq and sort each
144 Large Communities value */
145 new = lcommunity_uniq_sort(&tmp
);
147 return lcommunity_intern(new);
150 /* Duplicate the Large Communities Attribute structure. */
151 struct lcommunity
*lcommunity_dup(struct lcommunity
*lcom
)
153 struct lcommunity
*new;
155 new = lcommunity_new();
156 new->size
= lcom
->size
;
158 new->val
= XMALLOC(MTYPE_LCOMMUNITY_VAL
, lcom_length(lcom
));
159 memcpy(new->val
, lcom
->val
, lcom_length(lcom
));
165 /* Merge two Large Communities Attribute structure. */
166 struct lcommunity
*lcommunity_merge(struct lcommunity
*lcom1
,
167 struct lcommunity
*lcom2
)
169 lcom1
->val
= XREALLOC(MTYPE_LCOMMUNITY_VAL
, lcom1
->val
,
170 lcom_length(lcom1
) + lcom_length(lcom2
));
172 memcpy(lcom1
->val
+ lcom_length(lcom1
), lcom2
->val
, lcom_length(lcom2
));
173 lcom1
->size
+= lcom2
->size
;
178 static void set_lcommunity_string(struct lcommunity
*lcom
, bool make_json
)
184 uint32_t global
, local1
, local2
;
185 json_object
*json_lcommunity_list
= NULL
;
186 json_object
*json_string
= NULL
;
188 /* 3 32-bit integers, 2 colons, and a space */
189 #define LCOMMUNITY_STRLEN (10 * 3 + 2 + 1)
195 lcom
->json
= json_object_new_object();
196 json_lcommunity_list
= json_object_new_array();
199 if (lcom
->size
== 0) {
200 str_buf
= XCALLOC(MTYPE_LCOMMUNITY_STR
, 1);
203 json_object_string_add(lcom
->json
, "string", "");
204 json_object_object_add(lcom
->json
, "list",
205 json_lcommunity_list
);
212 /* 1 space + lcom->size lcom strings + null terminator */
213 size_t str_buf_sz
= BUFSIZ
;
214 str_buf
= XCALLOC(MTYPE_LCOMMUNITY_STR
, str_buf_sz
);
216 for (i
= 0; i
< lcom
->size
; i
++) {
218 strlcat(str_buf
, " ", str_buf_sz
);
220 pnt
= lcom
->val
+ (i
* LCOMMUNITY_SIZE
);
221 pnt
= ptr_get_be32(pnt
, &global
);
222 pnt
= ptr_get_be32(pnt
, &local1
);
223 pnt
= ptr_get_be32(pnt
, &local2
);
226 char lcsb
[LCOMMUNITY_STRLEN
+ 1];
228 snprintf(lcsb
, sizeof(lcsb
), "%u:%u:%u", global
, local1
,
231 const char *com2alias
= bgp_community2alias(lcsb
);
233 len
= strlcat(str_buf
, com2alias
, str_buf_sz
);
234 assert((unsigned int)len
< str_buf_sz
);
237 json_string
= json_object_new_string(com2alias
);
238 json_object_array_add(json_lcommunity_list
,
244 json_object_string_add(lcom
->json
, "string", str_buf
);
245 json_object_object_add(lcom
->json
, "list",
246 json_lcommunity_list
);
252 /* Intern Large Communities Attribute. */
253 struct lcommunity
*lcommunity_intern(struct lcommunity
*lcom
)
255 struct lcommunity
*find
;
257 assert(lcom
->refcnt
== 0);
259 find
= (struct lcommunity
*)hash_get(lcomhash
, lcom
, hash_alloc_intern
);
262 lcommunity_free(&lcom
);
267 set_lcommunity_string(find
, false);
272 /* Unintern Large Communities Attribute. */
273 void lcommunity_unintern(struct lcommunity
**lcom
)
275 struct lcommunity
*ret
;
280 /* Pull off from hash. */
281 if ((*lcom
)->refcnt
== 0) {
282 /* Large community must be in the hash. */
283 ret
= (struct lcommunity
*)hash_release(lcomhash
, *lcom
);
286 lcommunity_free(lcom
);
290 /* Return string representation of lcommunities attribute. */
291 char *lcommunity_str(struct lcommunity
*lcom
, bool make_json
)
296 if (make_json
&& !lcom
->json
&& lcom
->str
)
297 XFREE(MTYPE_LCOMMUNITY_STR
, lcom
->str
);
300 set_lcommunity_string(lcom
, make_json
);
305 /* Utility function to make hash key. */
306 unsigned int lcommunity_hash_make(const void *arg
)
308 const struct lcommunity
*lcom
= arg
;
309 int size
= lcom_length(lcom
);
311 return jhash(lcom
->val
, size
, 0xab125423);
314 /* Compare two Large Communities Attribute structure. */
315 bool lcommunity_cmp(const void *arg1
, const void *arg2
)
317 const struct lcommunity
*lcom1
= arg1
;
318 const struct lcommunity
*lcom2
= arg2
;
320 if (lcom1
== NULL
&& lcom2
== NULL
)
323 if (lcom1
== NULL
|| lcom2
== NULL
)
326 return (lcom1
->size
== lcom2
->size
327 && memcmp(lcom1
->val
, lcom2
->val
, lcom_length(lcom1
)) == 0);
330 /* Return communities hash. */
331 struct hash
*lcommunity_hash(void)
336 /* Initialize Large Comminities related hash. */
337 void lcommunity_init(void)
339 lcomhash
= hash_create(lcommunity_hash_make
, lcommunity_cmp
,
340 "BGP lcommunity hash");
343 void lcommunity_finish(void)
345 hash_clean(lcomhash
, (void (*)(void *))lcommunity_hash_free
);
350 /* Get next Large Communities token from the string.
351 * Assumes str is space-delimeted and describes 0 or more
352 * valid large communities
354 static const char *lcommunity_gettoken(const char *str
,
355 struct lcommunity_val
*lval
)
359 /* Skip white space. */
360 while (isspace((unsigned char)*p
)) {
365 /* Check the end of the line. */
369 /* Community value. */
372 uint32_t globaladmin
= 0;
373 uint32_t localdata1
= 0;
374 uint32_t localdata2
= 0;
376 while (*p
&& *p
!= ' ') {
377 /* large community valid chars */
378 assert(isdigit((unsigned char)*p
) || *p
== ':');
383 if (separator
== 1) {
384 globaladmin
= localdata2
;
386 localdata1
= localdata2
;
391 /* left shift the accumulated value and add current
395 localdata2
+= (*p
- '0');
400 /* Assert str was a valid large community */
401 assert(separator
== 2 && digit
== 1);
404 * Copy the large comm.
406 lval
->val
[0] = (globaladmin
>> 24) & 0xff;
407 lval
->val
[1] = (globaladmin
>> 16) & 0xff;
408 lval
->val
[2] = (globaladmin
>> 8) & 0xff;
409 lval
->val
[3] = globaladmin
& 0xff;
410 lval
->val
[4] = (localdata1
>> 24) & 0xff;
411 lval
->val
[5] = (localdata1
>> 16) & 0xff;
412 lval
->val
[6] = (localdata1
>> 8) & 0xff;
413 lval
->val
[7] = localdata1
& 0xff;
414 lval
->val
[8] = (localdata2
>> 24) & 0xff;
415 lval
->val
[9] = (localdata2
>> 16) & 0xff;
416 lval
->val
[10] = (localdata2
>> 8) & 0xff;
417 lval
->val
[11] = localdata2
& 0xff;
423 Convert string to large community attribute.
424 When type is already known, please specify both str and type.
426 When string includes keyword for each large community value.
427 Please specify keyword_included as non-zero value.
429 struct lcommunity
*lcommunity_str2com(const char *str
)
431 struct lcommunity
*lcom
= NULL
;
432 struct lcommunity_val lval
;
434 if (!lcommunity_list_valid(str
, LARGE_COMMUNITY_LIST_STANDARD
))
438 str
= lcommunity_gettoken(str
, &lval
);
440 lcom
= lcommunity_new();
441 lcommunity_add_val(lcom
, &lval
);
447 bool lcommunity_include(struct lcommunity
*lcom
, uint8_t *ptr
)
452 for (i
= 0; i
< lcom
->size
; i
++) {
453 lcom_ptr
= lcom
->val
+ (i
* LCOMMUNITY_SIZE
);
454 if (memcmp(ptr
, lcom_ptr
, LCOMMUNITY_SIZE
) == 0)
460 bool lcommunity_match(const struct lcommunity
*lcom1
,
461 const struct lcommunity
*lcom2
)
466 if (lcom1
== NULL
&& lcom2
== NULL
)
469 if (lcom1
== NULL
|| lcom2
== NULL
)
472 if (lcom1
->size
< lcom2
->size
)
475 /* Every community on com2 needs to be on com1 for this to match */
476 while (i
< lcom1
->size
&& j
< lcom2
->size
) {
477 if (memcmp(lcom1
->val
+ (i
* LCOMMUNITY_SIZE
),
478 lcom2
->val
+ (j
* LCOMMUNITY_SIZE
), LCOMMUNITY_SIZE
)
484 if (j
== lcom2
->size
)
490 /* Delete one lcommunity. */
491 void lcommunity_del_val(struct lcommunity
*lcom
, uint8_t *ptr
)
499 while (i
< lcom
->size
) {
500 if (memcmp(lcom
->val
+ i
* LCOMMUNITY_SIZE
, ptr
,
503 c
= lcom
->size
- i
- 1;
506 memmove(lcom
->val
+ i
* LCOMMUNITY_SIZE
,
507 lcom
->val
+ (i
+ 1) * LCOMMUNITY_SIZE
,
508 c
* LCOMMUNITY_SIZE
);
514 XREALLOC(MTYPE_LCOMMUNITY_VAL
,
515 lcom
->val
, lcom_length(lcom
));
517 XFREE(MTYPE_LCOMMUNITY_VAL
, lcom
->val
);
525 static struct lcommunity
*bgp_aggr_lcommunity_lookup(
526 struct bgp_aggregate
*aggregate
,
527 struct lcommunity
*lcommunity
)
529 return hash_lookup(aggregate
->lcommunity_hash
, lcommunity
);
532 static void *bgp_aggr_lcommunty_hash_alloc(void *p
)
534 struct lcommunity
*ref
= (struct lcommunity
*)p
;
535 struct lcommunity
*lcommunity
= NULL
;
537 lcommunity
= lcommunity_dup(ref
);
541 static void bgp_aggr_lcommunity_prepare(struct hash_bucket
*hb
, void *arg
)
543 struct lcommunity
*hb_lcommunity
= hb
->data
;
544 struct lcommunity
**aggr_lcommunity
= arg
;
546 if (*aggr_lcommunity
)
547 *aggr_lcommunity
= lcommunity_merge(*aggr_lcommunity
,
550 *aggr_lcommunity
= lcommunity_dup(hb_lcommunity
);
553 void bgp_aggr_lcommunity_remove(void *arg
)
555 struct lcommunity
*lcommunity
= arg
;
557 lcommunity_free(&lcommunity
);
560 void bgp_compute_aggregate_lcommunity(struct bgp_aggregate
*aggregate
,
561 struct lcommunity
*lcommunity
)
564 bgp_compute_aggregate_lcommunity_hash(aggregate
, lcommunity
);
565 bgp_compute_aggregate_lcommunity_val(aggregate
);
568 void bgp_compute_aggregate_lcommunity_hash(struct bgp_aggregate
*aggregate
,
569 struct lcommunity
*lcommunity
)
572 struct lcommunity
*aggr_lcommunity
= NULL
;
574 if ((aggregate
== NULL
) || (lcommunity
== NULL
))
577 /* Create hash if not already created.
579 if (aggregate
->lcommunity_hash
== NULL
)
580 aggregate
->lcommunity_hash
= hash_create(
581 lcommunity_hash_make
, lcommunity_cmp
,
582 "BGP Aggregator lcommunity hash");
584 aggr_lcommunity
= bgp_aggr_lcommunity_lookup(aggregate
, lcommunity
);
585 if (aggr_lcommunity
== NULL
) {
586 /* Insert lcommunity into hash.
588 aggr_lcommunity
= hash_get(aggregate
->lcommunity_hash
,
590 bgp_aggr_lcommunty_hash_alloc
);
593 /* Increment reference counter.
595 aggr_lcommunity
->refcnt
++;
598 void bgp_compute_aggregate_lcommunity_val(struct bgp_aggregate
*aggregate
)
600 struct lcommunity
*lcommerge
= NULL
;
602 if (aggregate
== NULL
)
605 /* Re-compute aggregate's lcommunity.
607 if (aggregate
->lcommunity
)
608 lcommunity_free(&aggregate
->lcommunity
);
609 if (aggregate
->lcommunity_hash
&&
610 aggregate
->lcommunity_hash
->count
) {
611 hash_iterate(aggregate
->lcommunity_hash
,
612 bgp_aggr_lcommunity_prepare
,
613 &aggregate
->lcommunity
);
614 lcommerge
= aggregate
->lcommunity
;
615 aggregate
->lcommunity
= lcommunity_uniq_sort(lcommerge
);
617 lcommunity_free(&lcommerge
);
621 void bgp_remove_lcommunity_from_aggregate(struct bgp_aggregate
*aggregate
,
622 struct lcommunity
*lcommunity
)
624 struct lcommunity
*aggr_lcommunity
= NULL
;
625 struct lcommunity
*ret_lcomm
= NULL
;
628 || (!aggregate
->lcommunity_hash
)
632 /* Look-up the lcommunity in the hash.
634 aggr_lcommunity
= bgp_aggr_lcommunity_lookup(aggregate
, lcommunity
);
635 if (aggr_lcommunity
) {
636 aggr_lcommunity
->refcnt
--;
638 if (aggr_lcommunity
->refcnt
== 0) {
639 ret_lcomm
= hash_release(aggregate
->lcommunity_hash
,
641 lcommunity_free(&ret_lcomm
);
643 bgp_compute_aggregate_lcommunity_val(aggregate
);
649 void bgp_remove_lcomm_from_aggregate_hash(struct bgp_aggregate
*aggregate
,
650 struct lcommunity
*lcommunity
)
652 struct lcommunity
*aggr_lcommunity
= NULL
;
653 struct lcommunity
*ret_lcomm
= NULL
;
656 || (!aggregate
->lcommunity_hash
)
660 /* Look-up the lcommunity in the hash.
662 aggr_lcommunity
= bgp_aggr_lcommunity_lookup(aggregate
, lcommunity
);
663 if (aggr_lcommunity
) {
664 aggr_lcommunity
->refcnt
--;
666 if (aggr_lcommunity
->refcnt
== 0) {
667 ret_lcomm
= hash_release(aggregate
->lcommunity_hash
,
669 lcommunity_free(&ret_lcomm
);