]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_lcommunity.c
bgpd: enumerate support for dscp values
[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
57d187bc 163/* Merge two Large Communities Attribute structure. */
d62a17ae 164struct lcommunity *lcommunity_merge(struct lcommunity *lcom1,
165 struct lcommunity *lcom2)
57d187bc 166{
d62a17ae 167 if (lcom1->val)
996c9314
LB
168 lcom1->val = XREALLOC(MTYPE_LCOMMUNITY_VAL, lcom1->val,
169 lcom_length(lcom1) + lcom_length(lcom2));
d62a17ae 170 else
996c9314
LB
171 lcom1->val = XMALLOC(MTYPE_LCOMMUNITY_VAL,
172 lcom_length(lcom1) + lcom_length(lcom2));
d62a17ae 173
79dab4b7 174 memcpy(lcom1->val + lcom_length(lcom1), lcom2->val, lcom_length(lcom2));
d62a17ae 175 lcom1->size += lcom2->size;
176
177 return lcom1;
57d187bc
JS
178}
179
8d9b8ed9
PM
180static void set_lcommunity_string(struct lcommunity *lcom, bool make_json)
181{
182 int i;
183 int len;
184 bool first = 1;
185 char *str_buf;
186 char *str_pnt;
187 uint8_t *pnt;
188 uint32_t global, local1, local2;
189 json_object *json_lcommunity_list = NULL;
190 json_object *json_string = NULL;
191
192#define LCOMMUNITY_STR_DEFAULT_LEN 32
193
194 if (!lcom)
195 return;
196
197 if (make_json) {
198 lcom->json = json_object_new_object();
199 json_lcommunity_list = json_object_new_array();
200 }
201
202 if (lcom->size == 0) {
203 str_buf = XMALLOC(MTYPE_LCOMMUNITY_STR, 1);
204 str_buf[0] = '\0';
205
206 if (make_json) {
207 json_object_string_add(lcom->json, "string", "");
208 json_object_object_add(lcom->json, "list",
209 json_lcommunity_list);
210 }
211
212 lcom->str = str_buf;
213 return;
214 }
215
216 str_buf = str_pnt =
217 XMALLOC(MTYPE_LCOMMUNITY_STR,
218 (LCOMMUNITY_STR_DEFAULT_LEN * lcom->size) + 1);
219
220 for (i = 0; i < lcom->size; i++) {
221 if (first)
222 first = 0;
223 else
224 *str_pnt++ = ' ';
225
226 pnt = lcom->val + (i * LCOMMUNITY_SIZE);
227 pnt = ptr_get_be32(pnt, &global);
228 pnt = ptr_get_be32(pnt, &local1);
229 pnt = ptr_get_be32(pnt, &local2);
230 (void)pnt;
231
232 len = sprintf(str_pnt, "%u:%u:%u", global, local1, local2);
233 if (make_json) {
234 json_string = json_object_new_string(str_pnt);
235 json_object_array_add(json_lcommunity_list,
236 json_string);
237 }
238
239 str_pnt += len;
240 }
241
242 str_buf =
243 XREALLOC(MTYPE_LCOMMUNITY_STR, str_buf, str_pnt - str_buf + 1);
244
245 if (make_json) {
246 json_object_string_add(lcom->json, "string", str_buf);
247 json_object_object_add(lcom->json, "list",
248 json_lcommunity_list);
249 }
250
251 lcom->str = str_buf;
252}
253
57d187bc 254/* Intern Large Communities Attribute. */
d62a17ae 255struct lcommunity *lcommunity_intern(struct lcommunity *lcom)
57d187bc 256{
d62a17ae 257 struct lcommunity *find;
57d187bc 258
d62a17ae 259 assert(lcom->refcnt == 0);
57d187bc 260
d62a17ae 261 find = (struct lcommunity *)hash_get(lcomhash, lcom, hash_alloc_intern);
57d187bc 262
d62a17ae 263 if (find != lcom)
264 lcommunity_free(&lcom);
57d187bc 265
d62a17ae 266 find->refcnt++;
57d187bc 267
d62a17ae 268 if (!find->str)
8d9b8ed9 269 set_lcommunity_string(find, false);
57d187bc 270
d62a17ae 271 return find;
57d187bc
JS
272}
273
274/* Unintern Large Communities Attribute. */
d62a17ae 275void lcommunity_unintern(struct lcommunity **lcom)
57d187bc 276{
d62a17ae 277 struct lcommunity *ret;
57d187bc 278
d62a17ae 279 if ((*lcom)->refcnt)
280 (*lcom)->refcnt--;
57d187bc 281
d62a17ae 282 /* Pull off from hash. */
283 if ((*lcom)->refcnt == 0) {
284 /* Large community must be in the hash. */
285 ret = (struct lcommunity *)hash_release(lcomhash, *lcom);
286 assert(ret != NULL);
57d187bc 287
d62a17ae 288 lcommunity_free(lcom);
289 }
57d187bc
JS
290}
291
8d9b8ed9
PM
292/* Retrun string representation of communities attribute. */
293char *lcommunity_str(struct lcommunity *lcom, bool make_json)
294{
295 if (!lcom)
296 return NULL;
297
298 if (make_json && !lcom->json && lcom->str)
299 XFREE(MTYPE_LCOMMUNITY_STR, lcom->str);
300
301 if (!lcom->str)
302 set_lcommunity_string(lcom, make_json);
303
304 return lcom->str;
305}
306
57d187bc 307/* Utility function to make hash key. */
d62a17ae 308unsigned int lcommunity_hash_make(void *arg)
57d187bc 309{
d62a17ae 310 const struct lcommunity *lcom = arg;
79dab4b7 311 int size = lcom_length(lcom);
d62a17ae 312
3f65c5b1 313 return jhash(lcom->val, size, 0xab125423);
57d187bc
JS
314}
315
316/* Compare two Large Communities Attribute structure. */
d62a17ae 317int lcommunity_cmp(const void *arg1, const void *arg2)
57d187bc 318{
d62a17ae 319 const struct lcommunity *lcom1 = arg1;
320 const struct lcommunity *lcom2 = arg2;
57d187bc 321
d62a17ae 322 return (lcom1->size == lcom2->size
996c9314 323 && memcmp(lcom1->val, lcom2->val, lcom_length(lcom1)) == 0);
57d187bc
JS
324}
325
326/* Return communities hash. */
d62a17ae 327struct hash *lcommunity_hash(void)
57d187bc 328{
d62a17ae 329 return lcomhash;
57d187bc
JS
330}
331
332/* Initialize Large Comminities related hash. */
d62a17ae 333void lcommunity_init(void)
57d187bc 334{
996c9314 335 lcomhash = hash_create(lcommunity_hash_make, lcommunity_cmp,
3f65c5b1 336 "BGP lcommunity hash");
57d187bc
JS
337}
338
d62a17ae 339void lcommunity_finish(void)
57d187bc 340{
d62a17ae 341 hash_clean(lcomhash, (void (*)(void *))lcommunity_hash_free);
342 hash_free(lcomhash);
343 lcomhash = NULL;
57d187bc
JS
344}
345
346/* Large Communities token enum. */
d62a17ae 347enum lcommunity_token {
348 lcommunity_token_unknown = 0,
349 lcommunity_token_val,
57d187bc
JS
350};
351
352/* Get next Large Communities token from the string. */
d62a17ae 353static const char *lcommunity_gettoken(const char *str,
354 struct lcommunity_val *lval,
355 enum lcommunity_token *token)
57d187bc 356{
d62a17ae 357 const char *p = str;
358
359 /* Skip white space. */
360 while (isspace((int)*p)) {
361 p++;
362 str++;
363 }
364
365 /* Check the end of the line. */
366 if (*p == '\0')
367 return NULL;
368
369 /* Community value. */
370 if (isdigit((int)*p)) {
371 int separator = 0;
372 int digit = 0;
d7c0a89a
QY
373 uint32_t globaladmin = 0;
374 uint32_t localdata1 = 0;
375 uint32_t localdata2 = 0;
d62a17ae 376
377 while (isdigit((int)*p) || *p == ':') {
378 if (*p == ':') {
379 if (separator == 2) {
380 *token = lcommunity_token_unknown;
381 return NULL;
382 } else {
383 separator++;
384 digit = 0;
385 if (separator == 1) {
386 globaladmin = localdata2;
387 } else {
388 localdata1 = localdata2;
389 }
390 localdata2 = 0;
391 }
392 } else {
393 digit = 1;
394 localdata2 *= 10;
395 localdata2 += (*p - '0');
396 }
397 p++;
57d187bc 398 }
d62a17ae 399 if (!digit) {
400 *token = lcommunity_token_unknown;
401 return NULL;
57d187bc 402 }
57d187bc 403
d62a17ae 404 /*
405 * Copy the large comm.
406 */
407 lval->val[0] = (globaladmin >> 24) & 0xff;
408 lval->val[1] = (globaladmin >> 16) & 0xff;
409 lval->val[2] = (globaladmin >> 8) & 0xff;
410 lval->val[3] = globaladmin & 0xff;
411 lval->val[4] = (localdata1 >> 24) & 0xff;
412 lval->val[5] = (localdata1 >> 16) & 0xff;
413 lval->val[6] = (localdata1 >> 8) & 0xff;
414 lval->val[7] = localdata1 & 0xff;
415 lval->val[8] = (localdata2 >> 24) & 0xff;
416 lval->val[9] = (localdata2 >> 16) & 0xff;
417 lval->val[10] = (localdata2 >> 8) & 0xff;
418 lval->val[11] = localdata2 & 0xff;
419
420 *token = lcommunity_token_val;
421 return p;
422 }
423 *token = lcommunity_token_unknown;
424 return p;
57d187bc
JS
425}
426
427/*
428 Convert string to large community attribute.
429 When type is already known, please specify both str and type.
430
431 When string includes keyword for each large community value.
432 Please specify keyword_included as non-zero value.
433*/
d62a17ae 434struct lcommunity *lcommunity_str2com(const char *str)
57d187bc 435{
d62a17ae 436 struct lcommunity *lcom = NULL;
437 enum lcommunity_token token = lcommunity_token_unknown;
438 struct lcommunity_val lval;
439
440 while ((str = lcommunity_gettoken(str, &lval, &token))) {
441 switch (token) {
442 case lcommunity_token_val:
443 if (lcom == NULL)
444 lcom = lcommunity_new();
445 lcommunity_add_val(lcom, &lval);
446 break;
447 case lcommunity_token_unknown:
448 default:
449 if (lcom)
450 lcommunity_free(&lcom);
451 return NULL;
452 }
453 }
454 return lcom;
57d187bc
JS
455}
456
d7c0a89a 457int lcommunity_include(struct lcommunity *lcom, uint8_t *ptr)
57d187bc 458{
d62a17ae 459 int i;
d7c0a89a 460 uint8_t *lcom_ptr;
d62a17ae 461
d62a17ae 462 for (i = 0; i < lcom->size; i++) {
3ac053e6 463 lcom_ptr = lcom->val + (i * LCOMMUNITY_SIZE);
d62a17ae 464 if (memcmp(ptr, lcom_ptr, LCOMMUNITY_SIZE) == 0)
465 return 1;
466 }
467 return 0;
57d187bc
JS
468}
469
d62a17ae 470int lcommunity_match(const struct lcommunity *lcom1,
471 const struct lcommunity *lcom2)
57d187bc 472{
d62a17ae 473 int i = 0;
474 int j = 0;
475
476 if (lcom1 == NULL && lcom2 == NULL)
477 return 1;
478
479 if (lcom1 == NULL || lcom2 == NULL)
480 return 0;
481
482 if (lcom1->size < lcom2->size)
483 return 0;
484
485 /* Every community on com2 needs to be on com1 for this to match */
486 while (i < lcom1->size && j < lcom2->size) {
996c9314
LB
487 if (memcmp(lcom1->val + (i * LCOMMUNITY_SIZE),
488 lcom2->val + (j * LCOMMUNITY_SIZE), LCOMMUNITY_SIZE)
d62a17ae 489 == 0)
490 j++;
491 i++;
492 }
493
494 if (j == lcom2->size)
495 return 1;
496 else
497 return 0;
57d187bc
JS
498}
499
500/* Delete one lcommunity. */
d7c0a89a 501void lcommunity_del_val(struct lcommunity *lcom, uint8_t *ptr)
57d187bc 502{
d62a17ae 503 int i = 0;
504 int c = 0;
505
506 if (!lcom->val)
507 return;
508
509 while (i < lcom->size) {
510 if (memcmp(lcom->val + i * LCOMMUNITY_SIZE, ptr,
511 LCOMMUNITY_SIZE)
512 == 0) {
513 c = lcom->size - i - 1;
514
515 if (c > 0)
516 memmove(lcom->val + i * LCOMMUNITY_SIZE,
517 lcom->val + (i + 1) * LCOMMUNITY_SIZE,
518 c * LCOMMUNITY_SIZE);
519
520 lcom->size--;
521
522 if (lcom->size > 0)
523 lcom->val =
996c9314
LB
524 XREALLOC(MTYPE_LCOMMUNITY_VAL,
525 lcom->val, lcom_length(lcom));
d62a17ae 526 else {
600d7ff8 527 XFREE(MTYPE_LCOMMUNITY_VAL, lcom->val);
d62a17ae 528 lcom->val = NULL;
529 }
530 return;
531 }
532 i++;
57d187bc 533 }
57d187bc 534}