Coverage Report

Created: 2025-08-26 06:20

/src/frr/bgpd/bgp_lcommunity.c
Line
Count
Source (jump to first uncovered line)
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/* BGP Large Communities Attribute
3
 *
4
 * Copyright (C) 2016 Keyur Patel <keyur@arrcus.com>
5
 */
6
7
#include <zebra.h>
8
9
#include "hash.h"
10
#include "memory.h"
11
#include "prefix.h"
12
#include "command.h"
13
#include "filter.h"
14
#include "jhash.h"
15
#include "stream.h"
16
17
#include "bgpd/bgpd.h"
18
#include "bgpd/bgp_lcommunity.h"
19
#include "bgpd/bgp_community_alias.h"
20
#include "bgpd/bgp_aspath.h"
21
22
/* Hash of community attribute. */
23
static struct hash *lcomhash;
24
25
/* Allocate a new lcommunities.  */
26
static struct lcommunity *lcommunity_new(void)
27
12
{
28
12
  return XCALLOC(MTYPE_LCOMMUNITY, sizeof(struct lcommunity));
29
12
}
30
31
/* Allocate lcommunities.  */
32
void lcommunity_free(struct lcommunity **lcom)
33
12
{
34
12
  if (!(*lcom))
35
0
    return;
36
37
12
  XFREE(MTYPE_LCOMMUNITY_VAL, (*lcom)->val);
38
12
  XFREE(MTYPE_LCOMMUNITY_STR, (*lcom)->str);
39
12
  if ((*lcom)->json)
40
0
    json_object_free((*lcom)->json);
41
12
  XFREE(MTYPE_LCOMMUNITY, *lcom);
42
12
}
43
44
static void lcommunity_hash_free(struct lcommunity *lcom)
45
0
{
46
0
  lcommunity_free(&lcom);
47
0
}
48
49
/* Add a new Large Communities value to Large Communities
50
   Attribute structure.  When the value is already exists in the
51
   structure, we don't add the value.  Newly added value is sorted by
52
   numerical order.  When the value is added to the structure return 1
53
   else return 0.  */
54
static bool lcommunity_add_val(struct lcommunity *lcom,
55
             struct lcommunity_val *lval)
56
203
{
57
203
  uint8_t *p;
58
203
  int ret;
59
203
  int c;
60
61
  /* When this is fist value, just add it.  */
62
203
  if (lcom->val == NULL) {
63
12
    lcom->size++;
64
12
    lcom->val = XMALLOC(MTYPE_LCOMMUNITY_VAL, lcom_length(lcom));
65
12
    memcpy(lcom->val, lval->val, LCOMMUNITY_SIZE);
66
12
    return true;
67
12
  }
68
69
  /* If the value already exists in the structure return 0.  */
70
191
  c = 0;
71
1.01k
  for (p = lcom->val; c < lcom->size; p += LCOMMUNITY_SIZE, c++) {
72
999
    ret = memcmp(p, lval->val, LCOMMUNITY_SIZE);
73
999
    if (ret == 0)
74
72
      return false;
75
927
    if (ret > 0)
76
102
      break;
77
927
  }
78
79
  /* Add the value to the structure with numerical sorting.  */
80
119
  lcom->size++;
81
119
  lcom->val =
82
119
    XREALLOC(MTYPE_LCOMMUNITY_VAL, lcom->val, lcom_length(lcom));
83
84
119
  memmove(lcom->val + (c + 1) * LCOMMUNITY_SIZE,
85
119
    lcom->val + c * LCOMMUNITY_SIZE,
86
119
    (lcom->size - 1 - c) * LCOMMUNITY_SIZE);
87
119
  memcpy(lcom->val + c * LCOMMUNITY_SIZE, lval->val, LCOMMUNITY_SIZE);
88
89
119
  return true;
90
191
}
91
92
/* This function takes pointer to Large Communites structure then
93
   create a new Large Communities structure by uniq and sort each
94
   Large Communities value.  */
95
struct lcommunity *lcommunity_uniq_sort(struct lcommunity *lcom)
96
12
{
97
12
  int i;
98
12
  struct lcommunity *new;
99
12
  struct lcommunity_val *lval;
100
101
12
  if (!lcom)
102
0
    return NULL;
103
104
12
  new = lcommunity_new();
105
106
215
  for (i = 0; i < lcom->size; i++) {
107
203
    lval = (struct lcommunity_val *)(lcom->val
108
203
             + (i * LCOMMUNITY_SIZE));
109
203
    lcommunity_add_val(new, lval);
110
203
  }
111
12
  return new;
112
12
}
113
114
/* Parse Large Communites Attribute in BGP packet.  */
115
struct lcommunity *lcommunity_parse(uint8_t *pnt, unsigned short length)
116
14
{
117
14
  struct lcommunity tmp;
118
14
  struct lcommunity *new;
119
120
  /* Length check.  */
121
14
  if (length % LCOMMUNITY_SIZE)
122
2
    return NULL;
123
124
  /* Prepare tmporary structure for making a new Large Communities
125
     Attribute.  */
126
12
  tmp.size = length / LCOMMUNITY_SIZE;
127
12
  tmp.val = pnt;
128
129
  /* Create a new Large Communities Attribute by uniq and sort each
130
     Large Communities value  */
131
12
  new = lcommunity_uniq_sort(&tmp);
132
133
12
  return lcommunity_intern(new);
134
14
}
135
136
/* Duplicate the Large Communities Attribute structure.  */
137
struct lcommunity *lcommunity_dup(struct lcommunity *lcom)
138
0
{
139
0
  struct lcommunity *new;
140
141
0
  new = lcommunity_new();
142
0
  new->size = lcom->size;
143
0
  if (new->size) {
144
0
    new->val = XMALLOC(MTYPE_LCOMMUNITY_VAL, lcom_length(lcom));
145
0
    memcpy(new->val, lcom->val, lcom_length(lcom));
146
0
  } else
147
0
    new->val = NULL;
148
0
  return new;
149
0
}
150
151
/* Merge two Large Communities Attribute structure.  */
152
struct lcommunity *lcommunity_merge(struct lcommunity *lcom1,
153
            struct lcommunity *lcom2)
154
0
{
155
0
  lcom1->val = XREALLOC(MTYPE_LCOMMUNITY_VAL, lcom1->val,
156
0
            lcom_length(lcom1) + lcom_length(lcom2));
157
158
0
  memcpy(lcom1->val + lcom_length(lcom1), lcom2->val, lcom_length(lcom2));
159
0
  lcom1->size += lcom2->size;
160
161
0
  return lcom1;
162
0
}
163
164
static void set_lcommunity_string(struct lcommunity *lcom, bool make_json,
165
          bool translate_alias)
166
12
{
167
12
  int i;
168
12
  int len;
169
12
  char *str_buf;
170
12
  const uint8_t *pnt;
171
12
  uint32_t global, local1, local2;
172
12
  json_object *json_lcommunity_list = NULL;
173
12
  json_object *json_string = NULL;
174
175
  /* 3 32-bit integers, 2 colons, and a space */
176
12
#define LCOMMUNITY_STRLEN (10 * 3 + 2 + 1)
177
178
12
  if (!lcom)
179
0
    return;
180
181
12
  if (make_json) {
182
0
    lcom->json = json_object_new_object();
183
0
    json_lcommunity_list = json_object_new_array();
184
0
  }
185
186
12
  if (lcom->size == 0) {
187
0
    str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, 1);
188
189
0
    if (make_json) {
190
0
      json_object_string_add(lcom->json, "string", "");
191
0
      json_object_object_add(lcom->json, "list",
192
0
                 json_lcommunity_list);
193
0
    }
194
195
0
    lcom->str = str_buf;
196
0
    return;
197
0
  }
198
199
  /* 1 space + lcom->size lcom strings + null terminator */
200
12
  size_t str_buf_sz = (LCOMMUNITY_STRLEN * lcom->size) + 2;
201
12
  str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, str_buf_sz);
202
203
12
  len = 0;
204
143
  for (i = 0; i < lcom->size; i++) {
205
131
    if (i > 0)
206
119
      len = strlcat(str_buf, " ", str_buf_sz);
207
208
131
    pnt = lcom->val + (i * LCOMMUNITY_SIZE);
209
131
    pnt = ptr_get_be32(pnt, &global);
210
131
    pnt = ptr_get_be32(pnt, &local1);
211
131
    pnt = ptr_get_be32(pnt, &local2);
212
131
    (void)pnt;
213
214
131
    char lcsb[LCOMMUNITY_STRLEN + 1];
215
216
131
    snprintf(lcsb, sizeof(lcsb), "%u:%u:%u", global, local1,
217
131
       local2);
218
219
    /*
220
     * Aliases can cause havoc, if the alias length is greater
221
     * than the LCOMMUNITY_STRLEN for a particular item
222
     * then we need to realloc the memory associated
223
     * with the string so that it can fit
224
     */
225
131
    const char *com2alias =
226
131
      translate_alias ? bgp_community2alias(lcsb) : lcsb;
227
131
    size_t individual_len = strlen(com2alias);
228
131
    if (individual_len + len > str_buf_sz) {
229
0
      str_buf_sz = individual_len + len + 1;
230
0
      str_buf = XREALLOC(MTYPE_LCOMMUNITY_STR, str_buf,
231
0
             str_buf_sz);
232
0
    }
233
234
131
    len = strlcat(str_buf, com2alias, str_buf_sz);
235
236
131
    if (make_json) {
237
0
      json_string = json_object_new_string(com2alias);
238
0
      json_object_array_add(json_lcommunity_list,
239
0
                json_string);
240
0
    }
241
131
  }
242
243
12
  if (make_json) {
244
0
    json_object_string_add(lcom->json, "string", str_buf);
245
0
    json_object_object_add(lcom->json, "list",
246
0
               json_lcommunity_list);
247
0
  }
248
249
12
  lcom->str = str_buf;
250
12
}
251
252
/* Intern Large Communities Attribute.  */
253
struct lcommunity *lcommunity_intern(struct lcommunity *lcom)
254
12
{
255
12
  struct lcommunity *find;
256
257
12
  assert(lcom->refcnt == 0);
258
259
12
  find = (struct lcommunity *)hash_get(lcomhash, lcom, hash_alloc_intern);
260
261
12
  if (find != lcom)
262
0
    lcommunity_free(&lcom);
263
264
12
  find->refcnt++;
265
266
12
  if (!find->str)
267
12
    set_lcommunity_string(find, false, true);
268
269
12
  return find;
270
12
}
271
272
/* Unintern Large Communities Attribute.  */
273
void lcommunity_unintern(struct lcommunity **lcom)
274
473
{
275
473
  struct lcommunity *ret;
276
277
473
  if (!*lcom)
278
461
    return;
279
280
12
  if ((*lcom)->refcnt)
281
12
    (*lcom)->refcnt--;
282
283
  /* Pull off from hash.  */
284
12
  if ((*lcom)->refcnt == 0) {
285
    /* Large community must be in the hash.  */
286
12
    ret = (struct lcommunity *)hash_release(lcomhash, *lcom);
287
12
    assert(ret != NULL);
288
289
12
    lcommunity_free(lcom);
290
12
  }
291
12
}
292
293
/* Return string representation of lcommunities attribute. */
294
char *lcommunity_str(struct lcommunity *lcom, bool make_json,
295
         bool translate_alias)
296
0
{
297
0
  if (!lcom)
298
0
    return NULL;
299
300
0
  if (make_json && !lcom->json && lcom->str)
301
0
    XFREE(MTYPE_LCOMMUNITY_STR, lcom->str);
302
303
0
  if (!lcom->str)
304
0
    set_lcommunity_string(lcom, make_json, translate_alias);
305
306
0
  return lcom->str;
307
0
}
308
309
/* Utility function to make hash key.  */
310
unsigned int lcommunity_hash_make(const void *arg)
311
24
{
312
24
  const struct lcommunity *lcom = arg;
313
24
  int size = lcom_length(lcom);
314
315
24
  return jhash(lcom->val, size, 0xab125423);
316
24
}
317
318
/* Compare two Large Communities Attribute structure.  */
319
bool lcommunity_cmp(const void *arg1, const void *arg2)
320
12
{
321
12
  const struct lcommunity *lcom1 = arg1;
322
12
  const struct lcommunity *lcom2 = arg2;
323
324
12
  if (lcom1 == NULL && lcom2 == NULL)
325
0
    return true;
326
327
12
  if (lcom1 == NULL || lcom2 == NULL)
328
0
    return false;
329
330
12
  return (lcom1->size == lcom2->size
331
12
    && memcmp(lcom1->val, lcom2->val, lcom_length(lcom1)) == 0);
332
12
}
333
334
/* Return communities hash.  */
335
struct hash *lcommunity_hash(void)
336
0
{
337
0
  return lcomhash;
338
0
}
339
340
/* Initialize Large Comminities related hash. */
341
void lcommunity_init(void)
342
1
{
343
1
  lcomhash = hash_create(lcommunity_hash_make, lcommunity_cmp,
344
1
             "BGP lcommunity hash");
345
1
}
346
347
void lcommunity_finish(void)
348
0
{
349
0
  hash_clean_and_free(&lcomhash, (void (*)(void *))lcommunity_hash_free);
350
0
}
351
352
/* Get next Large Communities token from the string.
353
 * Assumes str is space-delimeted and describes 0 or more
354
 * valid large communities
355
 */
356
static const char *lcommunity_gettoken(const char *str,
357
               struct lcommunity_val *lval)
358
0
{
359
0
  const char *p = str;
360
361
  /* Skip white space. */
362
0
  while (isspace((unsigned char)*p)) {
363
0
    p++;
364
0
    str++;
365
0
  }
366
367
  /* Check the end of the line. */
368
0
  if (*p == '\0')
369
0
    return NULL;
370
371
  /* Community value. */
372
0
  int separator = 0;
373
0
  int digit = 0;
374
0
  uint32_t globaladmin = 0;
375
0
  uint32_t localdata1 = 0;
376
0
  uint32_t localdata2 = 0;
377
378
0
  while (*p && *p != ' ') {
379
    /* large community valid chars */
380
0
    assert(isdigit((unsigned char)*p) || *p == ':');
381
382
0
    if (*p == ':') {
383
0
      separator++;
384
0
      digit = 0;
385
0
      if (separator == 1) {
386
0
        globaladmin = localdata2;
387
0
      } else {
388
0
        localdata1 = localdata2;
389
0
      }
390
0
      localdata2 = 0;
391
0
    } else {
392
0
      digit = 1;
393
      /* left shift the accumulated value and add current
394
       * digit
395
       */
396
0
      localdata2 *= 10;
397
0
      localdata2 += (*p - '0');
398
0
    }
399
0
    p++;
400
0
  }
401
402
  /* Assert str was a valid large community */
403
0
  assert(separator == 2 && digit == 1);
404
405
  /*
406
   * Copy the large comm.
407
   */
408
0
  lval->val[0] = (globaladmin >> 24) & 0xff;
409
0
  lval->val[1] = (globaladmin >> 16) & 0xff;
410
0
  lval->val[2] = (globaladmin >> 8) & 0xff;
411
0
  lval->val[3] = globaladmin & 0xff;
412
0
  lval->val[4] = (localdata1 >> 24) & 0xff;
413
0
  lval->val[5] = (localdata1 >> 16) & 0xff;
414
0
  lval->val[6] = (localdata1 >> 8) & 0xff;
415
0
  lval->val[7] = localdata1 & 0xff;
416
0
  lval->val[8] = (localdata2 >> 24) & 0xff;
417
0
  lval->val[9] = (localdata2 >> 16) & 0xff;
418
0
  lval->val[10] = (localdata2 >> 8) & 0xff;
419
0
  lval->val[11] = localdata2 & 0xff;
420
421
0
  return p;
422
0
}
423
424
/*
425
  Convert string to large community attribute.
426
  When type is already known, please specify both str and type.
427
428
  When string includes keyword for each large community value.
429
  Please specify keyword_included as non-zero value.
430
*/
431
struct lcommunity *lcommunity_str2com(const char *str)
432
0
{
433
0
  struct lcommunity *lcom = NULL;
434
0
  struct lcommunity_val lval;
435
436
0
  if (!lcommunity_list_valid(str, LARGE_COMMUNITY_LIST_STANDARD))
437
0
    return NULL;
438
439
0
  do {
440
0
    str = lcommunity_gettoken(str, &lval);
441
0
    if (lcom == NULL)
442
0
      lcom = lcommunity_new();
443
0
    lcommunity_add_val(lcom, &lval);
444
0
  } while (str);
445
446
0
  return lcom;
447
0
}
448
449
bool lcommunity_include(struct lcommunity *lcom, uint8_t *ptr)
450
0
{
451
0
  int i;
452
0
  uint8_t *lcom_ptr;
453
454
0
  for (i = 0; i < lcom->size; i++) {
455
0
    lcom_ptr = lcom->val + (i * LCOMMUNITY_SIZE);
456
0
    if (memcmp(ptr, lcom_ptr, LCOMMUNITY_SIZE) == 0)
457
0
      return true;
458
0
  }
459
0
  return false;
460
0
}
461
462
bool lcommunity_match(const struct lcommunity *lcom1,
463
          const struct lcommunity *lcom2)
464
0
{
465
0
  int i = 0;
466
0
  int j = 0;
467
468
0
  if (lcom1 == NULL && lcom2 == NULL)
469
0
    return true;
470
471
0
  if (lcom1 == NULL || lcom2 == NULL)
472
0
    return false;
473
474
0
  if (lcom1->size < lcom2->size)
475
0
    return false;
476
477
  /* Every community on com2 needs to be on com1 for this to match */
478
0
  while (i < lcom1->size && j < lcom2->size) {
479
0
    if (memcmp(lcom1->val + (i * LCOMMUNITY_SIZE),
480
0
         lcom2->val + (j * LCOMMUNITY_SIZE), LCOMMUNITY_SIZE)
481
0
        == 0)
482
0
      j++;
483
0
    i++;
484
0
  }
485
486
0
  if (j == lcom2->size)
487
0
    return true;
488
0
  else
489
0
    return false;
490
0
}
491
492
/* Delete one lcommunity. */
493
void lcommunity_del_val(struct lcommunity *lcom, uint8_t *ptr)
494
0
{
495
0
  int i = 0;
496
0
  int c = 0;
497
498
0
  if (!lcom->val)
499
0
    return;
500
501
0
  while (i < lcom->size) {
502
0
    if (memcmp(lcom->val + i * LCOMMUNITY_SIZE, ptr,
503
0
         LCOMMUNITY_SIZE)
504
0
        == 0) {
505
0
      c = lcom->size - i - 1;
506
507
0
      if (c > 0)
508
0
        memmove(lcom->val + i * LCOMMUNITY_SIZE,
509
0
          lcom->val + (i + 1) * LCOMMUNITY_SIZE,
510
0
          c * LCOMMUNITY_SIZE);
511
512
0
      lcom->size--;
513
514
0
      if (lcom->size > 0)
515
0
        lcom->val =
516
0
          XREALLOC(MTYPE_LCOMMUNITY_VAL,
517
0
             lcom->val, lcom_length(lcom));
518
0
      else {
519
0
        XFREE(MTYPE_LCOMMUNITY_VAL, lcom->val);
520
0
      }
521
0
      return;
522
0
    }
523
0
    i++;
524
0
  }
525
0
}
526
527
static struct lcommunity *bgp_aggr_lcommunity_lookup(
528
            struct bgp_aggregate *aggregate,
529
            struct lcommunity *lcommunity)
530
0
{
531
0
  return hash_lookup(aggregate->lcommunity_hash, lcommunity);
532
0
}
533
534
static void *bgp_aggr_lcommunty_hash_alloc(void *p)
535
0
{
536
0
  struct lcommunity *ref = (struct lcommunity *)p;
537
0
  struct lcommunity *lcommunity = NULL;
538
539
0
  lcommunity = lcommunity_dup(ref);
540
0
  return lcommunity;
541
0
}
542
543
static void bgp_aggr_lcommunity_prepare(struct hash_bucket *hb, void *arg)
544
0
{
545
0
  struct lcommunity *hb_lcommunity = hb->data;
546
0
  struct lcommunity **aggr_lcommunity = arg;
547
548
0
  if (*aggr_lcommunity)
549
0
    *aggr_lcommunity = lcommunity_merge(*aggr_lcommunity,
550
0
                hb_lcommunity);
551
0
  else
552
0
    *aggr_lcommunity = lcommunity_dup(hb_lcommunity);
553
0
}
554
555
void bgp_aggr_lcommunity_remove(void *arg)
556
0
{
557
0
  struct lcommunity *lcommunity = arg;
558
559
0
  lcommunity_free(&lcommunity);
560
0
}
561
562
void bgp_compute_aggregate_lcommunity(struct bgp_aggregate *aggregate,
563
              struct lcommunity *lcommunity)
564
0
{
565
566
0
  bgp_compute_aggregate_lcommunity_hash(aggregate, lcommunity);
567
0
  bgp_compute_aggregate_lcommunity_val(aggregate);
568
0
}
569
570
void bgp_compute_aggregate_lcommunity_hash(struct bgp_aggregate *aggregate,
571
             struct lcommunity *lcommunity)
572
0
{
573
574
0
  struct lcommunity *aggr_lcommunity = NULL;
575
576
0
  if ((aggregate == NULL) || (lcommunity == NULL))
577
0
    return;
578
579
  /* Create hash if not already created.
580
   */
581
0
  if (aggregate->lcommunity_hash == NULL)
582
0
    aggregate->lcommunity_hash = hash_create(
583
0
          lcommunity_hash_make, lcommunity_cmp,
584
0
          "BGP Aggregator lcommunity hash");
585
586
0
  aggr_lcommunity = bgp_aggr_lcommunity_lookup(aggregate, lcommunity);
587
0
  if (aggr_lcommunity == NULL) {
588
    /* Insert lcommunity into hash.
589
     */
590
0
    aggr_lcommunity = hash_get(aggregate->lcommunity_hash,
591
0
             lcommunity,
592
0
             bgp_aggr_lcommunty_hash_alloc);
593
0
  }
594
595
  /* Increment reference counter.
596
   */
597
0
  aggr_lcommunity->refcnt++;
598
0
}
599
600
void bgp_compute_aggregate_lcommunity_val(struct bgp_aggregate *aggregate)
601
0
{
602
0
  struct lcommunity *lcommerge = NULL;
603
604
0
  if (aggregate == NULL)
605
0
    return;
606
607
  /* Re-compute aggregate's lcommunity.
608
   */
609
0
  if (aggregate->lcommunity)
610
0
    lcommunity_free(&aggregate->lcommunity);
611
0
  if (aggregate->lcommunity_hash &&
612
0
      aggregate->lcommunity_hash->count) {
613
0
    hash_iterate(aggregate->lcommunity_hash,
614
0
           bgp_aggr_lcommunity_prepare,
615
0
           &aggregate->lcommunity);
616
0
    lcommerge = aggregate->lcommunity;
617
0
    aggregate->lcommunity = lcommunity_uniq_sort(lcommerge);
618
0
    if (lcommerge)
619
0
      lcommunity_free(&lcommerge);
620
0
  }
621
0
}
622
623
void bgp_remove_lcommunity_from_aggregate(struct bgp_aggregate *aggregate,
624
            struct lcommunity *lcommunity)
625
0
{
626
0
  struct lcommunity *aggr_lcommunity = NULL;
627
0
  struct lcommunity *ret_lcomm = NULL;
628
629
0
  if ((!aggregate)
630
0
      || (!aggregate->lcommunity_hash)
631
0
      || (!lcommunity))
632
0
    return;
633
634
  /* Look-up the lcommunity in the hash.
635
   */
636
0
  aggr_lcommunity = bgp_aggr_lcommunity_lookup(aggregate, lcommunity);
637
0
  if (aggr_lcommunity) {
638
0
    aggr_lcommunity->refcnt--;
639
640
0
    if (aggr_lcommunity->refcnt == 0) {
641
0
      ret_lcomm = hash_release(aggregate->lcommunity_hash,
642
0
             aggr_lcommunity);
643
0
      lcommunity_free(&ret_lcomm);
644
645
0
      bgp_compute_aggregate_lcommunity_val(aggregate);
646
647
0
    }
648
0
  }
649
0
}
650
651
void bgp_remove_lcomm_from_aggregate_hash(struct bgp_aggregate *aggregate,
652
            struct lcommunity *lcommunity)
653
0
{
654
0
  struct lcommunity *aggr_lcommunity = NULL;
655
0
  struct lcommunity *ret_lcomm = NULL;
656
657
0
  if ((!aggregate)
658
0
      || (!aggregate->lcommunity_hash)
659
0
      || (!lcommunity))
660
0
    return;
661
662
  /* Look-up the lcommunity in the hash.
663
   */
664
0
  aggr_lcommunity = bgp_aggr_lcommunity_lookup(aggregate, lcommunity);
665
0
  if (aggr_lcommunity) {
666
0
    aggr_lcommunity->refcnt--;
667
668
0
    if (aggr_lcommunity->refcnt == 0) {
669
0
      ret_lcomm = hash_release(aggregate->lcommunity_hash,
670
0
             aggr_lcommunity);
671
0
      lcommunity_free(&ret_lcomm);
672
0
    }
673
0
  }
674
0
}