[bgpd] Pass NOSUB to regexec
[jleu-quagga.git] / bgpd / bgp_clist.c
blobf75fc55b379ebed0cf4f6729462a97aed37b8734
1 /* BGP community-list and extcommunity-list.
2 Copyright (C) 1999 Kunihiro Ishiguro
4 This file is part of GNU Zebra.
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.
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.
16 You should have received a copy of the GNU General Public License
17 along with GNU Zebra; see the file COPYING. If not, write to the Free
18 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA. */
21 #include <zebra.h>
23 #include "command.h"
24 #include "prefix.h"
25 #include "memory.h"
27 #include "bgpd/bgpd.h"
28 #include "bgpd/bgp_community.h"
29 #include "bgpd/bgp_ecommunity.h"
30 #include "bgpd/bgp_aspath.h"
31 #include "bgpd/bgp_regex.h"
32 #include "bgpd/bgp_clist.h"
34 /* Lookup master structure for community-list or
35 extcommunity-list. */
36 struct community_list_master *
37 community_list_master_lookup (struct community_list_handler *ch, int master)
39 if (ch)
40 switch (master)
42 case COMMUNITY_LIST_MASTER:
43 return &ch->community_list;
44 case EXTCOMMUNITY_LIST_MASTER:
45 return &ch->extcommunity_list;
47 return NULL;
50 /* Allocate a new community list entry. */
51 static struct community_entry *
52 community_entry_new ()
54 struct community_entry *new;
56 new = XMALLOC (MTYPE_COMMUNITY_LIST_ENTRY, sizeof (struct community_entry));
57 memset (new, 0, sizeof (struct community_entry));
58 return new;
61 /* Free community list entry. */
62 static void
63 community_entry_free (struct community_entry *entry)
65 switch (entry->style)
67 case COMMUNITY_LIST_STANDARD:
68 if (entry->u.com)
69 community_free (entry->u.com);
70 break;
71 case EXTCOMMUNITY_LIST_STANDARD:
72 /* In case of standard extcommunity-list, configuration string
73 is made by ecommunity_ecom2str(). */
74 if (entry->config)
75 XFREE (MTYPE_ECOMMUNITY_STR, entry->config);
76 if (entry->u.ecom)
77 ecommunity_free (entry->u.ecom);
78 break;
79 case COMMUNITY_LIST_EXPANDED:
80 case EXTCOMMUNITY_LIST_EXPANDED:
81 if (entry->config)
82 XFREE (MTYPE_COMMUNITY_LIST_CONFIG, entry->config);
83 if (entry->reg)
84 bgp_regex_free (entry->reg);
85 default:
86 break;
88 XFREE (MTYPE_COMMUNITY_LIST_ENTRY, entry);
91 /* Allocate a new community-list. */
92 static struct community_list *
93 community_list_new ()
95 struct community_list *new;
97 new = XMALLOC (MTYPE_COMMUNITY_LIST, sizeof (struct community_list));
98 memset (new, 0, sizeof (struct community_list));
99 return new;
102 /* Free community-list. */
103 static void
104 community_list_free (struct community_list *list)
106 if (list->name)
107 XFREE (MTYPE_COMMUNITY_LIST_NAME, list->name);
108 XFREE (MTYPE_COMMUNITY_LIST, list);
111 static struct community_list *
112 community_list_insert (struct community_list_handler *ch,
113 const char *name, int master)
115 size_t i;
116 long number;
117 struct community_list *new;
118 struct community_list *point;
119 struct community_list_list *list;
120 struct community_list_master *cm;
122 /* Lookup community-list master. */
123 cm = community_list_master_lookup (ch, master);
124 if (!cm)
125 return NULL;
127 /* Allocate new community_list and copy given name. */
128 new = community_list_new ();
129 new->name = XSTRDUP (MTYPE_COMMUNITY_LIST_NAME, name);
131 /* If name is made by all digit character. We treat it as
132 number. */
133 for (number = 0, i = 0; i < strlen (name); i++)
135 if (isdigit ((int) name[i]))
136 number = (number * 10) + (name[i] - '0');
137 else
138 break;
141 /* In case of name is all digit character */
142 if (i == strlen (name))
144 new->sort = COMMUNITY_LIST_NUMBER;
146 /* Set access_list to number list. */
147 list = &cm->num;
149 for (point = list->head; point; point = point->next)
150 if (atol (point->name) >= number)
151 break;
153 else
155 new->sort = COMMUNITY_LIST_STRING;
157 /* Set access_list to string list. */
158 list = &cm->str;
160 /* Set point to insertion point. */
161 for (point = list->head; point; point = point->next)
162 if (strcmp (point->name, name) >= 0)
163 break;
166 /* Link to upper list. */
167 new->parent = list;
169 /* In case of this is the first element of master. */
170 if (list->head == NULL)
172 list->head = list->tail = new;
173 return new;
176 /* In case of insertion is made at the tail of access_list. */
177 if (point == NULL)
179 new->prev = list->tail;
180 list->tail->next = new;
181 list->tail = new;
182 return new;
185 /* In case of insertion is made at the head of access_list. */
186 if (point == list->head)
188 new->next = list->head;
189 list->head->prev = new;
190 list->head = new;
191 return new;
194 /* Insertion is made at middle of the access_list. */
195 new->next = point;
196 new->prev = point->prev;
198 if (point->prev)
199 point->prev->next = new;
200 point->prev = new;
202 return new;
205 struct community_list *
206 community_list_lookup (struct community_list_handler *ch,
207 const char *name, int master)
209 struct community_list *list;
210 struct community_list_master *cm;
212 if (!name)
213 return NULL;
215 cm = community_list_master_lookup (ch, master);
216 if (!cm)
217 return NULL;
219 for (list = cm->num.head; list; list = list->next)
220 if (strcmp (list->name, name) == 0)
221 return list;
222 for (list = cm->str.head; list; list = list->next)
223 if (strcmp (list->name, name) == 0)
224 return list;
226 return NULL;
229 static struct community_list *
230 community_list_get (struct community_list_handler *ch,
231 const char *name, int master)
233 struct community_list *list;
235 list = community_list_lookup (ch, name, master);
236 if (!list)
237 list = community_list_insert (ch, name, master);
238 return list;
241 static void
242 community_list_delete (struct community_list *list)
244 struct community_list_list *clist;
245 struct community_entry *entry, *next;
247 for (entry = list->head; entry; entry = next)
249 next = entry->next;
250 community_entry_free (entry);
253 clist = list->parent;
255 if (list->next)
256 list->next->prev = list->prev;
257 else
258 clist->tail = list->prev;
260 if (list->prev)
261 list->prev->next = list->next;
262 else
263 clist->head = list->next;
265 community_list_free (list);
268 static int
269 community_list_empty_p (struct community_list *list)
271 return (list->head == NULL && list->tail == NULL) ? 1 : 0;
274 /* Add community-list entry to the list. */
275 static void
276 community_list_entry_add (struct community_list *list,
277 struct community_entry *entry)
279 entry->next = NULL;
280 entry->prev = list->tail;
282 if (list->tail)
283 list->tail->next = entry;
284 else
285 list->head = entry;
286 list->tail = entry;
289 /* Delete community-list entry from the list. */
290 static void
291 community_list_entry_delete (struct community_list *list,
292 struct community_entry *entry, int style)
294 if (entry->next)
295 entry->next->prev = entry->prev;
296 else
297 list->tail = entry->prev;
299 if (entry->prev)
300 entry->prev->next = entry->next;
301 else
302 list->head = entry->next;
304 community_entry_free (entry);
306 if (community_list_empty_p (list))
307 community_list_delete (list);
310 /* Lookup community-list entry from the list. */
311 static struct community_entry *
312 community_list_entry_lookup (struct community_list *list, const void *arg,
313 int direct)
315 struct community_entry *entry;
317 for (entry = list->head; entry; entry = entry->next)
319 switch (entry->style)
321 case COMMUNITY_LIST_STANDARD:
322 if (community_cmp (entry->u.com, arg))
323 return entry;
324 break;
325 case EXTCOMMUNITY_LIST_STANDARD:
326 if (ecommunity_cmp (entry->u.ecom, arg))
327 return entry;
328 break;
329 case COMMUNITY_LIST_EXPANDED:
330 case EXTCOMMUNITY_LIST_EXPANDED:
331 if (strcmp (entry->config, arg) == 0)
332 return entry;
333 break;
334 default:
335 break;
338 return NULL;
341 /* Internal function to perform regular expression match for community
342 attribute. */
343 static int
344 community_regexp_match (struct community *com, regex_t * reg)
346 const char *str;
348 /* When there is no communities attribute it is treated as empty
349 string. */
350 if (com == NULL || com->size == 0)
351 str = "";
352 else
353 str = community_str (com);
355 /* Regular expression match. */
356 if (regexec (reg, str, 0, NULL, 0) == 0)
357 return 1;
359 /* No match. */
360 return 0;
363 static int
364 ecommunity_regexp_match (struct ecommunity *ecom, regex_t * reg)
366 const char *str;
368 /* When there is no communities attribute it is treated as empty
369 string. */
370 if (ecom == NULL || ecom->size == 0)
371 str = "";
372 else
373 str = ecommunity_str (ecom);
375 /* Regular expression match. */
376 if (regexec (reg, str, 0, NULL, 0) == 0)
377 return 1;
379 /* No match. */
380 return 0;
383 /* Delete community attribute using regular expression match. Return
384 modified communites attribute. */
385 static struct community *
386 community_regexp_delete (struct community *com, regex_t * reg)
388 int i;
389 u_int32_t comval;
390 /* Maximum is "65535:65535" + '\0'. */
391 char c[12];
392 const char *str;
394 if (!com)
395 return NULL;
397 i = 0;
398 while (i < com->size)
400 memcpy (&comval, com_nthval (com, i), sizeof (u_int32_t));
401 comval = ntohl (comval);
403 switch (comval)
405 case COMMUNITY_INTERNET:
406 str = "internet";
407 break;
408 case COMMUNITY_NO_EXPORT:
409 str = "no-export";
410 break;
411 case COMMUNITY_NO_ADVERTISE:
412 str = "no-advertise";
413 break;
414 case COMMUNITY_LOCAL_AS:
415 str = "local-AS";
416 break;
417 default:
418 sprintf (c, "%d:%d", (comval >> 16) & 0xFFFF, comval & 0xFFFF);
419 str = c;
420 break;
423 if (regexec (reg, str, 0, NULL, 0) == 0)
424 community_del_val (com, com_nthval (com, i));
425 else
426 i++;
428 return com;
431 /* When given community attribute matches to the community-list return
432 1 else return 0. */
434 community_list_match (struct community *com, struct community_list *list)
436 struct community_entry *entry;
438 for (entry = list->head; entry; entry = entry->next)
440 if (entry->any)
441 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
443 if (entry->style == COMMUNITY_LIST_STANDARD)
445 if (community_include (entry->u.com, COMMUNITY_INTERNET))
446 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
448 if (community_match (com, entry->u.com))
449 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
451 else if (entry->style == COMMUNITY_LIST_EXPANDED)
453 if (community_regexp_match (com, entry->reg))
454 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
457 return 0;
461 ecommunity_list_match (struct ecommunity *ecom, struct community_list *list)
463 struct community_entry *entry;
465 for (entry = list->head; entry; entry = entry->next)
467 if (entry->any)
468 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
470 if (entry->style == EXTCOMMUNITY_LIST_STANDARD)
472 if (ecommunity_match (ecom, entry->u.ecom))
473 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
475 else if (entry->style == EXTCOMMUNITY_LIST_EXPANDED)
477 if (ecommunity_regexp_match (ecom, entry->reg))
478 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
481 return 0;
484 /* Perform exact matching. In case of expanded community-list, do
485 same thing as community_list_match(). */
487 community_list_exact_match (struct community *com,
488 struct community_list *list)
490 struct community_entry *entry;
492 for (entry = list->head; entry; entry = entry->next)
494 if (entry->any)
495 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
497 if (entry->style == COMMUNITY_LIST_STANDARD)
499 if (community_include (entry->u.com, COMMUNITY_INTERNET))
500 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
502 if (community_cmp (com, entry->u.com))
503 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
505 else if (entry->style == COMMUNITY_LIST_EXPANDED)
507 if (community_regexp_match (com, entry->reg))
508 return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
511 return 0;
514 /* Delete all permitted communities in the list from com. */
515 struct community *
516 community_list_match_delete (struct community *com,
517 struct community_list *list)
519 struct community_entry *entry;
521 for (entry = list->head; entry; entry = entry->next)
523 if (entry->any)
525 if (entry->direct == COMMUNITY_PERMIT)
527 /* This is a tricky part. Currently only
528 * route_set_community_delete() uses this function. In the
529 * function com->size is zero, it free the community
530 * structure.
532 com->size = 0;
534 return com;
537 if ((entry->style == COMMUNITY_LIST_STANDARD)
538 && (community_include (entry->u.com, COMMUNITY_INTERNET)
539 || community_match (com, entry->u.com) ))
541 if (entry->direct == COMMUNITY_PERMIT)
542 community_delete (com, entry->u.com);
543 else
544 break;
546 else if ((entry->style == COMMUNITY_LIST_EXPANDED)
547 && community_regexp_match (com, entry->reg))
549 if (entry->direct == COMMUNITY_PERMIT)
550 community_regexp_delete (com, entry->reg);
551 else
552 break;
555 return com;
558 /* To avoid duplicated entry in the community-list, this function
559 compares specified entry to existing entry. */
560 static int
561 community_list_dup_check (struct community_list *list,
562 struct community_entry *new)
564 struct community_entry *entry;
566 for (entry = list->head; entry; entry = entry->next)
568 if (entry->style != new->style)
569 continue;
571 if (entry->direct != new->direct)
572 continue;
574 if (entry->any != new->any)
575 continue;
577 if (entry->any)
578 return 1;
580 switch (entry->style)
582 case COMMUNITY_LIST_STANDARD:
583 if (community_cmp (entry->u.com, new->u.com))
584 return 1;
585 break;
586 case EXTCOMMUNITY_LIST_STANDARD:
587 if (ecommunity_cmp (entry->u.ecom, new->u.ecom))
588 return 1;
589 break;
590 case COMMUNITY_LIST_EXPANDED:
591 case EXTCOMMUNITY_LIST_EXPANDED:
592 if (strcmp (entry->config, new->config) == 0)
593 return 1;
594 break;
595 default:
596 break;
599 return 0;
602 /* Set community-list. */
604 community_list_set (struct community_list_handler *ch,
605 const char *name, const char *str, int direct, int style)
607 struct community_entry *entry = NULL;
608 struct community_list *list;
609 struct community *com = NULL;
610 regex_t *regex = NULL;
612 /* Get community list. */
613 list = community_list_get (ch, name, COMMUNITY_LIST_MASTER);
615 /* When community-list already has entry, new entry should have same
616 style. If you want to have mixed style community-list, you can
617 comment out this check. */
618 if (!community_list_empty_p (list))
620 struct community_entry *first;
622 first = list->head;
624 if (style != first->style)
626 return (first->style == COMMUNITY_LIST_STANDARD
627 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
628 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT);
632 if (str)
634 if (style == COMMUNITY_LIST_STANDARD)
635 com = community_str2com (str);
636 else
637 regex = bgp_regcomp (str);
639 if (! com && ! regex)
640 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
643 entry = community_entry_new ();
644 entry->direct = direct;
645 entry->style = style;
646 entry->any = (str ? 0 : 1);
647 entry->u.com = com;
648 entry->reg = regex;
649 entry->config = (regex ? XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG, str) : NULL);
651 /* Do not put duplicated community entry. */
652 if (community_list_dup_check (list, entry))
653 community_entry_free (entry);
654 else
655 community_list_entry_add (list, entry);
657 return 0;
660 /* Unset community-list. When str is NULL, delete all of
661 community-list entry belongs to the specified name. */
663 community_list_unset (struct community_list_handler *ch,
664 const char *name, const char *str,
665 int direct, int style)
667 struct community_entry *entry = NULL;
668 struct community_list *list;
669 struct community *com = NULL;
670 regex_t *regex = NULL;
672 /* Lookup community list. */
673 list = community_list_lookup (ch, name, COMMUNITY_LIST_MASTER);
674 if (list == NULL)
675 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
677 /* Delete all of entry belongs to this community-list. */
678 if (!str)
680 community_list_delete (list);
681 return 0;
684 if (style == COMMUNITY_LIST_STANDARD)
685 com = community_str2com (str);
686 else
687 regex = bgp_regcomp (str);
689 if (! com && ! regex)
690 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
692 if (com)
693 entry = community_list_entry_lookup (list, com, direct);
694 else
695 entry = community_list_entry_lookup (list, str, direct);
697 if (com)
698 community_free (com);
699 if (regex)
700 bgp_regex_free (regex);
702 if (!entry)
703 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
705 community_list_entry_delete (list, entry, style);
707 return 0;
710 /* Set extcommunity-list. */
712 extcommunity_list_set (struct community_list_handler *ch,
713 const char *name, const char *str,
714 int direct, int style)
716 struct community_entry *entry = NULL;
717 struct community_list *list;
718 struct ecommunity *ecom = NULL;
719 regex_t *regex = NULL;
721 entry = NULL;
723 /* Get community list. */
724 list = community_list_get (ch, name, EXTCOMMUNITY_LIST_MASTER);
726 /* When community-list already has entry, new entry should have same
727 style. If you want to have mixed style community-list, you can
728 comment out this check. */
729 if (!community_list_empty_p (list))
731 struct community_entry *first;
733 first = list->head;
735 if (style != first->style)
737 return (first->style == EXTCOMMUNITY_LIST_STANDARD
738 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
739 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT);
743 if (str)
745 if (style == EXTCOMMUNITY_LIST_STANDARD)
746 ecom = ecommunity_str2com (str, 0, 1);
747 else
748 regex = bgp_regcomp (str);
750 if (! ecom && ! regex)
751 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
754 if (ecom)
755 ecom->str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_DISPLAY);
757 entry = community_entry_new ();
758 entry->direct = direct;
759 entry->style = style;
760 entry->any = (str ? 0 : 1);
761 if (ecom)
762 entry->config = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_COMMUNITY_LIST);
763 else if (regex)
764 entry->config = XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG, str);
765 else
766 entry->config = NULL;
767 entry->u.ecom = ecom;
768 entry->reg = regex;
770 /* Do not put duplicated community entry. */
771 if (community_list_dup_check (list, entry))
772 community_entry_free (entry);
773 else
774 community_list_entry_add (list, entry);
776 return 0;
779 /* Unset extcommunity-list. When str is NULL, delete all of
780 extcommunity-list entry belongs to the specified name. */
782 extcommunity_list_unset (struct community_list_handler *ch,
783 const char *name, const char *str,
784 int direct, int style)
786 struct community_entry *entry = NULL;
787 struct community_list *list;
788 struct ecommunity *ecom = NULL;
789 regex_t *regex = NULL;
791 /* Lookup extcommunity list. */
792 list = community_list_lookup (ch, name, EXTCOMMUNITY_LIST_MASTER);
793 if (list == NULL)
794 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
796 /* Delete all of entry belongs to this extcommunity-list. */
797 if (!str)
799 community_list_delete (list);
800 return 0;
803 if (style == EXTCOMMUNITY_LIST_STANDARD)
804 ecom = ecommunity_str2com (str, 0, 1);
805 else
806 regex = bgp_regcomp (str);
808 if (! ecom && ! regex)
809 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
811 if (ecom)
812 entry = community_list_entry_lookup (list, ecom, direct);
813 else
814 entry = community_list_entry_lookup (list, str, direct);
816 if (ecom)
817 ecommunity_free (ecom);
818 if (regex)
819 bgp_regex_free (regex);
821 if (!entry)
822 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
824 community_list_entry_delete (list, entry, style);
826 return 0;
829 /* Initializa community-list. Return community-list handler. */
830 struct community_list_handler *
831 community_list_init (void)
833 struct community_list_handler *ch;
834 ch = XCALLOC (MTYPE_COMMUNITY_LIST_HANDLER,
835 sizeof (struct community_list_handler));
836 return ch;
839 /* Terminate community-list. */
840 static void
841 community_list_terminate (struct community_list_handler *ch)
843 struct community_list_master *cm;
844 struct community_list *list;
846 cm = &ch->community_list;
847 while ((list = cm->num.head) != NULL)
848 community_list_delete (list);
849 while ((list = cm->str.head) != NULL)
850 community_list_delete (list);
852 cm = &ch->extcommunity_list;
853 while ((list = cm->num.head) != NULL)
854 community_list_delete (list);
855 while ((list = cm->str.head) != NULL)
856 community_list_delete (list);
858 XFREE (MTYPE_COMMUNITY_LIST_HANDLER, ch);