Coverage Report

Created: 2025-07-14 06:48

/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
10
{
28
10
  return XCALLOC(MTYPE_LCOMMUNITY, sizeof(struct lcommunity));
29
10
}
30
31
/* Allocate lcommunities.  */
32
void lcommunity_free(struct lcommunity **lcom)
33
10
{
34
10
  if (!(*lcom))
35
0
    return;
36
37
10
  XFREE(MTYPE_LCOMMUNITY_VAL, (*lcom)->val);
38
10
  XFREE(MTYPE_LCOMMUNITY_STR, (*lcom)->str);
39
10
  if ((*lcom)->json)
40
0
    json_object_free((*lcom)->json);
41
10
  XFREE(MTYPE_LCOMMUNITY, *lcom);
42
10
}
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
360
{
57
360
  uint8_t *p;
58
360
  int ret;
59
360
  int c;
60
61
  /* When this is fist value, just add it.  */
62
360
  if (lcom->val == NULL) {
63
10
    lcom->size++;
64
10
    lcom->val = XMALLOC(MTYPE_LCOMMUNITY_VAL, lcom_length(lcom));
65
10
    memcpy(lcom->val, lval->val, LCOMMUNITY_SIZE);
66
10
    return true;
67
10
  }
68
69
  /* If the value already exists in the structure return 0.  */
70
350
  c = 0;
71
2.19k
  for (p = lcom->val; c < lcom->size; p += LCOMMUNITY_SIZE, c++) {
72
2.17k
    ret = memcmp(p, lval->val, LCOMMUNITY_SIZE);
73
2.17k
    if (ret == 0)
74
117
      return false;
75
2.05k
    if (ret > 0)
76
207
      break;
77
2.05k
  }
78
79
  /* Add the value to the structure with numerical sorting.  */
80
233
  lcom->size++;
81
233
  lcom->val =
82
233
    XREALLOC(MTYPE_LCOMMUNITY_VAL, lcom->val, lcom_length(lcom));
83
84
233
  memmove(lcom->val + (c + 1) * LCOMMUNITY_SIZE,
85
233
    lcom->val + c * LCOMMUNITY_SIZE,
86
233
    (lcom->size - 1 - c) * LCOMMUNITY_SIZE);
87
233
  memcpy(lcom->val + c * LCOMMUNITY_SIZE, lval->val, LCOMMUNITY_SIZE);
88
89
233
  return true;
90
350
}
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
10
{
97
10
  int i;
98
10
  struct lcommunity *new;
99
10
  struct lcommunity_val *lval;
100
101
10
  if (!lcom)
102
0
    return NULL;
103
104
10
  new = lcommunity_new();
105
106
370
  for (i = 0; i < lcom->size; i++) {
107
360
    lval = (struct lcommunity_val *)(lcom->val
108
360
             + (i * LCOMMUNITY_SIZE));
109
360
    lcommunity_add_val(new, lval);
110
360
  }
111
10
  return new;
112
10
}
113
114
/* Parse Large Communites Attribute in BGP packet.  */
115
struct lcommunity *lcommunity_parse(uint8_t *pnt, unsigned short length)
116
13
{
117
13
  struct lcommunity tmp;
118
13
  struct lcommunity *new;
119
120
  /* Length check.  */
121
13
  if (length % LCOMMUNITY_SIZE)
122
3
    return NULL;
123
124
  /* Prepare tmporary structure for making a new Large Communities
125
     Attribute.  */
126
10
  tmp.size = length / LCOMMUNITY_SIZE;
127
10
  tmp.val = pnt;
128
129
  /* Create a new Large Communities Attribute by uniq and sort each
130
     Large Communities value  */
131
10
  new = lcommunity_uniq_sort(&tmp);
132
133
10
  return lcommunity_intern(new);
134
13
}
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
10
{
167
10
  int i;
168
10
  int len;
169
10
  char *str_buf;
170
10
  const uint8_t *pnt;
171
10
  uint32_t global, local1, local2;
172
10
  json_object *json_lcommunity_list = NULL;
173
10
  json_object *json_string = NULL;
174
175
  /* 3 32-bit integers, 2 colons, and a space */
176
10
#define LCOMMUNITY_STRLEN (10 * 3 + 2 + 1)
177
178
10
  if (!lcom)
179
0
    return;
180
181
10
  if (make_json) {
182
0
    lcom->json = json_object_new_object();
183
0
    json_lcommunity_list = json_object_new_array();
184
0
  }
185
186
10
  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
10
  size_t str_buf_sz = (LCOMMUNITY_STRLEN * lcom->size) + 2;
201
10
  str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, str_buf_sz);
202
203
10
  len = 0;
204
253
  for (i = 0; i < lcom->size; i++) {
205
243
    if (i > 0)
206
233
      len = strlcat(str_buf, " ", str_buf_sz);
207
208
243
    pnt = lcom->val + (i * LCOMMUNITY_SIZE);
209
243
    pnt = ptr_get_be32(pnt, &global);
210
243
    pnt = ptr_get_be32(pnt, &local1);
211
243
    pnt = ptr_get_be32(pnt, &local2);
212
243
    (void)pnt;
213
214
243
    char lcsb[LCOMMUNITY_STRLEN + 1];
215
216
243
    snprintf(lcsb, sizeof(lcsb), "%u:%u:%u", global, local1,
217
243
       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
243
    const char *com2alias =
226
243
      translate_alias ? bgp_community2alias(lcsb) : lcsb;
227
243
    size_t individual_len = strlen(com2alias);
228
243
    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
243
    len = strlcat(str_buf, com2alias, str_buf_sz);
235
236
243
    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
243
  }
242
243
10
  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
10
  lcom->str = str_buf;
250
10
}
251
252
/* Intern Large Communities Attribute.  */
253
struct lcommunity *lcommunity_intern(struct lcommunity *lcom)
254
10
{
255
10
  struct lcommunity *find;
256
257
10
  assert(lcom->refcnt == 0);
258
259
10
  find = (struct lcommunity *)hash_get(lcomhash, lcom, hash_alloc_intern);
260
261
10
  if (find != lcom)
262
0
    lcommunity_free(&lcom);
263
264
10
  find->refcnt++;
265
266
10
  if (!find->str)
267
10
    set_lcommunity_string(find, false, true);
268
269
10
  return find;
270
10
}
271
272
/* Unintern Large Communities Attribute.  */
273
void lcommunity_unintern(struct lcommunity **lcom)
274
538
{
275
538
  struct lcommunity *ret;
276
277
538
  if (!*lcom)
278
528
    return;
279
280
10
  if ((*lcom)->refcnt)
281
10
    (*lcom)->refcnt--;
282
283
  /* Pull off from hash.  */
284
10
  if ((*lcom)->refcnt == 0) {
285
    /* Large community must be in the hash.  */
286
10
    ret = (struct lcommunity *)hash_release(lcomhash, *lcom);
287
10
    assert(ret != NULL);
288
289
10
    lcommunity_free(lcom);
290
10
  }
291
10
}
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
20
{
312
20
  const struct lcommunity *lcom = arg;
313
20
  int size = lcom_length(lcom);
314
315
20
  return jhash(lcom->val, size, 0xab125423);
316
20
}
317
318
/* Compare two Large Communities Attribute structure.  */
319
bool lcommunity_cmp(const void *arg1, const void *arg2)
320
10
{
321
10
  const struct lcommunity *lcom1 = arg1;
322
10
  const struct lcommunity *lcom2 = arg2;
323
324
10
  if (lcom1 == NULL && lcom2 == NULL)
325
0
    return true;
326
327
10
  if (lcom1 == NULL || lcom2 == NULL)
328
0
    return false;
329
330
10
  return (lcom1->size == lcom2->size
331
10
    && memcmp(lcom1->val, lcom2->val, lcom_length(lcom1)) == 0);
332
10
}
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
}