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