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
);
56 static void lcommunity_hash_free(struct lcommunity
*lcom
)
58 lcommunity_free(&lcom
);
61 /* Add a new Large Communities value to Large Communities
62 Attribute structure. When the value is already exists in the
63 structure, we don't add the value. Newly added value is sorted by
64 numerical order. When the value is added to the structure return 1
66 static int lcommunity_add_val(struct lcommunity
*lcom
,
67 struct lcommunity_val
*lval
)
73 /* When this is fist value, just add it. */
74 if (lcom
->val
== NULL
) {
76 lcom
->val
= XMALLOC(MTYPE_LCOMMUNITY_VAL
, lcom_length(lcom
));
77 memcpy(lcom
->val
, lval
->val
, LCOMMUNITY_SIZE
);
81 /* If the value already exists in the structure return 0. */
83 for (p
= lcom
->val
; c
< lcom
->size
; p
+= LCOMMUNITY_SIZE
, c
++) {
84 ret
= memcmp(p
, lval
->val
, LCOMMUNITY_SIZE
);
91 /* Add the value to the structure with numerical sorting. */
94 XREALLOC(MTYPE_LCOMMUNITY_VAL
, lcom
->val
, lcom_length(lcom
));
96 memmove(lcom
->val
+ (c
+ 1) * LCOMMUNITY_SIZE
,
97 lcom
->val
+ c
* LCOMMUNITY_SIZE
,
98 (lcom
->size
- 1 - c
) * LCOMMUNITY_SIZE
);
99 memcpy(lcom
->val
+ c
* LCOMMUNITY_SIZE
, lval
->val
, LCOMMUNITY_SIZE
);
104 /* This function takes pointer to Large Communites strucutre then
105 create a new Large Communities structure by uniq and sort each
106 Large Communities value. */
107 struct lcommunity
*lcommunity_uniq_sort(struct lcommunity
*lcom
)
110 struct lcommunity
*new;
111 struct lcommunity_val
*lval
;
116 new = lcommunity_new();
118 for (i
= 0; i
< lcom
->size
; i
++) {
119 lval
= (struct lcommunity_val
*)(lcom
->val
120 + (i
* LCOMMUNITY_SIZE
));
121 lcommunity_add_val(new, lval
);
126 /* Parse Large Communites Attribute in BGP packet. */
127 struct lcommunity
*lcommunity_parse(uint8_t *pnt
, unsigned short length
)
129 struct lcommunity tmp
;
130 struct lcommunity
*new;
133 if (length
% LCOMMUNITY_SIZE
)
136 /* Prepare tmporary structure for making a new Large Communities
138 tmp
.size
= length
/ LCOMMUNITY_SIZE
;
141 /* Create a new Large Communities Attribute by uniq and sort each
142 Large Communities value */
143 new = lcommunity_uniq_sort(&tmp
);
145 return lcommunity_intern(new);
148 /* Duplicate the Large Communities Attribute structure. */
149 struct lcommunity
*lcommunity_dup(struct lcommunity
*lcom
)
151 struct lcommunity
*new;
153 new = lcommunity_new();
154 new->size
= lcom
->size
;
156 new->val
= XMALLOC(MTYPE_LCOMMUNITY_VAL
, lcom_length(lcom
));
157 memcpy(new->val
, lcom
->val
, lcom_length(lcom
));
163 /* Retrun string representation of communities attribute. */
164 char *lcommunity_str(struct lcommunity
*lcom
)
168 lcommunity_lcom2str(lcom
, LCOMMUNITY_FORMAT_DISPLAY
);
172 /* Merge two Large Communities Attribute structure. */
173 struct lcommunity
*lcommunity_merge(struct lcommunity
*lcom1
,
174 struct lcommunity
*lcom2
)
177 lcom1
->val
= XREALLOC(MTYPE_LCOMMUNITY_VAL
, lcom1
->val
,
178 lcom_length(lcom1
) + lcom_length(lcom2
));
180 lcom1
->val
= XMALLOC(MTYPE_LCOMMUNITY_VAL
,
181 lcom_length(lcom1
) + lcom_length(lcom2
));
183 memcpy(lcom1
->val
+ lcom_length(lcom1
), lcom2
->val
, lcom_length(lcom2
));
184 lcom1
->size
+= lcom2
->size
;
189 /* Intern Large Communities Attribute. */
190 struct lcommunity
*lcommunity_intern(struct lcommunity
*lcom
)
192 struct lcommunity
*find
;
194 assert(lcom
->refcnt
== 0);
196 find
= (struct lcommunity
*)hash_get(lcomhash
, lcom
, hash_alloc_intern
);
199 lcommunity_free(&lcom
);
205 lcommunity_lcom2str(find
, LCOMMUNITY_FORMAT_DISPLAY
);
210 /* Unintern Large Communities Attribute. */
211 void lcommunity_unintern(struct lcommunity
**lcom
)
213 struct lcommunity
*ret
;
218 /* Pull off from hash. */
219 if ((*lcom
)->refcnt
== 0) {
220 /* Large community must be in the hash. */
221 ret
= (struct lcommunity
*)hash_release(lcomhash
, *lcom
);
224 lcommunity_free(lcom
);
228 /* Utility function to make hash key. */
229 unsigned int lcommunity_hash_make(void *arg
)
231 const struct lcommunity
*lcom
= arg
;
232 int size
= lcom_length(lcom
);
234 return jhash(lcom
->val
, size
, 0xab125423);
237 /* Compare two Large Communities Attribute structure. */
238 int lcommunity_cmp(const void *arg1
, const void *arg2
)
240 const struct lcommunity
*lcom1
= arg1
;
241 const struct lcommunity
*lcom2
= arg2
;
243 return (lcom1
->size
== lcom2
->size
244 && memcmp(lcom1
->val
, lcom2
->val
, lcom_length(lcom1
)) == 0);
247 /* Return communities hash. */
248 struct hash
*lcommunity_hash(void)
253 /* Initialize Large Comminities related hash. */
254 void lcommunity_init(void)
256 lcomhash
= hash_create(lcommunity_hash_make
, lcommunity_cmp
,
257 "BGP lcommunity hash");
260 void lcommunity_finish(void)
262 hash_clean(lcomhash
, (void (*)(void *))lcommunity_hash_free
);
267 /* Large Communities token enum. */
268 enum lcommunity_token
{
269 lcommunity_token_unknown
= 0,
270 lcommunity_token_val
,
273 /* Get next Large Communities token from the string. */
274 static const char *lcommunity_gettoken(const char *str
,
275 struct lcommunity_val
*lval
,
276 enum lcommunity_token
*token
)
280 /* Skip white space. */
281 while (isspace((int)*p
)) {
286 /* Check the end of the line. */
290 /* Community value. */
291 if (isdigit((int)*p
)) {
294 uint32_t globaladmin
= 0;
295 uint32_t localdata1
= 0;
296 uint32_t localdata2
= 0;
298 while (isdigit((int)*p
) || *p
== ':') {
300 if (separator
== 2) {
301 *token
= lcommunity_token_unknown
;
306 if (separator
== 1) {
307 globaladmin
= localdata2
;
309 localdata1
= localdata2
;
316 localdata2
+= (*p
- '0');
321 *token
= lcommunity_token_unknown
;
326 * Copy the large comm.
328 lval
->val
[0] = (globaladmin
>> 24) & 0xff;
329 lval
->val
[1] = (globaladmin
>> 16) & 0xff;
330 lval
->val
[2] = (globaladmin
>> 8) & 0xff;
331 lval
->val
[3] = globaladmin
& 0xff;
332 lval
->val
[4] = (localdata1
>> 24) & 0xff;
333 lval
->val
[5] = (localdata1
>> 16) & 0xff;
334 lval
->val
[6] = (localdata1
>> 8) & 0xff;
335 lval
->val
[7] = localdata1
& 0xff;
336 lval
->val
[8] = (localdata2
>> 24) & 0xff;
337 lval
->val
[9] = (localdata2
>> 16) & 0xff;
338 lval
->val
[10] = (localdata2
>> 8) & 0xff;
339 lval
->val
[11] = localdata2
& 0xff;
341 *token
= lcommunity_token_val
;
344 *token
= lcommunity_token_unknown
;
349 Convert string to large community attribute.
350 When type is already known, please specify both str and type.
352 When string includes keyword for each large community value.
353 Please specify keyword_included as non-zero value.
355 struct lcommunity
*lcommunity_str2com(const char *str
)
357 struct lcommunity
*lcom
= NULL
;
358 enum lcommunity_token token
= lcommunity_token_unknown
;
359 struct lcommunity_val lval
;
361 while ((str
= lcommunity_gettoken(str
, &lval
, &token
))) {
363 case lcommunity_token_val
:
365 lcom
= lcommunity_new();
366 lcommunity_add_val(lcom
, &lval
);
368 case lcommunity_token_unknown
:
371 lcommunity_free(&lcom
);
378 int lcommunity_include(struct lcommunity
*lcom
, uint8_t *ptr
)
383 for (i
= 0; i
< lcom
->size
; i
++) {
384 lcom_ptr
= lcom
->val
+ (i
* LCOMMUNITY_SIZE
);
385 if (memcmp(ptr
, lcom_ptr
, LCOMMUNITY_SIZE
) == 0)
391 /* Convert large community attribute to string.
392 The large coms will be in 65535:65531:0 format.
394 char *lcommunity_lcom2str(struct lcommunity
*lcom
, int format
)
398 #define LCOMMUNITY_STR_DEFAULT_LEN 40
404 uint32_t globaladmin
, localdata1
, localdata2
;
406 if (lcom
->size
== 0) {
407 str_buf
= XMALLOC(MTYPE_LCOMMUNITY_STR
, 1);
412 /* Prepare buffer. */
413 str_buf
= XMALLOC(MTYPE_LCOMMUNITY_STR
, LCOMMUNITY_STR_DEFAULT_LEN
+ 1);
414 str_size
= LCOMMUNITY_STR_DEFAULT_LEN
+ 1;
417 for (i
= 0; i
< lcom
->size
; i
++) {
418 /* Make it sure size is enough. */
419 while (str_pnt
+ LCOMMUNITY_STR_DEFAULT_LEN
>= str_size
) {
421 str_buf
= XREALLOC(MTYPE_LCOMMUNITY_STR
, str_buf
,
425 /* Space between each value. */
427 str_buf
[str_pnt
++] = ' ';
429 pnt
= lcom
->val
+ (i
* LCOMMUNITY_SIZE
);
431 pnt
= ptr_get_be32(pnt
, &globaladmin
);
432 pnt
= ptr_get_be32(pnt
, &localdata1
);
433 pnt
= ptr_get_be32(pnt
, &localdata2
);
434 (void)pnt
; /* consume value */
436 len
= sprintf(str_buf
+ str_pnt
, "%u:%u:%u", globaladmin
,
437 localdata1
, localdata2
);
444 int lcommunity_match(const struct lcommunity
*lcom1
,
445 const struct lcommunity
*lcom2
)
450 if (lcom1
== NULL
&& lcom2
== NULL
)
453 if (lcom1
== NULL
|| lcom2
== NULL
)
456 if (lcom1
->size
< lcom2
->size
)
459 /* Every community on com2 needs to be on com1 for this to match */
460 while (i
< lcom1
->size
&& j
< lcom2
->size
) {
461 if (memcmp(lcom1
->val
+ (i
* LCOMMUNITY_SIZE
),
462 lcom2
->val
+ (j
* LCOMMUNITY_SIZE
), LCOMMUNITY_SIZE
)
468 if (j
== lcom2
->size
)
474 /* Delete one lcommunity. */
475 void lcommunity_del_val(struct lcommunity
*lcom
, uint8_t *ptr
)
483 while (i
< lcom
->size
) {
484 if (memcmp(lcom
->val
+ i
* LCOMMUNITY_SIZE
, ptr
,
487 c
= lcom
->size
- i
- 1;
490 memmove(lcom
->val
+ i
* LCOMMUNITY_SIZE
,
491 lcom
->val
+ (i
+ 1) * LCOMMUNITY_SIZE
,
492 c
* LCOMMUNITY_SIZE
);
498 XREALLOC(MTYPE_LCOMMUNITY_VAL
,
499 lcom
->val
, lcom_length(lcom
));
501 XFREE(MTYPE_LCOMMUNITY_VAL
, lcom
->val
);