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_aspath.h"
35 /* Hash of community attribute. */
36 static struct hash
*lcomhash
;
38 /* Allocate a new lcommunities. */
39 static struct lcommunity
*lcommunity_new(void)
41 return XCALLOC(MTYPE_LCOMMUNITY
, sizeof(struct lcommunity
));
44 /* Allocate lcommunities. */
45 void lcommunity_free(struct lcommunity
**lcom
)
50 XFREE(MTYPE_LCOMMUNITY_VAL
, (*lcom
)->val
);
51 XFREE(MTYPE_LCOMMUNITY_STR
, (*lcom
)->str
);
53 json_object_free((*lcom
)->json
);
54 XFREE(MTYPE_LCOMMUNITY
, *lcom
);
57 static void lcommunity_hash_free(struct lcommunity
*lcom
)
59 lcommunity_free(&lcom
);
62 /* Add a new Large Communities value to Large Communities
63 Attribute structure. When the value is already exists in the
64 structure, we don't add the value. Newly added value is sorted by
65 numerical order. When the value is added to the structure return 1
67 static bool lcommunity_add_val(struct lcommunity
*lcom
,
68 struct lcommunity_val
*lval
)
74 /* When this is fist value, just add it. */
75 if (lcom
->val
== NULL
) {
77 lcom
->val
= XMALLOC(MTYPE_LCOMMUNITY_VAL
, lcom_length(lcom
));
78 memcpy(lcom
->val
, lval
->val
, LCOMMUNITY_SIZE
);
82 /* If the value already exists in the structure return 0. */
84 for (p
= lcom
->val
; c
< lcom
->size
; p
+= LCOMMUNITY_SIZE
, c
++) {
85 ret
= memcmp(p
, lval
->val
, LCOMMUNITY_SIZE
);
92 /* Add the value to the structure with numerical sorting. */
95 XREALLOC(MTYPE_LCOMMUNITY_VAL
, lcom
->val
, lcom_length(lcom
));
97 memmove(lcom
->val
+ (c
+ 1) * LCOMMUNITY_SIZE
,
98 lcom
->val
+ c
* LCOMMUNITY_SIZE
,
99 (lcom
->size
- 1 - c
) * LCOMMUNITY_SIZE
);
100 memcpy(lcom
->val
+ c
* LCOMMUNITY_SIZE
, lval
->val
, LCOMMUNITY_SIZE
);
105 /* This function takes pointer to Large Communites strucutre then
106 create a new Large Communities structure by uniq and sort each
107 Large Communities value. */
108 struct lcommunity
*lcommunity_uniq_sort(struct lcommunity
*lcom
)
111 struct lcommunity
*new;
112 struct lcommunity_val
*lval
;
117 new = lcommunity_new();
119 for (i
= 0; i
< lcom
->size
; i
++) {
120 lval
= (struct lcommunity_val
*)(lcom
->val
121 + (i
* LCOMMUNITY_SIZE
));
122 lcommunity_add_val(new, lval
);
127 /* Parse Large Communites Attribute in BGP packet. */
128 struct lcommunity
*lcommunity_parse(uint8_t *pnt
, unsigned short length
)
130 struct lcommunity tmp
;
131 struct lcommunity
*new;
134 if (length
% LCOMMUNITY_SIZE
)
137 /* Prepare tmporary structure for making a new Large Communities
139 tmp
.size
= length
/ LCOMMUNITY_SIZE
;
142 /* Create a new Large Communities Attribute by uniq and sort each
143 Large Communities value */
144 new = lcommunity_uniq_sort(&tmp
);
146 return lcommunity_intern(new);
149 /* Duplicate the Large Communities Attribute structure. */
150 struct lcommunity
*lcommunity_dup(struct lcommunity
*lcom
)
152 struct lcommunity
*new;
154 new = lcommunity_new();
155 new->size
= lcom
->size
;
157 new->val
= XMALLOC(MTYPE_LCOMMUNITY_VAL
, lcom_length(lcom
));
158 memcpy(new->val
, lcom
->val
, lcom_length(lcom
));
164 /* Merge two Large Communities Attribute structure. */
165 struct lcommunity
*lcommunity_merge(struct lcommunity
*lcom1
,
166 struct lcommunity
*lcom2
)
169 lcom1
->val
= XREALLOC(MTYPE_LCOMMUNITY_VAL
, lcom1
->val
,
170 lcom_length(lcom1
) + lcom_length(lcom2
));
172 lcom1
->val
= XMALLOC(MTYPE_LCOMMUNITY_VAL
,
173 lcom_length(lcom1
) + lcom_length(lcom2
));
175 memcpy(lcom1
->val
+ lcom_length(lcom1
), lcom2
->val
, lcom_length(lcom2
));
176 lcom1
->size
+= lcom2
->size
;
181 static void set_lcommunity_string(struct lcommunity
*lcom
, bool make_json
)
187 uint32_t global
, local1
, local2
;
188 json_object
*json_lcommunity_list
= NULL
;
189 json_object
*json_string
= NULL
;
191 /* 3 32-bit integers, 2 colons, and a space */
192 #define LCOMMUNITY_STRLEN (10 * 3 + 2 + 1)
198 lcom
->json
= json_object_new_object();
199 json_lcommunity_list
= json_object_new_array();
202 if (lcom
->size
== 0) {
203 str_buf
= XCALLOC(MTYPE_LCOMMUNITY_STR
, 1);
206 json_object_string_add(lcom
->json
, "string", "");
207 json_object_object_add(lcom
->json
, "list",
208 json_lcommunity_list
);
215 /* 1 space + lcom->size lcom strings + null terminator */
216 size_t str_buf_sz
= (LCOMMUNITY_STRLEN
* lcom
->size
) + 2;
217 str_buf
= XCALLOC(MTYPE_LCOMMUNITY_STR
, str_buf_sz
);
219 for (i
= 0; i
< lcom
->size
; i
++) {
221 strlcat(str_buf
, " ", str_buf_sz
);
223 pnt
= lcom
->val
+ (i
* LCOMMUNITY_SIZE
);
224 pnt
= ptr_get_be32(pnt
, &global
);
225 pnt
= ptr_get_be32(pnt
, &local1
);
226 pnt
= ptr_get_be32(pnt
, &local2
);
229 char lcsb
[LCOMMUNITY_STRLEN
+ 1];
231 snprintf(lcsb
, sizeof(lcsb
), "%u:%u:%u", global
, local1
,
234 len
= strlcat(str_buf
, lcsb
, str_buf_sz
);
235 assert((unsigned int)len
< str_buf_sz
);
238 json_string
= json_object_new_string(lcsb
);
239 json_object_array_add(json_lcommunity_list
,
245 json_object_string_add(lcom
->json
, "string", str_buf
);
246 json_object_object_add(lcom
->json
, "list",
247 json_lcommunity_list
);
253 /* Intern Large Communities Attribute. */
254 struct lcommunity
*lcommunity_intern(struct lcommunity
*lcom
)
256 struct lcommunity
*find
;
258 assert(lcom
->refcnt
== 0);
260 find
= (struct lcommunity
*)hash_get(lcomhash
, lcom
, hash_alloc_intern
);
263 lcommunity_free(&lcom
);
268 set_lcommunity_string(find
, false);
273 /* Unintern Large Communities Attribute. */
274 void lcommunity_unintern(struct lcommunity
**lcom
)
276 struct lcommunity
*ret
;
281 /* Pull off from hash. */
282 if ((*lcom
)->refcnt
== 0) {
283 /* Large community must be in the hash. */
284 ret
= (struct lcommunity
*)hash_release(lcomhash
, *lcom
);
287 lcommunity_free(lcom
);
291 /* Retrun string representation of communities attribute. */
292 char *lcommunity_str(struct lcommunity
*lcom
, bool make_json
)
297 if (make_json
&& !lcom
->json
&& lcom
->str
)
298 XFREE(MTYPE_LCOMMUNITY_STR
, lcom
->str
);
301 set_lcommunity_string(lcom
, make_json
);
306 /* Utility function to make hash key. */
307 unsigned int lcommunity_hash_make(const void *arg
)
309 const struct lcommunity
*lcom
= arg
;
310 int size
= lcom_length(lcom
);
312 return jhash(lcom
->val
, size
, 0xab125423);
315 /* Compare two Large Communities Attribute structure. */
316 bool lcommunity_cmp(const void *arg1
, const void *arg2
)
318 const struct lcommunity
*lcom1
= arg1
;
319 const struct lcommunity
*lcom2
= arg2
;
321 if (lcom1
== NULL
&& lcom2
== NULL
)
324 if (lcom1
== NULL
|| lcom2
== NULL
)
327 return (lcom1
->size
== lcom2
->size
328 && memcmp(lcom1
->val
, lcom2
->val
, lcom_length(lcom1
)) == 0);
331 /* Return communities hash. */
332 struct hash
*lcommunity_hash(void)
337 /* Initialize Large Comminities related hash. */
338 void lcommunity_init(void)
340 lcomhash
= hash_create(lcommunity_hash_make
, lcommunity_cmp
,
341 "BGP lcommunity hash");
344 void lcommunity_finish(void)
346 hash_clean(lcomhash
, (void (*)(void *))lcommunity_hash_free
);
351 /* Get next Large Communities token from the string.
352 * Assumes str is space-delimeted and describes 0 or more
353 * valid large communities
355 static const char *lcommunity_gettoken(const char *str
,
356 struct lcommunity_val
*lval
)
360 /* Skip white space. */
361 while (isspace((unsigned char)*p
)) {
366 /* Check the end of the line. */
370 /* Community value. */
373 uint32_t globaladmin
= 0;
374 uint32_t localdata1
= 0;
375 uint32_t localdata2
= 0;
377 while (*p
&& *p
!= ' ') {
378 /* large community valid chars */
379 assert(isdigit((unsigned char)*p
) || *p
== ':');
384 if (separator
== 1) {
385 globaladmin
= localdata2
;
387 localdata1
= localdata2
;
392 /* left shift the accumulated value and add current
396 localdata2
+= (*p
- '0');
401 /* Assert str was a valid large community */
402 assert(separator
== 2 && digit
== 1);
405 * Copy the large comm.
407 lval
->val
[0] = (globaladmin
>> 24) & 0xff;
408 lval
->val
[1] = (globaladmin
>> 16) & 0xff;
409 lval
->val
[2] = (globaladmin
>> 8) & 0xff;
410 lval
->val
[3] = globaladmin
& 0xff;
411 lval
->val
[4] = (localdata1
>> 24) & 0xff;
412 lval
->val
[5] = (localdata1
>> 16) & 0xff;
413 lval
->val
[6] = (localdata1
>> 8) & 0xff;
414 lval
->val
[7] = localdata1
& 0xff;
415 lval
->val
[8] = (localdata2
>> 24) & 0xff;
416 lval
->val
[9] = (localdata2
>> 16) & 0xff;
417 lval
->val
[10] = (localdata2
>> 8) & 0xff;
418 lval
->val
[11] = localdata2
& 0xff;
424 Convert string to large community attribute.
425 When type is already known, please specify both str and type.
427 When string includes keyword for each large community value.
428 Please specify keyword_included as non-zero value.
430 struct lcommunity
*lcommunity_str2com(const char *str
)
432 struct lcommunity
*lcom
= NULL
;
433 struct lcommunity_val lval
;
435 if (!lcommunity_list_valid(str
, LARGE_COMMUNITY_LIST_STANDARD
))
439 str
= lcommunity_gettoken(str
, &lval
);
441 lcom
= lcommunity_new();
442 lcommunity_add_val(lcom
, &lval
);
448 bool lcommunity_include(struct lcommunity
*lcom
, uint8_t *ptr
)
453 for (i
= 0; i
< lcom
->size
; i
++) {
454 lcom_ptr
= lcom
->val
+ (i
* LCOMMUNITY_SIZE
);
455 if (memcmp(ptr
, lcom_ptr
, LCOMMUNITY_SIZE
) == 0)
461 bool lcommunity_match(const struct lcommunity
*lcom1
,
462 const struct lcommunity
*lcom2
)
467 if (lcom1
== NULL
&& lcom2
== NULL
)
470 if (lcom1
== NULL
|| lcom2
== NULL
)
473 if (lcom1
->size
< lcom2
->size
)
476 /* Every community on com2 needs to be on com1 for this to match */
477 while (i
< lcom1
->size
&& j
< lcom2
->size
) {
478 if (memcmp(lcom1
->val
+ (i
* LCOMMUNITY_SIZE
),
479 lcom2
->val
+ (j
* LCOMMUNITY_SIZE
), LCOMMUNITY_SIZE
)
485 if (j
== lcom2
->size
)
491 /* Delete one lcommunity. */
492 void lcommunity_del_val(struct lcommunity
*lcom
, uint8_t *ptr
)
500 while (i
< lcom
->size
) {
501 if (memcmp(lcom
->val
+ i
* LCOMMUNITY_SIZE
, ptr
,
504 c
= lcom
->size
- i
- 1;
507 memmove(lcom
->val
+ i
* LCOMMUNITY_SIZE
,
508 lcom
->val
+ (i
+ 1) * LCOMMUNITY_SIZE
,
509 c
* LCOMMUNITY_SIZE
);
515 XREALLOC(MTYPE_LCOMMUNITY_VAL
,
516 lcom
->val
, lcom_length(lcom
));
518 XFREE(MTYPE_LCOMMUNITY_VAL
, lcom
->val
);
526 static struct lcommunity
*bgp_aggr_lcommunity_lookup(
527 struct bgp_aggregate
*aggregate
,
528 struct lcommunity
*lcommunity
)
530 return hash_lookup(aggregate
->lcommunity_hash
, lcommunity
);
533 static void *bgp_aggr_lcommunty_hash_alloc(void *p
)
535 struct lcommunity
*ref
= (struct lcommunity
*)p
;
536 struct lcommunity
*lcommunity
= NULL
;
538 lcommunity
= lcommunity_dup(ref
);
542 static void bgp_aggr_lcommunity_prepare(struct hash_bucket
*hb
, void *arg
)
544 struct lcommunity
*hb_lcommunity
= hb
->data
;
545 struct lcommunity
**aggr_lcommunity
= arg
;
547 if (*aggr_lcommunity
)
548 *aggr_lcommunity
= lcommunity_merge(*aggr_lcommunity
,
551 *aggr_lcommunity
= lcommunity_dup(hb_lcommunity
);
554 void bgp_aggr_lcommunity_remove(void *arg
)
556 struct lcommunity
*lcommunity
= arg
;
558 lcommunity_free(&lcommunity
);
561 void bgp_compute_aggregate_lcommunity(struct bgp_aggregate
*aggregate
,
562 struct lcommunity
*lcommunity
)
565 bgp_compute_aggregate_lcommunity_hash(aggregate
, lcommunity
);
566 bgp_compute_aggregate_lcommunity_val(aggregate
);
569 void bgp_compute_aggregate_lcommunity_hash(struct bgp_aggregate
*aggregate
,
570 struct lcommunity
*lcommunity
)
573 struct lcommunity
*aggr_lcommunity
= NULL
;
575 if ((aggregate
== NULL
) || (lcommunity
== NULL
))
578 /* Create hash if not already created.
580 if (aggregate
->lcommunity_hash
== NULL
)
581 aggregate
->lcommunity_hash
= hash_create(
582 lcommunity_hash_make
, lcommunity_cmp
,
583 "BGP Aggregator lcommunity hash");
585 aggr_lcommunity
= bgp_aggr_lcommunity_lookup(aggregate
, lcommunity
);
586 if (aggr_lcommunity
== NULL
) {
587 /* Insert lcommunity into hash.
589 aggr_lcommunity
= hash_get(aggregate
->lcommunity_hash
,
591 bgp_aggr_lcommunty_hash_alloc
);
594 /* Increment reference counter.
596 aggr_lcommunity
->refcnt
++;
599 void bgp_compute_aggregate_lcommunity_val(struct bgp_aggregate
*aggregate
)
601 struct lcommunity
*lcommerge
= NULL
;
603 if (aggregate
== NULL
)
606 /* Re-compute aggregate's lcommunity.
608 if (aggregate
->lcommunity
)
609 lcommunity_free(&aggregate
->lcommunity
);
610 if (aggregate
->lcommunity_hash
&&
611 aggregate
->lcommunity_hash
->count
) {
612 hash_iterate(aggregate
->lcommunity_hash
,
613 bgp_aggr_lcommunity_prepare
,
614 &aggregate
->lcommunity
);
615 lcommerge
= aggregate
->lcommunity
;
616 aggregate
->lcommunity
= lcommunity_uniq_sort(lcommerge
);
618 lcommunity_free(&lcommerge
);
622 void bgp_remove_lcommunity_from_aggregate(struct bgp_aggregate
*aggregate
,
623 struct lcommunity
*lcommunity
)
625 struct lcommunity
*aggr_lcommunity
= NULL
;
626 struct lcommunity
*ret_lcomm
= NULL
;
629 || (!aggregate
->lcommunity_hash
)
633 /* Look-up the lcommunity in the hash.
635 aggr_lcommunity
= bgp_aggr_lcommunity_lookup(aggregate
, lcommunity
);
636 if (aggr_lcommunity
) {
637 aggr_lcommunity
->refcnt
--;
639 if (aggr_lcommunity
->refcnt
== 0) {
640 ret_lcomm
= hash_release(aggregate
->lcommunity_hash
,
642 lcommunity_free(&ret_lcomm
);
644 bgp_compute_aggregate_lcommunity_val(aggregate
);
650 void bgp_remove_lcomm_from_aggregate_hash(struct bgp_aggregate
*aggregate
,
651 struct lcommunity
*lcommunity
)
653 struct lcommunity
*aggr_lcommunity
= NULL
;
654 struct lcommunity
*ret_lcomm
= NULL
;
657 || (!aggregate
->lcommunity_hash
)
661 /* Look-up the lcommunity in the hash.
663 aggr_lcommunity
= bgp_aggr_lcommunity_lookup(aggregate
, lcommunity
);
664 if (aggr_lcommunity
) {
665 aggr_lcommunity
->refcnt
--;
667 if (aggr_lcommunity
->refcnt
== 0) {
668 ret_lcomm
= hash_release(aggregate
->lcommunity_hash
,
670 lcommunity_free(&ret_lcomm
);