]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_lcommunity.c
bgpd: Replace bgp_flag_* to [UN]SET/CHECK_FLAG macros
[mirror_frr.git] / bgpd / bgp_lcommunity.c
1 /* BGP Large Communities Attribute
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
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
19 */
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"
28 #include "jhash.h"
29 #include "stream.h"
30
31 #include "bgpd/bgpd.h"
32 #include "bgpd/bgp_lcommunity.h"
33 #include "bgpd/bgp_aspath.h"
34
35 /* Hash of community attribute. */
36 static struct hash *lcomhash;
37
38 /* Allocate a new lcommunities. */
39 static struct lcommunity *lcommunity_new(void)
40 {
41 return XCALLOC(MTYPE_LCOMMUNITY, sizeof(struct lcommunity));
42 }
43
44 /* Allocate lcommunities. */
45 void lcommunity_free(struct lcommunity **lcom)
46 {
47 XFREE(MTYPE_LCOMMUNITY_VAL, (*lcom)->val);
48 XFREE(MTYPE_LCOMMUNITY_STR, (*lcom)->str);
49 XFREE(MTYPE_LCOMMUNITY, *lcom);
50 }
51
52 static void lcommunity_hash_free(struct lcommunity *lcom)
53 {
54 lcommunity_free(&lcom);
55 }
56
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
61 else return 0. */
62 static int lcommunity_add_val(struct lcommunity *lcom,
63 struct lcommunity_val *lval)
64 {
65 uint8_t *p;
66 int ret;
67 int c;
68
69 /* When this is fist value, just add it. */
70 if (lcom->val == NULL) {
71 lcom->size++;
72 lcom->val = XMALLOC(MTYPE_LCOMMUNITY_VAL, lcom_length(lcom));
73 memcpy(lcom->val, lval->val, LCOMMUNITY_SIZE);
74 return 1;
75 }
76
77 /* If the value already exists in the structure return 0. */
78 c = 0;
79 for (p = lcom->val; c < lcom->size; p += LCOMMUNITY_SIZE, c++) {
80 ret = memcmp(p, lval->val, LCOMMUNITY_SIZE);
81 if (ret == 0)
82 return 0;
83 if (ret > 0)
84 break;
85 }
86
87 /* Add the value to the structure with numerical sorting. */
88 lcom->size++;
89 lcom->val =
90 XREALLOC(MTYPE_LCOMMUNITY_VAL, lcom->val, lcom_length(lcom));
91
92 memmove(lcom->val + (c + 1) * LCOMMUNITY_SIZE,
93 lcom->val + c * LCOMMUNITY_SIZE,
94 (lcom->size - 1 - c) * LCOMMUNITY_SIZE);
95 memcpy(lcom->val + c * LCOMMUNITY_SIZE, lval->val, LCOMMUNITY_SIZE);
96
97 return 1;
98 }
99
100 /* This function takes pointer to Large Communites strucutre then
101 create a new Large Communities structure by uniq and sort each
102 Large Communities value. */
103 struct lcommunity *lcommunity_uniq_sort(struct lcommunity *lcom)
104 {
105 int i;
106 struct lcommunity *new;
107 struct lcommunity_val *lval;
108
109 if (!lcom)
110 return NULL;
111
112 new = lcommunity_new();
113
114 for (i = 0; i < lcom->size; i++) {
115 lval = (struct lcommunity_val *)(lcom->val
116 + (i * LCOMMUNITY_SIZE));
117 lcommunity_add_val(new, lval);
118 }
119 return new;
120 }
121
122 /* Parse Large Communites Attribute in BGP packet. */
123 struct lcommunity *lcommunity_parse(uint8_t *pnt, unsigned short length)
124 {
125 struct lcommunity tmp;
126 struct lcommunity *new;
127
128 /* Length check. */
129 if (length % LCOMMUNITY_SIZE)
130 return NULL;
131
132 /* Prepare tmporary structure for making a new Large Communities
133 Attribute. */
134 tmp.size = length / LCOMMUNITY_SIZE;
135 tmp.val = pnt;
136
137 /* Create a new Large Communities Attribute by uniq and sort each
138 Large Communities value */
139 new = lcommunity_uniq_sort(&tmp);
140
141 return lcommunity_intern(new);
142 }
143
144 /* Duplicate the Large Communities Attribute structure. */
145 struct lcommunity *lcommunity_dup(struct lcommunity *lcom)
146 {
147 struct lcommunity *new;
148
149 new = lcommunity_new();
150 new->size = lcom->size;
151 if (new->size) {
152 new->val = XMALLOC(MTYPE_LCOMMUNITY_VAL, lcom_length(lcom));
153 memcpy(new->val, lcom->val, lcom_length(lcom));
154 } else
155 new->val = NULL;
156 return new;
157 }
158
159 /* Merge two Large Communities Attribute structure. */
160 struct lcommunity *lcommunity_merge(struct lcommunity *lcom1,
161 struct lcommunity *lcom2)
162 {
163 if (lcom1->val)
164 lcom1->val = XREALLOC(MTYPE_LCOMMUNITY_VAL, lcom1->val,
165 lcom_length(lcom1) + lcom_length(lcom2));
166 else
167 lcom1->val = XMALLOC(MTYPE_LCOMMUNITY_VAL,
168 lcom_length(lcom1) + lcom_length(lcom2));
169
170 memcpy(lcom1->val + lcom_length(lcom1), lcom2->val, lcom_length(lcom2));
171 lcom1->size += lcom2->size;
172
173 return lcom1;
174 }
175
176 static void set_lcommunity_string(struct lcommunity *lcom, bool make_json)
177 {
178 int i;
179 int len;
180 char *str_buf;
181 uint8_t *pnt;
182 uint32_t global, local1, local2;
183 json_object *json_lcommunity_list = NULL;
184 json_object *json_string = NULL;
185
186 /* 3 32-bit integers, 2 colons, and a space */
187 #define LCOMMUNITY_STRLEN (10 * 3 + 2 + 1)
188
189 if (!lcom)
190 return;
191
192 if (make_json) {
193 lcom->json = json_object_new_object();
194 json_lcommunity_list = json_object_new_array();
195 }
196
197 if (lcom->size == 0) {
198 str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, 1);
199
200 if (make_json) {
201 json_object_string_add(lcom->json, "string", "");
202 json_object_object_add(lcom->json, "list",
203 json_lcommunity_list);
204 }
205
206 lcom->str = str_buf;
207 return;
208 }
209
210 /* 1 space + lcom->size lcom strings + null terminator */
211 size_t str_buf_sz = (LCOMMUNITY_STRLEN * lcom->size) + 2;
212 str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, str_buf_sz);
213
214 for (i = 0; i < lcom->size; i++) {
215 if (i > 0)
216 strlcat(str_buf, " ", str_buf_sz);
217
218 pnt = lcom->val + (i * LCOMMUNITY_SIZE);
219 pnt = ptr_get_be32(pnt, &global);
220 pnt = ptr_get_be32(pnt, &local1);
221 pnt = ptr_get_be32(pnt, &local2);
222 (void)pnt;
223
224 char lcsb[LCOMMUNITY_STRLEN + 1];
225
226 snprintf(lcsb, sizeof(lcsb), "%u:%u:%u", global, local1,
227 local2);
228
229 len = strlcat(str_buf, lcsb, str_buf_sz);
230 assert((unsigned int)len < str_buf_sz);
231
232 if (make_json) {
233 json_string = json_object_new_string(lcsb);
234 json_object_array_add(json_lcommunity_list,
235 json_string);
236 }
237 }
238
239 if (make_json) {
240 json_object_string_add(lcom->json, "string", str_buf);
241 json_object_object_add(lcom->json, "list",
242 json_lcommunity_list);
243 }
244
245 lcom->str = str_buf;
246 }
247
248 /* Intern Large Communities Attribute. */
249 struct lcommunity *lcommunity_intern(struct lcommunity *lcom)
250 {
251 struct lcommunity *find;
252
253 assert(lcom->refcnt == 0);
254
255 find = (struct lcommunity *)hash_get(lcomhash, lcom, hash_alloc_intern);
256
257 if (find != lcom)
258 lcommunity_free(&lcom);
259
260 find->refcnt++;
261
262 if (!find->str)
263 set_lcommunity_string(find, false);
264
265 return find;
266 }
267
268 /* Unintern Large Communities Attribute. */
269 void lcommunity_unintern(struct lcommunity **lcom)
270 {
271 struct lcommunity *ret;
272
273 if ((*lcom)->refcnt)
274 (*lcom)->refcnt--;
275
276 /* Pull off from hash. */
277 if ((*lcom)->refcnt == 0) {
278 /* Large community must be in the hash. */
279 ret = (struct lcommunity *)hash_release(lcomhash, *lcom);
280 assert(ret != NULL);
281
282 lcommunity_free(lcom);
283 }
284 }
285
286 /* Retrun string representation of communities attribute. */
287 char *lcommunity_str(struct lcommunity *lcom, bool make_json)
288 {
289 if (!lcom)
290 return NULL;
291
292 if (make_json && !lcom->json && lcom->str)
293 XFREE(MTYPE_LCOMMUNITY_STR, lcom->str);
294
295 if (!lcom->str)
296 set_lcommunity_string(lcom, make_json);
297
298 return lcom->str;
299 }
300
301 /* Utility function to make hash key. */
302 unsigned int lcommunity_hash_make(const void *arg)
303 {
304 const struct lcommunity *lcom = arg;
305 int size = lcom_length(lcom);
306
307 return jhash(lcom->val, size, 0xab125423);
308 }
309
310 /* Compare two Large Communities Attribute structure. */
311 bool lcommunity_cmp(const void *arg1, const void *arg2)
312 {
313 const struct lcommunity *lcom1 = arg1;
314 const struct lcommunity *lcom2 = arg2;
315
316 if (lcom1 == NULL && lcom2 == NULL)
317 return true;
318
319 if (lcom1 == NULL || lcom2 == NULL)
320 return false;
321
322 return (lcom1->size == lcom2->size
323 && memcmp(lcom1->val, lcom2->val, lcom_length(lcom1)) == 0);
324 }
325
326 /* Return communities hash. */
327 struct hash *lcommunity_hash(void)
328 {
329 return lcomhash;
330 }
331
332 /* Initialize Large Comminities related hash. */
333 void lcommunity_init(void)
334 {
335 lcomhash = hash_create(lcommunity_hash_make, lcommunity_cmp,
336 "BGP lcommunity hash");
337 }
338
339 void lcommunity_finish(void)
340 {
341 hash_clean(lcomhash, (void (*)(void *))lcommunity_hash_free);
342 hash_free(lcomhash);
343 lcomhash = NULL;
344 }
345
346 /* Large Communities token enum. */
347 enum lcommunity_token {
348 lcommunity_token_unknown = 0,
349 lcommunity_token_val,
350 };
351
352 /* Get next Large Communities token from the string. */
353 static const char *lcommunity_gettoken(const char *str,
354 struct lcommunity_val *lval,
355 enum lcommunity_token *token)
356 {
357 const char *p = str;
358
359 /* Skip white space. */
360 while (isspace((unsigned char)*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((unsigned char)*p)) {
371 int separator = 0;
372 int digit = 0;
373 uint32_t globaladmin = 0;
374 uint32_t localdata1 = 0;
375 uint32_t localdata2 = 0;
376
377 while (isdigit((unsigned char)*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++;
398 }
399 if (!digit) {
400 *token = lcommunity_token_unknown;
401 return NULL;
402 }
403
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;
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 */
434 struct lcommunity *lcommunity_str2com(const char *str)
435 {
436 struct lcommunity *lcom = NULL;
437 enum lcommunity_token token = lcommunity_token_unknown;
438 struct lcommunity_val lval;
439
440 do {
441 str = lcommunity_gettoken(str, &lval, &token);
442 switch (token) {
443 case lcommunity_token_val:
444 if (lcom == NULL)
445 lcom = lcommunity_new();
446 lcommunity_add_val(lcom, &lval);
447 break;
448 case lcommunity_token_unknown:
449 default:
450 if (lcom)
451 lcommunity_free(&lcom);
452 return NULL;
453 }
454 } while (str);
455
456 return lcom;
457 }
458
459 int lcommunity_include(struct lcommunity *lcom, uint8_t *ptr)
460 {
461 int i;
462 uint8_t *lcom_ptr;
463
464 for (i = 0; i < lcom->size; i++) {
465 lcom_ptr = lcom->val + (i * LCOMMUNITY_SIZE);
466 if (memcmp(ptr, lcom_ptr, LCOMMUNITY_SIZE) == 0)
467 return 1;
468 }
469 return 0;
470 }
471
472 int lcommunity_match(const struct lcommunity *lcom1,
473 const struct lcommunity *lcom2)
474 {
475 int i = 0;
476 int j = 0;
477
478 if (lcom1 == NULL && lcom2 == NULL)
479 return 1;
480
481 if (lcom1 == NULL || lcom2 == NULL)
482 return 0;
483
484 if (lcom1->size < lcom2->size)
485 return 0;
486
487 /* Every community on com2 needs to be on com1 for this to match */
488 while (i < lcom1->size && j < lcom2->size) {
489 if (memcmp(lcom1->val + (i * LCOMMUNITY_SIZE),
490 lcom2->val + (j * LCOMMUNITY_SIZE), LCOMMUNITY_SIZE)
491 == 0)
492 j++;
493 i++;
494 }
495
496 if (j == lcom2->size)
497 return 1;
498 else
499 return 0;
500 }
501
502 /* Delete one lcommunity. */
503 void lcommunity_del_val(struct lcommunity *lcom, uint8_t *ptr)
504 {
505 int i = 0;
506 int c = 0;
507
508 if (!lcom->val)
509 return;
510
511 while (i < lcom->size) {
512 if (memcmp(lcom->val + i * LCOMMUNITY_SIZE, ptr,
513 LCOMMUNITY_SIZE)
514 == 0) {
515 c = lcom->size - i - 1;
516
517 if (c > 0)
518 memmove(lcom->val + i * LCOMMUNITY_SIZE,
519 lcom->val + (i + 1) * LCOMMUNITY_SIZE,
520 c * LCOMMUNITY_SIZE);
521
522 lcom->size--;
523
524 if (lcom->size > 0)
525 lcom->val =
526 XREALLOC(MTYPE_LCOMMUNITY_VAL,
527 lcom->val, lcom_length(lcom));
528 else {
529 XFREE(MTYPE_LCOMMUNITY_VAL, lcom->val);
530 }
531 return;
532 }
533 i++;
534 }
535 }
536
537 static struct lcommunity *bgp_aggr_lcommunity_lookup(
538 struct bgp_aggregate *aggregate,
539 struct lcommunity *lcommunity)
540 {
541 return hash_lookup(aggregate->lcommunity_hash, lcommunity);
542 }
543
544 static void *bgp_aggr_lcommunty_hash_alloc(void *p)
545 {
546 struct lcommunity *ref = (struct lcommunity *)p;
547 struct lcommunity *lcommunity = NULL;
548
549 lcommunity = lcommunity_dup(ref);
550 return lcommunity;
551 }
552
553 static void bgp_aggr_lcommunity_prepare(struct hash_backet *hb, void *arg)
554 {
555 struct lcommunity *hb_lcommunity = hb->data;
556 struct lcommunity **aggr_lcommunity = arg;
557
558 if (*aggr_lcommunity)
559 *aggr_lcommunity = lcommunity_merge(*aggr_lcommunity,
560 hb_lcommunity);
561 else
562 *aggr_lcommunity = lcommunity_dup(hb_lcommunity);
563 }
564
565 void bgp_aggr_lcommunity_remove(void *arg)
566 {
567 struct lcommunity *lcommunity = arg;
568
569 lcommunity_free(&lcommunity);
570 }
571
572 void bgp_compute_aggregate_lcommunity(struct bgp_aggregate *aggregate,
573 struct lcommunity *lcommunity)
574 {
575
576 bgp_compute_aggregate_lcommunity_hash(aggregate, lcommunity);
577 bgp_compute_aggregate_lcommunity_val(aggregate);
578 }
579
580 void bgp_compute_aggregate_lcommunity_hash(struct bgp_aggregate *aggregate,
581 struct lcommunity *lcommunity)
582 {
583
584 struct lcommunity *aggr_lcommunity = NULL;
585
586 if ((aggregate == NULL) || (lcommunity == NULL))
587 return;
588
589 /* Create hash if not already created.
590 */
591 if (aggregate->lcommunity_hash == NULL)
592 aggregate->lcommunity_hash = hash_create(
593 lcommunity_hash_make, lcommunity_cmp,
594 "BGP Aggregator lcommunity hash");
595
596 aggr_lcommunity = bgp_aggr_lcommunity_lookup(aggregate, lcommunity);
597 if (aggr_lcommunity == NULL) {
598 /* Insert lcommunity into hash.
599 */
600 aggr_lcommunity = hash_get(aggregate->lcommunity_hash,
601 lcommunity,
602 bgp_aggr_lcommunty_hash_alloc);
603 }
604
605 /* Increment reference counter.
606 */
607 aggr_lcommunity->refcnt++;
608 }
609
610 void bgp_compute_aggregate_lcommunity_val(struct bgp_aggregate *aggregate)
611 {
612 struct lcommunity *lcommerge = NULL;
613
614 if (aggregate == NULL)
615 return;
616
617 /* Re-compute aggregate's lcommunity.
618 */
619 if (aggregate->lcommunity)
620 lcommunity_free(&aggregate->lcommunity);
621 if (aggregate->lcommunity_hash &&
622 aggregate->lcommunity_hash->count) {
623 hash_iterate(aggregate->lcommunity_hash,
624 bgp_aggr_lcommunity_prepare,
625 &aggregate->lcommunity);
626 lcommerge = aggregate->lcommunity;
627 aggregate->lcommunity = lcommunity_uniq_sort(lcommerge);
628 if (lcommerge)
629 lcommunity_free(&lcommerge);
630 }
631 }
632
633 void bgp_remove_lcommunity_from_aggregate(struct bgp_aggregate *aggregate,
634 struct lcommunity *lcommunity)
635 {
636 struct lcommunity *aggr_lcommunity = NULL;
637 struct lcommunity *ret_lcomm = NULL;
638
639 if ((!aggregate)
640 || (!aggregate->lcommunity_hash)
641 || (!lcommunity))
642 return;
643
644 /* Look-up the lcommunity in the hash.
645 */
646 aggr_lcommunity = bgp_aggr_lcommunity_lookup(aggregate, lcommunity);
647 if (aggr_lcommunity) {
648 aggr_lcommunity->refcnt--;
649
650 if (aggr_lcommunity->refcnt == 0) {
651 ret_lcomm = hash_release(aggregate->lcommunity_hash,
652 aggr_lcommunity);
653 lcommunity_free(&ret_lcomm);
654
655 bgp_compute_aggregate_lcommunity_val(aggregate);
656
657 }
658 }
659 }
660
661 void bgp_remove_lcomm_from_aggregate_hash(struct bgp_aggregate *aggregate,
662 struct lcommunity *lcommunity)
663 {
664 struct lcommunity *aggr_lcommunity = NULL;
665 struct lcommunity *ret_lcomm = NULL;
666
667 if ((!aggregate)
668 || (!aggregate->lcommunity_hash)
669 || (!lcommunity))
670 return;
671
672 /* Look-up the lcommunity in the hash.
673 */
674 aggr_lcommunity = bgp_aggr_lcommunity_lookup(aggregate, lcommunity);
675 if (aggr_lcommunity) {
676 aggr_lcommunity->refcnt--;
677
678 if (aggr_lcommunity->refcnt == 0) {
679 ret_lcomm = hash_release(aggregate->lcommunity_hash,
680 aggr_lcommunity);
681 lcommunity_free(&ret_lcomm);
682 }
683 }
684 }