1 /* BGP Large Communities Attribute
3 * Copyright (C) 2016 Keyur Patel <keyur@arrcus.com>
5 * This file is part of FreeRangeRouting (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 (struct lcommunity
*)XCALLOC(MTYPE_LCOMMUNITY
,
42 sizeof(struct lcommunity
));
45 /* Allocate lcommunities. */
46 void lcommunity_free(struct lcommunity
**lcom
)
49 XFREE(MTYPE_LCOMMUNITY_VAL
, (*lcom
)->val
);
51 XFREE(MTYPE_LCOMMUNITY_STR
, (*lcom
)->str
);
52 XFREE(MTYPE_LCOMMUNITY
, *lcom
);
55 static void lcommunity_hash_free(struct lcommunity
*lcom
)
57 lcommunity_free(&lcom
);
60 /* Add a new Large Communities value to Large Communities
61 Attribute structure. When the value is already exists in the
62 structure, we don't add the value. Newly added value is sorted by
63 numerical order. When the value is added to the structure return 1
65 static int lcommunity_add_val(struct lcommunity
*lcom
,
66 struct lcommunity_val
*lval
)
72 /* When this is fist value, just add it. */
73 if (lcom
->val
== NULL
) {
75 lcom
->val
= XMALLOC(MTYPE_LCOMMUNITY_VAL
, lcom_length(lcom
));
76 memcpy(lcom
->val
, lval
->val
, LCOMMUNITY_SIZE
);
80 /* If the value already exists in the structure return 0. */
82 for (p
= lcom
->val
; c
< lcom
->size
; p
+= LCOMMUNITY_SIZE
, c
++) {
83 ret
= memcmp(p
, lval
->val
, LCOMMUNITY_SIZE
);
90 /* Add the value to the structure with numerical sorting. */
93 XREALLOC(MTYPE_LCOMMUNITY_VAL
, lcom
->val
, lcom_length(lcom
));
95 memmove(lcom
->val
+ (c
+ 1) * LCOMMUNITY_SIZE
,
96 lcom
->val
+ c
* LCOMMUNITY_SIZE
,
97 (lcom
->size
- 1 - c
) * LCOMMUNITY_SIZE
);
98 memcpy(lcom
->val
+ c
* LCOMMUNITY_SIZE
, lval
->val
, LCOMMUNITY_SIZE
);
103 /* This function takes pointer to Large Communites strucutre then
104 create a new Large Communities structure by uniq and sort each
105 Large Communities value. */
106 struct lcommunity
*lcommunity_uniq_sort(struct lcommunity
*lcom
)
109 struct lcommunity
*new;
110 struct lcommunity_val
*lval
;
115 new = lcommunity_new();
117 for (i
= 0; i
< lcom
->size
; i
++) {
118 lval
= (struct lcommunity_val
*)(lcom
->val
119 + (i
* LCOMMUNITY_SIZE
));
120 lcommunity_add_val(new, lval
);
125 /* Parse Large Communites Attribute in BGP packet. */
126 struct lcommunity
*lcommunity_parse(uint8_t *pnt
, unsigned short length
)
128 struct lcommunity tmp
;
129 struct lcommunity
*new;
132 if (length
% LCOMMUNITY_SIZE
)
135 /* Prepare tmporary structure for making a new Large Communities
137 tmp
.size
= length
/ LCOMMUNITY_SIZE
;
140 /* Create a new Large Communities Attribute by uniq and sort each
141 Large Communities value */
142 new = lcommunity_uniq_sort(&tmp
);
144 return lcommunity_intern(new);
147 /* Duplicate the Large Communities Attribute structure. */
148 struct lcommunity
*lcommunity_dup(struct lcommunity
*lcom
)
150 struct lcommunity
*new;
152 new = lcommunity_new();
153 new->size
= lcom
->size
;
155 new->val
= XMALLOC(MTYPE_LCOMMUNITY_VAL
, lcom_length(lcom
));
156 memcpy(new->val
, lcom
->val
, lcom_length(lcom
));
162 /* Merge two Large Communities Attribute structure. */
163 struct lcommunity
*lcommunity_merge(struct lcommunity
*lcom1
,
164 struct lcommunity
*lcom2
)
167 lcom1
->val
= XREALLOC(MTYPE_LCOMMUNITY_VAL
, lcom1
->val
,
168 lcom_length(lcom1
) + lcom_length(lcom2
));
170 lcom1
->val
= XMALLOC(MTYPE_LCOMMUNITY_VAL
,
171 lcom_length(lcom1
) + lcom_length(lcom2
));
173 memcpy(lcom1
->val
+ lcom_length(lcom1
), lcom2
->val
, lcom_length(lcom2
));
174 lcom1
->size
+= lcom2
->size
;
179 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 #define LCOMMUNITY_STR_DEFAULT_LEN 32
197 lcom
->json
= json_object_new_object();
198 json_lcommunity_list
= json_object_new_array();
201 if (lcom
->size
== 0) {
202 str_buf
= XMALLOC(MTYPE_LCOMMUNITY_STR
, 1);
206 json_object_string_add(lcom
->json
, "string", "");
207 json_object_object_add(lcom
->json
, "list",
208 json_lcommunity_list
);
216 XMALLOC(MTYPE_LCOMMUNITY_STR
,
217 (LCOMMUNITY_STR_DEFAULT_LEN
* lcom
->size
) + 1);
219 for (i
= 0; i
< lcom
->size
; i
++) {
225 pnt
= lcom
->val
+ (i
* LCOMMUNITY_SIZE
);
226 pnt
= ptr_get_be32(pnt
, &global
);
227 pnt
= ptr_get_be32(pnt
, &local1
);
228 pnt
= ptr_get_be32(pnt
, &local2
);
231 len
= sprintf(str_pnt
, "%u:%u:%u", global
, local1
, local2
);
233 json_string
= json_object_new_string(str_pnt
);
234 json_object_array_add(json_lcommunity_list
,
242 XREALLOC(MTYPE_LCOMMUNITY_STR
, str_buf
, str_pnt
- str_buf
+ 1);
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(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 int 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 /* Large Communities token enum. */
352 enum lcommunity_token
{
353 lcommunity_token_unknown
= 0,
354 lcommunity_token_val
,
357 /* Get next Large Communities token from the string. */
358 static const char *lcommunity_gettoken(const char *str
,
359 struct lcommunity_val
*lval
,
360 enum lcommunity_token
*token
)
364 /* Skip white space. */
365 while (isspace((int)*p
)) {
370 /* Check the end of the line. */
374 /* Community value. */
375 if (isdigit((int)*p
)) {
378 uint32_t globaladmin
= 0;
379 uint32_t localdata1
= 0;
380 uint32_t localdata2
= 0;
382 while (isdigit((int)*p
) || *p
== ':') {
384 if (separator
== 2) {
385 *token
= lcommunity_token_unknown
;
390 if (separator
== 1) {
391 globaladmin
= localdata2
;
393 localdata1
= localdata2
;
400 localdata2
+= (*p
- '0');
405 *token
= lcommunity_token_unknown
;
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;
425 *token
= lcommunity_token_val
;
428 *token
= lcommunity_token_unknown
;
433 Convert string to large community attribute.
434 When type is already known, please specify both str and type.
436 When string includes keyword for each large community value.
437 Please specify keyword_included as non-zero value.
439 struct lcommunity
*lcommunity_str2com(const char *str
)
441 struct lcommunity
*lcom
= NULL
;
442 enum lcommunity_token token
= lcommunity_token_unknown
;
443 struct lcommunity_val lval
;
445 while ((str
= lcommunity_gettoken(str
, &lval
, &token
))) {
447 case lcommunity_token_val
:
449 lcom
= lcommunity_new();
450 lcommunity_add_val(lcom
, &lval
);
452 case lcommunity_token_unknown
:
455 lcommunity_free(&lcom
);
462 int lcommunity_include(struct lcommunity
*lcom
, uint8_t *ptr
)
467 for (i
= 0; i
< lcom
->size
; i
++) {
468 lcom_ptr
= lcom
->val
+ (i
* LCOMMUNITY_SIZE
);
469 if (memcmp(ptr
, lcom_ptr
, LCOMMUNITY_SIZE
) == 0)
475 int lcommunity_match(const struct lcommunity
*lcom1
,
476 const struct lcommunity
*lcom2
)
481 if (lcom1
== NULL
&& lcom2
== NULL
)
484 if (lcom1
== NULL
|| lcom2
== NULL
)
487 if (lcom1
->size
< lcom2
->size
)
490 /* Every community on com2 needs to be on com1 for this to match */
491 while (i
< lcom1
->size
&& j
< lcom2
->size
) {
492 if (memcmp(lcom1
->val
+ (i
* LCOMMUNITY_SIZE
),
493 lcom2
->val
+ (j
* LCOMMUNITY_SIZE
), LCOMMUNITY_SIZE
)
499 if (j
== lcom2
->size
)
505 /* Delete one lcommunity. */
506 void lcommunity_del_val(struct lcommunity
*lcom
, uint8_t *ptr
)
514 while (i
< lcom
->size
) {
515 if (memcmp(lcom
->val
+ i
* LCOMMUNITY_SIZE
, ptr
,
518 c
= lcom
->size
- i
- 1;
521 memmove(lcom
->val
+ i
* LCOMMUNITY_SIZE
,
522 lcom
->val
+ (i
+ 1) * LCOMMUNITY_SIZE
,
523 c
* LCOMMUNITY_SIZE
);
529 XREALLOC(MTYPE_LCOMMUNITY_VAL
,
530 lcom
->val
, lcom_length(lcom
));
532 XFREE(MTYPE_LCOMMUNITY_VAL
, lcom
->val
);