blob: cc67e124527f646e1257b5237d54da262080b3d2 [file] [log] [blame]
Job Snijders3334bab2017-01-20 14:47:12 +00001/* 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 for (i = 0; i < lcom->size; i++) {
426 lcom_ptr = lcom->val + (i * LCOMMUNITY_SIZE);
427 if (memcmp (ptr, lcom_ptr, LCOMMUNITY_SIZE) == 0)
428 return 1;
429 }
430 return 0;
431}
432
433/* Convert large community attribute to string.
434 The large coms will be in 65535:65531:0 format.
435*/
436char *
437lcommunity_lcom2str (struct lcommunity *lcom, int format)
438{
439 int i;
440 u_int8_t *pnt;
441#define LCOMMUNITY_STR_DEFAULT_LEN 40
442 int str_size;
443 int str_pnt;
444 char *str_buf;
445 int len = 0;
446 int first = 1;
447 u_int32_t globaladmin, localdata1, localdata2;
448
449 if (lcom->size == 0)
450 {
451 str_buf = XMALLOC (MTYPE_LCOMMUNITY_STR, 1);
452 str_buf[0] = '\0';
453 return str_buf;
454 }
455
456 /* Prepare buffer. */
457 str_buf = XMALLOC (MTYPE_LCOMMUNITY_STR, LCOMMUNITY_STR_DEFAULT_LEN + 1);
458 str_size = LCOMMUNITY_STR_DEFAULT_LEN + 1;
459 str_pnt = 0;
460
461 for (i = 0; i < lcom->size; i++)
462 {
463 /* Make it sure size is enough. */
464 while (str_pnt + LCOMMUNITY_STR_DEFAULT_LEN >= str_size)
465 {
466 str_size *= 2;
467 str_buf = XREALLOC (MTYPE_LCOMMUNITY_STR, str_buf, str_size);
468 }
469
470 /* Space between each value. */
471 if (! first)
472 str_buf[str_pnt++] = ' ';
473
474 pnt = lcom->val + (i * 12);
475
476 globaladmin = (*pnt++ << 24);
477 globaladmin |= (*pnt++ << 16);
478 globaladmin |= (*pnt++ << 8);
479 globaladmin |= (*pnt++);
480
481 localdata1 = (*pnt++ << 24);
482 localdata1 |= (*pnt++ << 16);
483 localdata1 |= (*pnt++ << 8);
484 localdata1 |= (*pnt++);
485
486 localdata2 = (*pnt++ << 24);
487 localdata2 |= (*pnt++ << 16);
488 localdata2 |= (*pnt++ << 8);
489 localdata2 |= (*pnt++);
490
491 len = sprintf( str_buf + str_pnt, "%u:%u:%u", globaladmin,
492 localdata1, localdata2);
493 str_pnt += len;
494 first = 0;
495 }
496 return str_buf;
497}
498
499int
500lcommunity_match (const struct lcommunity *lcom1,
501 const struct lcommunity *lcom2)
502{
503 int i = 0;
504 int j = 0;
505
506 if (lcom1 == NULL && lcom2 == NULL)
507 return 1;
508
509 if (lcom1 == NULL || lcom2 == NULL)
510 return 0;
511
512 if (lcom1->size < lcom2->size)
513 return 0;
514
515 /* Every community on com2 needs to be on com1 for this to match */
516 while (i < lcom1->size && j < lcom2->size)
517 {
518 if (memcmp (lcom1->val + (i*12), lcom2->val + (j*12), LCOMMUNITY_SIZE) == 0)
519 j++;
520 i++;
521 }
522
523 if (j == lcom2->size)
524 return 1;
525 else
526 return 0;
527}
528
529/* Delete one lcommunity. */
530void
531lcommunity_del_val (struct lcommunity *lcom, u_char *ptr)
532{
533 int i = 0;
534 int c = 0;
535
536 if (! lcom->val)
537 return;
538
539 while (i < lcom->size)
540 {
541 if (memcmp (lcom->val + i*LCOMMUNITY_SIZE, ptr, LCOMMUNITY_SIZE) == 0)
542 {
543 c = lcom->size -i -1;
544
545 if (c > 0)
546 memmove (lcom->val + i*LCOMMUNITY_SIZE, lcom->val + (i + 1)*LCOMMUNITY_SIZE, c * LCOMMUNITY_SIZE);
547
548 lcom->size--;
549
550 if (lcom->size > 0)
551 lcom->val = XREALLOC (MTYPE_COMMUNITY_VAL, lcom->val,
552 lcom_length (lcom));
553 else
554 {
555 XFREE (MTYPE_COMMUNITY_VAL, lcom->val);
556 lcom->val = NULL;
557 }
558 return;
559 }
560 i++;
561 }
562}