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 FRR; see the file COPYING. If not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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
*
40 return (struct lcommunity
*) XCALLOC (MTYPE_LCOMMUNITY
,
41 sizeof (struct lcommunity
));
44 /* Allocate lcommunities. */
46 lcommunity_free (struct lcommunity
**lcom
)
49 XFREE (MTYPE_LCOMMUNITY_VAL
, (*lcom
)->val
);
51 XFREE (MTYPE_LCOMMUNITY_STR
, (*lcom
)->str
);
52 XFREE (MTYPE_LCOMMUNITY
, *lcom
);
57 lcommunity_hash_free (struct lcommunity
*lcom
)
59 lcommunity_free (&lcom
);
62 /* Add a new Large Communities value to Large Communities
63 Attribute structure. When the value is already exists in the
64 structure, we don't add the value. Newly added value is sorted by
65 numerical order. When the value is added to the structure return 1
68 lcommunity_add_val (struct lcommunity
*lcom
, struct lcommunity_val
*lval
)
74 /* When this is fist value, just add it. */
75 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
++)
87 ret
= memcmp (p
, lval
->val
, LCOMMUNITY_SIZE
);
94 /* Add the value to the structure with numerical sorting. */
96 lcom
->val
= 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 strucutre then
107 create a new Large Communities structure by uniq and sort each
108 Large Communities value. */
110 lcommunity_uniq_sort (struct lcommunity
*lcom
)
113 struct lcommunity
*new;
114 struct lcommunity_val
*lval
;
119 new = lcommunity_new ();
121 for (i
= 0; i
< lcom
->size
; i
++)
123 lval
= (struct lcommunity_val
*) (lcom
->val
+ (i
* LCOMMUNITY_SIZE
));
124 lcommunity_add_val (new, lval
);
129 /* Parse Large Communites Attribute in BGP packet. */
131 lcommunity_parse (u_int8_t
*pnt
, u_short length
)
133 struct lcommunity tmp
;
134 struct lcommunity
*new;
137 if (length
% LCOMMUNITY_SIZE
)
140 /* Prepare tmporary structure for making a new Large Communities
142 tmp
.size
= length
/ LCOMMUNITY_SIZE
;
145 /* Create a new Large Communities Attribute by uniq and sort each
146 Large Communities value */
147 new = lcommunity_uniq_sort (&tmp
);
149 return lcommunity_intern (new);
152 /* Duplicate the Large Communities Attribute structure. */
154 lcommunity_dup (struct lcommunity
*lcom
)
156 struct lcommunity
*new;
158 new = XCALLOC (MTYPE_LCOMMUNITY
, sizeof (struct lcommunity
));
159 new->size
= lcom
->size
;
162 new->val
= XMALLOC (MTYPE_LCOMMUNITY_VAL
, lcom
->size
* LCOMMUNITY_SIZE
);
163 memcpy (new->val
, lcom
->val
, lcom
->size
* LCOMMUNITY_SIZE
);
170 /* Retrun string representation of communities attribute. */
172 lcommunity_str (struct lcommunity
*lcom
)
175 lcom
->str
= lcommunity_lcom2str (lcom
, LCOMMUNITY_FORMAT_DISPLAY
);
179 /* Merge two Large Communities Attribute structure. */
181 lcommunity_merge (struct lcommunity
*lcom1
, struct lcommunity
*lcom2
)
184 lcom1
->val
= XREALLOC (MTYPE_LCOMMUNITY_VAL
, lcom1
->val
,
185 (lcom1
->size
+ lcom2
->size
) * LCOMMUNITY_SIZE
);
187 lcom1
->val
= XMALLOC (MTYPE_LCOMMUNITY_VAL
,
188 (lcom1
->size
+ lcom2
->size
) * LCOMMUNITY_SIZE
);
190 memcpy (lcom1
->val
+ (lcom1
->size
* LCOMMUNITY_SIZE
),
191 lcom2
->val
, lcom2
->size
* LCOMMUNITY_SIZE
);
192 lcom1
->size
+= lcom2
->size
;
197 /* Intern Large Communities Attribute. */
199 lcommunity_intern (struct lcommunity
*lcom
)
201 struct lcommunity
*find
;
203 assert (lcom
->refcnt
== 0);
205 find
= (struct lcommunity
*) hash_get (lcomhash
, lcom
, hash_alloc_intern
);
208 lcommunity_free (&lcom
);
213 find
->str
= lcommunity_lcom2str (find
, LCOMMUNITY_FORMAT_DISPLAY
);
218 /* Unintern Large Communities Attribute. */
220 lcommunity_unintern (struct lcommunity
**lcom
)
222 struct lcommunity
*ret
;
227 /* Pull off from hash. */
228 if ((*lcom
)->refcnt
== 0)
230 /* Large community must be in the hash. */
231 ret
= (struct lcommunity
*) hash_release (lcomhash
, *lcom
);
232 assert (ret
!= NULL
);
234 lcommunity_free (lcom
);
238 /* Utility function to make hash key. */
240 lcommunity_hash_make (void *arg
)
242 const struct lcommunity
*lcom
= arg
;
243 int size
= lcom
->size
* LCOMMUNITY_SIZE
;
244 u_int8_t
*pnt
= lcom
->val
;
245 unsigned int key
= 0;
248 for (c
= 0; c
< size
; c
+= LCOMMUNITY_SIZE
)
267 /* Compare two Large Communities Attribute structure. */
269 lcommunity_cmp (const void *arg1
, const void *arg2
)
271 const struct lcommunity
*lcom1
= arg1
;
272 const struct lcommunity
*lcom2
= arg2
;
274 return (lcom1
->size
== lcom2
->size
275 && memcmp (lcom1
->val
, lcom2
->val
, lcom1
->size
* LCOMMUNITY_SIZE
) == 0);
278 /* Return communities hash. */
280 lcommunity_hash (void)
285 /* Initialize Large Comminities related hash. */
287 lcommunity_init (void)
289 lcomhash
= hash_create (lcommunity_hash_make
, lcommunity_cmp
);
293 lcommunity_finish (void)
295 hash_clean (lcomhash
, (void (*)(void *))lcommunity_hash_free
);
296 hash_free (lcomhash
);
300 /* Large Communities token enum. */
301 enum lcommunity_token
303 lcommunity_token_unknown
= 0,
304 lcommunity_token_val
,
307 /* Get next Large Communities token from the string. */
309 lcommunity_gettoken (const char *str
, struct lcommunity_val
*lval
,
310 enum lcommunity_token
*token
)
314 /* Skip white space. */
315 while (isspace ((int) *p
))
321 /* Check the end of the line. */
325 /* Community value. */
326 if (isdigit ((int) *p
))
330 u_int32_t globaladmin
= 0;
331 u_int32_t localdata1
= 0;
332 u_int32_t localdata2
= 0;
334 while (isdigit ((int) *p
) || *p
== ':')
340 *token
= lcommunity_token_unknown
;
347 if (separator
== 1) {
348 globaladmin
= localdata2
;
350 localdata1
= localdata2
;
359 localdata2
+= (*p
- '0');
365 *token
= lcommunity_token_unknown
;
370 * Copy the large comm.
372 lval
->val
[0] = (globaladmin
>> 24) & 0xff;
373 lval
->val
[1] = (globaladmin
>> 16) & 0xff;
374 lval
->val
[2] = (globaladmin
>> 8) & 0xff;
375 lval
->val
[3] = globaladmin
& 0xff;
376 lval
->val
[4] = (localdata1
>> 24) & 0xff;
377 lval
->val
[5] = (localdata1
>> 16) & 0xff;
378 lval
->val
[6] = (localdata1
>> 8) & 0xff;
379 lval
->val
[7] = localdata1
& 0xff;
380 lval
->val
[8] = (localdata2
>> 24) & 0xff;
381 lval
->val
[9] = (localdata2
>> 16) & 0xff;
382 lval
->val
[10] = (localdata2
>> 8) & 0xff;
383 lval
->val
[11] = localdata2
& 0xff;
385 *token
= lcommunity_token_val
;
388 *token
= lcommunity_token_unknown
;
393 Convert string to large community attribute.
394 When type is already known, please specify both str and type.
396 When string includes keyword for each large community value.
397 Please specify keyword_included as non-zero value.
400 lcommunity_str2com (const char *str
)
402 struct lcommunity
*lcom
= NULL
;
403 enum lcommunity_token token
= lcommunity_token_unknown
;
404 struct lcommunity_val lval
;
406 while ((str
= lcommunity_gettoken (str
, &lval
, &token
)))
410 case lcommunity_token_val
:
412 lcom
= lcommunity_new ();
413 lcommunity_add_val (lcom
, &lval
);
415 case lcommunity_token_unknown
:
418 lcommunity_free (&lcom
);
426 lcommunity_include (struct lcommunity
*lcom
, u_char
*ptr
)
431 lcom_ptr
= lcom
->val
;
432 for (i
= 0; i
< lcom
->size
; i
++) {
433 lcom_ptr
+= (i
* LCOMMUNITY_SIZE
);
434 if (memcmp (ptr
, lcom_ptr
, LCOMMUNITY_SIZE
) == 0)
440 /* Convert large community attribute to string.
441 The large coms will be in 65535:65531:0 format.
444 lcommunity_lcom2str (struct lcommunity
*lcom
, int format
)
448 #define LCOMMUNITY_STR_DEFAULT_LEN 40
454 u_int32_t globaladmin
, localdata1
, localdata2
;
458 str_buf
= XMALLOC (MTYPE_LCOMMUNITY_STR
, 1);
463 /* Prepare buffer. */
464 str_buf
= XMALLOC (MTYPE_LCOMMUNITY_STR
, LCOMMUNITY_STR_DEFAULT_LEN
+ 1);
465 str_size
= LCOMMUNITY_STR_DEFAULT_LEN
+ 1;
468 for (i
= 0; i
< lcom
->size
; i
++)
470 /* Make it sure size is enough. */
471 while (str_pnt
+ LCOMMUNITY_STR_DEFAULT_LEN
>= str_size
)
474 str_buf
= XREALLOC (MTYPE_LCOMMUNITY_STR
, str_buf
, str_size
);
477 /* Space between each value. */
479 str_buf
[str_pnt
++] = ' ';
481 pnt
= lcom
->val
+ (i
* 12);
483 globaladmin
= (*pnt
++ << 24);
484 globaladmin
|= (*pnt
++ << 16);
485 globaladmin
|= (*pnt
++ << 8);
486 globaladmin
|= (*pnt
++);
488 localdata1
= (*pnt
++ << 24);
489 localdata1
|= (*pnt
++ << 16);
490 localdata1
|= (*pnt
++ << 8);
491 localdata1
|= (*pnt
++);
493 localdata2
= (*pnt
++ << 24);
494 localdata2
|= (*pnt
++ << 16);
495 localdata2
|= (*pnt
++ << 8);
496 localdata2
|= (*pnt
++);
498 len
= sprintf( str_buf
+ str_pnt
, "%u:%u:%u", globaladmin
,
499 localdata1
, localdata2
);
507 lcommunity_match (const struct lcommunity
*lcom1
,
508 const struct lcommunity
*lcom2
)
513 if (lcom1
== NULL
&& lcom2
== NULL
)
516 if (lcom1
== NULL
|| lcom2
== NULL
)
519 if (lcom1
->size
< lcom2
->size
)
522 /* Every community on com2 needs to be on com1 for this to match */
523 while (i
< lcom1
->size
&& j
< lcom2
->size
)
525 if (memcmp (lcom1
->val
+ (i
*12), lcom2
->val
+ (j
*12), LCOMMUNITY_SIZE
) == 0)
530 if (j
== lcom2
->size
)
536 /* Delete one lcommunity. */
538 lcommunity_del_val (struct lcommunity
*lcom
, u_char
*ptr
)
546 while (i
< lcom
->size
)
548 if (memcmp (lcom
->val
+ i
*LCOMMUNITY_SIZE
, ptr
, LCOMMUNITY_SIZE
) == 0)
550 c
= lcom
->size
-i
-1;
553 memmove (lcom
->val
+ i
*LCOMMUNITY_SIZE
, lcom
->val
+ (i
+ 1)*LCOMMUNITY_SIZE
, c
* LCOMMUNITY_SIZE
);
558 lcom
->val
= XREALLOC (MTYPE_COMMUNITY_VAL
, lcom
->val
,
562 XFREE (MTYPE_COMMUNITY_VAL
, lcom
->val
);