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
29 #include "bgpd/bgpd.h"
30 #include "bgpd/bgp_lcommunity.h"
31 #include "bgpd/bgp_aspath.h"
33 /* Hash of community attribute. */
34 static struct hash
*lcomhash
;
36 /* Allocate a new lcommunities. */
37 static struct lcommunity
*lcommunity_new(void)
39 return (struct lcommunity
*)XCALLOC(MTYPE_LCOMMUNITY
,
40 sizeof(struct lcommunity
));
43 /* Allocate lcommunities. */
44 void lcommunity_free(struct lcommunity
**lcom
)
47 XFREE(MTYPE_LCOMMUNITY_VAL
, (*lcom
)->val
);
49 XFREE(MTYPE_LCOMMUNITY_STR
, (*lcom
)->str
);
50 XFREE(MTYPE_LCOMMUNITY
, *lcom
);
54 static void lcommunity_hash_free(struct lcommunity
*lcom
)
56 lcommunity_free(&lcom
);
59 /* Add a new Large Communities value to Large Communities
60 Attribute structure. When the value is already exists in the
61 structure, we don't add the value. Newly added value is sorted by
62 numerical order. When the value is added to the structure return 1
64 static int lcommunity_add_val(struct lcommunity
*lcom
,
65 struct lcommunity_val
*lval
)
71 /* When this is fist value, just add it. */
72 if (lcom
->val
== NULL
) {
74 lcom
->val
= XMALLOC(MTYPE_LCOMMUNITY_VAL
, lcom_length(lcom
));
75 memcpy(lcom
->val
, lval
->val
, LCOMMUNITY_SIZE
);
79 /* If the value already exists in the structure return 0. */
81 for (p
= lcom
->val
; c
< lcom
->size
; p
+= LCOMMUNITY_SIZE
, c
++) {
82 ret
= memcmp(p
, lval
->val
, LCOMMUNITY_SIZE
);
89 /* Add the value to the structure with numerical sorting. */
92 XREALLOC(MTYPE_LCOMMUNITY_VAL
, lcom
->val
, lcom_length(lcom
));
94 memmove(lcom
->val
+ (c
+ 1) * LCOMMUNITY_SIZE
,
95 lcom
->val
+ c
* LCOMMUNITY_SIZE
,
96 (lcom
->size
- 1 - c
) * LCOMMUNITY_SIZE
);
97 memcpy(lcom
->val
+ c
* LCOMMUNITY_SIZE
, lval
->val
, LCOMMUNITY_SIZE
);
102 /* This function takes pointer to Large Communites strucutre then
103 create a new Large Communities structure by uniq and sort each
104 Large Communities value. */
105 struct lcommunity
*lcommunity_uniq_sort(struct lcommunity
*lcom
)
108 struct lcommunity
*new;
109 struct lcommunity_val
*lval
;
114 new = lcommunity_new();
116 for (i
= 0; i
< lcom
->size
; i
++) {
117 lval
= (struct lcommunity_val
*)(lcom
->val
118 + (i
* LCOMMUNITY_SIZE
));
119 lcommunity_add_val(new, lval
);
124 /* Parse Large Communites Attribute in BGP packet. */
125 struct lcommunity
*lcommunity_parse(u_int8_t
*pnt
, u_short length
)
127 struct lcommunity tmp
;
128 struct lcommunity
*new;
131 if (length
% LCOMMUNITY_SIZE
)
134 /* Prepare tmporary structure for making a new Large Communities
136 tmp
.size
= length
/ LCOMMUNITY_SIZE
;
139 /* Create a new Large Communities Attribute by uniq and sort each
140 Large Communities value */
141 new = lcommunity_uniq_sort(&tmp
);
143 return lcommunity_intern(new);
146 /* Duplicate the Large Communities Attribute structure. */
147 struct lcommunity
*lcommunity_dup(struct lcommunity
*lcom
)
149 struct lcommunity
*new;
151 new = XCALLOC(MTYPE_LCOMMUNITY
, sizeof(struct lcommunity
));
152 new->size
= lcom
->size
;
154 new->val
= XMALLOC(MTYPE_LCOMMUNITY_VAL
,
155 lcom
->size
* LCOMMUNITY_SIZE
);
156 memcpy(new->val
, lcom
->val
, lcom
->size
* LCOMMUNITY_SIZE
);
162 /* Retrun string representation of communities attribute. */
163 char *lcommunity_str(struct lcommunity
*lcom
)
167 lcommunity_lcom2str(lcom
, LCOMMUNITY_FORMAT_DISPLAY
);
171 /* Merge two Large Communities Attribute structure. */
172 struct lcommunity
*lcommunity_merge(struct lcommunity
*lcom1
,
173 struct lcommunity
*lcom2
)
177 XREALLOC(MTYPE_LCOMMUNITY_VAL
, lcom1
->val
,
178 (lcom1
->size
+ lcom2
->size
) * LCOMMUNITY_SIZE
);
181 XMALLOC(MTYPE_LCOMMUNITY_VAL
,
182 (lcom1
->size
+ lcom2
->size
) * LCOMMUNITY_SIZE
);
184 memcpy(lcom1
->val
+ (lcom1
->size
* LCOMMUNITY_SIZE
), lcom2
->val
,
185 lcom2
->size
* LCOMMUNITY_SIZE
);
186 lcom1
->size
+= lcom2
->size
;
191 /* Intern Large Communities Attribute. */
192 struct lcommunity
*lcommunity_intern(struct lcommunity
*lcom
)
194 struct lcommunity
*find
;
196 assert(lcom
->refcnt
== 0);
198 find
= (struct lcommunity
*)hash_get(lcomhash
, lcom
, hash_alloc_intern
);
201 lcommunity_free(&lcom
);
207 lcommunity_lcom2str(find
, LCOMMUNITY_FORMAT_DISPLAY
);
212 /* Unintern Large Communities Attribute. */
213 void lcommunity_unintern(struct lcommunity
**lcom
)
215 struct lcommunity
*ret
;
220 /* Pull off from hash. */
221 if ((*lcom
)->refcnt
== 0) {
222 /* Large community must be in the hash. */
223 ret
= (struct lcommunity
*)hash_release(lcomhash
, *lcom
);
226 lcommunity_free(lcom
);
230 /* Utility function to make hash key. */
231 unsigned int lcommunity_hash_make(void *arg
)
233 const struct lcommunity
*lcom
= arg
;
234 int size
= lcom
->size
* LCOMMUNITY_SIZE
;
235 u_int8_t
*pnt
= lcom
->val
;
236 unsigned int key
= 0;
239 for (c
= 0; c
< size
; c
+= LCOMMUNITY_SIZE
) {
257 /* Compare two Large Communities Attribute structure. */
258 int lcommunity_cmp(const void *arg1
, const void *arg2
)
260 const struct lcommunity
*lcom1
= arg1
;
261 const struct lcommunity
*lcom2
= arg2
;
263 return (lcom1
->size
== lcom2
->size
264 && memcmp(lcom1
->val
, lcom2
->val
, lcom1
->size
* LCOMMUNITY_SIZE
)
268 /* Return communities hash. */
269 struct hash
*lcommunity_hash(void)
274 /* Initialize Large Comminities related hash. */
275 void lcommunity_init(void)
277 lcomhash
= hash_create(lcommunity_hash_make
, lcommunity_cmp
, NULL
);
280 void lcommunity_finish(void)
282 hash_clean(lcomhash
, (void (*)(void *))lcommunity_hash_free
);
287 /* Large Communities token enum. */
288 enum lcommunity_token
{
289 lcommunity_token_unknown
= 0,
290 lcommunity_token_val
,
293 /* Get next Large Communities token from the string. */
294 static const char *lcommunity_gettoken(const char *str
,
295 struct lcommunity_val
*lval
,
296 enum lcommunity_token
*token
)
300 /* Skip white space. */
301 while (isspace((int)*p
)) {
306 /* Check the end of the line. */
310 /* Community value. */
311 if (isdigit((int)*p
)) {
314 u_int32_t globaladmin
= 0;
315 u_int32_t localdata1
= 0;
316 u_int32_t localdata2
= 0;
318 while (isdigit((int)*p
) || *p
== ':') {
320 if (separator
== 2) {
321 *token
= lcommunity_token_unknown
;
326 if (separator
== 1) {
327 globaladmin
= localdata2
;
329 localdata1
= localdata2
;
336 localdata2
+= (*p
- '0');
341 *token
= lcommunity_token_unknown
;
346 * Copy the large comm.
348 lval
->val
[0] = (globaladmin
>> 24) & 0xff;
349 lval
->val
[1] = (globaladmin
>> 16) & 0xff;
350 lval
->val
[2] = (globaladmin
>> 8) & 0xff;
351 lval
->val
[3] = globaladmin
& 0xff;
352 lval
->val
[4] = (localdata1
>> 24) & 0xff;
353 lval
->val
[5] = (localdata1
>> 16) & 0xff;
354 lval
->val
[6] = (localdata1
>> 8) & 0xff;
355 lval
->val
[7] = localdata1
& 0xff;
356 lval
->val
[8] = (localdata2
>> 24) & 0xff;
357 lval
->val
[9] = (localdata2
>> 16) & 0xff;
358 lval
->val
[10] = (localdata2
>> 8) & 0xff;
359 lval
->val
[11] = localdata2
& 0xff;
361 *token
= lcommunity_token_val
;
364 *token
= lcommunity_token_unknown
;
369 Convert string to large community attribute.
370 When type is already known, please specify both str and type.
372 When string includes keyword for each large community value.
373 Please specify keyword_included as non-zero value.
375 struct lcommunity
*lcommunity_str2com(const char *str
)
377 struct lcommunity
*lcom
= NULL
;
378 enum lcommunity_token token
= lcommunity_token_unknown
;
379 struct lcommunity_val lval
;
381 while ((str
= lcommunity_gettoken(str
, &lval
, &token
))) {
383 case lcommunity_token_val
:
385 lcom
= lcommunity_new();
386 lcommunity_add_val(lcom
, &lval
);
388 case lcommunity_token_unknown
:
391 lcommunity_free(&lcom
);
398 int lcommunity_include(struct lcommunity
*lcom
, u_char
*ptr
)
403 lcom_ptr
= lcom
->val
;
404 for (i
= 0; i
< lcom
->size
; i
++) {
405 lcom_ptr
+= (i
* LCOMMUNITY_SIZE
);
406 if (memcmp(ptr
, lcom_ptr
, LCOMMUNITY_SIZE
) == 0)
412 /* Convert large community attribute to string.
413 The large coms will be in 65535:65531:0 format.
415 char *lcommunity_lcom2str(struct lcommunity
*lcom
, int format
)
419 #define LCOMMUNITY_STR_DEFAULT_LEN 40
425 u_int32_t globaladmin
, localdata1
, localdata2
;
427 if (lcom
->size
== 0) {
428 str_buf
= XMALLOC(MTYPE_LCOMMUNITY_STR
, 1);
433 /* Prepare buffer. */
434 str_buf
= XMALLOC(MTYPE_LCOMMUNITY_STR
, LCOMMUNITY_STR_DEFAULT_LEN
+ 1);
435 str_size
= LCOMMUNITY_STR_DEFAULT_LEN
+ 1;
438 for (i
= 0; i
< lcom
->size
; i
++) {
439 /* Make it sure size is enough. */
440 while (str_pnt
+ LCOMMUNITY_STR_DEFAULT_LEN
>= str_size
) {
442 str_buf
= XREALLOC(MTYPE_LCOMMUNITY_STR
, str_buf
,
446 /* Space between each value. */
448 str_buf
[str_pnt
++] = ' ';
450 pnt
= lcom
->val
+ (i
* 12);
452 globaladmin
= (*pnt
++ << 24);
453 globaladmin
|= (*pnt
++ << 16);
454 globaladmin
|= (*pnt
++ << 8);
455 globaladmin
|= (*pnt
++);
457 localdata1
= (*pnt
++ << 24);
458 localdata1
|= (*pnt
++ << 16);
459 localdata1
|= (*pnt
++ << 8);
460 localdata1
|= (*pnt
++);
462 localdata2
= (*pnt
++ << 24);
463 localdata2
|= (*pnt
++ << 16);
464 localdata2
|= (*pnt
++ << 8);
465 localdata2
|= (*pnt
++);
467 len
= sprintf(str_buf
+ str_pnt
, "%u:%u:%u", globaladmin
,
468 localdata1
, localdata2
);
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
* 12), lcom2
->val
+ (j
* 12),
499 if (j
== lcom2
->size
)
505 /* Delete one lcommunity. */
506 void lcommunity_del_val(struct lcommunity
*lcom
, u_char
*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_COMMUNITY_VAL
, lcom
->val
,
532 XFREE(MTYPE_COMMUNITY_VAL
, lcom
->val
);