source: src/router/quagga/bgpd/bgp_lcommunity.c @ 31640

Last change on this file since 31640 was 31640, checked in by brainslayer, 3 months ago

update quagga

File size: 12.9 KB
Line 
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  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}
Note: See TracBrowser for help on using the repository browser.