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
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
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
36 struct community_list_master
*
37 community_list_master_lookup (struct community_list_handler
*ch
, int master
)
42 case COMMUNITY_LIST_MASTER
:
43 return &ch
->community_list
;
44 case EXTCOMMUNITY_LIST_MASTER
:
45 return &ch
->extcommunity_list
;
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
));
61 /* Free community list entry. */
63 community_entry_free (struct community_entry
*entry
)
67 case COMMUNITY_LIST_STANDARD
:
69 community_free (entry
->u
.com
);
71 case EXTCOMMUNITY_LIST_STANDARD
:
72 /* In case of standard extcommunity-list, configuration string
73 is made by ecommunity_ecom2str(). */
75 XFREE (MTYPE_ECOMMUNITY_STR
, entry
->config
);
77 ecommunity_free (entry
->u
.ecom
);
79 case COMMUNITY_LIST_EXPANDED
:
80 case EXTCOMMUNITY_LIST_EXPANDED
:
82 XFREE (MTYPE_COMMUNITY_LIST_CONFIG
, entry
->config
);
84 bgp_regex_free (entry
->reg
);
88 XFREE (MTYPE_COMMUNITY_LIST_ENTRY
, entry
);
91 /* Allocate a new community-list. */
92 static struct community_list
*
95 struct community_list
*new;
97 new = XMALLOC (MTYPE_COMMUNITY_LIST
, sizeof (struct community_list
));
98 memset (new, 0, sizeof (struct community_list
));
102 /* Free community-list. */
104 community_list_free (struct community_list
*list
)
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
)
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
);
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
133 for (number
= 0, i
= 0; i
< strlen (name
); i
++)
135 if (isdigit ((int) name
[i
]))
136 number
= (number
* 10) + (name
[i
] - '0');
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. */
149 for (point
= list
->head
; point
; point
= point
->next
)
150 if (atol (point
->name
) >= number
)
155 new->sort
= COMMUNITY_LIST_STRING
;
157 /* Set access_list to string list. */
160 /* Set point to insertion point. */
161 for (point
= list
->head
; point
; point
= point
->next
)
162 if (strcmp (point
->name
, name
) >= 0)
166 /* Link to upper list. */
169 /* In case of this is the first element of master. */
170 if (list
->head
== NULL
)
172 list
->head
= list
->tail
= new;
176 /* In case of insertion is made at the tail of access_list. */
179 new->prev
= list
->tail
;
180 list
->tail
->next
= 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;
194 /* Insertion is made at middle of the access_list. */
196 new->prev
= point
->prev
;
199 point
->prev
->next
= 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
;
215 cm
= community_list_master_lookup (ch
, master
);
219 for (list
= cm
->num
.head
; list
; list
= list
->next
)
220 if (strcmp (list
->name
, name
) == 0)
222 for (list
= cm
->str
.head
; list
; list
= list
->next
)
223 if (strcmp (list
->name
, name
) == 0)
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
);
237 list
= community_list_insert (ch
, name
, master
);
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
)
250 community_entry_free (entry
);
253 clist
= list
->parent
;
256 list
->next
->prev
= list
->prev
;
258 clist
->tail
= list
->prev
;
261 list
->prev
->next
= list
->next
;
263 clist
->head
= list
->next
;
265 community_list_free (list
);
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. */
276 community_list_entry_add (struct community_list
*list
,
277 struct community_entry
*entry
)
280 entry
->prev
= list
->tail
;
283 list
->tail
->next
= entry
;
289 /* Delete community-list entry from the list. */
291 community_list_entry_delete (struct community_list
*list
,
292 struct community_entry
*entry
, int style
)
295 entry
->next
->prev
= entry
->prev
;
297 list
->tail
= entry
->prev
;
300 entry
->prev
->next
= entry
->next
;
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
,
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
))
325 case EXTCOMMUNITY_LIST_STANDARD
:
326 if (ecommunity_cmp (entry
->u
.ecom
, arg
))
329 case COMMUNITY_LIST_EXPANDED
:
330 case EXTCOMMUNITY_LIST_EXPANDED
:
331 if (strcmp (entry
->config
, arg
) == 0)
341 /* Internal function to perform regular expression match for community
344 community_regexp_match (struct community
*com
, regex_t
* reg
)
348 /* When there is no communities attribute it is treated as empty
350 if (com
== NULL
|| com
->size
== 0)
353 str
= community_str (com
);
355 /* Regular expression match. */
356 if (regexec (reg
, str
, 0, NULL
, 0) == 0)
364 ecommunity_regexp_match (struct ecommunity
*ecom
, regex_t
* reg
)
368 /* When there is no communities attribute it is treated as empty
370 if (ecom
== NULL
|| ecom
->size
== 0)
373 str
= ecommunity_str (ecom
);
375 /* Regular expression match. */
376 if (regexec (reg
, str
, 0, NULL
, 0) == 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
)
390 /* Maximum is "65535:65535" + '\0'. */
398 while (i
< com
->size
)
400 memcpy (&comval
, com_nthval (com
, i
), sizeof (u_int32_t
));
401 comval
= ntohl (comval
);
405 case COMMUNITY_INTERNET
:
408 case COMMUNITY_NO_EXPORT
:
411 case COMMUNITY_NO_ADVERTISE
:
412 str
= "no-advertise";
414 case COMMUNITY_LOCAL_AS
:
418 sprintf (c
, "%d:%d", (comval
>> 16) & 0xFFFF, comval
& 0xFFFF);
423 if (regexec (reg
, str
, 0, NULL
, 0) == 0)
424 community_del_val (com
, com_nthval (com
, i
));
431 /* When given community attribute matches to the community-list return
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
)
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;
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
)
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;
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
)
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;
514 /* Delete all permitted communities in the list from com. */
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
)
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
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
);
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
);
558 /* To avoid duplicated entry in the community-list, this function
559 compares specified entry to existing entry. */
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
)
571 if (entry
->direct
!= new->direct
)
574 if (entry
->any
!= new->any
)
580 switch (entry
->style
)
582 case COMMUNITY_LIST_STANDARD
:
583 if (community_cmp (entry
->u
.com
, new->u
.com
))
586 case EXTCOMMUNITY_LIST_STANDARD
:
587 if (ecommunity_cmp (entry
->u
.ecom
, new->u
.ecom
))
590 case COMMUNITY_LIST_EXPANDED
:
591 case EXTCOMMUNITY_LIST_EXPANDED
:
592 if (strcmp (entry
->config
, new->config
) == 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
;
624 if (style
!= first
->style
)
626 return (first
->style
== COMMUNITY_LIST_STANDARD
627 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
628 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT
);
634 if (style
== COMMUNITY_LIST_STANDARD
)
635 com
= community_str2com (str
);
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);
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
);
655 community_list_entry_add (list
, entry
);
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
);
675 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
677 /* Delete all of entry belongs to this community-list. */
680 community_list_delete (list
);
684 if (style
== COMMUNITY_LIST_STANDARD
)
685 com
= community_str2com (str
);
687 regex
= bgp_regcomp (str
);
689 if (! com
&& ! regex
)
690 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
693 entry
= community_list_entry_lookup (list
, com
, direct
);
695 entry
= community_list_entry_lookup (list
, str
, direct
);
698 community_free (com
);
700 bgp_regex_free (regex
);
703 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
705 community_list_entry_delete (list
, entry
, style
);
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
;
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
;
735 if (style
!= first
->style
)
737 return (first
->style
== EXTCOMMUNITY_LIST_STANDARD
738 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
739 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT
);
745 if (style
== EXTCOMMUNITY_LIST_STANDARD
)
746 ecom
= ecommunity_str2com (str
, 0, 1);
748 regex
= bgp_regcomp (str
);
750 if (! ecom
&& ! regex
)
751 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
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);
762 entry
->config
= ecommunity_ecom2str (ecom
, ECOMMUNITY_FORMAT_COMMUNITY_LIST
);
764 entry
->config
= XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG
, str
);
766 entry
->config
= NULL
;
767 entry
->u
.ecom
= ecom
;
770 /* Do not put duplicated community entry. */
771 if (community_list_dup_check (list
, entry
))
772 community_entry_free (entry
);
774 community_list_entry_add (list
, entry
);
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
);
794 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
796 /* Delete all of entry belongs to this extcommunity-list. */
799 community_list_delete (list
);
803 if (style
== EXTCOMMUNITY_LIST_STANDARD
)
804 ecom
= ecommunity_str2com (str
, 0, 1);
806 regex
= bgp_regcomp (str
);
808 if (! ecom
&& ! regex
)
809 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
812 entry
= community_list_entry_lookup (list
, ecom
, direct
);
814 entry
= community_list_entry_lookup (list
, str
, direct
);
817 ecommunity_free (ecom
);
819 bgp_regex_free (regex
);
822 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
824 community_list_entry_delete (list
, entry
, style
);
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
));
839 /* Terminate community-list. */
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
);