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 structure 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
,
179 bool translate_alias
)
185 uint32_t global
, local1
, local2
;
186 json_object
*json_lcommunity_list
= NULL
;
187 json_object
*json_string
= NULL
;
189 /* 3 32-bit integers, 2 colons, and a space */
190 #define LCOMMUNITY_STRLEN (10 * 3 + 2 + 1)
196 lcom
->json
= json_object_new_object();
197 json_lcommunity_list
= json_object_new_array();
200 if (lcom
->size
== 0) {
201 str_buf
= XCALLOC(MTYPE_LCOMMUNITY_STR
, 1);
204 json_object_string_add(lcom
->json
, "string", "");
205 json_object_object_add(lcom
->json
, "list",
206 json_lcommunity_list
);
213 /* 1 space + lcom->size lcom strings + null terminator */
214 size_t str_buf_sz
= BUFSIZ
;
215 str_buf
= XCALLOC(MTYPE_LCOMMUNITY_STR
, str_buf_sz
);
217 for (i
= 0; i
< lcom
->size
; i
++) {
219 strlcat(str_buf
, " ", str_buf_sz
);
221 pnt
= lcom
->val
+ (i
* LCOMMUNITY_SIZE
);
222 pnt
= ptr_get_be32(pnt
, &global
);
223 pnt
= ptr_get_be32(pnt
, &local1
);
224 pnt
= ptr_get_be32(pnt
, &local2
);
227 char lcsb
[LCOMMUNITY_STRLEN
+ 1];
229 snprintf(lcsb
, sizeof(lcsb
), "%u:%u:%u", global
, local1
,
232 const char *com2alias
=
233 translate_alias
? bgp_community2alias(lcsb
) : lcsb
;
235 len
= strlcat(str_buf
, com2alias
, str_buf_sz
);
236 assert((unsigned int)len
< str_buf_sz
);
239 json_string
= json_object_new_string(com2alias
);
240 json_object_array_add(json_lcommunity_list
,
246 json_object_string_add(lcom
->json
, "string", str_buf
);
247 json_object_object_add(lcom
->json
, "list",
248 json_lcommunity_list
);
254 /* Intern Large Communities Attribute. */
255 struct lcommunity
*lcommunity_intern(struct lcommunity
*lcom
)
257 struct lcommunity
*find
;
259 assert(lcom
->refcnt
== 0);
261 find
= (struct lcommunity
*)hash_get(lcomhash
, lcom
, hash_alloc_intern
);
264 lcommunity_free(&lcom
);
269 set_lcommunity_string(find
, false, true);
274 /* Unintern Large Communities Attribute. */
275 void lcommunity_unintern(struct lcommunity
**lcom
)
277 struct lcommunity
*ret
;
285 /* Pull off from hash. */
286 if ((*lcom
)->refcnt
== 0) {
287 /* Large community must be in the hash. */
288 ret
= (struct lcommunity
*)hash_release(lcomhash
, *lcom
);
291 lcommunity_free(lcom
);
295 /* Return string representation of lcommunities attribute. */
296 char *lcommunity_str(struct lcommunity
*lcom
, bool make_json
,
297 bool translate_alias
)
302 if (make_json
&& !lcom
->json
&& lcom
->str
)
303 XFREE(MTYPE_LCOMMUNITY_STR
, lcom
->str
);
306 set_lcommunity_string(lcom
, make_json
, translate_alias
);
311 /* Utility function to make hash key. */
312 unsigned int lcommunity_hash_make(const void *arg
)
314 const struct lcommunity
*lcom
= arg
;
315 int size
= lcom_length(lcom
);
317 return jhash(lcom
->val
, size
, 0xab125423);
320 /* Compare two Large Communities Attribute structure. */
321 bool lcommunity_cmp(const void *arg1
, const void *arg2
)
323 const struct lcommunity
*lcom1
= arg1
;
324 const struct lcommunity
*lcom2
= arg2
;
326 if (lcom1
== NULL
&& lcom2
== NULL
)
329 if (lcom1
== NULL
|| lcom2
== NULL
)
332 return (lcom1
->size
== lcom2
->size
333 && memcmp(lcom1
->val
, lcom2
->val
, lcom_length(lcom1
)) == 0);
336 /* Return communities hash. */
337 struct hash
*lcommunity_hash(void)
342 /* Initialize Large Comminities related hash. */
343 void lcommunity_init(void)
345 lcomhash
= hash_create(lcommunity_hash_make
, lcommunity_cmp
,
346 "BGP lcommunity hash");
349 void lcommunity_finish(void)
351 hash_clean(lcomhash
, (void (*)(void *))lcommunity_hash_free
);
356 /* Get next Large Communities token from the string.
357 * Assumes str is space-delimeted and describes 0 or more
358 * valid large communities
360 static const char *lcommunity_gettoken(const char *str
,
361 struct lcommunity_val
*lval
)
365 /* Skip white space. */
366 while (isspace((unsigned char)*p
)) {
371 /* Check the end of the line. */
375 /* Community value. */
378 uint32_t globaladmin
= 0;
379 uint32_t localdata1
= 0;
380 uint32_t localdata2
= 0;
382 while (*p
&& *p
!= ' ') {
383 /* large community valid chars */
384 assert(isdigit((unsigned char)*p
) || *p
== ':');
389 if (separator
== 1) {
390 globaladmin
= localdata2
;
392 localdata1
= localdata2
;
397 /* left shift the accumulated value and add current
401 localdata2
+= (*p
- '0');
406 /* Assert str was a valid large community */
407 assert(separator
== 2 && digit
== 1);
410 * Copy the large comm.
412 lval
->val
[0] = (globaladmin
>> 24) & 0xff;
413 lval
->val
[1] = (globaladmin
>> 16) & 0xff;
414 lval
->val
[2] = (globaladmin
>> 8) & 0xff;
415 lval
->val
[3] = globaladmin
& 0xff;
416 lval
->val
[4] = (localdata1
>> 24) & 0xff;
417 lval
->val
[5] = (localdata1
>> 16) & 0xff;
418 lval
->val
[6] = (localdata1
>> 8) & 0xff;
419 lval
->val
[7] = localdata1
& 0xff;
420 lval
->val
[8] = (localdata2
>> 24) & 0xff;
421 lval
->val
[9] = (localdata2
>> 16) & 0xff;
422 lval
->val
[10] = (localdata2
>> 8) & 0xff;
423 lval
->val
[11] = localdata2
& 0xff;
429 Convert string to large community attribute.
430 When type is already known, please specify both str and type.
432 When string includes keyword for each large community value.
433 Please specify keyword_included as non-zero value.
435 struct lcommunity
*lcommunity_str2com(const char *str
)
437 struct lcommunity
*lcom
= NULL
;
438 struct lcommunity_val lval
;
440 if (!lcommunity_list_valid(str
, LARGE_COMMUNITY_LIST_STANDARD
))
444 str
= lcommunity_gettoken(str
, &lval
);
446 lcom
= lcommunity_new();
447 lcommunity_add_val(lcom
, &lval
);
453 bool lcommunity_include(struct lcommunity
*lcom
, uint8_t *ptr
)
458 for (i
= 0; i
< lcom
->size
; i
++) {
459 lcom_ptr
= lcom
->val
+ (i
* LCOMMUNITY_SIZE
);
460 if (memcmp(ptr
, lcom_ptr
, LCOMMUNITY_SIZE
) == 0)
466 bool lcommunity_match(const struct lcommunity
*lcom1
,
467 const struct lcommunity
*lcom2
)
472 if (lcom1
== NULL
&& lcom2
== NULL
)
475 if (lcom1
== NULL
|| lcom2
== NULL
)
478 if (lcom1
->size
< lcom2
->size
)
481 /* Every community on com2 needs to be on com1 for this to match */
482 while (i
< lcom1
->size
&& j
< lcom2
->size
) {
483 if (memcmp(lcom1
->val
+ (i
* LCOMMUNITY_SIZE
),
484 lcom2
->val
+ (j
* LCOMMUNITY_SIZE
), LCOMMUNITY_SIZE
)
490 if (j
== lcom2
->size
)
496 /* Delete one lcommunity. */
497 void lcommunity_del_val(struct lcommunity
*lcom
, uint8_t *ptr
)
505 while (i
< lcom
->size
) {
506 if (memcmp(lcom
->val
+ i
* LCOMMUNITY_SIZE
, ptr
,
509 c
= lcom
->size
- i
- 1;
512 memmove(lcom
->val
+ i
* LCOMMUNITY_SIZE
,
513 lcom
->val
+ (i
+ 1) * LCOMMUNITY_SIZE
,
514 c
* LCOMMUNITY_SIZE
);
520 XREALLOC(MTYPE_LCOMMUNITY_VAL
,
521 lcom
->val
, lcom_length(lcom
));
523 XFREE(MTYPE_LCOMMUNITY_VAL
, lcom
->val
);
531 static struct lcommunity
*bgp_aggr_lcommunity_lookup(
532 struct bgp_aggregate
*aggregate
,
533 struct lcommunity
*lcommunity
)
535 return hash_lookup(aggregate
->lcommunity_hash
, lcommunity
);
538 static void *bgp_aggr_lcommunty_hash_alloc(void *p
)
540 struct lcommunity
*ref
= (struct lcommunity
*)p
;
541 struct lcommunity
*lcommunity
= NULL
;
543 lcommunity
= lcommunity_dup(ref
);
547 static void bgp_aggr_lcommunity_prepare(struct hash_bucket
*hb
, void *arg
)
549 struct lcommunity
*hb_lcommunity
= hb
->data
;
550 struct lcommunity
**aggr_lcommunity
= arg
;
552 if (*aggr_lcommunity
)
553 *aggr_lcommunity
= lcommunity_merge(*aggr_lcommunity
,
556 *aggr_lcommunity
= lcommunity_dup(hb_lcommunity
);
559 void bgp_aggr_lcommunity_remove(void *arg
)
561 struct lcommunity
*lcommunity
= arg
;
563 lcommunity_free(&lcommunity
);
566 void bgp_compute_aggregate_lcommunity(struct bgp_aggregate
*aggregate
,
567 struct lcommunity
*lcommunity
)
570 bgp_compute_aggregate_lcommunity_hash(aggregate
, lcommunity
);
571 bgp_compute_aggregate_lcommunity_val(aggregate
);
574 void bgp_compute_aggregate_lcommunity_hash(struct bgp_aggregate
*aggregate
,
575 struct lcommunity
*lcommunity
)
578 struct lcommunity
*aggr_lcommunity
= NULL
;
580 if ((aggregate
== NULL
) || (lcommunity
== NULL
))
583 /* Create hash if not already created.
585 if (aggregate
->lcommunity_hash
== NULL
)
586 aggregate
->lcommunity_hash
= hash_create(
587 lcommunity_hash_make
, lcommunity_cmp
,
588 "BGP Aggregator lcommunity hash");
590 aggr_lcommunity
= bgp_aggr_lcommunity_lookup(aggregate
, lcommunity
);
591 if (aggr_lcommunity
== NULL
) {
592 /* Insert lcommunity into hash.
594 aggr_lcommunity
= hash_get(aggregate
->lcommunity_hash
,
596 bgp_aggr_lcommunty_hash_alloc
);
599 /* Increment reference counter.
601 aggr_lcommunity
->refcnt
++;
604 void bgp_compute_aggregate_lcommunity_val(struct bgp_aggregate
*aggregate
)
606 struct lcommunity
*lcommerge
= NULL
;
608 if (aggregate
== NULL
)
611 /* Re-compute aggregate's lcommunity.
613 if (aggregate
->lcommunity
)
614 lcommunity_free(&aggregate
->lcommunity
);
615 if (aggregate
->lcommunity_hash
&&
616 aggregate
->lcommunity_hash
->count
) {
617 hash_iterate(aggregate
->lcommunity_hash
,
618 bgp_aggr_lcommunity_prepare
,
619 &aggregate
->lcommunity
);
620 lcommerge
= aggregate
->lcommunity
;
621 aggregate
->lcommunity
= lcommunity_uniq_sort(lcommerge
);
623 lcommunity_free(&lcommerge
);
627 void bgp_remove_lcommunity_from_aggregate(struct bgp_aggregate
*aggregate
,
628 struct lcommunity
*lcommunity
)
630 struct lcommunity
*aggr_lcommunity
= NULL
;
631 struct lcommunity
*ret_lcomm
= NULL
;
634 || (!aggregate
->lcommunity_hash
)
638 /* Look-up the lcommunity in the hash.
640 aggr_lcommunity
= bgp_aggr_lcommunity_lookup(aggregate
, lcommunity
);
641 if (aggr_lcommunity
) {
642 aggr_lcommunity
->refcnt
--;
644 if (aggr_lcommunity
->refcnt
== 0) {
645 ret_lcomm
= hash_release(aggregate
->lcommunity_hash
,
647 lcommunity_free(&ret_lcomm
);
649 bgp_compute_aggregate_lcommunity_val(aggregate
);
655 void bgp_remove_lcomm_from_aggregate_hash(struct bgp_aggregate
*aggregate
,
656 struct lcommunity
*lcommunity
)
658 struct lcommunity
*aggr_lcommunity
= NULL
;
659 struct lcommunity
*ret_lcomm
= NULL
;
662 || (!aggregate
->lcommunity_hash
)
666 /* Look-up the lcommunity in the hash.
668 aggr_lcommunity
= bgp_aggr_lcommunity_lookup(aggregate
, lcommunity
);
669 if (aggr_lcommunity
) {
670 aggr_lcommunity
->refcnt
--;
672 if (aggr_lcommunity
->refcnt
== 0) {
673 ret_lcomm
= hash_release(aggregate
->lcommunity_hash
,
675 lcommunity_free(&ret_lcomm
);