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