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(u_int8_t
*pnt
, u_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
)
178 XREALLOC(MTYPE_LCOMMUNITY_VAL
, lcom1
->val
,
179 lcom_length(lcom1
) + lcom_length(lcom2
));
182 XMALLOC(MTYPE_LCOMMUNITY_VAL
,
183 lcom_length(lcom1
) + lcom_length(lcom2
));
185 memcpy(lcom1
->val
+ lcom_length(lcom1
), lcom2
->val
, lcom_length(lcom2
));
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_length(lcom
);
236 return jhash(lcom
->val
, size
, 0xab125423);
239 /* Compare two Large Communities Attribute structure. */
240 int lcommunity_cmp(const void *arg1
, const void *arg2
)
242 const struct lcommunity
*lcom1
= arg1
;
243 const struct lcommunity
*lcom2
= arg2
;
245 return (lcom1
->size
== lcom2
->size
246 && memcmp(lcom1
->val
, lcom2
->val
, lcom_length(lcom1
))
250 /* Return communities hash. */
251 struct hash
*lcommunity_hash(void)
256 /* Initialize Large Comminities related hash. */
257 void lcommunity_init(void)
259 lcomhash
= hash_create(lcommunity_hash_make
,
261 "BGP lcommunity hash");
264 void lcommunity_finish(void)
266 hash_clean(lcomhash
, (void (*)(void *))lcommunity_hash_free
);
271 /* Large Communities token enum. */
272 enum lcommunity_token
{
273 lcommunity_token_unknown
= 0,
274 lcommunity_token_val
,
277 /* Get next Large Communities token from the string. */
278 static const char *lcommunity_gettoken(const char *str
,
279 struct lcommunity_val
*lval
,
280 enum lcommunity_token
*token
)
284 /* Skip white space. */
285 while (isspace((int)*p
)) {
290 /* Check the end of the line. */
294 /* Community value. */
295 if (isdigit((int)*p
)) {
298 u_int32_t globaladmin
= 0;
299 u_int32_t localdata1
= 0;
300 u_int32_t localdata2
= 0;
302 while (isdigit((int)*p
) || *p
== ':') {
304 if (separator
== 2) {
305 *token
= lcommunity_token_unknown
;
310 if (separator
== 1) {
311 globaladmin
= localdata2
;
313 localdata1
= localdata2
;
320 localdata2
+= (*p
- '0');
325 *token
= lcommunity_token_unknown
;
330 * Copy the large comm.
332 lval
->val
[0] = (globaladmin
>> 24) & 0xff;
333 lval
->val
[1] = (globaladmin
>> 16) & 0xff;
334 lval
->val
[2] = (globaladmin
>> 8) & 0xff;
335 lval
->val
[3] = globaladmin
& 0xff;
336 lval
->val
[4] = (localdata1
>> 24) & 0xff;
337 lval
->val
[5] = (localdata1
>> 16) & 0xff;
338 lval
->val
[6] = (localdata1
>> 8) & 0xff;
339 lval
->val
[7] = localdata1
& 0xff;
340 lval
->val
[8] = (localdata2
>> 24) & 0xff;
341 lval
->val
[9] = (localdata2
>> 16) & 0xff;
342 lval
->val
[10] = (localdata2
>> 8) & 0xff;
343 lval
->val
[11] = localdata2
& 0xff;
345 *token
= lcommunity_token_val
;
348 *token
= lcommunity_token_unknown
;
353 Convert string to large community attribute.
354 When type is already known, please specify both str and type.
356 When string includes keyword for each large community value.
357 Please specify keyword_included as non-zero value.
359 struct lcommunity
*lcommunity_str2com(const char *str
)
361 struct lcommunity
*lcom
= NULL
;
362 enum lcommunity_token token
= lcommunity_token_unknown
;
363 struct lcommunity_val lval
;
365 while ((str
= lcommunity_gettoken(str
, &lval
, &token
))) {
367 case lcommunity_token_val
:
369 lcom
= lcommunity_new();
370 lcommunity_add_val(lcom
, &lval
);
372 case lcommunity_token_unknown
:
375 lcommunity_free(&lcom
);
382 int lcommunity_include(struct lcommunity
*lcom
, u_char
*ptr
)
387 for (i
= 0; i
< lcom
->size
; i
++) {
388 lcom_ptr
= lcom
->val
+ (i
* LCOMMUNITY_SIZE
);
389 if (memcmp(ptr
, lcom_ptr
, LCOMMUNITY_SIZE
) == 0)
395 /* Convert large community attribute to string.
396 The large coms will be in 65535:65531:0 format.
398 char *lcommunity_lcom2str(struct lcommunity
*lcom
, int format
)
402 #define LCOMMUNITY_STR_DEFAULT_LEN 40
408 u_int32_t globaladmin
, localdata1
, localdata2
;
410 if (lcom
->size
== 0) {
411 str_buf
= XMALLOC(MTYPE_LCOMMUNITY_STR
, 1);
416 /* Prepare buffer. */
417 str_buf
= XMALLOC(MTYPE_LCOMMUNITY_STR
, LCOMMUNITY_STR_DEFAULT_LEN
+ 1);
418 str_size
= LCOMMUNITY_STR_DEFAULT_LEN
+ 1;
421 for (i
= 0; i
< lcom
->size
; i
++) {
422 /* Make it sure size is enough. */
423 while (str_pnt
+ LCOMMUNITY_STR_DEFAULT_LEN
>= str_size
) {
425 str_buf
= XREALLOC(MTYPE_LCOMMUNITY_STR
, str_buf
,
429 /* Space between each value. */
431 str_buf
[str_pnt
++] = ' ';
433 pnt
= lcom
->val
+ (i
* LCOMMUNITY_SIZE
);
435 pnt
= ptr_get_be32(pnt
, &globaladmin
);
436 pnt
= ptr_get_be32(pnt
, &localdata1
);
437 pnt
= ptr_get_be32(pnt
, &localdata2
);
438 (void)pnt
; /* consume value */
440 len
= sprintf(str_buf
+ str_pnt
, "%u:%u:%u", globaladmin
,
441 localdata1
, localdata2
);
448 int lcommunity_match(const struct lcommunity
*lcom1
,
449 const struct lcommunity
*lcom2
)
454 if (lcom1
== NULL
&& lcom2
== NULL
)
457 if (lcom1
== NULL
|| lcom2
== NULL
)
460 if (lcom1
->size
< lcom2
->size
)
463 /* Every community on com2 needs to be on com1 for this to match */
464 while (i
< lcom1
->size
&& j
< lcom2
->size
) {
465 if (memcmp(lcom1
->val
+ (i
* LCOMMUNITY_SIZE
), lcom2
->val
+ (j
* LCOMMUNITY_SIZE
),
472 if (j
== lcom2
->size
)
478 /* Delete one lcommunity. */
479 void lcommunity_del_val(struct lcommunity
*lcom
, u_char
*ptr
)
487 while (i
< lcom
->size
) {
488 if (memcmp(lcom
->val
+ i
* LCOMMUNITY_SIZE
, ptr
,
491 c
= lcom
->size
- i
- 1;
494 memmove(lcom
->val
+ i
* LCOMMUNITY_SIZE
,
495 lcom
->val
+ (i
+ 1) * LCOMMUNITY_SIZE
,
496 c
* LCOMMUNITY_SIZE
);
502 XREALLOC(MTYPE_LCOMMUNITY_VAL
, lcom
->val
,
505 XFREE(MTYPE_LCOMMUNITY_VAL
, lcom
->val
);