]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_lcommunity.c
Support for BGP Large Communities
[mirror_frr.git] / bgpd / bgp_lcommunity.c
CommitLineData
57d187bc
JS
1/* BGP Large Communities Attribute
2
3Copyright (C) 2016 Keyur Patel <keyur@arrcus.com>
4
5This file is part of GNU Zebra.
6
7GNU Zebra is free software; you can redistribute it and/or modify it
8under the terms of the GNU General Public License as published by the
9Free Software Foundation; either version 2, or (at your option) any
10later version.
11
12GNU Zebra is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Zebra; see the file COPYING. If not, write to the Free
19Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
2002111-1307, USA. */
21
22#include <zebra.h>
23
24#include "hash.h"
25#include "memory.h"
26#include "prefix.h"
27#include "command.h"
28#include "filter.h"
29
30#include "bgpd/bgpd.h"
31#include "bgpd/bgp_lcommunity.h"
32#include "bgpd/bgp_aspath.h"
33
34/* Hash of community attribute. */
35static struct hash *lcomhash;
36
37/* Allocate a new lcommunities. */
38static struct lcommunity *
39lcommunity_new (void)
40{
41 return (struct lcommunity *) XCALLOC (MTYPE_LCOMMUNITY,
42 sizeof (struct lcommunity));
43}
44
45/* Allocate lcommunities. */
46void
47lcommunity_free (struct lcommunity **lcom)
48{
49 if ((*lcom)->val)
50 XFREE (MTYPE_LCOMMUNITY_VAL, (*lcom)->val);
51 if ((*lcom)->str)
52 XFREE (MTYPE_LCOMMUNITY_STR, (*lcom)->str);
53 XFREE (MTYPE_LCOMMUNITY, *lcom);
54 lcom = NULL;
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. */
62static int
63lcommunity_add_val (struct lcommunity *lcom, struct lcommunity_val *lval)
64{
65 u_int8_t *p;
66 int ret;
67 int c;
68
69 /* When this is fist value, just add it. */
70 if (lcom->val == NULL)
71 {
72 lcom->size++;
73 lcom->val = XMALLOC (MTYPE_LCOMMUNITY_VAL, lcom_length (lcom));
74 memcpy (lcom->val, lval->val, LCOMMUNITY_SIZE);
75 return 1;
76 }
77
78 /* If the value already exists in the structure return 0. */
79 c = 0;
80 for (p = lcom->val; c < lcom->size; p += LCOMMUNITY_SIZE, c++)
81 {
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 = XREALLOC (MTYPE_LCOMMUNITY_VAL, lcom->val, lcom_length (lcom));
92
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);
97
98 return 1;
99}
100
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. */
104struct lcommunity *
105lcommunity_uniq_sort (struct lcommunity *lcom)
106{
107 int i;
108 struct lcommunity *new;
109 struct lcommunity_val *lval;
110
111 if (! lcom)
112 return NULL;
113
114 new = lcommunity_new ();
115
116 for (i = 0; i < lcom->size; i++)
117 {
118 lval = (struct lcommunity_val *) (lcom->val + (i * LCOMMUNITY_SIZE));
119 lcommunity_add_val (new, lval);
120 }
121 return new;
122}
123
124/* Parse Large Communites Attribute in BGP packet. */
125struct lcommunity *
126lcommunity_parse (u_int8_t *pnt, u_short length)
127{
128 struct lcommunity tmp;
129 struct lcommunity *new;
130
131 /* Length check. */
132 if (length % LCOMMUNITY_SIZE)
133 return NULL;
134
135 /* Prepare tmporary structure for making a new Large Communities
136 Attribute. */
137 tmp.size = length / LCOMMUNITY_SIZE;
138 tmp.val = pnt;
139
140 /* Create a new Large Communities Attribute by uniq and sort each
141 Large Communities value */
142 new = lcommunity_uniq_sort (&tmp);
143
144 return lcommunity_intern (new);
145}
146
147/* Duplicate the Large Communities Attribute structure. */
148struct lcommunity *
149lcommunity_dup (struct lcommunity *lcom)
150{
151 struct lcommunity *new;
152
153 new = XCALLOC (MTYPE_LCOMMUNITY, sizeof (struct lcommunity));
154 new->size = lcom->size;
155 if (new->size)
156 {
157 new->val = XMALLOC (MTYPE_LCOMMUNITY_VAL, lcom->size * LCOMMUNITY_SIZE);
158 memcpy (new->val, lcom->val, lcom->size * LCOMMUNITY_SIZE);
159 }
160 else
161 new->val = NULL;
162 return new;
163}
164
165/* Retrun string representation of communities attribute. */
166char *
167lcommunity_str (struct lcommunity *lcom)
168{
169 if (! lcom->str)
170 lcom->str = lcommunity_lcom2str (lcom, LCOMMUNITY_FORMAT_DISPLAY);
171 return lcom->str;
172}
173
174/* Merge two Large Communities Attribute structure. */
175struct lcommunity *
176lcommunity_merge (struct lcommunity *lcom1, struct lcommunity *lcom2)
177{
178 if (lcom1->val)
179 lcom1->val = XREALLOC (MTYPE_LCOMMUNITY_VAL, lcom1->val,
180 (lcom1->size + lcom2->size) * LCOMMUNITY_SIZE);
181 else
182 lcom1->val = XMALLOC (MTYPE_LCOMMUNITY_VAL,
183 (lcom1->size + lcom2->size) * LCOMMUNITY_SIZE);
184
185 memcpy (lcom1->val + (lcom1->size * LCOMMUNITY_SIZE),
186 lcom2->val, lcom2->size * LCOMMUNITY_SIZE);
187 lcom1->size += lcom2->size;
188
189 return lcom1;
190}
191
192/* Intern Large Communities Attribute. */
193struct lcommunity *
194lcommunity_intern (struct lcommunity *lcom)
195{
196 struct lcommunity *find;
197
198 assert (lcom->refcnt == 0);
199
200 find = (struct lcommunity *) hash_get (lcomhash, lcom, hash_alloc_intern);
201
202 if (find != lcom)
203 lcommunity_free (&lcom);
204
205 find->refcnt++;
206
207 if (! find->str)
208 find->str = lcommunity_lcom2str (find, LCOMMUNITY_FORMAT_DISPLAY);
209
210 return find;
211}
212
213/* Unintern Large Communities Attribute. */
214void
215lcommunity_unintern (struct lcommunity **lcom)
216{
217 struct lcommunity *ret;
218
219 if ((*lcom)->refcnt)
220 (*lcom)->refcnt--;
221
222 /* Pull off from hash. */
223 if ((*lcom)->refcnt == 0)
224 {
225 /* Large community must be in the hash. */
226 ret = (struct lcommunity *) hash_release (lcomhash, *lcom);
227 assert (ret != NULL);
228
229 lcommunity_free (lcom);
230 }
231}
232
233/* Utility function to make hash key. */
234unsigned int
235lcommunity_hash_make (void *arg)
236{
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;
241 int c;
242
243 for (c = 0; c < size; c += LCOMMUNITY_SIZE)
244 {
245 key += pnt[c];
246 key += pnt[c + 1];
247 key += pnt[c + 2];
248 key += pnt[c + 3];
249 key += pnt[c + 4];
250 key += pnt[c + 5];
251 key += pnt[c + 6];
252 key += pnt[c + 7];
253 key += pnt[c + 8];
254 key += pnt[c + 9];
255 key += pnt[c + 10];
256 key += pnt[c + 11];
257 }
258
259 return key;
260}
261
262/* Compare two Large Communities Attribute structure. */
263int
264lcommunity_cmp (const void *arg1, const void *arg2)
265{
266 const struct lcommunity *lcom1 = arg1;
267 const struct lcommunity *lcom2 = arg2;
268
269 return (lcom1->size == lcom2->size
270 && memcmp (lcom1->val, lcom2->val, lcom1->size * LCOMMUNITY_SIZE) == 0);
271}
272
273/* Return communities hash. */
274struct hash *
275lcommunity_hash (void)
276{
277 return lcomhash;
278}
279
280/* Initialize Large Comminities related hash. */
281void
282lcommunity_init (void)
283{
284 lcomhash = hash_create (lcommunity_hash_make, lcommunity_cmp);
285}
286
287void
288lcommunity_finish (void)
289{
290 hash_free (lcomhash);
291 lcomhash = NULL;
292}
293
294/* Large Communities token enum. */
295enum lcommunity_token
296{
297 lcommunity_token_unknown = 0,
298 lcommunity_token_val,
299};
300
301/* Get next Large Communities token from the string. */
302static const char *
303lcommunity_gettoken (const char *str, struct lcommunity_val *lval,
304 enum lcommunity_token *token)
305{
306 const char *p = str;
307
308 /* Skip white space. */
309 while (isspace ((int) *p))
310 {
311 p++;
312 str++;
313 }
314
315 /* Check the end of the line. */
316 if (*p == '\0')
317 return NULL;
318
319 /* Community value. */
320 if (isdigit ((int) *p))
321 {
322 int separator = 0;
323 int digit = 0;
324 u_int32_t globaladmin = 0;
325 u_int32_t localdata1 = 0;
326 u_int32_t localdata2 = 0;
327
328 while (isdigit ((int) *p) || *p == ':')
329 {
330 if (*p == ':')
331 {
332 if (separator == 2)
333 {
334 *token = lcommunity_token_unknown;
335 return NULL;
336 }
337 else
338 {
339 separator++;
340 digit = 0;
341 if (separator == 1) {
342 globaladmin = localdata2;
343 } else {
344 localdata1 = localdata2;
345 }
346 localdata2 = 0;
347 }
348 }
349 else
350 {
351 digit = 1;
352 localdata2 *= 10;
353 localdata2 += (*p - '0');
354 }
355 p++;
356 }
357 if (! digit)
358 {
359 *token = lcommunity_token_unknown;
360 return NULL;
361 }
362
363 /*
364 * Copy the large comm.
365 */
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;
378
379 *token = lcommunity_token_val;
380 return p;
381 }
382 *token = lcommunity_token_unknown;
383 return p;
384}
385
386/*
387 Convert string to large community attribute.
388 When type is already known, please specify both str and type.
389
390 When string includes keyword for each large community value.
391 Please specify keyword_included as non-zero value.
392*/
393struct lcommunity *
394lcommunity_str2com (const char *str)
395{
396 struct lcommunity *lcom = NULL;
397 enum lcommunity_token token = lcommunity_token_unknown;
398 struct lcommunity_val lval;
399
400 while ((str = lcommunity_gettoken (str, &lval, &token)))
401 {
402 switch (token)
403 {
404 case lcommunity_token_val:
405 if (lcom == NULL)
406 lcom = lcommunity_new ();
407 lcommunity_add_val (lcom, &lval);
408 break;
409 case lcommunity_token_unknown:
410 default:
411 if (lcom)
412 lcommunity_free (&lcom);
413 return NULL;
414 }
415 }
416 return lcom;
417}
418
419int
420lcommunity_include (struct lcommunity *lcom, u_char *ptr)
421{
422 int i;
423 u_char *lcom_ptr;
424
425 lcom_ptr = lcom->val;
426 for (i = 0; i < lcom->size; i++) {
427 lcom_ptr += (i * LCOMMUNITY_SIZE);
428 if (memcmp (ptr, lcom_ptr, LCOMMUNITY_SIZE) == 0)
429 return 1;
430 }
431 return 0;
432}
433
434/* Convert large community attribute to string.
435 The large coms will be in 65535:65531:0 format.
436*/
437char *
438lcommunity_lcom2str (struct lcommunity *lcom, int format)
439{
440 int i;
441 u_int8_t *pnt;
442#define LCOMMUNITY_STR_DEFAULT_LEN 40
443 int str_size;
444 int str_pnt;
445 char *str_buf;
446 int len = 0;
447 int first = 1;
448 u_int32_t globaladmin, localdata1, localdata2;
449
450 if (lcom->size == 0)
451 {
452 str_buf = XMALLOC (MTYPE_LCOMMUNITY_STR, 1);
453 str_buf[0] = '\0';
454 return str_buf;
455 }
456
457 /* Prepare buffer. */
458 str_buf = XMALLOC (MTYPE_LCOMMUNITY_STR, LCOMMUNITY_STR_DEFAULT_LEN + 1);
459 str_size = LCOMMUNITY_STR_DEFAULT_LEN + 1;
460 str_pnt = 0;
461
462 for (i = 0; i < lcom->size; i++)
463 {
464 /* Make it sure size is enough. */
465 while (str_pnt + LCOMMUNITY_STR_DEFAULT_LEN >= str_size)
466 {
467 str_size *= 2;
468 str_buf = XREALLOC (MTYPE_LCOMMUNITY_STR, str_buf, str_size);
469 }
470
471 /* Space between each value. */
472 if (! first)
473 str_buf[str_pnt++] = ' ';
474
475 pnt = lcom->val + (i * 12);
476
477 globaladmin = (*pnt++ << 24);
478 globaladmin |= (*pnt++ << 16);
479 globaladmin |= (*pnt++ << 8);
480 globaladmin |= (*pnt++);
481
482 localdata1 = (*pnt++ << 24);
483 localdata1 |= (*pnt++ << 16);
484 localdata1 |= (*pnt++ << 8);
485 localdata1 |= (*pnt++);
486
487 localdata2 = (*pnt++ << 24);
488 localdata2 |= (*pnt++ << 16);
489 localdata2 |= (*pnt++ << 8);
490 localdata2 |= (*pnt++);
491
492 len = sprintf( str_buf + str_pnt, "%u:%u:%u", globaladmin,
493 localdata1, localdata2);
494 str_pnt += len;
495 first = 0;
496 }
497 return str_buf;
498}
499
500int
501lcommunity_match (const struct lcommunity *lcom1,
502 const struct lcommunity *lcom2)
503{
504 int i = 0;
505 int j = 0;
506
507 if (lcom1 == NULL && lcom2 == NULL)
508 return 1;
509
510 if (lcom1 == NULL || lcom2 == NULL)
511 return 0;
512
513 if (lcom1->size < lcom2->size)
514 return 0;
515
516 /* Every community on com2 needs to be on com1 for this to match */
517 while (i < lcom1->size && j < lcom2->size)
518 {
519 if (memcmp (lcom1->val + (i*12), lcom2->val + (j*12), LCOMMUNITY_SIZE) == 0)
520 j++;
521 i++;
522 }
523
524 if (j == lcom2->size)
525 return 1;
526 else
527 return 0;
528}
529
530/* Delete one lcommunity. */
531void
532lcommunity_del_val (struct lcommunity *lcom, u_char *ptr)
533{
534 int i = 0;
535 int c = 0;
536
537 if (! lcom->val)
538 return;
539
540 while (i < lcom->size)
541 {
542 if (memcmp (lcom->val + i*LCOMMUNITY_SIZE, ptr, LCOMMUNITY_SIZE) == 0)
543 {
544 c = lcom->size -i -1;
545
546 if (c > 0)
547 memmove (lcom->val + i*LCOMMUNITY_SIZE, lcom->val + (i + 1)*LCOMMUNITY_SIZE, c * LCOMMUNITY_SIZE);
548
549 lcom->size--;
550
551 if (lcom->size > 0)
552 lcom->val = XREALLOC (MTYPE_COMMUNITY_VAL, lcom->val,
553 lcom_length (lcom));
554 else
555 {
556 XFREE (MTYPE_COMMUNITY_VAL, lcom->val);
557 lcom->val = NULL;
558 }
559 return;
560 }
561 i++;
562 }
563}