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