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