]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_lcommunity.c
Merge pull request #2217 from donaldsharp/pim_threads
[mirror_frr.git] / bgpd / bgp_lcommunity.c
CommitLineData
57d187bc 1/* BGP Large Communities Attribute
2acb4ac2
DL
2 *
3 * Copyright (C) 2016 Keyur Patel <keyur@arrcus.com>
4 *
5 * This file is part of FreeRangeRouting (FRR).
6 *
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.
10 *
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
14 * details.
15 *
16 * You should have received a copy of the GNU General Public License along
896014f4
DL
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
2acb4ac2 19 */
57d187bc
JS
20
21#include <zebra.h>
22
23#include "hash.h"
24#include "memory.h"
25#include "prefix.h"
26#include "command.h"
27#include "filter.h"
3f65c5b1 28#include "jhash.h"
937652c6 29#include "stream.h"
57d187bc
JS
30
31#include "bgpd/bgpd.h"
32#include "bgpd/bgp_lcommunity.h"
33#include "bgpd/bgp_aspath.h"
34
35/* Hash of community attribute. */
36static struct hash *lcomhash;
37
38/* Allocate a new lcommunities. */
d62a17ae 39static struct lcommunity *lcommunity_new(void)
57d187bc 40{
d62a17ae 41 return (struct lcommunity *)XCALLOC(MTYPE_LCOMMUNITY,
42 sizeof(struct lcommunity));
57d187bc
JS
43}
44
45/* Allocate lcommunities. */
d62a17ae 46void lcommunity_free(struct lcommunity **lcom)
57d187bc 47{
d62a17ae 48 if ((*lcom)->val)
49 XFREE(MTYPE_LCOMMUNITY_VAL, (*lcom)->val);
50 if ((*lcom)->str)
51 XFREE(MTYPE_LCOMMUNITY_STR, (*lcom)->str);
52 XFREE(MTYPE_LCOMMUNITY, *lcom);
53 lcom = NULL;
57d187bc
JS
54}
55
d62a17ae 56static void lcommunity_hash_free(struct lcommunity *lcom)
47350fd9 57{
d62a17ae 58 lcommunity_free(&lcom);
47350fd9
DS
59}
60
57d187bc
JS
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
65 else return 0. */
d62a17ae 66static int lcommunity_add_val(struct lcommunity *lcom,
67 struct lcommunity_val *lval)
57d187bc 68{
d7c0a89a 69 uint8_t *p;
d62a17ae 70 int ret;
71 int c;
72
73 /* When this is fist value, just add it. */
74 if (lcom->val == NULL) {
75 lcom->size++;
76 lcom->val = XMALLOC(MTYPE_LCOMMUNITY_VAL, lcom_length(lcom));
77 memcpy(lcom->val, lval->val, LCOMMUNITY_SIZE);
78 return 1;
79 }
80
81 /* If the value already exists in the structure return 0. */
82 c = 0;
83 for (p = lcom->val; c < lcom->size; p += LCOMMUNITY_SIZE, c++) {
84 ret = memcmp(p, lval->val, LCOMMUNITY_SIZE);
85 if (ret == 0)
86 return 0;
87 if (ret > 0)
88 break;
89 }
90
91 /* Add the value to the structure with numerical sorting. */
92 lcom->size++;
93 lcom->val =
94 XREALLOC(MTYPE_LCOMMUNITY_VAL, lcom->val, lcom_length(lcom));
95
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);
100
101 return 1;
57d187bc
JS
102}
103
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. */
d62a17ae 107struct lcommunity *lcommunity_uniq_sort(struct lcommunity *lcom)
57d187bc 108{
d62a17ae 109 int i;
110 struct lcommunity *new;
111 struct lcommunity_val *lval;
57d187bc 112
d62a17ae 113 if (!lcom)
114 return NULL;
57d187bc 115
d62a17ae 116 new = lcommunity_new();
57d187bc 117
d62a17ae 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);
122 }
123 return new;
57d187bc
JS
124}
125
126/* Parse Large Communites Attribute in BGP packet. */
d7c0a89a 127struct lcommunity *lcommunity_parse(uint8_t *pnt, unsigned short length)
57d187bc 128{
d62a17ae 129 struct lcommunity tmp;
130 struct lcommunity *new;
57d187bc 131
d62a17ae 132 /* Length check. */
133 if (length % LCOMMUNITY_SIZE)
134 return NULL;
57d187bc 135
d62a17ae 136 /* Prepare tmporary structure for making a new Large Communities
137 Attribute. */
138 tmp.size = length / LCOMMUNITY_SIZE;
139 tmp.val = pnt;
57d187bc 140
d62a17ae 141 /* Create a new Large Communities Attribute by uniq and sort each
142 Large Communities value */
143 new = lcommunity_uniq_sort(&tmp);
57d187bc 144
d62a17ae 145 return lcommunity_intern(new);
57d187bc
JS
146}
147
148/* Duplicate the Large Communities Attribute structure. */
d62a17ae 149struct lcommunity *lcommunity_dup(struct lcommunity *lcom)
57d187bc 150{
d62a17ae 151 struct lcommunity *new;
152
cb0c2da3 153 new = lcommunity_new();
d62a17ae 154 new->size = lcom->size;
155 if (new->size) {
79dab4b7
NK
156 new->val = XMALLOC(MTYPE_LCOMMUNITY_VAL, lcom_length(lcom));
157 memcpy(new->val, lcom->val, lcom_length(lcom));
d62a17ae 158 } else
159 new->val = NULL;
160 return new;
57d187bc
JS
161}
162
163/* Retrun string representation of communities attribute. */
d62a17ae 164char *lcommunity_str(struct lcommunity *lcom)
57d187bc 165{
d62a17ae 166 if (!lcom->str)
167 lcom->str =
168 lcommunity_lcom2str(lcom, LCOMMUNITY_FORMAT_DISPLAY);
169 return lcom->str;
57d187bc
JS
170}
171
172/* Merge two Large Communities Attribute structure. */
d62a17ae 173struct lcommunity *lcommunity_merge(struct lcommunity *lcom1,
174 struct lcommunity *lcom2)
57d187bc 175{
d62a17ae 176 if (lcom1->val)
996c9314
LB
177 lcom1->val = XREALLOC(MTYPE_LCOMMUNITY_VAL, lcom1->val,
178 lcom_length(lcom1) + lcom_length(lcom2));
d62a17ae 179 else
996c9314
LB
180 lcom1->val = XMALLOC(MTYPE_LCOMMUNITY_VAL,
181 lcom_length(lcom1) + lcom_length(lcom2));
d62a17ae 182
79dab4b7 183 memcpy(lcom1->val + lcom_length(lcom1), lcom2->val, lcom_length(lcom2));
d62a17ae 184 lcom1->size += lcom2->size;
185
186 return lcom1;
57d187bc
JS
187}
188
189/* Intern Large Communities Attribute. */
d62a17ae 190struct lcommunity *lcommunity_intern(struct lcommunity *lcom)
57d187bc 191{
d62a17ae 192 struct lcommunity *find;
57d187bc 193
d62a17ae 194 assert(lcom->refcnt == 0);
57d187bc 195
d62a17ae 196 find = (struct lcommunity *)hash_get(lcomhash, lcom, hash_alloc_intern);
57d187bc 197
d62a17ae 198 if (find != lcom)
199 lcommunity_free(&lcom);
57d187bc 200
d62a17ae 201 find->refcnt++;
57d187bc 202
d62a17ae 203 if (!find->str)
204 find->str =
205 lcommunity_lcom2str(find, LCOMMUNITY_FORMAT_DISPLAY);
57d187bc 206
d62a17ae 207 return find;
57d187bc
JS
208}
209
210/* Unintern Large Communities Attribute. */
d62a17ae 211void lcommunity_unintern(struct lcommunity **lcom)
57d187bc 212{
d62a17ae 213 struct lcommunity *ret;
57d187bc 214
d62a17ae 215 if ((*lcom)->refcnt)
216 (*lcom)->refcnt--;
57d187bc 217
d62a17ae 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);
222 assert(ret != NULL);
57d187bc 223
d62a17ae 224 lcommunity_free(lcom);
225 }
57d187bc
JS
226}
227
228/* Utility function to make hash key. */
d62a17ae 229unsigned int lcommunity_hash_make(void *arg)
57d187bc 230{
d62a17ae 231 const struct lcommunity *lcom = arg;
79dab4b7 232 int size = lcom_length(lcom);
d62a17ae 233
3f65c5b1 234 return jhash(lcom->val, size, 0xab125423);
57d187bc
JS
235}
236
237/* Compare two Large Communities Attribute structure. */
d62a17ae 238int lcommunity_cmp(const void *arg1, const void *arg2)
57d187bc 239{
d62a17ae 240 const struct lcommunity *lcom1 = arg1;
241 const struct lcommunity *lcom2 = arg2;
57d187bc 242
d62a17ae 243 return (lcom1->size == lcom2->size
996c9314 244 && memcmp(lcom1->val, lcom2->val, lcom_length(lcom1)) == 0);
57d187bc
JS
245}
246
247/* Return communities hash. */
d62a17ae 248struct hash *lcommunity_hash(void)
57d187bc 249{
d62a17ae 250 return lcomhash;
57d187bc
JS
251}
252
253/* Initialize Large Comminities related hash. */
d62a17ae 254void lcommunity_init(void)
57d187bc 255{
996c9314 256 lcomhash = hash_create(lcommunity_hash_make, lcommunity_cmp,
3f65c5b1 257 "BGP lcommunity hash");
57d187bc
JS
258}
259
d62a17ae 260void lcommunity_finish(void)
57d187bc 261{
d62a17ae 262 hash_clean(lcomhash, (void (*)(void *))lcommunity_hash_free);
263 hash_free(lcomhash);
264 lcomhash = NULL;
57d187bc
JS
265}
266
267/* Large Communities token enum. */
d62a17ae 268enum lcommunity_token {
269 lcommunity_token_unknown = 0,
270 lcommunity_token_val,
57d187bc
JS
271};
272
273/* Get next Large Communities token from the string. */
d62a17ae 274static const char *lcommunity_gettoken(const char *str,
275 struct lcommunity_val *lval,
276 enum lcommunity_token *token)
57d187bc 277{
d62a17ae 278 const char *p = str;
279
280 /* Skip white space. */
281 while (isspace((int)*p)) {
282 p++;
283 str++;
284 }
285
286 /* Check the end of the line. */
287 if (*p == '\0')
288 return NULL;
289
290 /* Community value. */
291 if (isdigit((int)*p)) {
292 int separator = 0;
293 int digit = 0;
d7c0a89a
QY
294 uint32_t globaladmin = 0;
295 uint32_t localdata1 = 0;
296 uint32_t localdata2 = 0;
d62a17ae 297
298 while (isdigit((int)*p) || *p == ':') {
299 if (*p == ':') {
300 if (separator == 2) {
301 *token = lcommunity_token_unknown;
302 return NULL;
303 } else {
304 separator++;
305 digit = 0;
306 if (separator == 1) {
307 globaladmin = localdata2;
308 } else {
309 localdata1 = localdata2;
310 }
311 localdata2 = 0;
312 }
313 } else {
314 digit = 1;
315 localdata2 *= 10;
316 localdata2 += (*p - '0');
317 }
318 p++;
57d187bc 319 }
d62a17ae 320 if (!digit) {
321 *token = lcommunity_token_unknown;
322 return NULL;
57d187bc 323 }
57d187bc 324
d62a17ae 325 /*
326 * Copy the large comm.
327 */
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;
340
341 *token = lcommunity_token_val;
342 return p;
343 }
344 *token = lcommunity_token_unknown;
345 return p;
57d187bc
JS
346}
347
348/*
349 Convert string to large community attribute.
350 When type is already known, please specify both str and type.
351
352 When string includes keyword for each large community value.
353 Please specify keyword_included as non-zero value.
354*/
d62a17ae 355struct lcommunity *lcommunity_str2com(const char *str)
57d187bc 356{
d62a17ae 357 struct lcommunity *lcom = NULL;
358 enum lcommunity_token token = lcommunity_token_unknown;
359 struct lcommunity_val lval;
360
361 while ((str = lcommunity_gettoken(str, &lval, &token))) {
362 switch (token) {
363 case lcommunity_token_val:
364 if (lcom == NULL)
365 lcom = lcommunity_new();
366 lcommunity_add_val(lcom, &lval);
367 break;
368 case lcommunity_token_unknown:
369 default:
370 if (lcom)
371 lcommunity_free(&lcom);
372 return NULL;
373 }
374 }
375 return lcom;
57d187bc
JS
376}
377
d7c0a89a 378int lcommunity_include(struct lcommunity *lcom, uint8_t *ptr)
57d187bc 379{
d62a17ae 380 int i;
d7c0a89a 381 uint8_t *lcom_ptr;
d62a17ae 382
d62a17ae 383 for (i = 0; i < lcom->size; i++) {
3ac053e6 384 lcom_ptr = lcom->val + (i * LCOMMUNITY_SIZE);
d62a17ae 385 if (memcmp(ptr, lcom_ptr, LCOMMUNITY_SIZE) == 0)
386 return 1;
387 }
388 return 0;
57d187bc
JS
389}
390
391/* Convert large community attribute to string.
392 The large coms will be in 65535:65531:0 format.
393*/
d62a17ae 394char *lcommunity_lcom2str(struct lcommunity *lcom, int format)
57d187bc 395{
d62a17ae 396 int i;
d7c0a89a 397 uint8_t *pnt;
57d187bc 398#define LCOMMUNITY_STR_DEFAULT_LEN 40
d62a17ae 399 int str_size;
400 int str_pnt;
401 char *str_buf;
402 int len = 0;
403 int first = 1;
d7c0a89a 404 uint32_t globaladmin, localdata1, localdata2;
d62a17ae 405
406 if (lcom->size == 0) {
407 str_buf = XMALLOC(MTYPE_LCOMMUNITY_STR, 1);
408 str_buf[0] = '\0';
409 return str_buf;
57d187bc
JS
410 }
411
d62a17ae 412 /* Prepare buffer. */
413 str_buf = XMALLOC(MTYPE_LCOMMUNITY_STR, LCOMMUNITY_STR_DEFAULT_LEN + 1);
414 str_size = LCOMMUNITY_STR_DEFAULT_LEN + 1;
415 str_pnt = 0;
416
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) {
420 str_size *= 2;
421 str_buf = XREALLOC(MTYPE_LCOMMUNITY_STR, str_buf,
422 str_size);
423 }
424
425 /* Space between each value. */
426 if (!first)
427 str_buf[str_pnt++] = ' ';
428
8ea01902 429 pnt = lcom->val + (i * LCOMMUNITY_SIZE);
d62a17ae 430
937652c6
DL
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 */
d62a17ae 435
436 len = sprintf(str_buf + str_pnt, "%u:%u:%u", globaladmin,
437 localdata1, localdata2);
438 str_pnt += len;
439 first = 0;
440 }
441 return str_buf;
57d187bc
JS
442}
443
d62a17ae 444int lcommunity_match(const struct lcommunity *lcom1,
445 const struct lcommunity *lcom2)
57d187bc 446{
d62a17ae 447 int i = 0;
448 int j = 0;
449
450 if (lcom1 == NULL && lcom2 == NULL)
451 return 1;
452
453 if (lcom1 == NULL || lcom2 == NULL)
454 return 0;
455
456 if (lcom1->size < lcom2->size)
457 return 0;
458
459 /* Every community on com2 needs to be on com1 for this to match */
460 while (i < lcom1->size && j < lcom2->size) {
996c9314
LB
461 if (memcmp(lcom1->val + (i * LCOMMUNITY_SIZE),
462 lcom2->val + (j * LCOMMUNITY_SIZE), LCOMMUNITY_SIZE)
d62a17ae 463 == 0)
464 j++;
465 i++;
466 }
467
468 if (j == lcom2->size)
469 return 1;
470 else
471 return 0;
57d187bc
JS
472}
473
474/* Delete one lcommunity. */
d7c0a89a 475void lcommunity_del_val(struct lcommunity *lcom, uint8_t *ptr)
57d187bc 476{
d62a17ae 477 int i = 0;
478 int c = 0;
479
480 if (!lcom->val)
481 return;
482
483 while (i < lcom->size) {
484 if (memcmp(lcom->val + i * LCOMMUNITY_SIZE, ptr,
485 LCOMMUNITY_SIZE)
486 == 0) {
487 c = lcom->size - i - 1;
488
489 if (c > 0)
490 memmove(lcom->val + i * LCOMMUNITY_SIZE,
491 lcom->val + (i + 1) * LCOMMUNITY_SIZE,
492 c * LCOMMUNITY_SIZE);
493
494 lcom->size--;
495
496 if (lcom->size > 0)
497 lcom->val =
996c9314
LB
498 XREALLOC(MTYPE_LCOMMUNITY_VAL,
499 lcom->val, lcom_length(lcom));
d62a17ae 500 else {
600d7ff8 501 XFREE(MTYPE_LCOMMUNITY_VAL, lcom->val);
d62a17ae 502 lcom->val = NULL;
503 }
504 return;
505 }
506 i++;
57d187bc 507 }
57d187bc 508}