Coverage Report

Created: 2025-10-08 06:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/bgpd/bgp_clist.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/* BGP community-list and extcommunity-list.
3
 * Copyright (C) 1999 Kunihiro Ishiguro
4
 */
5
6
#include <zebra.h>
7
8
#include "command.h"
9
#include "prefix.h"
10
#include "memory.h"
11
#include "queue.h"
12
#include "filter.h"
13
#include "stream.h"
14
#include "jhash.h"
15
#include "frrstr.h"
16
17
#include "bgpd/bgpd.h"
18
#include "bgpd/bgp_community.h"
19
#include "bgpd/bgp_ecommunity.h"
20
#include "bgpd/bgp_lcommunity.h"
21
#include "bgpd/bgp_community_alias.h"
22
#include "bgpd/bgp_aspath.h"
23
#include "bgpd/bgp_regex.h"
24
#include "bgpd/bgp_clist.h"
25
26
/* Calculate new sequential number. */
27
static int64_t bgp_clist_new_seq_get(struct community_list *list)
28
0
{
29
0
  int64_t maxseq;
30
0
  int64_t newseq;
31
0
  struct community_entry *entry;
32
33
0
  maxseq = 0;
34
35
0
  for (entry = list->head; entry; entry = entry->next) {
36
0
    if (maxseq < entry->seq)
37
0
      maxseq = entry->seq;
38
0
  }
39
40
0
  newseq = ((maxseq / 5) * 5) + 5;
41
42
0
  return (newseq > UINT_MAX) ? UINT_MAX : newseq;
43
0
}
44
45
/* Return community-list entry which has same seq number. */
46
static struct community_entry *bgp_clist_seq_check(struct community_list *list,
47
               int64_t seq)
48
0
{
49
0
  struct community_entry *entry;
50
51
0
  for (entry = list->head; entry; entry = entry->next)
52
0
    if (entry->seq == seq)
53
0
      return entry;
54
0
  return NULL;
55
0
}
56
57
static uint32_t bgp_clist_hash_key_community_list(const void *data)
58
0
{
59
0
  struct community_list *cl = (struct community_list *) data;
60
61
0
  if (cl->name_hash)
62
0
    return cl->name_hash;
63
64
0
  cl->name_hash = bgp_clist_hash_key(cl->name);
65
0
  return cl->name_hash;
66
0
}
67
68
static bool bgp_clist_hash_cmp_community_list(const void *a1, const void *a2)
69
0
{
70
0
  const struct community_list *cl1 = a1;
71
0
  const struct community_list *cl2 = a2;
72
73
0
  if (cl1->name_hash != cl2->name_hash)
74
0
    return false;
75
76
0
  if (strcmp(cl1->name, cl2->name) == 0)
77
0
    return true;
78
79
0
  return false;
80
0
}
81
82
/* Lookup master structure for community-list or
83
   extcommunity-list.  */
84
struct community_list_master *
85
community_list_master_lookup(struct community_list_handler *ch, int master)
86
0
{
87
0
  if (ch)
88
0
    switch (master) {
89
0
    case COMMUNITY_LIST_MASTER:
90
0
      return &ch->community_list;
91
0
    case EXTCOMMUNITY_LIST_MASTER:
92
0
      return &ch->extcommunity_list;
93
0
    case LARGE_COMMUNITY_LIST_MASTER:
94
0
      return &ch->lcommunity_list;
95
0
    }
96
0
  return NULL;
97
0
}
98
99
/* Allocate a new community list entry.  */
100
static struct community_entry *community_entry_new(void)
101
0
{
102
0
  return XCALLOC(MTYPE_COMMUNITY_LIST_ENTRY,
103
0
           sizeof(struct community_entry));
104
0
}
105
106
/* Free community list entry.  */
107
static void community_entry_free(struct community_entry *entry)
108
0
{
109
0
  switch (entry->style) {
110
0
  case COMMUNITY_LIST_STANDARD:
111
0
    if (entry->u.com)
112
0
      community_free(&entry->u.com);
113
0
    break;
114
0
  case LARGE_COMMUNITY_LIST_STANDARD:
115
0
    if (entry->u.lcom)
116
0
      lcommunity_free(&entry->u.lcom);
117
0
    break;
118
0
  case EXTCOMMUNITY_LIST_STANDARD:
119
    /* In case of standard extcommunity-list, configuration string
120
       is made by ecommunity_ecom2str().  */
121
0
    XFREE(MTYPE_ECOMMUNITY_STR, entry->config);
122
0
    if (entry->u.ecom)
123
0
      ecommunity_free(&entry->u.ecom);
124
0
    break;
125
0
  case COMMUNITY_LIST_EXPANDED:
126
0
  case EXTCOMMUNITY_LIST_EXPANDED:
127
0
  case LARGE_COMMUNITY_LIST_EXPANDED:
128
0
    XFREE(MTYPE_COMMUNITY_LIST_CONFIG, entry->config);
129
0
    if (entry->reg)
130
0
      bgp_regex_free(entry->reg);
131
0
  default:
132
0
    break;
133
0
  }
134
0
  XFREE(MTYPE_COMMUNITY_LIST_ENTRY, entry);
135
0
}
136
137
/* Allocate a new community-list.  */
138
static struct community_list *community_list_new(void)
139
0
{
140
0
  return XCALLOC(MTYPE_COMMUNITY_LIST, sizeof(struct community_list));
141
0
}
142
143
/* Free community-list.  */
144
static void community_list_free(struct community_list *list)
145
0
{
146
0
  XFREE(MTYPE_COMMUNITY_LIST_NAME, list->name);
147
0
  XFREE(MTYPE_COMMUNITY_LIST, list);
148
0
}
149
150
static struct community_list *
151
community_list_insert(struct community_list_handler *ch, const char *name,
152
          int master)
153
0
{
154
0
  size_t i;
155
0
  long number;
156
0
  struct community_list *new;
157
0
  struct community_list *point;
158
0
  struct community_list_list *list;
159
0
  struct community_list_master *cm;
160
161
  /* Lookup community-list master.  */
162
0
  cm = community_list_master_lookup(ch, master);
163
0
  if (!cm)
164
0
    return NULL;
165
166
  /* Allocate new community_list and copy given name. */
167
0
  new = community_list_new();
168
0
  new->name = XSTRDUP(MTYPE_COMMUNITY_LIST_NAME, name);
169
0
  new->name_hash = bgp_clist_hash_key_community_list(new);
170
171
  /* Save for later */
172
0
  (void)hash_get(cm->hash, new, hash_alloc_intern);
173
174
  /* If name is made by all digit character.  We treat it as
175
     number. */
176
0
  for (number = 0, i = 0; i < strlen(name); i++) {
177
0
    if (isdigit((unsigned char)name[i]))
178
0
      number = (number * 10) + (name[i] - '0');
179
0
    else
180
0
      break;
181
0
  }
182
183
  /* In case of name is all digit character */
184
0
  if (i == strlen(name)) {
185
0
    new->sort = COMMUNITY_LIST_NUMBER;
186
187
    /* Set access_list to number list. */
188
0
    list = &cm->num;
189
190
0
    for (point = list->head; point; point = point->next)
191
0
      if (atol(point->name) >= number)
192
0
        break;
193
0
  } else {
194
0
    new->sort = COMMUNITY_LIST_STRING;
195
196
    /* Set access_list to string list. */
197
0
    list = &cm->str;
198
199
    /* Set point to insertion point. */
200
0
    for (point = list->head; point; point = point->next)
201
0
      if (strcmp(point->name, name) >= 0)
202
0
        break;
203
0
  }
204
205
  /* Link to upper list.  */
206
0
  new->parent = list;
207
208
  /* In case of this is the first element of master. */
209
0
  if (list->head == NULL) {
210
0
    list->head = list->tail = new;
211
0
    return new;
212
0
  }
213
214
  /* In case of insertion is made at the tail of access_list. */
215
0
  if (point == NULL) {
216
0
    new->prev = list->tail;
217
0
    list->tail->next = new;
218
0
    list->tail = new;
219
0
    return new;
220
0
  }
221
222
  /* In case of insertion is made at the head of access_list. */
223
0
  if (point == list->head) {
224
0
    new->next = list->head;
225
0
    list->head->prev = new;
226
0
    list->head = new;
227
0
    return new;
228
0
  }
229
230
  /* Insertion is made at middle of the access_list. */
231
0
  new->next = point;
232
0
  new->prev = point->prev;
233
234
0
  if (point->prev)
235
0
    point->prev->next = new;
236
0
  point->prev = new;
237
238
0
  return new;
239
0
}
240
241
struct community_list *community_list_lookup(struct community_list_handler *ch,
242
               const char *name,
243
               uint32_t name_hash,
244
               int master)
245
0
{
246
0
  struct community_list lookup;
247
0
  struct community_list_master *cm;
248
249
0
  if (!name)
250
0
    return NULL;
251
252
0
  cm = community_list_master_lookup(ch, master);
253
0
  if (!cm)
254
0
    return NULL;
255
256
0
  lookup.name = (char *)name;
257
0
  lookup.name_hash = name_hash;
258
0
  return hash_get(cm->hash, &lookup, NULL);
259
0
}
260
261
static struct community_list *
262
community_list_get(struct community_list_handler *ch, const char *name,
263
       int master)
264
0
{
265
0
  struct community_list *list;
266
267
0
  list = community_list_lookup(ch, name, 0, master);
268
0
  if (!list)
269
0
    list = community_list_insert(ch, name, master);
270
0
  return list;
271
0
}
272
273
static void community_list_delete(struct community_list_master *cm,
274
          struct community_list *list)
275
0
{
276
0
  struct community_list_list *clist;
277
0
  struct community_entry *entry, *next;
278
279
0
  for (entry = list->head; entry; entry = next) {
280
0
    next = entry->next;
281
0
    community_entry_free(entry);
282
0
  }
283
284
0
  clist = list->parent;
285
286
0
  if (list->next)
287
0
    list->next->prev = list->prev;
288
0
  else
289
0
    clist->tail = list->prev;
290
291
0
  if (list->prev)
292
0
    list->prev->next = list->next;
293
0
  else
294
0
    clist->head = list->next;
295
296
0
  hash_release(cm->hash, list);
297
0
  community_list_free(list);
298
0
}
299
300
static bool community_list_empty_p(struct community_list *list)
301
0
{
302
0
  return list->head == NULL && list->tail == NULL;
303
0
}
304
305
/* Delete community-list entry from the list.  */
306
static void community_list_entry_delete(struct community_list_master *cm,
307
          struct community_list *list,
308
          struct community_entry *entry)
309
0
{
310
0
  if (entry->next)
311
0
    entry->next->prev = entry->prev;
312
0
  else
313
0
    list->tail = entry->prev;
314
315
0
  if (entry->prev)
316
0
    entry->prev->next = entry->next;
317
0
  else
318
0
    list->head = entry->next;
319
320
0
  community_entry_free(entry);
321
322
0
  if (community_list_empty_p(list))
323
0
    community_list_delete(cm, list);
324
0
}
325
326
/*
327
 * Replace community-list entry in the list. Note that entry is the new one
328
 * and replace is one one being replaced.
329
 */
330
static void community_list_entry_replace(struct community_list *list,
331
           struct community_entry *replace,
332
           struct community_entry *entry)
333
0
{
334
0
  if (replace->next) {
335
0
    entry->next = replace->next;
336
0
    replace->next->prev = entry;
337
0
  } else {
338
0
    entry->next = NULL;
339
0
    list->tail = entry;
340
0
  }
341
342
0
  if (replace->prev) {
343
0
    entry->prev = replace->prev;
344
0
    replace->prev->next = entry;
345
0
  } else {
346
0
    entry->prev = NULL;
347
0
    list->head = entry;
348
0
  }
349
350
0
  community_entry_free(replace);
351
0
}
352
353
/* Add community-list entry to the list.  */
354
static void community_list_entry_add(struct community_list *list,
355
             struct community_entry *entry,
356
             struct community_list_handler *ch,
357
             int master)
358
0
{
359
0
  struct community_entry *replace;
360
0
  struct community_entry *point;
361
362
  /* Automatic assignment of seq no. */
363
0
  if (entry->seq == COMMUNITY_SEQ_NUMBER_AUTO)
364
0
    entry->seq = bgp_clist_new_seq_get(list);
365
366
0
  if (list->tail && entry->seq > list->tail->seq)
367
0
    point = NULL;
368
0
  else {
369
0
    replace = bgp_clist_seq_check(list, entry->seq);
370
0
    if (replace) {
371
0
      community_list_entry_replace(list, replace, entry);
372
0
      return;
373
0
    }
374
375
    /* Check insert point. */
376
0
    for (point = list->head; point; point = point->next)
377
0
      if (point->seq >= entry->seq)
378
0
        break;
379
0
  }
380
381
  /* In case of this is the first element of the list. */
382
0
  entry->next = point;
383
384
0
  if (point) {
385
0
    if (point->prev)
386
0
      point->prev->next = entry;
387
0
    else
388
0
      list->head = entry;
389
390
0
    entry->prev = point->prev;
391
0
    point->prev = entry;
392
0
  } else {
393
0
    if (list->tail)
394
0
      list->tail->next = entry;
395
0
    else
396
0
      list->head = entry;
397
398
0
    entry->prev = list->tail;
399
0
    list->tail = entry;
400
0
  }
401
0
}
402
403
/* Lookup community-list entry from the list.  */
404
static struct community_entry *
405
community_list_entry_lookup(struct community_list *list, const void *arg,
406
          int direct)
407
0
{
408
0
  struct community_entry *entry;
409
410
0
  for (entry = list->head; entry; entry = entry->next) {
411
0
    switch (entry->style) {
412
0
    case COMMUNITY_LIST_STANDARD:
413
0
      if (entry->direct == direct
414
0
          && community_cmp(entry->u.com, arg))
415
0
        return entry;
416
0
      break;
417
0
    case EXTCOMMUNITY_LIST_STANDARD:
418
0
      if (entry->direct == direct
419
0
          && ecommunity_cmp(entry->u.ecom, arg))
420
0
        return entry;
421
0
      break;
422
0
    case LARGE_COMMUNITY_LIST_STANDARD:
423
0
      if (entry->direct == direct
424
0
          && lcommunity_cmp(entry->u.lcom, arg))
425
0
        return entry;
426
0
      break;
427
0
    case COMMUNITY_LIST_EXPANDED:
428
0
    case EXTCOMMUNITY_LIST_EXPANDED:
429
0
    case LARGE_COMMUNITY_LIST_EXPANDED:
430
0
      if (entry->direct == direct
431
0
          && strcmp(entry->config, arg) == 0)
432
0
        return entry;
433
0
      break;
434
0
    default:
435
0
      break;
436
0
    }
437
0
  }
438
0
  return NULL;
439
0
}
440
441
static char *community_str_get(struct community *com, int i)
442
0
{
443
0
  uint32_t comval;
444
0
  uint16_t as;
445
0
  uint16_t val;
446
0
  char *str;
447
448
0
  memcpy(&comval, com_nthval(com, i), sizeof(uint32_t));
449
0
  comval = ntohl(comval);
450
451
0
  switch (comval) {
452
#if CONFDATE > 20230801
453
CPP_NOTICE("Deprecate COMMUNITY_INTERNET BGP community")
454
#endif
455
0
  case COMMUNITY_INTERNET:
456
0
    str = XSTRDUP(MTYPE_COMMUNITY_STR, "internet");
457
0
    zlog_warn("`internet` community is deprecated");
458
0
    break;
459
0
  case COMMUNITY_GSHUT:
460
0
    str = XSTRDUP(MTYPE_COMMUNITY_STR, "graceful-shutdown");
461
0
    break;
462
0
  case COMMUNITY_ACCEPT_OWN:
463
0
    str = XSTRDUP(MTYPE_COMMUNITY_STR, "accept-own");
464
0
    break;
465
0
  case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4:
466
0
    str = XSTRDUP(MTYPE_COMMUNITY_STR,
467
0
            "route-filter-translated-v4");
468
0
    break;
469
0
  case COMMUNITY_ROUTE_FILTER_v4:
470
0
    str = XSTRDUP(MTYPE_COMMUNITY_STR, "route-filter-v4");
471
0
    break;
472
0
  case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6:
473
0
    str = XSTRDUP(MTYPE_COMMUNITY_STR,
474
0
            "route-filter-translated-v6");
475
0
    break;
476
0
  case COMMUNITY_ROUTE_FILTER_v6:
477
0
    str = XSTRDUP(MTYPE_COMMUNITY_STR, "route-filter-v6");
478
0
    break;
479
0
  case COMMUNITY_LLGR_STALE:
480
0
    str = XSTRDUP(MTYPE_COMMUNITY_STR, "llgr-stale");
481
0
    break;
482
0
  case COMMUNITY_NO_LLGR:
483
0
    str = XSTRDUP(MTYPE_COMMUNITY_STR, "no-llgr");
484
0
    break;
485
0
  case COMMUNITY_ACCEPT_OWN_NEXTHOP:
486
0
    str = XSTRDUP(MTYPE_COMMUNITY_STR, "accept-own-nexthop");
487
0
    break;
488
0
  case COMMUNITY_BLACKHOLE:
489
0
    str = XSTRDUP(MTYPE_COMMUNITY_STR, "blackhole");
490
0
    break;
491
0
  case COMMUNITY_NO_EXPORT:
492
0
    str = XSTRDUP(MTYPE_COMMUNITY_STR, "no-export");
493
0
    break;
494
0
  case COMMUNITY_NO_ADVERTISE:
495
0
    str = XSTRDUP(MTYPE_COMMUNITY_STR, "no-advertise");
496
0
    break;
497
0
  case COMMUNITY_LOCAL_AS:
498
0
    str = XSTRDUP(MTYPE_COMMUNITY_STR, "local-AS");
499
0
    break;
500
0
  case COMMUNITY_NO_PEER:
501
0
    str = XSTRDUP(MTYPE_COMMUNITY_STR, "no-peer");
502
0
    break;
503
0
  default:
504
0
    str = XSTRDUP(MTYPE_COMMUNITY_STR, "65536:65535");
505
0
    as = (comval >> 16) & 0xFFFF;
506
0
    val = comval & 0xFFFF;
507
0
    snprintf(str, strlen(str), "%u:%d", as, val);
508
0
    break;
509
0
  }
510
511
0
  return str;
512
0
}
513
514
/* Internal function to perform regular expression match for
515
 * a single community. */
516
static bool community_regexp_include(regex_t *reg, struct community *com, int i)
517
0
{
518
0
  char *str;
519
0
  int rv;
520
521
  /* When there is no communities attribute it is treated as empty string.
522
   */
523
0
  if (com == NULL || com->size == 0)
524
0
    str = XSTRDUP(MTYPE_COMMUNITY_STR, "");
525
0
  else
526
0
    str = community_str_get(com, i);
527
528
  /* Regular expression match.  */
529
0
  rv = regexec(reg, str, 0, NULL, 0);
530
531
0
  XFREE(MTYPE_COMMUNITY_STR, str);
532
533
0
  return rv == 0;
534
0
}
535
536
/* Internal function to perform regular expression match for community
537
   attribute.  */
538
static bool community_regexp_match(struct community *com, regex_t *reg)
539
0
{
540
0
  const char *str;
541
0
  char *regstr;
542
0
  int rv;
543
544
  /* When there is no communities attribute it is treated as empty
545
     string.  */
546
0
  if (com == NULL || com->size == 0)
547
0
    str = "";
548
0
  else
549
0
    str = community_str(com, false, true);
550
551
0
  regstr = bgp_alias2community_str(str);
552
553
  /* Regular expression match.  */
554
0
  rv = regexec(reg, regstr, 0, NULL, 0);
555
556
0
  XFREE(MTYPE_TMP, regstr);
557
558
0
  return rv == 0;
559
0
}
560
561
static char *lcommunity_str_get(struct lcommunity *lcom, int i)
562
0
{
563
0
  struct lcommunity_val lcomval;
564
0
  uint32_t globaladmin;
565
0
  uint32_t localdata1;
566
0
  uint32_t localdata2;
567
0
  char *str;
568
0
  const uint8_t *ptr;
569
570
0
  ptr = lcom->val + (i * LCOMMUNITY_SIZE);
571
572
0
  memcpy(&lcomval, ptr, LCOMMUNITY_SIZE);
573
574
  /* Allocate memory.  48 bytes taken off bgp_lcommunity.c */
575
0
  ptr = (uint8_t *)lcomval.val;
576
0
  ptr = ptr_get_be32(ptr, &globaladmin);
577
0
  ptr = ptr_get_be32(ptr, &localdata1);
578
0
  ptr = ptr_get_be32(ptr, &localdata2);
579
0
  (void)ptr; /* consume value */
580
581
0
  str = XMALLOC(MTYPE_LCOMMUNITY_STR, 48);
582
0
  snprintf(str, 48, "%u:%u:%u", globaladmin, localdata1, localdata2);
583
584
0
  return str;
585
0
}
586
587
/* Internal function to perform regular expression match for
588
 * a single community. */
589
static bool lcommunity_regexp_include(regex_t *reg, struct lcommunity *lcom,
590
              int i)
591
0
{
592
0
  char *str;
593
594
  /* When there is no communities attribute it is treated as empty string.
595
   */
596
0
  if (lcom == NULL || lcom->size == 0)
597
0
    str = XSTRDUP(MTYPE_LCOMMUNITY_STR, "");
598
0
  else
599
0
    str = lcommunity_str_get(lcom, i);
600
601
  /* Regular expression match.  */
602
0
  if (regexec(reg, str, 0, NULL, 0) == 0) {
603
0
    XFREE(MTYPE_LCOMMUNITY_STR, str);
604
0
    return true;
605
0
  }
606
607
0
  XFREE(MTYPE_LCOMMUNITY_STR, str);
608
  /* No match.  */
609
0
  return false;
610
0
}
611
612
static bool lcommunity_regexp_match(struct lcommunity *com, regex_t *reg)
613
0
{
614
0
  const char *str;
615
0
  char *regstr;
616
0
  int rv;
617
618
  /* When there is no communities attribute it is treated as empty
619
     string.  */
620
0
  if (com == NULL || com->size == 0)
621
0
    str = "";
622
0
  else
623
0
    str = lcommunity_str(com, false, true);
624
625
0
  regstr = bgp_alias2community_str(str);
626
627
  /* Regular expression match.  */
628
0
  rv = regexec(reg, regstr, 0, NULL, 0);
629
630
0
  XFREE(MTYPE_TMP, regstr);
631
632
0
  return rv == 0;
633
0
}
634
635
636
static bool ecommunity_regexp_match(struct ecommunity *ecom, regex_t *reg)
637
0
{
638
0
  const char *str;
639
640
  /* When there is no communities attribute it is treated as empty
641
     string.  */
642
0
  if (ecom == NULL || ecom->size == 0)
643
0
    str = "";
644
0
  else
645
0
    str = ecommunity_str(ecom);
646
647
  /* Regular expression match.  */
648
0
  if (regexec(reg, str, 0, NULL, 0) == 0)
649
0
    return true;
650
651
  /* No match.  */
652
0
  return false;
653
0
}
654
655
/* When given community attribute matches to the community-list return
656
   1 else return 0.  */
657
bool community_list_match(struct community *com, struct community_list *list)
658
0
{
659
0
  struct community_entry *entry;
660
661
0
  for (entry = list->head; entry; entry = entry->next) {
662
0
    if (entry->style == COMMUNITY_LIST_STANDARD) {
663
0
      if (community_include(entry->u.com, COMMUNITY_INTERNET))
664
0
        return entry->direct == COMMUNITY_PERMIT;
665
666
0
      if (community_match(com, entry->u.com))
667
0
        return entry->direct == COMMUNITY_PERMIT;
668
0
    } else if (entry->style == COMMUNITY_LIST_EXPANDED) {
669
0
      if (community_regexp_match(com, entry->reg))
670
0
        return entry->direct == COMMUNITY_PERMIT;
671
0
    }
672
0
  }
673
0
  return false;
674
0
}
675
676
bool lcommunity_list_match(struct lcommunity *lcom, struct community_list *list)
677
0
{
678
0
  struct community_entry *entry;
679
680
0
  for (entry = list->head; entry; entry = entry->next) {
681
0
    if (entry->style == LARGE_COMMUNITY_LIST_STANDARD) {
682
0
      if (lcommunity_match(lcom, entry->u.lcom))
683
0
        return entry->direct == COMMUNITY_PERMIT;
684
0
    } else if (entry->style == LARGE_COMMUNITY_LIST_EXPANDED) {
685
0
      if (lcommunity_regexp_match(lcom, entry->reg))
686
0
        return entry->direct == COMMUNITY_PERMIT;
687
0
    }
688
0
  }
689
0
  return false;
690
0
}
691
692
693
/* Perform exact matching.  In case of expanded large-community-list, do
694
 * same thing as lcommunity_list_match().
695
 */
696
bool lcommunity_list_exact_match(struct lcommunity *lcom,
697
         struct community_list *list)
698
0
{
699
0
  struct community_entry *entry;
700
701
0
  for (entry = list->head; entry; entry = entry->next) {
702
0
    if (entry->style == LARGE_COMMUNITY_LIST_STANDARD) {
703
0
      if (lcommunity_cmp(lcom, entry->u.lcom))
704
0
        return entry->direct == COMMUNITY_PERMIT;
705
0
    } else if (entry->style == LARGE_COMMUNITY_LIST_EXPANDED) {
706
0
      if (lcommunity_regexp_match(lcom, entry->reg))
707
0
        return entry->direct == COMMUNITY_PERMIT;
708
0
    }
709
0
  }
710
0
  return false;
711
0
}
712
713
bool ecommunity_list_match(struct ecommunity *ecom, struct community_list *list)
714
0
{
715
0
  struct community_entry *entry;
716
717
0
  for (entry = list->head; entry; entry = entry->next) {
718
0
    if (entry->style == EXTCOMMUNITY_LIST_STANDARD) {
719
0
      if (ecommunity_match(ecom, entry->u.ecom))
720
0
        return entry->direct == COMMUNITY_PERMIT;
721
0
    } else if (entry->style == EXTCOMMUNITY_LIST_EXPANDED) {
722
0
      if (ecommunity_regexp_match(ecom, entry->reg))
723
0
        return entry->direct == COMMUNITY_PERMIT;
724
0
    }
725
0
  }
726
0
  return false;
727
0
}
728
729
/* Perform exact matching.  In case of expanded community-list, do
730
   same thing as community_list_match().  */
731
bool community_list_exact_match(struct community *com,
732
        struct community_list *list)
733
0
{
734
0
  struct community_entry *entry;
735
736
0
  for (entry = list->head; entry; entry = entry->next) {
737
0
    if (entry->style == COMMUNITY_LIST_STANDARD) {
738
0
      if (community_include(entry->u.com, COMMUNITY_INTERNET))
739
0
        return entry->direct == COMMUNITY_PERMIT;
740
741
0
      if (community_cmp(com, entry->u.com))
742
0
        return entry->direct == COMMUNITY_PERMIT;
743
0
    } else if (entry->style == COMMUNITY_LIST_EXPANDED) {
744
0
      if (community_regexp_match(com, entry->reg))
745
0
        return entry->direct == COMMUNITY_PERMIT;
746
0
    }
747
0
  }
748
0
  return false;
749
0
}
750
751
/* Delete all permitted communities in the list from com.  */
752
struct community *community_list_match_delete(struct community *com,
753
                struct community_list *list)
754
0
{
755
0
  struct community_entry *entry;
756
0
  uint32_t val;
757
0
  uint32_t com_index_to_delete[com->size];
758
0
  int delete_index = 0;
759
0
  int i;
760
761
  /* Loop over each community value and evaluate each against the
762
   * community-list.  If we need to delete a community value add its index
763
   * to com_index_to_delete.
764
   */
765
0
  for (i = 0; i < com->size; i++) {
766
0
    val = community_val_get(com, i);
767
768
0
    for (entry = list->head; entry; entry = entry->next) {
769
0
      if ((entry->style == COMMUNITY_LIST_STANDARD) &&
770
0
          (community_include(entry->u.com,
771
0
                 COMMUNITY_INTERNET) ||
772
0
           community_include(entry->u.com, val))) {
773
0
        if (entry->direct == COMMUNITY_PERMIT) {
774
0
          com_index_to_delete[delete_index] = i;
775
0
          delete_index++;
776
0
        }
777
0
        break;
778
0
      } else if ((entry->style == COMMUNITY_LIST_EXPANDED) &&
779
0
           community_regexp_include(entry->reg, com,
780
0
                  i)) {
781
0
        if (entry->direct == COMMUNITY_PERMIT) {
782
0
          com_index_to_delete[delete_index] = i;
783
0
          delete_index++;
784
0
        }
785
0
        break;
786
0
      }
787
0
    }
788
0
  }
789
790
  /* Delete all of the communities we flagged for deletion */
791
0
  for (i = delete_index - 1; i >= 0; i--) {
792
0
    val = community_val_get(com, com_index_to_delete[i]);
793
0
    val = htonl(val);
794
0
    community_del_val(com, &val);
795
0
  }
796
797
0
  return com;
798
0
}
799
800
/* To avoid duplicated entry in the community-list, this function
801
   compares specified entry to existing entry.  */
802
static bool community_list_dup_check(struct community_list *list,
803
             struct community_entry *new)
804
0
{
805
0
  struct community_entry *entry;
806
807
0
  for (entry = list->head; entry; entry = entry->next) {
808
0
    if (entry->style != new->style)
809
0
      continue;
810
811
0
    if (entry->direct != new->direct)
812
0
      continue;
813
814
0
    switch (entry->style) {
815
0
    case COMMUNITY_LIST_STANDARD:
816
0
      if (community_cmp(entry->u.com, new->u.com))
817
0
        return true;
818
0
      break;
819
0
    case LARGE_COMMUNITY_LIST_STANDARD:
820
0
      if (lcommunity_cmp(entry->u.lcom, new->u.lcom))
821
0
        return true;
822
0
      break;
823
0
    case EXTCOMMUNITY_LIST_STANDARD:
824
0
      if (ecommunity_cmp(entry->u.ecom, new->u.ecom))
825
0
        return true;
826
0
      break;
827
0
    case COMMUNITY_LIST_EXPANDED:
828
0
    case EXTCOMMUNITY_LIST_EXPANDED:
829
0
    case LARGE_COMMUNITY_LIST_EXPANDED:
830
0
      if (strcmp(entry->config, new->config) == 0)
831
0
        return true;
832
0
      break;
833
0
    default:
834
0
      break;
835
0
    }
836
0
  }
837
0
  return false;
838
0
}
839
840
/* Set community-list.  */
841
int community_list_set(struct community_list_handler *ch, const char *name,
842
           const char *str, const char *seq, int direct, int style)
843
0
{
844
0
  struct community_entry *entry = NULL;
845
0
  struct community_list *list;
846
0
  struct community *com = NULL;
847
0
  regex_t *regex = NULL;
848
0
  int64_t seqnum = COMMUNITY_SEQ_NUMBER_AUTO;
849
850
0
  if (seq)
851
0
    seqnum = (int64_t)atol(seq);
852
853
  /* Get community list. */
854
0
  list = community_list_get(ch, name, COMMUNITY_LIST_MASTER);
855
856
  /* When community-list already has entry, new entry should have same
857
     style.  If you want to have mixed style community-list, you can
858
     comment out this check.  */
859
0
  if (!community_list_empty_p(list)) {
860
0
    struct community_entry *first;
861
862
0
    first = list->head;
863
864
0
    if (style != first->style) {
865
0
      return (first->style == COMMUNITY_LIST_STANDARD
866
0
          ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
867
0
          : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT);
868
0
    }
869
0
  }
870
871
0
  if (style == COMMUNITY_LIST_STANDARD)
872
0
    com = community_str2com(str);
873
0
  else
874
0
    regex = bgp_regcomp(str);
875
876
0
  if (!com && !regex)
877
0
    return COMMUNITY_LIST_ERR_MALFORMED_VAL;
878
879
0
  entry = community_entry_new();
880
0
  entry->direct = direct;
881
0
  entry->style = style;
882
0
  entry->u.com = com;
883
0
  entry->reg = regex;
884
0
  entry->seq = seqnum;
885
0
  entry->config =
886
0
    (regex ? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG, str) : NULL);
887
888
  /* Do not put duplicated community entry.  */
889
0
  if (community_list_dup_check(list, entry))
890
0
    community_entry_free(entry);
891
0
  else {
892
0
    community_list_entry_add(list, entry, ch,
893
0
           COMMUNITY_LIST_MASTER);
894
0
    route_map_notify_dependencies(name, RMAP_EVENT_CLIST_ADDED);
895
0
  }
896
897
0
  return 0;
898
0
}
899
900
/* Unset community-list */
901
int community_list_unset(struct community_list_handler *ch, const char *name,
902
       const char *str, const char *seq, int direct,
903
       int style)
904
0
{
905
0
  struct community_list_master *cm = NULL;
906
0
  struct community_entry *entry = NULL;
907
0
  struct community_list *list;
908
0
  struct community *com = NULL;
909
910
  /* Lookup community list.  */
911
0
  list = community_list_lookup(ch, name, 0, COMMUNITY_LIST_MASTER);
912
0
  if (list == NULL)
913
0
    return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
914
915
0
  cm = community_list_master_lookup(ch, COMMUNITY_LIST_MASTER);
916
  /* Delete all of entry belongs to this community-list.  */
917
0
  if (!str) {
918
0
    community_list_delete(cm, list);
919
0
    route_map_notify_dependencies(name, RMAP_EVENT_CLIST_DELETED);
920
0
    return 0;
921
0
  }
922
923
0
  if (style == COMMUNITY_LIST_STANDARD)
924
0
    com = community_str2com(str);
925
926
0
  if (com) {
927
0
    entry = community_list_entry_lookup(list, com, direct);
928
0
    community_free(&com);
929
0
  } else
930
0
    entry = community_list_entry_lookup(list, str, direct);
931
932
0
  if (!entry)
933
0
    return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
934
935
0
  community_list_entry_delete(cm, list, entry);
936
0
  route_map_notify_dependencies(name, RMAP_EVENT_CLIST_DELETED);
937
938
0
  return 0;
939
0
}
940
941
/* Delete all permitted large communities in the list from com.  */
942
struct lcommunity *lcommunity_list_match_delete(struct lcommunity *lcom,
943
            struct community_list *list)
944
0
{
945
0
  struct community_entry *entry;
946
0
  uint32_t com_index_to_delete[lcom->size];
947
0
  uint8_t *ptr;
948
0
  int delete_index = 0;
949
0
  int i;
950
951
  /* Loop over each lcommunity value and evaluate each against the
952
   * community-list.  If we need to delete a community value add its index
953
   * to com_index_to_delete.
954
   */
955
0
  for (i = 0; i < lcom->size; i++) {
956
0
    ptr = lcom->val + (i * LCOMMUNITY_SIZE);
957
0
    for (entry = list->head; entry; entry = entry->next) {
958
0
      if ((entry->style == LARGE_COMMUNITY_LIST_STANDARD) &&
959
0
          lcommunity_include(entry->u.lcom, ptr)) {
960
0
        if (entry->direct == COMMUNITY_PERMIT) {
961
0
          com_index_to_delete[delete_index] = i;
962
0
          delete_index++;
963
0
        }
964
0
        break;
965
0
      }
966
967
0
      else if ((entry->style ==
968
0
          LARGE_COMMUNITY_LIST_EXPANDED) &&
969
0
         lcommunity_regexp_include(entry->reg, lcom,
970
0
                 i)) {
971
0
        if (entry->direct == COMMUNITY_PERMIT) {
972
0
          com_index_to_delete[delete_index] = i;
973
0
          delete_index++;
974
0
        }
975
0
        break;
976
0
      }
977
0
    }
978
0
  }
979
980
  /* Delete all of the communities we flagged for deletion */
981
0
  for (i = delete_index - 1; i >= 0; i--) {
982
0
    ptr = lcom->val + (com_index_to_delete[i] * LCOMMUNITY_SIZE);
983
0
    lcommunity_del_val(lcom, ptr);
984
0
  }
985
986
0
  return lcom;
987
0
}
988
989
/* Helper to check if every octet do not exceed UINT_MAX */
990
bool lcommunity_list_valid(const char *community, int style)
991
0
{
992
0
  int octets;
993
0
  char **splits, **communities;
994
0
  char *endptr;
995
0
  int num, num_communities;
996
0
  regex_t *regres;
997
0
  int invalid = 0;
998
999
0
  frrstr_split(community, " ", &communities, &num_communities);
1000
1001
0
  for (int j = 0; j < num_communities; j++) {
1002
0
    octets = 0;
1003
0
    frrstr_split(communities[j], ":", &splits, &num);
1004
1005
0
    for (int i = 0; i < num; i++) {
1006
0
      if (strlen(splits[i]) == 0)
1007
        /* There is no digit to check */
1008
0
        invalid++;
1009
1010
0
      if (style == LARGE_COMMUNITY_LIST_STANDARD) {
1011
0
        if (*splits[i] == '-')
1012
          /* Must not be negative */
1013
0
          invalid++;
1014
0
        else if (strtoul(splits[i], &endptr, 10)
1015
0
           > UINT_MAX)
1016
          /* Larger than 4 octets */
1017
0
          invalid++;
1018
0
        else if (*endptr)
1019
          /* Not all characters were digits */
1020
0
          invalid++;
1021
0
      } else {
1022
0
        regres = bgp_regcomp(communities[j]);
1023
0
        if (!regres)
1024
          /* malformed regex */
1025
0
          invalid++;
1026
0
        else
1027
0
          bgp_regex_free(regres);
1028
0
      }
1029
1030
0
      octets++;
1031
0
      XFREE(MTYPE_TMP, splits[i]);
1032
0
    }
1033
0
    XFREE(MTYPE_TMP, splits);
1034
1035
0
    if (octets != 3)
1036
0
      invalid++;
1037
1038
0
    XFREE(MTYPE_TMP, communities[j]);
1039
0
  }
1040
0
  XFREE(MTYPE_TMP, communities);
1041
1042
0
  return (invalid > 0) ? false : true;
1043
0
}
1044
1045
/* Set lcommunity-list.  */
1046
int lcommunity_list_set(struct community_list_handler *ch, const char *name,
1047
      const char *str, const char *seq, int direct, int style)
1048
0
{
1049
0
  struct community_entry *entry = NULL;
1050
0
  struct community_list *list;
1051
0
  struct lcommunity *lcom = NULL;
1052
0
  regex_t *regex = NULL;
1053
0
  int64_t seqnum = COMMUNITY_SEQ_NUMBER_AUTO;
1054
1055
0
  if (seq)
1056
0
    seqnum = (int64_t)atol(seq);
1057
1058
  /* Get community list. */
1059
0
  list = community_list_get(ch, name, LARGE_COMMUNITY_LIST_MASTER);
1060
1061
  /* When community-list already has entry, new entry should have same
1062
     style.  If you want to have mixed style community-list, you can
1063
     comment out this check.  */
1064
0
  if (!community_list_empty_p(list)) {
1065
0
    struct community_entry *first;
1066
1067
0
    first = list->head;
1068
1069
0
    if (style != first->style) {
1070
0
      return (first->style == COMMUNITY_LIST_STANDARD
1071
0
          ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
1072
0
          : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT);
1073
0
    }
1074
0
  }
1075
1076
0
  if (str) {
1077
0
    if (style == LARGE_COMMUNITY_LIST_STANDARD)
1078
0
      lcom = lcommunity_str2com(str);
1079
0
    else
1080
0
      regex = bgp_regcomp(str);
1081
1082
0
    if (!lcom && !regex)
1083
0
      return COMMUNITY_LIST_ERR_MALFORMED_VAL;
1084
0
  }
1085
1086
0
  entry = community_entry_new();
1087
0
  entry->direct = direct;
1088
0
  entry->style = style;
1089
0
  entry->u.lcom = lcom;
1090
0
  entry->reg = regex;
1091
0
  entry->seq = seqnum;
1092
0
  entry->config =
1093
0
    (regex ? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG, str) : NULL);
1094
1095
  /* Do not put duplicated community entry.  */
1096
0
  if (community_list_dup_check(list, entry))
1097
0
    community_entry_free(entry);
1098
0
  else {
1099
0
    community_list_entry_add(list, entry, ch,
1100
0
           LARGE_COMMUNITY_LIST_MASTER);
1101
0
    route_map_notify_dependencies(name, RMAP_EVENT_LLIST_ADDED);
1102
0
  }
1103
1104
0
  return 0;
1105
0
}
1106
1107
/* Unset community-list.  When str is NULL, delete all of
1108
   community-list entry belongs to the specified name.  */
1109
int lcommunity_list_unset(struct community_list_handler *ch, const char *name,
1110
        const char *str, const char *seq, int direct,
1111
        int style)
1112
0
{
1113
0
  struct community_list_master *cm = NULL;
1114
0
  struct community_entry *entry = NULL;
1115
0
  struct community_list *list;
1116
0
  struct lcommunity *lcom = NULL;
1117
0
  regex_t *regex = NULL;
1118
1119
  /* Lookup community list.  */
1120
0
  list = community_list_lookup(ch, name, 0, LARGE_COMMUNITY_LIST_MASTER);
1121
0
  if (list == NULL)
1122
0
    return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
1123
1124
0
  cm = community_list_master_lookup(ch, LARGE_COMMUNITY_LIST_MASTER);
1125
  /* Delete all of entry belongs to this community-list.  */
1126
0
  if (!str) {
1127
0
    community_list_delete(cm, list);
1128
0
    route_map_notify_dependencies(name, RMAP_EVENT_LLIST_DELETED);
1129
0
    return 0;
1130
0
  }
1131
1132
0
  if (style == LARGE_COMMUNITY_LIST_STANDARD)
1133
0
    lcom = lcommunity_str2com(str);
1134
0
  else
1135
0
    regex = bgp_regcomp(str);
1136
1137
0
  if (!lcom && !regex)
1138
0
    return COMMUNITY_LIST_ERR_MALFORMED_VAL;
1139
1140
0
  if (lcom)
1141
0
    entry = community_list_entry_lookup(list, lcom, direct);
1142
0
  else
1143
0
    entry = community_list_entry_lookup(list, str, direct);
1144
1145
0
  if (lcom)
1146
0
    lcommunity_free(&lcom);
1147
0
  if (regex)
1148
0
    bgp_regex_free(regex);
1149
1150
0
  if (!entry)
1151
0
    return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
1152
1153
0
  community_list_entry_delete(cm, list, entry);
1154
0
  route_map_notify_dependencies(name, RMAP_EVENT_LLIST_DELETED);
1155
1156
0
  return 0;
1157
0
}
1158
1159
/* Set extcommunity-list.  */
1160
int extcommunity_list_set(struct community_list_handler *ch, const char *name,
1161
        const char *str, const char *seq, int direct,
1162
        int style)
1163
0
{
1164
0
  struct community_entry *entry = NULL;
1165
0
  struct community_list *list;
1166
0
  struct ecommunity *ecom = NULL;
1167
0
  regex_t *regex = NULL;
1168
0
  int64_t seqnum = COMMUNITY_SEQ_NUMBER_AUTO;
1169
1170
0
  if (seq)
1171
0
    seqnum = (int64_t)atol(seq);
1172
1173
0
  if (str == NULL)
1174
0
    return COMMUNITY_LIST_ERR_MALFORMED_VAL;
1175
1176
  /* Get community list. */
1177
0
  list = community_list_get(ch, name, EXTCOMMUNITY_LIST_MASTER);
1178
1179
  /* When community-list already has entry, new entry should have same
1180
     style.  If you want to have mixed style community-list, you can
1181
     comment out this check.  */
1182
0
  if (!community_list_empty_p(list)) {
1183
0
    struct community_entry *first;
1184
1185
0
    first = list->head;
1186
1187
0
    if (style != first->style) {
1188
0
      return (first->style == EXTCOMMUNITY_LIST_STANDARD
1189
0
          ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
1190
0
          : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT);
1191
0
    }
1192
0
  }
1193
1194
0
  if (style == EXTCOMMUNITY_LIST_STANDARD)
1195
0
    ecom = ecommunity_str2com(str, 0, 1);
1196
0
  else
1197
0
    regex = bgp_regcomp(str);
1198
1199
0
  if (!ecom && !regex)
1200
0
    return COMMUNITY_LIST_ERR_MALFORMED_VAL;
1201
1202
0
  if (ecom)
1203
0
    ecom->str =
1204
0
      ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_DISPLAY, 0);
1205
1206
0
  entry = community_entry_new();
1207
0
  entry->direct = direct;
1208
0
  entry->style = style;
1209
0
  if (ecom)
1210
0
    entry->config = ecommunity_ecom2str(
1211
0
      ecom, ECOMMUNITY_FORMAT_COMMUNITY_LIST, 0);
1212
0
  else if (regex)
1213
0
    entry->config = XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG, str);
1214
1215
0
  entry->u.ecom = ecom;
1216
0
  entry->reg = regex;
1217
0
  entry->seq = seqnum;
1218
1219
  /* Do not put duplicated community entry.  */
1220
0
  if (community_list_dup_check(list, entry))
1221
0
    community_entry_free(entry);
1222
0
  else {
1223
0
    community_list_entry_add(list, entry, ch,
1224
0
           EXTCOMMUNITY_LIST_MASTER);
1225
0
    route_map_notify_dependencies(name, RMAP_EVENT_ECLIST_ADDED);
1226
0
  }
1227
1228
0
  return 0;
1229
0
}
1230
1231
/* Unset extcommunity-list.
1232
 *
1233
 * When str is NULL, delete all extcommunity-list entries belonging to the
1234
 * specified name.
1235
 */
1236
int extcommunity_list_unset(struct community_list_handler *ch, const char *name,
1237
          const char *str, const char *seq, int direct,
1238
          int style)
1239
0
{
1240
0
  struct community_list_master *cm = NULL;
1241
0
  struct community_entry *entry = NULL;
1242
0
  struct community_list *list;
1243
0
  struct ecommunity *ecom = NULL;
1244
1245
  /* Lookup extcommunity list.  */
1246
0
  list = community_list_lookup(ch, name, 0, EXTCOMMUNITY_LIST_MASTER);
1247
0
  if (list == NULL)
1248
0
    return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
1249
1250
0
  cm = community_list_master_lookup(ch, EXTCOMMUNITY_LIST_MASTER);
1251
  /* Delete all of entry belongs to this extcommunity-list.  */
1252
0
  if (!str) {
1253
0
    community_list_delete(cm, list);
1254
0
    route_map_notify_dependencies(name, RMAP_EVENT_ECLIST_DELETED);
1255
0
    return 0;
1256
0
  }
1257
1258
0
  if (style == EXTCOMMUNITY_LIST_STANDARD)
1259
0
    ecom = ecommunity_str2com(str, 0, 1);
1260
1261
0
  if (ecom) {
1262
0
    entry = community_list_entry_lookup(list, ecom, direct);
1263
0
    ecommunity_free(&ecom);
1264
0
  } else
1265
0
    entry = community_list_entry_lookup(list, str, direct);
1266
1267
0
  if (!entry)
1268
0
    return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
1269
1270
0
  community_list_entry_delete(cm, list, entry);
1271
0
  route_map_notify_dependencies(name, RMAP_EVENT_ECLIST_DELETED);
1272
1273
0
  return 0;
1274
0
}
1275
1276
/* Initializa community-list.  Return community-list handler.  */
1277
struct community_list_handler *community_list_init(void)
1278
1
{
1279
1
  struct community_list_handler *ch;
1280
1
  ch = XCALLOC(MTYPE_COMMUNITY_LIST_HANDLER,
1281
1
         sizeof(struct community_list_handler));
1282
1283
1
  ch->community_list.hash =
1284
1
    hash_create_size(4, bgp_clist_hash_key_community_list,
1285
1
         bgp_clist_hash_cmp_community_list,
1286
1
         "Community List Number Quick Lookup");
1287
1288
1
  ch->extcommunity_list.hash =
1289
1
    hash_create_size(4, bgp_clist_hash_key_community_list,
1290
1
         bgp_clist_hash_cmp_community_list,
1291
1
         "Extended Community List Quick Lookup");
1292
1293
1
  ch->lcommunity_list.hash =
1294
1
    hash_create_size(4, bgp_clist_hash_key_community_list,
1295
1
         bgp_clist_hash_cmp_community_list,
1296
1
         "Large Community List Quick Lookup");
1297
1298
1
  return ch;
1299
1
}
1300
1301
/* Terminate community-list.  */
1302
void community_list_terminate(struct community_list_handler *ch)
1303
0
{
1304
0
  struct community_list_master *cm;
1305
0
  struct community_list *list;
1306
1307
0
  cm = &ch->community_list;
1308
0
  while ((list = cm->num.head) != NULL)
1309
0
    community_list_delete(cm, list);
1310
0
  while ((list = cm->str.head) != NULL)
1311
0
    community_list_delete(cm, list);
1312
0
  hash_free(cm->hash);
1313
1314
0
  cm = &ch->lcommunity_list;
1315
0
  while ((list = cm->num.head) != NULL)
1316
0
    community_list_delete(cm, list);
1317
0
  while ((list = cm->str.head) != NULL)
1318
0
    community_list_delete(cm, list);
1319
0
  hash_free(cm->hash);
1320
1321
0
  cm = &ch->extcommunity_list;
1322
0
  while ((list = cm->num.head) != NULL)
1323
0
    community_list_delete(cm, list);
1324
0
  while ((list = cm->str.head) != NULL)
1325
0
    community_list_delete(cm, list);
1326
0
  hash_free(cm->hash);
1327
1328
0
  XFREE(MTYPE_COMMUNITY_LIST_HANDLER, ch);
1329
0
}
1330
1331
static int bgp_community_list_vector_walker(struct hash_bucket *bucket,
1332
              void *data)
1333
0
{
1334
0
  vector *comps = data;
1335
0
  struct community_list *list = bucket->data;
1336
1337
0
  vector_set(*comps, XSTRDUP(MTYPE_COMPLETION, list->name));
1338
1339
0
  return 1;
1340
0
}
1341
1342
static void bgp_community_list_cmd_completion(vector comps,
1343
                struct cmd_token *token)
1344
0
{
1345
0
  struct community_list_master *cm;
1346
1347
0
  cm = community_list_master_lookup(bgp_clist, COMMUNITY_LIST_MASTER);
1348
1349
0
  hash_walk(cm->hash, bgp_community_list_vector_walker, &comps);
1350
0
}
1351
1352
static void bgp_lcommunity_list_cmd_completion(vector comps,
1353
                 struct cmd_token *token)
1354
0
{
1355
0
  struct community_list_master *cm;
1356
1357
0
  cm = community_list_master_lookup(bgp_clist,
1358
0
            LARGE_COMMUNITY_LIST_MASTER);
1359
1360
0
  hash_walk(cm->hash, bgp_community_list_vector_walker, &comps);
1361
0
}
1362
1363
static void bgp_extcommunity_list_cmd_completion(vector comps,
1364
             struct cmd_token *token)
1365
0
{
1366
0
  struct community_list_master *cm;
1367
1368
0
  cm = community_list_master_lookup(bgp_clist, EXTCOMMUNITY_LIST_MASTER);
1369
1370
0
  hash_walk(cm->hash, bgp_community_list_vector_walker, &comps);
1371
0
}
1372
1373
static const struct cmd_variable_handler community_list_handlers[] = {
1374
  {.tokenname = "COMMUNITY_LIST_NAME",
1375
   .completions = bgp_community_list_cmd_completion},
1376
  {.completions = NULL}};
1377
1378
static const struct cmd_variable_handler lcommunity_list_handlers[] = {
1379
  {.tokenname = "LCOMMUNITY_LIST_NAME",
1380
   .completions = bgp_lcommunity_list_cmd_completion},
1381
  {.completions = NULL}};
1382
1383
static const struct cmd_variable_handler extcommunity_list_handlers[] = {
1384
  {.tokenname = "EXTCOMMUNITY_LIST_NAME",
1385
   .completions = bgp_extcommunity_list_cmd_completion},
1386
  {.completions = NULL}};
1387
1388
void bgp_community_list_command_completion_setup(void)
1389
0
{
1390
0
  cmd_variable_handler_register(community_list_handlers);
1391
0
  cmd_variable_handler_register(lcommunity_list_handlers);
1392
0
  cmd_variable_handler_register(extcommunity_list_handlers);
1393
0
}