1 /* BGP Large Communities Attribute
3 Copyright (C) 2016 Keyur Patel <keyur@arrcus.com>
5 This file is part of GNU Zebra.
7 GNU Zebra is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
12 GNU Zebra is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Zebra; see the file COPYING. If not, write to the Free
19 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
30 #include "bgpd/bgpd.h"
31 #include "bgpd/bgp_lcommunity.h"
32 #include "bgpd/bgp_aspath.h"
34 /* Hash of community attribute. */
35 static struct hash *lcomhash;
37 /* Allocate a new lcommunities. */
38 static struct lcommunity *
41 return (struct lcommunity *) XCALLOC (MTYPE_LCOMMUNITY,
42 sizeof (struct lcommunity));
45 /* Allocate lcommunities. */
47 lcommunity_free (struct lcommunity **lcom)
50 XFREE (MTYPE_LCOMMUNITY_VAL, (*lcom)->val);
52 XFREE (MTYPE_LCOMMUNITY_STR, (*lcom)->str);
53 XFREE (MTYPE_LCOMMUNITY, *lcom);
57 /* Add a new Large Communities value to Large Communities
58 Attribute structure. When the value is already exists in the
59 structure, we don't add the value. Newly added value is sorted by
60 numerical order. When the value is added to the structure return 1
63 lcommunity_add_val (struct lcommunity *lcom, struct lcommunity_val *lval)
69 /* When this is fist value, just add it. */
70 if (lcom->val == NULL)
73 lcom->val = XMALLOC (MTYPE_LCOMMUNITY_VAL, lcom_length (lcom));
74 memcpy (lcom->val, lval->val, LCOMMUNITY_SIZE);
78 /* If the value already exists in the structure return 0. */
80 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. */
91 lcom->val = XREALLOC (MTYPE_LCOMMUNITY_VAL, lcom->val, lcom_length (lcom));
93 memmove (lcom->val + (c + 1) * LCOMMUNITY_SIZE,
94 lcom->val + c * LCOMMUNITY_SIZE,
95 (lcom->size - 1 - c) * LCOMMUNITY_SIZE);
96 memcpy (lcom->val + c * LCOMMUNITY_SIZE, lval->val, LCOMMUNITY_SIZE);
101 /* This function takes pointer to Large Communites strucutre then
102 create a new Large Communities structure by uniq and sort each
103 Large Communities value. */
105 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++)
118 lval = (struct lcommunity_val *) (lcom->val + (i * LCOMMUNITY_SIZE));
119 lcommunity_add_val (new, lval);
124 /* Parse Large Communites Attribute in BGP packet. */
126 lcommunity_parse (u_int8_t *pnt, u_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. */
149 lcommunity_dup (struct lcommunity *lcom)
151 struct lcommunity *new;
153 new = XCALLOC (MTYPE_LCOMMUNITY, sizeof (struct lcommunity));
154 new->size = lcom->size;
157 new->val = XMALLOC (MTYPE_LCOMMUNITY_VAL, lcom->size * LCOMMUNITY_SIZE);
158 memcpy (new->val, lcom->val, lcom->size * LCOMMUNITY_SIZE);
165 /* Retrun string representation of communities attribute. */
167 lcommunity_str (struct lcommunity *lcom)
170 lcom->str = lcommunity_lcom2str (lcom, LCOMMUNITY_FORMAT_DISPLAY);
174 /* Merge two Large Communities Attribute structure. */
176 lcommunity_merge (struct lcommunity *lcom1, struct lcommunity *lcom2)
179 lcom1->val = XREALLOC (MTYPE_LCOMMUNITY_VAL, lcom1->val,
180 (lcom1->size + lcom2->size) * LCOMMUNITY_SIZE);
182 lcom1->val = XMALLOC (MTYPE_LCOMMUNITY_VAL,
183 (lcom1->size + lcom2->size) * LCOMMUNITY_SIZE);
185 memcpy (lcom1->val + (lcom1->size * LCOMMUNITY_SIZE),
186 lcom2->val, lcom2->size * LCOMMUNITY_SIZE);
187 lcom1->size += lcom2->size;
192 /* Intern Large Communities Attribute. */
194 lcommunity_intern (struct lcommunity *lcom)
196 struct lcommunity *find;
198 assert (lcom->refcnt == 0);
200 find = (struct lcommunity *) hash_get (lcomhash, lcom, hash_alloc_intern);
203 lcommunity_free (&lcom);
208 find->str = lcommunity_lcom2str (find, LCOMMUNITY_FORMAT_DISPLAY);
213 /* Unintern Large Communities Attribute. */
215 lcommunity_unintern (struct lcommunity **lcom)
217 struct lcommunity *ret;
222 /* Pull off from hash. */
223 if ((*lcom)->refcnt == 0)
225 /* Large community must be in the hash. */
226 ret = (struct lcommunity *) hash_release (lcomhash, *lcom);
227 assert (ret != NULL);
229 lcommunity_free (lcom);
233 /* Utility function to make hash key. */
235 lcommunity_hash_make (void *arg)
237 const struct lcommunity *lcom = arg;
238 int size = lcom->size * LCOMMUNITY_SIZE;
239 u_int8_t *pnt = lcom->val;
240 unsigned int key = 0;
243 for (c = 0; c < size; c += LCOMMUNITY_SIZE)
262 /* Compare two Large Communities Attribute structure. */
264 lcommunity_cmp (const void *arg1, const void *arg2)
266 const struct lcommunity *lcom1 = arg1;
267 const struct lcommunity *lcom2 = arg2;
269 return (lcom1->size == lcom2->size
270 && memcmp (lcom1->val, lcom2->val, lcom1->size * LCOMMUNITY_SIZE) == 0);
273 /* Return communities hash. */
275 lcommunity_hash (void)
280 /* Initialize Large Comminities related hash. */
282 lcommunity_init (void)
284 lcomhash = hash_create (lcommunity_hash_make, lcommunity_cmp);
288 lcommunity_finish (void)
290 hash_free (lcomhash);
294 /* Large Communities token enum. */
295 enum lcommunity_token
297 lcommunity_token_unknown = 0,
298 lcommunity_token_val,
301 /* Get next Large Communities token from the string. */
303 lcommunity_gettoken (const char *str, struct lcommunity_val *lval,
304 enum lcommunity_token *token)
308 /* Skip white space. */
309 while (isspace ((int) *p))
315 /* Check the end of the line. */
319 /* Community value. */
320 if (isdigit ((int) *p))
324 u_int32_t globaladmin = 0;
325 u_int32_t localdata1 = 0;
326 u_int32_t localdata2 = 0;
328 while (isdigit ((int) *p) || *p == ':')
334 *token = lcommunity_token_unknown;
341 if (separator == 1) {
342 globaladmin = localdata2;
344 localdata1 = localdata2;
353 localdata2 += (*p - '0');
359 *token = lcommunity_token_unknown;
364 * Copy the large comm.
366 lval->val[0] = (globaladmin >> 24) & 0xff;
367 lval->val[1] = (globaladmin >> 16) & 0xff;
368 lval->val[2] = (globaladmin >> 8) & 0xff;
369 lval->val[3] = globaladmin & 0xff;
370 lval->val[4] = (localdata1 >> 24) & 0xff;
371 lval->val[5] = (localdata1 >> 16) & 0xff;
372 lval->val[6] = (localdata1 >> 8) & 0xff;
373 lval->val[7] = localdata1 & 0xff;
374 lval->val[8] = (localdata2 >> 24) & 0xff;
375 lval->val[9] = (localdata2 >> 16) & 0xff;
376 lval->val[10] = (localdata2 >> 8) & 0xff;
377 lval->val[11] = localdata2 & 0xff;
379 *token = lcommunity_token_val;
382 *token = lcommunity_token_unknown;
387 Convert string to large community attribute.
388 When type is already known, please specify both str and type.
390 When string includes keyword for each large community value.
391 Please specify keyword_included as non-zero value.
394 lcommunity_str2com (const char *str)
396 struct lcommunity *lcom = NULL;
397 enum lcommunity_token token = lcommunity_token_unknown;
398 struct lcommunity_val lval;
400 while ((str = lcommunity_gettoken (str, &lval, &token)))
404 case lcommunity_token_val:
406 lcom = lcommunity_new ();
407 lcommunity_add_val (lcom, &lval);
409 case lcommunity_token_unknown:
412 lcommunity_free (&lcom);
420 lcommunity_include (struct lcommunity *lcom, u_char *ptr)
425 for (i = 0; i < lcom->size; i++) {
426 lcom_ptr = lcom->val + (i * LCOMMUNITY_SIZE);
427 if (memcmp (ptr, lcom_ptr, LCOMMUNITY_SIZE) == 0)
433 /* Convert large community attribute to string.
434 The large coms will be in 65535:65531:0 format.
437 lcommunity_lcom2str (struct lcommunity *lcom, int format)
441 #define LCOMMUNITY_STR_DEFAULT_LEN 40
447 u_int32_t globaladmin, localdata1, localdata2;
451 str_buf = XMALLOC (MTYPE_LCOMMUNITY_STR, 1);
456 /* Prepare buffer. */
457 str_buf = XMALLOC (MTYPE_LCOMMUNITY_STR, LCOMMUNITY_STR_DEFAULT_LEN + 1);
458 str_size = LCOMMUNITY_STR_DEFAULT_LEN + 1;
461 for (i = 0; i < lcom->size; i++)
463 /* Make it sure size is enough. */
464 while (str_pnt + LCOMMUNITY_STR_DEFAULT_LEN >= str_size)
467 str_buf = XREALLOC (MTYPE_LCOMMUNITY_STR, str_buf, str_size);
470 /* Space between each value. */
472 str_buf[str_pnt++] = ' ';
474 pnt = lcom->val + (i * 12);
476 globaladmin = (*pnt++ << 24);
477 globaladmin |= (*pnt++ << 16);
478 globaladmin |= (*pnt++ << 8);
479 globaladmin |= (*pnt++);
481 localdata1 = (*pnt++ << 24);
482 localdata1 |= (*pnt++ << 16);
483 localdata1 |= (*pnt++ << 8);
484 localdata1 |= (*pnt++);
486 localdata2 = (*pnt++ << 24);
487 localdata2 |= (*pnt++ << 16);
488 localdata2 |= (*pnt++ << 8);
489 localdata2 |= (*pnt++);
491 len = sprintf( str_buf + str_pnt, "%u:%u:%u", globaladmin,
492 localdata1, localdata2);
500 lcommunity_match (const struct lcommunity *lcom1,
501 const struct lcommunity *lcom2)
506 if (lcom1 == NULL && lcom2 == NULL)
509 if (lcom1 == NULL || lcom2 == NULL)
512 if (lcom1->size < lcom2->size)
515 /* Every community on com2 needs to be on com1 for this to match */
516 while (i < lcom1->size && j < lcom2->size)
518 if (memcmp (lcom1->val + (i*12), lcom2->val + (j*12), LCOMMUNITY_SIZE) == 0)
523 if (j == lcom2->size)
529 /* Delete one lcommunity. */
531 lcommunity_del_val (struct lcommunity *lcom, u_char *ptr)
539 while (i < lcom->size)
541 if (memcmp (lcom->val + i*LCOMMUNITY_SIZE, ptr, LCOMMUNITY_SIZE) == 0)
543 c = lcom->size -i -1;
546 memmove (lcom->val + i*LCOMMUNITY_SIZE, lcom->val + (i + 1)*LCOMMUNITY_SIZE, c * LCOMMUNITY_SIZE);
551 lcom->val = XREALLOC (MTYPE_COMMUNITY_VAL, lcom->val,
555 XFREE (MTYPE_COMMUNITY_VAL, lcom->val);