]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_lcommunity.c
bgpd: Fixed pointer arithmatic miscalculation
[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"
28
29#include "bgpd/bgpd.h"
30#include "bgpd/bgp_lcommunity.h"
31#include "bgpd/bgp_aspath.h"
32
33/* Hash of community attribute. */
34static struct hash *lcomhash;
35
36/* Allocate a new lcommunities. */
d62a17ae 37static struct lcommunity *lcommunity_new(void)
57d187bc 38{
d62a17ae 39 return (struct lcommunity *)XCALLOC(MTYPE_LCOMMUNITY,
40 sizeof(struct lcommunity));
57d187bc
JS
41}
42
43/* Allocate lcommunities. */
d62a17ae 44void lcommunity_free(struct lcommunity **lcom)
57d187bc 45{
d62a17ae 46 if ((*lcom)->val)
47 XFREE(MTYPE_LCOMMUNITY_VAL, (*lcom)->val);
48 if ((*lcom)->str)
49 XFREE(MTYPE_LCOMMUNITY_STR, (*lcom)->str);
50 XFREE(MTYPE_LCOMMUNITY, *lcom);
51 lcom = NULL;
57d187bc
JS
52}
53
d62a17ae 54static void lcommunity_hash_free(struct lcommunity *lcom)
47350fd9 55{
d62a17ae 56 lcommunity_free(&lcom);
47350fd9
DS
57}
58
57d187bc
JS
59/* Add a new Large Communities value to Large Communities
60 Attribute structure. When the value is already exists in the
61 structure, we don't add the value. Newly added value is sorted by
62 numerical order. When the value is added to the structure return 1
63 else return 0. */
d62a17ae 64static int lcommunity_add_val(struct lcommunity *lcom,
65 struct lcommunity_val *lval)
57d187bc 66{
d62a17ae 67 u_int8_t *p;
68 int ret;
69 int c;
70
71 /* When this is fist value, just add it. */
72 if (lcom->val == NULL) {
73 lcom->size++;
74 lcom->val = XMALLOC(MTYPE_LCOMMUNITY_VAL, lcom_length(lcom));
75 memcpy(lcom->val, lval->val, LCOMMUNITY_SIZE);
76 return 1;
77 }
78
79 /* If the value already exists in the structure return 0. */
80 c = 0;
81 for (p = lcom->val; c < lcom->size; p += LCOMMUNITY_SIZE, c++) {
82 ret = memcmp(p, lval->val, LCOMMUNITY_SIZE);
83 if (ret == 0)
84 return 0;
85 if (ret > 0)
86 break;
87 }
88
89 /* Add the value to the structure with numerical sorting. */
90 lcom->size++;
91 lcom->val =
92 XREALLOC(MTYPE_LCOMMUNITY_VAL, lcom->val, lcom_length(lcom));
93
94 memmove(lcom->val + (c + 1) * LCOMMUNITY_SIZE,
95 lcom->val + c * LCOMMUNITY_SIZE,
96 (lcom->size - 1 - c) * LCOMMUNITY_SIZE);
97 memcpy(lcom->val + c * LCOMMUNITY_SIZE, lval->val, LCOMMUNITY_SIZE);
98
99 return 1;
57d187bc
JS
100}
101
102/* This function takes pointer to Large Communites strucutre then
103 create a new Large Communities structure by uniq and sort each
104 Large Communities value. */
d62a17ae 105struct lcommunity *lcommunity_uniq_sort(struct lcommunity *lcom)
57d187bc 106{
d62a17ae 107 int i;
108 struct lcommunity *new;
109 struct lcommunity_val *lval;
57d187bc 110
d62a17ae 111 if (!lcom)
112 return NULL;
57d187bc 113
d62a17ae 114 new = lcommunity_new();
57d187bc 115
d62a17ae 116 for (i = 0; i < lcom->size; i++) {
117 lval = (struct lcommunity_val *)(lcom->val
118 + (i * LCOMMUNITY_SIZE));
119 lcommunity_add_val(new, lval);
120 }
121 return new;
57d187bc
JS
122}
123
124/* Parse Large Communites Attribute in BGP packet. */
d62a17ae 125struct lcommunity *lcommunity_parse(u_int8_t *pnt, u_short length)
57d187bc 126{
d62a17ae 127 struct lcommunity tmp;
128 struct lcommunity *new;
57d187bc 129
d62a17ae 130 /* Length check. */
131 if (length % LCOMMUNITY_SIZE)
132 return NULL;
57d187bc 133
d62a17ae 134 /* Prepare tmporary structure for making a new Large Communities
135 Attribute. */
136 tmp.size = length / LCOMMUNITY_SIZE;
137 tmp.val = pnt;
57d187bc 138
d62a17ae 139 /* Create a new Large Communities Attribute by uniq and sort each
140 Large Communities value */
141 new = lcommunity_uniq_sort(&tmp);
57d187bc 142
d62a17ae 143 return lcommunity_intern(new);
57d187bc
JS
144}
145
146/* Duplicate the Large Communities Attribute structure. */
d62a17ae 147struct lcommunity *lcommunity_dup(struct lcommunity *lcom)
57d187bc 148{
d62a17ae 149 struct lcommunity *new;
150
151 new = XCALLOC(MTYPE_LCOMMUNITY, sizeof(struct lcommunity));
152 new->size = lcom->size;
153 if (new->size) {
79dab4b7
NK
154 new->val = XMALLOC(MTYPE_LCOMMUNITY_VAL, lcom_length(lcom));
155 memcpy(new->val, lcom->val, lcom_length(lcom));
d62a17ae 156 } else
157 new->val = NULL;
158 return new;
57d187bc
JS
159}
160
161/* Retrun string representation of communities attribute. */
d62a17ae 162char *lcommunity_str(struct lcommunity *lcom)
57d187bc 163{
d62a17ae 164 if (!lcom->str)
165 lcom->str =
166 lcommunity_lcom2str(lcom, LCOMMUNITY_FORMAT_DISPLAY);
167 return lcom->str;
57d187bc
JS
168}
169
170/* Merge two Large Communities Attribute structure. */
d62a17ae 171struct lcommunity *lcommunity_merge(struct lcommunity *lcom1,
172 struct lcommunity *lcom2)
57d187bc 173{
d62a17ae 174 if (lcom1->val)
175 lcom1->val =
176 XREALLOC(MTYPE_LCOMMUNITY_VAL, lcom1->val,
79dab4b7 177 lcom_length(lcom1) + lcom_length(lcom2));
d62a17ae 178 else
179 lcom1->val =
180 XMALLOC(MTYPE_LCOMMUNITY_VAL,
79dab4b7 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 u_int8_t *pnt = lcom->val;
234 unsigned int key = 0;
235 int c;
236
237 for (c = 0; c < size; c += LCOMMUNITY_SIZE) {
238 key += pnt[c];
239 key += pnt[c + 1];
240 key += pnt[c + 2];
241 key += pnt[c + 3];
242 key += pnt[c + 4];
243 key += pnt[c + 5];
244 key += pnt[c + 6];
245 key += pnt[c + 7];
246 key += pnt[c + 8];
247 key += pnt[c + 9];
248 key += pnt[c + 10];
249 key += pnt[c + 11];
250 }
251
252 return key;
57d187bc
JS
253}
254
255/* Compare two Large Communities Attribute structure. */
d62a17ae 256int lcommunity_cmp(const void *arg1, const void *arg2)
57d187bc 257{
d62a17ae 258 const struct lcommunity *lcom1 = arg1;
259 const struct lcommunity *lcom2 = arg2;
57d187bc 260
d62a17ae 261 return (lcom1->size == lcom2->size
79dab4b7 262 && memcmp(lcom1->val, lcom2->val, lcom_length(lcom1))
d62a17ae 263 == 0);
57d187bc
JS
264}
265
266/* Return communities hash. */
d62a17ae 267struct hash *lcommunity_hash(void)
57d187bc 268{
d62a17ae 269 return lcomhash;
57d187bc
JS
270}
271
272/* Initialize Large Comminities related hash. */
d62a17ae 273void lcommunity_init(void)
57d187bc 274{
d62a17ae 275 lcomhash = hash_create(lcommunity_hash_make, lcommunity_cmp, NULL);
57d187bc
JS
276}
277
d62a17ae 278void lcommunity_finish(void)
57d187bc 279{
d62a17ae 280 hash_clean(lcomhash, (void (*)(void *))lcommunity_hash_free);
281 hash_free(lcomhash);
282 lcomhash = NULL;
57d187bc
JS
283}
284
285/* Large Communities token enum. */
d62a17ae 286enum lcommunity_token {
287 lcommunity_token_unknown = 0,
288 lcommunity_token_val,
57d187bc
JS
289};
290
291/* Get next Large Communities token from the string. */
d62a17ae 292static const char *lcommunity_gettoken(const char *str,
293 struct lcommunity_val *lval,
294 enum lcommunity_token *token)
57d187bc 295{
d62a17ae 296 const char *p = str;
297
298 /* Skip white space. */
299 while (isspace((int)*p)) {
300 p++;
301 str++;
302 }
303
304 /* Check the end of the line. */
305 if (*p == '\0')
306 return NULL;
307
308 /* Community value. */
309 if (isdigit((int)*p)) {
310 int separator = 0;
311 int digit = 0;
312 u_int32_t globaladmin = 0;
313 u_int32_t localdata1 = 0;
314 u_int32_t localdata2 = 0;
315
316 while (isdigit((int)*p) || *p == ':') {
317 if (*p == ':') {
318 if (separator == 2) {
319 *token = lcommunity_token_unknown;
320 return NULL;
321 } else {
322 separator++;
323 digit = 0;
324 if (separator == 1) {
325 globaladmin = localdata2;
326 } else {
327 localdata1 = localdata2;
328 }
329 localdata2 = 0;
330 }
331 } else {
332 digit = 1;
333 localdata2 *= 10;
334 localdata2 += (*p - '0');
335 }
336 p++;
57d187bc 337 }
d62a17ae 338 if (!digit) {
339 *token = lcommunity_token_unknown;
340 return NULL;
57d187bc 341 }
57d187bc 342
d62a17ae 343 /*
344 * Copy the large comm.
345 */
346 lval->val[0] = (globaladmin >> 24) & 0xff;
347 lval->val[1] = (globaladmin >> 16) & 0xff;
348 lval->val[2] = (globaladmin >> 8) & 0xff;
349 lval->val[3] = globaladmin & 0xff;
350 lval->val[4] = (localdata1 >> 24) & 0xff;
351 lval->val[5] = (localdata1 >> 16) & 0xff;
352 lval->val[6] = (localdata1 >> 8) & 0xff;
353 lval->val[7] = localdata1 & 0xff;
354 lval->val[8] = (localdata2 >> 24) & 0xff;
355 lval->val[9] = (localdata2 >> 16) & 0xff;
356 lval->val[10] = (localdata2 >> 8) & 0xff;
357 lval->val[11] = localdata2 & 0xff;
358
359 *token = lcommunity_token_val;
360 return p;
361 }
362 *token = lcommunity_token_unknown;
363 return p;
57d187bc
JS
364}
365
366/*
367 Convert string to large community attribute.
368 When type is already known, please specify both str and type.
369
370 When string includes keyword for each large community value.
371 Please specify keyword_included as non-zero value.
372*/
d62a17ae 373struct lcommunity *lcommunity_str2com(const char *str)
57d187bc 374{
d62a17ae 375 struct lcommunity *lcom = NULL;
376 enum lcommunity_token token = lcommunity_token_unknown;
377 struct lcommunity_val lval;
378
379 while ((str = lcommunity_gettoken(str, &lval, &token))) {
380 switch (token) {
381 case lcommunity_token_val:
382 if (lcom == NULL)
383 lcom = lcommunity_new();
384 lcommunity_add_val(lcom, &lval);
385 break;
386 case lcommunity_token_unknown:
387 default:
388 if (lcom)
389 lcommunity_free(&lcom);
390 return NULL;
391 }
392 }
393 return lcom;
57d187bc
JS
394}
395
d62a17ae 396int lcommunity_include(struct lcommunity *lcom, u_char *ptr)
57d187bc 397{
d62a17ae 398 int i;
399 u_char *lcom_ptr;
400
d62a17ae 401 for (i = 0; i < lcom->size; i++) {
3ac053e6 402 lcom_ptr = lcom->val + (i * LCOMMUNITY_SIZE);
d62a17ae 403 if (memcmp(ptr, lcom_ptr, LCOMMUNITY_SIZE) == 0)
404 return 1;
405 }
406 return 0;
57d187bc
JS
407}
408
409/* Convert large community attribute to string.
410 The large coms will be in 65535:65531:0 format.
411*/
d62a17ae 412char *lcommunity_lcom2str(struct lcommunity *lcom, int format)
57d187bc 413{
d62a17ae 414 int i;
415 u_int8_t *pnt;
57d187bc 416#define LCOMMUNITY_STR_DEFAULT_LEN 40
d62a17ae 417 int str_size;
418 int str_pnt;
419 char *str_buf;
420 int len = 0;
421 int first = 1;
422 u_int32_t globaladmin, localdata1, localdata2;
423
424 if (lcom->size == 0) {
425 str_buf = XMALLOC(MTYPE_LCOMMUNITY_STR, 1);
426 str_buf[0] = '\0';
427 return str_buf;
57d187bc
JS
428 }
429
d62a17ae 430 /* Prepare buffer. */
431 str_buf = XMALLOC(MTYPE_LCOMMUNITY_STR, LCOMMUNITY_STR_DEFAULT_LEN + 1);
432 str_size = LCOMMUNITY_STR_DEFAULT_LEN + 1;
433 str_pnt = 0;
434
435 for (i = 0; i < lcom->size; i++) {
436 /* Make it sure size is enough. */
437 while (str_pnt + LCOMMUNITY_STR_DEFAULT_LEN >= str_size) {
438 str_size *= 2;
439 str_buf = XREALLOC(MTYPE_LCOMMUNITY_STR, str_buf,
440 str_size);
441 }
442
443 /* Space between each value. */
444 if (!first)
445 str_buf[str_pnt++] = ' ';
446
8ea01902 447 pnt = lcom->val + (i * LCOMMUNITY_SIZE);
d62a17ae 448
449 globaladmin = (*pnt++ << 24);
450 globaladmin |= (*pnt++ << 16);
451 globaladmin |= (*pnt++ << 8);
452 globaladmin |= (*pnt++);
453
454 localdata1 = (*pnt++ << 24);
455 localdata1 |= (*pnt++ << 16);
456 localdata1 |= (*pnt++ << 8);
457 localdata1 |= (*pnt++);
458
459 localdata2 = (*pnt++ << 24);
460 localdata2 |= (*pnt++ << 16);
461 localdata2 |= (*pnt++ << 8);
462 localdata2 |= (*pnt++);
463
464 len = sprintf(str_buf + str_pnt, "%u:%u:%u", globaladmin,
465 localdata1, localdata2);
466 str_pnt += len;
467 first = 0;
468 }
469 return str_buf;
57d187bc
JS
470}
471
d62a17ae 472int lcommunity_match(const struct lcommunity *lcom1,
473 const struct lcommunity *lcom2)
57d187bc 474{
d62a17ae 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) {
8ea01902 489 if (memcmp(lcom1->val + (i * LCOMMUNITY_SIZE), lcom2->val + (j * LCOMMUNITY_SIZE),
d62a17ae 490 LCOMMUNITY_SIZE)
491 == 0)
492 j++;
493 i++;
494 }
495
496 if (j == lcom2->size)
497 return 1;
498 else
499 return 0;
57d187bc
JS
500}
501
502/* Delete one lcommunity. */
d62a17ae 503void lcommunity_del_val(struct lcommunity *lcom, u_char *ptr)
57d187bc 504{
d62a17ae 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_COMMUNITY_VAL, lcom->val,
527 lcom_length(lcom));
528 else {
529 XFREE(MTYPE_COMMUNITY_VAL, lcom->val);
530 lcom->val = NULL;
531 }
532 return;
533 }
534 i++;
57d187bc 535 }
57d187bc 536}