]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_clist.c
Merge pull request #1069 from donaldsharp/rfc_plist
[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 int style)
279 {
280 if (entry->next)
281 entry->next->prev = entry->prev;
282 else
283 list->tail = entry->prev;
284
285 if (entry->prev)
286 entry->prev->next = entry->next;
287 else
288 list->head = entry->next;
289
290 community_entry_free(entry);
291
292 if (community_list_empty_p(list))
293 community_list_delete(list);
294 }
295
296 /* Lookup community-list entry from the list. */
297 static struct community_entry *
298 community_list_entry_lookup(struct community_list *list, const void *arg,
299 int direct)
300 {
301 struct community_entry *entry;
302
303 for (entry = list->head; entry; entry = entry->next) {
304 switch (entry->style) {
305 case COMMUNITY_LIST_STANDARD:
306 if (entry->direct == direct
307 && community_cmp(entry->u.com, arg))
308 return entry;
309 break;
310 case EXTCOMMUNITY_LIST_STANDARD:
311 if (entry->direct == direct
312 && ecommunity_cmp(entry->u.ecom, arg))
313 return entry;
314 break;
315 case LARGE_COMMUNITY_LIST_STANDARD:
316 if (entry->direct == direct
317 && lcommunity_cmp(entry->u.lcom, arg))
318 return entry;
319 break;
320 case COMMUNITY_LIST_EXPANDED:
321 case EXTCOMMUNITY_LIST_EXPANDED:
322 case LARGE_COMMUNITY_LIST_EXPANDED:
323 if (entry->direct == direct
324 && strcmp(entry->config, arg) == 0)
325 return entry;
326 break;
327 default:
328 break;
329 }
330 }
331 return NULL;
332 }
333
334 static char *community_str_get(struct community *com, int i)
335 {
336 int len;
337 u_int32_t comval;
338 u_int16_t as;
339 u_int16_t val;
340 char *str;
341 char *pnt;
342
343 memcpy(&comval, com_nthval(com, i), sizeof(u_int32_t));
344 comval = ntohl(comval);
345
346 switch (comval) {
347 case COMMUNITY_INTERNET:
348 len = strlen(" internet");
349 break;
350 case COMMUNITY_NO_EXPORT:
351 len = strlen(" no-export");
352 break;
353 case COMMUNITY_NO_ADVERTISE:
354 len = strlen(" no-advertise");
355 break;
356 case COMMUNITY_LOCAL_AS:
357 len = strlen(" local-AS");
358 break;
359 case COMMUNITY_GSHUT:
360 len = strlen(" graceful-shutdown");
361 break;
362 default:
363 len = strlen(" 65536:65535");
364 break;
365 }
366
367 /* Allocate memory. */
368 str = pnt = XMALLOC(MTYPE_COMMUNITY_STR, len);
369
370 switch (comval) {
371 case COMMUNITY_INTERNET:
372 strcpy(pnt, "internet");
373 pnt += strlen("internet");
374 break;
375 case COMMUNITY_NO_EXPORT:
376 strcpy(pnt, "no-export");
377 pnt += strlen("no-export");
378 break;
379 case COMMUNITY_NO_ADVERTISE:
380 strcpy(pnt, "no-advertise");
381 pnt += strlen("no-advertise");
382 break;
383 case COMMUNITY_LOCAL_AS:
384 strcpy(pnt, "local-AS");
385 pnt += strlen("local-AS");
386 break;
387 case COMMUNITY_GSHUT:
388 strcpy(pnt, "graceful-shutdown");
389 pnt += strlen("graceful-shutdown");
390 break;
391 default:
392 as = (comval >> 16) & 0xFFFF;
393 val = comval & 0xFFFF;
394 sprintf(pnt, "%u:%d", as, val);
395 pnt += strlen(pnt);
396 break;
397 }
398
399 *pnt = '\0';
400
401 return str;
402 }
403
404 /* Internal function to perform regular expression match for
405 * a single community. */
406 static int community_regexp_include(regex_t *reg, struct community *com, int i)
407 {
408 char *str;
409 int rv;
410
411 /* When there is no communities attribute it is treated as empty string.
412 */
413 if (com == NULL || com->size == 0)
414 str = XSTRDUP(MTYPE_COMMUNITY_STR, "");
415 else
416 str = community_str_get(com, i);
417
418 /* Regular expression match. */
419 rv = regexec(reg, str, 0, NULL, 0);
420
421 XFREE(MTYPE_COMMUNITY_STR, str);
422
423 if (rv == 0)
424 return 1;
425
426 /* No match. */
427 return 0;
428 }
429
430 /* Internal function to perform regular expression match for community
431 attribute. */
432 static int community_regexp_match(struct community *com, regex_t *reg)
433 {
434 const char *str;
435
436 /* When there is no communities attribute it is treated as empty
437 string. */
438 if (com == NULL || com->size == 0)
439 str = "";
440 else
441 str = community_str(com);
442
443 /* Regular expression match. */
444 if (regexec(reg, str, 0, NULL, 0) == 0)
445 return 1;
446
447 /* No match. */
448 return 0;
449 }
450
451 static char *lcommunity_str_get(struct lcommunity *lcom, int i)
452 {
453 struct lcommunity_val lcomval;
454 u_int32_t globaladmin;
455 u_int32_t localdata1;
456 u_int32_t localdata2;
457 char *str;
458 u_char *ptr;
459 char *pnt;
460
461 ptr = lcom->val + (i * LCOMMUNITY_SIZE);
462
463 memcpy(&lcomval, ptr, LCOMMUNITY_SIZE);
464
465 /* Allocate memory. 48 bytes taken off bgp_lcommunity.c */
466 str = pnt = XMALLOC(MTYPE_LCOMMUNITY_STR, 48);
467
468 ptr = (u_char *)lcomval.val;
469 ptr = ptr_get_be32(ptr, &globaladmin);
470 ptr = ptr_get_be32(ptr, &localdata1);
471 ptr = ptr_get_be32(ptr, &localdata2);
472 (void)ptr; /* consume value */
473
474 sprintf(pnt, "%u:%u:%u", globaladmin, localdata1, localdata2);
475 pnt += strlen(pnt);
476 *pnt = '\0';
477
478 return str;
479 }
480
481 /* Internal function to perform regular expression match for
482 * a single community. */
483 static int lcommunity_regexp_include(regex_t *reg, struct lcommunity *lcom,
484 int i)
485 {
486 char *str;
487
488 /* When there is no communities attribute it is treated as empty string.
489 */
490 if (lcom == NULL || lcom->size == 0)
491 str = XSTRDUP(MTYPE_LCOMMUNITY_STR, "");
492 else
493 str = lcommunity_str_get(lcom, i);
494
495 /* Regular expression match. */
496 if (regexec(reg, str, 0, NULL, 0) == 0) {
497 XFREE(MTYPE_LCOMMUNITY_STR, str);
498 return 1;
499 }
500
501 XFREE(MTYPE_LCOMMUNITY_STR, str);
502 /* No match. */
503 return 0;
504 }
505
506 static int lcommunity_regexp_match(struct lcommunity *com, regex_t *reg)
507 {
508 const char *str;
509
510 /* When there is no communities attribute it is treated as empty
511 string. */
512 if (com == NULL || com->size == 0)
513 str = "";
514 else
515 str = lcommunity_str(com);
516
517 /* Regular expression match. */
518 if (regexec(reg, str, 0, NULL, 0) == 0)
519 return 1;
520
521 /* No match. */
522 return 0;
523 }
524
525
526 static int ecommunity_regexp_match(struct ecommunity *ecom, regex_t *reg)
527 {
528 const char *str;
529
530 /* When there is no communities attribute it is treated as empty
531 string. */
532 if (ecom == NULL || ecom->size == 0)
533 str = "";
534 else
535 str = ecommunity_str(ecom);
536
537 /* Regular expression match. */
538 if (regexec(reg, str, 0, NULL, 0) == 0)
539 return 1;
540
541 /* No match. */
542 return 0;
543 }
544
545 #if 0
546 /* Delete community attribute using regular expression match. Return
547 modified communites attribute. */
548 static struct community *
549 community_regexp_delete (struct community *com, regex_t * reg)
550 {
551 int i;
552 u_int32_t comval;
553 /* Maximum is "65535:65535" + '\0'. */
554 char c[12];
555 const char *str;
556
557 if (!com)
558 return NULL;
559
560 i = 0;
561 while (i < com->size)
562 {
563 memcpy (&comval, com_nthval (com, i), sizeof (u_int32_t));
564 comval = ntohl (comval);
565
566 switch (comval)
567 {
568 case COMMUNITY_INTERNET:
569 str = "internet";
570 break;
571 case COMMUNITY_NO_EXPORT:
572 str = "no-export";
573 break;
574 case COMMUNITY_NO_ADVERTISE:
575 str = "no-advertise";
576 break;
577 case COMMUNITY_LOCAL_AS:
578 str = "local-AS";
579 break;
580 default:
581 sprintf (c, "%d:%d", (comval >> 16) & 0xFFFF, comval & 0xFFFF);
582 str = c;
583 break;
584 }
585
586 if (regexec (reg, str, 0, NULL, 0) == 0)
587 community_del_val (com, com_nthval (com, i));
588 else
589 i++;
590 }
591 return com;
592 }
593 #endif
594
595 /* When given community attribute matches to the community-list return
596 1 else return 0. */
597 int community_list_match(struct community *com, struct community_list *list)
598 {
599 struct community_entry *entry;
600
601 for (entry = list->head; entry; entry = entry->next) {
602 if (entry->any)
603 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
604
605 if (entry->style == COMMUNITY_LIST_STANDARD) {
606 if (community_include(entry->u.com, COMMUNITY_INTERNET))
607 return entry->direct == COMMUNITY_PERMIT ? 1
608 : 0;
609
610 if (community_match(com, entry->u.com))
611 return entry->direct == COMMUNITY_PERMIT ? 1
612 : 0;
613 } else if (entry->style == COMMUNITY_LIST_EXPANDED) {
614 if (community_regexp_match(com, entry->reg))
615 return entry->direct == COMMUNITY_PERMIT ? 1
616 : 0;
617 }
618 }
619 return 0;
620 }
621
622 int lcommunity_list_match(struct lcommunity *lcom, struct community_list *list)
623 {
624 struct community_entry *entry;
625
626 for (entry = list->head; entry; entry = entry->next) {
627 if (entry->any)
628 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
629
630 if (entry->style == LARGE_COMMUNITY_LIST_STANDARD) {
631 if (lcommunity_match(lcom, entry->u.lcom))
632 return entry->direct == COMMUNITY_PERMIT ? 1
633 : 0;
634 } else if (entry->style == LARGE_COMMUNITY_LIST_EXPANDED) {
635 if (lcommunity_regexp_match(lcom, entry->reg))
636 return entry->direct == COMMUNITY_PERMIT ? 1
637 : 0;
638 }
639 }
640 return 0;
641 }
642
643 int ecommunity_list_match(struct ecommunity *ecom, struct community_list *list)
644 {
645 struct community_entry *entry;
646
647 for (entry = list->head; entry; entry = entry->next) {
648 if (entry->any)
649 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
650
651 if (entry->style == EXTCOMMUNITY_LIST_STANDARD) {
652 if (ecommunity_match(ecom, entry->u.ecom))
653 return entry->direct == COMMUNITY_PERMIT ? 1
654 : 0;
655 } else if (entry->style == EXTCOMMUNITY_LIST_EXPANDED) {
656 if (ecommunity_regexp_match(ecom, entry->reg))
657 return entry->direct == COMMUNITY_PERMIT ? 1
658 : 0;
659 }
660 }
661 return 0;
662 }
663
664 /* Perform exact matching. In case of expanded community-list, do
665 same thing as community_list_match(). */
666 int community_list_exact_match(struct community *com,
667 struct community_list *list)
668 {
669 struct community_entry *entry;
670
671 for (entry = list->head; entry; entry = entry->next) {
672 if (entry->any)
673 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
674
675 if (entry->style == COMMUNITY_LIST_STANDARD) {
676 if (community_include(entry->u.com, COMMUNITY_INTERNET))
677 return entry->direct == COMMUNITY_PERMIT ? 1
678 : 0;
679
680 if (community_cmp(com, entry->u.com))
681 return entry->direct == COMMUNITY_PERMIT ? 1
682 : 0;
683 } else if (entry->style == COMMUNITY_LIST_EXPANDED) {
684 if (community_regexp_match(com, entry->reg))
685 return entry->direct == COMMUNITY_PERMIT ? 1
686 : 0;
687 }
688 }
689 return 0;
690 }
691
692 /* Delete all permitted communities in the list from com. */
693 struct community *community_list_match_delete(struct community *com,
694 struct community_list *list)
695 {
696 struct community_entry *entry;
697 u_int32_t val;
698 u_int32_t com_index_to_delete[com->size];
699 int delete_index = 0;
700 int i;
701
702 /* Loop over each community value and evaluate each against the
703 * community-list. If we need to delete a community value add its index
704 * to com_index_to_delete.
705 */
706 for (i = 0; i < com->size; i++) {
707 val = community_val_get(com, i);
708
709 for (entry = list->head; entry; entry = entry->next) {
710 if (entry->any) {
711 if (entry->direct == COMMUNITY_PERMIT) {
712 com_index_to_delete[delete_index] = i;
713 delete_index++;
714 }
715 break;
716 }
717
718 else if ((entry->style == COMMUNITY_LIST_STANDARD)
719 && (community_include(entry->u.com,
720 COMMUNITY_INTERNET)
721 || community_include(entry->u.com, val))) {
722 if (entry->direct == COMMUNITY_PERMIT) {
723 com_index_to_delete[delete_index] = i;
724 delete_index++;
725 }
726 break;
727 }
728
729 else if ((entry->style == COMMUNITY_LIST_EXPANDED)
730 && community_regexp_include(entry->reg, com,
731 i)) {
732 if (entry->direct == COMMUNITY_PERMIT) {
733 com_index_to_delete[delete_index] = i;
734 delete_index++;
735 }
736 break;
737 }
738 }
739 }
740
741 /* Delete all of the communities we flagged for deletion */
742 for (i = delete_index - 1; i >= 0; i--) {
743 val = community_val_get(com, com_index_to_delete[i]);
744 community_del_val(com, &val);
745 }
746
747 return com;
748 }
749
750 /* To avoid duplicated entry in the community-list, this function
751 compares specified entry to existing entry. */
752 static int community_list_dup_check(struct community_list *list,
753 struct community_entry *new)
754 {
755 struct community_entry *entry;
756
757 for (entry = list->head; entry; entry = entry->next) {
758 if (entry->style != new->style)
759 continue;
760
761 if (entry->direct != new->direct)
762 continue;
763
764 if (entry->any != new->any)
765 continue;
766
767 if (entry->any)
768 return 1;
769
770 switch (entry->style) {
771 case COMMUNITY_LIST_STANDARD:
772 if (community_cmp(entry->u.com, new->u.com))
773 return 1;
774 break;
775 case LARGE_COMMUNITY_LIST_STANDARD:
776 if (lcommunity_cmp(entry->u.lcom, new->u.lcom))
777 return 1;
778 break;
779 case EXTCOMMUNITY_LIST_STANDARD:
780 if (ecommunity_cmp(entry->u.ecom, new->u.ecom))
781 return 1;
782 break;
783 case COMMUNITY_LIST_EXPANDED:
784 case EXTCOMMUNITY_LIST_EXPANDED:
785 case LARGE_COMMUNITY_LIST_EXPANDED:
786 if (strcmp(entry->config, new->config) == 0)
787 return 1;
788 break;
789 default:
790 break;
791 }
792 }
793 return 0;
794 }
795
796 /* Set community-list. */
797 int community_list_set(struct community_list_handler *ch, const char *name,
798 const char *str, int direct, int style)
799 {
800 struct community_entry *entry = NULL;
801 struct community_list *list;
802 struct community *com = NULL;
803 regex_t *regex = NULL;
804
805 /* Get community list. */
806 list = community_list_get(ch, name, COMMUNITY_LIST_MASTER);
807
808 /* When community-list already has entry, new entry should have same
809 style. If you want to have mixed style community-list, you can
810 comment out this check. */
811 if (!community_list_empty_p(list)) {
812 struct community_entry *first;
813
814 first = list->head;
815
816 if (style != first->style) {
817 return (first->style == COMMUNITY_LIST_STANDARD
818 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
819 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT);
820 }
821 }
822
823 if (str) {
824 if (style == COMMUNITY_LIST_STANDARD)
825 com = community_str2com(str);
826 else
827 regex = bgp_regcomp(str);
828
829 if (!com && !regex)
830 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
831 }
832
833 entry = community_entry_new();
834 entry->direct = direct;
835 entry->style = style;
836 entry->any = (str ? 0 : 1);
837 entry->u.com = com;
838 entry->reg = regex;
839 entry->config =
840 (regex ? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG, str) : NULL);
841
842 /* Do not put duplicated community entry. */
843 if (community_list_dup_check(list, entry))
844 community_entry_free(entry);
845 else {
846 community_list_entry_add(list, entry);
847 route_map_notify_dependencies(name, RMAP_EVENT_CLIST_ADDED);
848 }
849
850 return 0;
851 }
852
853 /* Unset community-list */
854 int community_list_unset(struct community_list_handler *ch, const char *name,
855 const char *str, int direct, int style, int delete_all)
856 {
857 struct community_entry *entry = NULL;
858 struct community_list *list;
859 struct community *com = NULL;
860
861 /* Lookup community list. */
862 list = community_list_lookup(ch, name, COMMUNITY_LIST_MASTER);
863 if (list == NULL)
864 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
865
866 /* Delete all of entry belongs to this community-list. */
867 if (delete_all) {
868 community_list_delete(list);
869 route_map_notify_dependencies(name, RMAP_EVENT_CLIST_DELETED);
870 return 0;
871 }
872
873 if (style == COMMUNITY_LIST_STANDARD) {
874 if (str)
875 com = community_str2com(str);
876 }
877
878 if (com) {
879 entry = community_list_entry_lookup(list, com, direct);
880 community_free(com);
881 } else
882 entry = community_list_entry_lookup(list, str, direct);
883
884 if (!entry)
885 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
886
887 community_list_entry_delete(list, entry, style);
888 route_map_notify_dependencies(name, RMAP_EVENT_CLIST_DELETED);
889
890 return 0;
891 }
892
893 /* Delete all permitted large communities in the list from com. */
894 struct lcommunity *lcommunity_list_match_delete(struct lcommunity *lcom,
895 struct community_list *list)
896 {
897 struct community_entry *entry;
898 u_int32_t com_index_to_delete[lcom->size];
899 u_char *ptr;
900 int delete_index = 0;
901 int i;
902
903 /* Loop over each lcommunity value and evaluate each against the
904 * community-list. If we need to delete a community value add its index
905 * to com_index_to_delete.
906 */
907 for (i = 0; i < lcom->size; i++) {
908 ptr = lcom->val + (i * LCOMMUNITY_SIZE);
909 for (entry = list->head; entry; entry = entry->next) {
910 if (entry->any) {
911 if (entry->direct == COMMUNITY_PERMIT) {
912 com_index_to_delete[delete_index] = i;
913 delete_index++;
914 }
915 break;
916 }
917
918 else if ((entry->style == LARGE_COMMUNITY_LIST_STANDARD)
919 && lcommunity_include(entry->u.lcom, ptr)) {
920 if (entry->direct == COMMUNITY_PERMIT) {
921 com_index_to_delete[delete_index] = i;
922 delete_index++;
923 }
924 break;
925 }
926
927 else if ((entry->style == LARGE_COMMUNITY_LIST_EXPANDED)
928 && lcommunity_regexp_include(entry->reg, lcom,
929 i)) {
930 if (entry->direct == COMMUNITY_PERMIT) {
931 com_index_to_delete[delete_index] = i;
932 delete_index++;
933 }
934 break;
935 }
936 }
937 }
938
939 /* Delete all of the communities we flagged for deletion */
940 for (i = delete_index - 1; i >= 0; i--) {
941 ptr = lcom->val + (com_index_to_delete[i] * LCOMMUNITY_SIZE);
942 lcommunity_del_val(lcom, ptr);
943 }
944
945 return lcom;
946 }
947
948 /* Set lcommunity-list. */
949 int lcommunity_list_set(struct community_list_handler *ch, const char *name,
950 const char *str, int direct, int style)
951 {
952 struct community_entry *entry = NULL;
953 struct community_list *list;
954 struct lcommunity *lcom = NULL;
955 regex_t *regex = NULL;
956
957 /* Get community list. */
958 list = community_list_get(ch, name, LARGE_COMMUNITY_LIST_MASTER);
959
960 /* When community-list already has entry, new entry should have same
961 style. If you want to have mixed style community-list, you can
962 comment out this check. */
963 if (!community_list_empty_p(list)) {
964 struct community_entry *first;
965
966 first = list->head;
967
968 if (style != first->style) {
969 return (first->style == COMMUNITY_LIST_STANDARD
970 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
971 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT);
972 }
973 }
974
975 if (str) {
976 if (style == LARGE_COMMUNITY_LIST_STANDARD)
977 lcom = lcommunity_str2com(str);
978 else
979 regex = bgp_regcomp(str);
980
981 if (!lcom && !regex)
982 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
983 }
984
985 entry = community_entry_new();
986 entry->direct = direct;
987 entry->style = style;
988 entry->any = (str ? 0 : 1);
989 entry->u.lcom = lcom;
990 entry->reg = regex;
991 if (lcom)
992 entry->config = lcommunity_lcom2str(
993 lcom, LCOMMUNITY_FORMAT_COMMUNITY_LIST);
994 else if (regex)
995 entry->config = XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG, str);
996 else
997 entry->config = NULL;
998
999 /* Do not put duplicated community entry. */
1000 if (community_list_dup_check(list, entry))
1001 community_entry_free(entry);
1002 else
1003 community_list_entry_add(list, entry);
1004
1005 return 0;
1006 }
1007
1008 /* Unset community-list. When str is NULL, delete all of
1009 community-list entry belongs to the specified name. */
1010 int lcommunity_list_unset(struct community_list_handler *ch, const char *name,
1011 const char *str, int direct, int style)
1012 {
1013 struct community_entry *entry = NULL;
1014 struct community_list *list;
1015 struct lcommunity *lcom = NULL;
1016 regex_t *regex = NULL;
1017
1018 /* Lookup community list. */
1019 list = community_list_lookup(ch, name, LARGE_COMMUNITY_LIST_MASTER);
1020 if (list == NULL)
1021 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
1022
1023 /* Delete all of entry belongs to this community-list. */
1024 if (!str) {
1025 community_list_delete(list);
1026 return 0;
1027 }
1028
1029 if (style == LARGE_COMMUNITY_LIST_STANDARD)
1030 lcom = lcommunity_str2com(str);
1031 else
1032 regex = bgp_regcomp(str);
1033
1034 if (!lcom && !regex)
1035 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
1036
1037 if (lcom)
1038 entry = community_list_entry_lookup(list, lcom, direct);
1039 else
1040 entry = community_list_entry_lookup(list, str, direct);
1041
1042 if (lcom)
1043 lcommunity_free(&lcom);
1044 if (regex)
1045 bgp_regex_free(regex);
1046
1047 if (!entry)
1048 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
1049
1050 community_list_entry_delete(list, entry, style);
1051
1052 return 0;
1053 }
1054
1055 /* Set extcommunity-list. */
1056 int extcommunity_list_set(struct community_list_handler *ch, const char *name,
1057 const char *str, int direct, int style)
1058 {
1059 struct community_entry *entry = NULL;
1060 struct community_list *list;
1061 struct ecommunity *ecom = NULL;
1062 regex_t *regex = NULL;
1063
1064 entry = NULL;
1065
1066 /* Get community list. */
1067 list = community_list_get(ch, name, EXTCOMMUNITY_LIST_MASTER);
1068
1069 /* When community-list already has entry, new entry should have same
1070 style. If you want to have mixed style community-list, you can
1071 comment out this check. */
1072 if (!community_list_empty_p(list)) {
1073 struct community_entry *first;
1074
1075 first = list->head;
1076
1077 if (style != first->style) {
1078 return (first->style == EXTCOMMUNITY_LIST_STANDARD
1079 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
1080 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT);
1081 }
1082 }
1083
1084 if (style == EXTCOMMUNITY_LIST_STANDARD)
1085 ecom = ecommunity_str2com(str, 0, 1);
1086 else
1087 regex = bgp_regcomp(str);
1088
1089 if (!ecom && !regex)
1090 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
1091
1092 if (ecom)
1093 ecom->str =
1094 ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_DISPLAY, 0);
1095
1096 entry = community_entry_new();
1097 entry->direct = direct;
1098 entry->style = style;
1099 entry->any = (str ? 0 : 1);
1100 if (ecom)
1101 entry->config = ecommunity_ecom2str(
1102 ecom, ECOMMUNITY_FORMAT_COMMUNITY_LIST, 0);
1103 else if (regex)
1104 entry->config = XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG, str);
1105
1106 entry->u.ecom = ecom;
1107 entry->reg = regex;
1108
1109 /* Do not put duplicated community entry. */
1110 if (community_list_dup_check(list, entry))
1111 community_entry_free(entry);
1112 else {
1113 community_list_entry_add(list, entry);
1114 route_map_notify_dependencies(name, RMAP_EVENT_ECLIST_ADDED);
1115 }
1116
1117 return 0;
1118 }
1119
1120 /* Unset extcommunity-list. When str is NULL, delete all of
1121 extcommunity-list entry belongs to the specified name. */
1122 int extcommunity_list_unset(struct community_list_handler *ch, const char *name,
1123 const char *str, int direct, int style,
1124 int delete_all)
1125 {
1126 struct community_entry *entry = NULL;
1127 struct community_list *list;
1128 struct ecommunity *ecom = NULL;
1129
1130 /* Lookup extcommunity list. */
1131 list = community_list_lookup(ch, name, EXTCOMMUNITY_LIST_MASTER);
1132 if (list == NULL)
1133 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
1134
1135 /* Delete all of entry belongs to this extcommunity-list. */
1136 if (delete_all) {
1137 community_list_delete(list);
1138 route_map_notify_dependencies(name, RMAP_EVENT_ECLIST_DELETED);
1139 return 0;
1140 }
1141
1142 if (style == EXTCOMMUNITY_LIST_STANDARD) {
1143 if (str)
1144 ecom = ecommunity_str2com(str, 0, 1);
1145 }
1146
1147 if (ecom) {
1148 entry = community_list_entry_lookup(list, ecom, direct);
1149 ecommunity_free(&ecom);
1150 } else
1151 entry = community_list_entry_lookup(list, str, direct);
1152
1153 if (!entry)
1154 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
1155
1156 community_list_entry_delete(list, entry, style);
1157 route_map_notify_dependencies(name, RMAP_EVENT_ECLIST_DELETED);
1158
1159 return 0;
1160 }
1161
1162 /* Initializa community-list. Return community-list handler. */
1163 struct community_list_handler *community_list_init(void)
1164 {
1165 struct community_list_handler *ch;
1166 ch = XCALLOC(MTYPE_COMMUNITY_LIST_HANDLER,
1167 sizeof(struct community_list_handler));
1168 return ch;
1169 }
1170
1171 /* Terminate community-list. */
1172 void community_list_terminate(struct community_list_handler *ch)
1173 {
1174 struct community_list_master *cm;
1175 struct community_list *list;
1176
1177 cm = &ch->community_list;
1178 while ((list = cm->num.head) != NULL)
1179 community_list_delete(list);
1180 while ((list = cm->str.head) != NULL)
1181 community_list_delete(list);
1182
1183 cm = &ch->lcommunity_list;
1184 while ((list = cm->num.head) != NULL)
1185 community_list_delete(list);
1186 while ((list = cm->str.head) != NULL)
1187 community_list_delete(list);
1188
1189 cm = &ch->extcommunity_list;
1190 while ((list = cm->num.head) != NULL)
1191 community_list_delete(list);
1192 while ((list = cm->str.head) != NULL)
1193 community_list_delete(list);
1194
1195 XFREE(MTYPE_COMMUNITY_LIST_HANDLER, ch);
1196 }