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
;
45 case EXTCOMMUNITY_LIST_MASTER
:
46 return &ch
->extcommunity_list
;
51 /* Allocate a new community list entry. */
52 struct community_entry
*
53 community_entry_new ()
55 struct community_entry
*new;
57 new = XMALLOC (MTYPE_COMMUNITY_LIST_ENTRY
, sizeof (struct community_entry
));
58 memset (new, 0, sizeof (struct community_entry
));
62 /* Free community list entry. */
64 community_entry_free (struct community_entry
*entry
)
68 case COMMUNITY_LIST_STANDARD
:
70 community_free (entry
->u
.com
);
72 case EXTCOMMUNITY_LIST_STANDARD
:
73 /* In case of standard extcommunity-list, configuration string
74 is made by ecommunity_ecom2str(). */
76 XFREE (MTYPE_ECOMMUNITY_STR
, entry
->config
);
78 ecommunity_free (entry
->u
.ecom
);
80 case COMMUNITY_LIST_EXPANDED
:
81 case EXTCOMMUNITY_LIST_EXPANDED
:
83 XFREE (MTYPE_COMMUNITY_LIST_CONFIG
, entry
->config
);
85 bgp_regex_free (entry
->reg
);
89 XFREE (MTYPE_COMMUNITY_LIST_ENTRY
, entry
);
92 /* Allocate a new community-list. */
93 struct community_list
*
96 struct community_list
*new;
98 new = XMALLOC (MTYPE_COMMUNITY_LIST
, sizeof (struct community_list
));
99 memset (new, 0, sizeof (struct community_list
));
103 /* Free community-list. */
105 community_list_free (struct community_list
*list
)
108 XFREE (MTYPE_COMMUNITY_LIST_NAME
, list
->name
);
109 XFREE (MTYPE_COMMUNITY_LIST
, list
);
112 struct community_list
*
113 community_list_insert (struct community_list_handler
*ch
,
114 char *name
, int master
)
118 struct community_list
*new;
119 struct community_list
*point
;
120 struct community_list_list
*list
;
121 struct community_list_master
*cm
;
123 /* Lookup community-list master. */
124 cm
= community_list_master_lookup (ch
, master
);
128 /* Allocate new community_list and copy given name. */
129 new = community_list_new ();
130 new->name
= XSTRDUP (MTYPE_COMMUNITY_LIST_NAME
, name
);
132 /* If name is made by all digit character. We treat it as
134 for (number
= 0, i
= 0; i
< strlen (name
); i
++)
136 if (isdigit ((int) name
[i
]))
137 number
= (number
* 10) + (name
[i
] - '0');
142 /* In case of name is all digit character */
143 if (i
== strlen (name
))
145 new->sort
= COMMUNITY_LIST_NUMBER
;
147 /* Set access_list to number list. */
150 for (point
= list
->head
; point
; point
= point
->next
)
151 if (atol (point
->name
) >= number
)
156 new->sort
= COMMUNITY_LIST_STRING
;
158 /* Set access_list to string list. */
161 /* Set point to insertion point. */
162 for (point
= list
->head
; point
; point
= point
->next
)
163 if (strcmp (point
->name
, name
) >= 0)
167 /* Link to upper list. */
170 /* In case of this is the first element of master. */
171 if (list
->head
== NULL
)
173 list
->head
= list
->tail
= new;
177 /* In case of insertion is made at the tail of access_list. */
180 new->prev
= list
->tail
;
181 list
->tail
->next
= new;
186 /* In case of insertion is made at the head of access_list. */
187 if (point
== list
->head
)
189 new->next
= list
->head
;
190 list
->head
->prev
= new;
195 /* Insertion is made at middle of the access_list. */
197 new->prev
= point
->prev
;
200 point
->prev
->next
= new;
206 struct community_list
*
207 community_list_lookup (struct community_list_handler
*ch
,
208 char *name
, int master
)
210 struct community_list
*list
;
211 struct community_list_master
*cm
;
216 cm
= community_list_master_lookup (ch
, master
);
220 for (list
= cm
->num
.head
; list
; list
= list
->next
)
221 if (strcmp (list
->name
, name
) == 0)
223 for (list
= cm
->str
.head
; list
; list
= list
->next
)
224 if (strcmp (list
->name
, name
) == 0)
230 struct community_list
*
231 community_list_get (struct community_list_handler
*ch
, 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
, 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 /* When given community attribute matches to the community-list return
386 community_list_match (struct community
*com
, struct community_list
*list
)
388 struct community_entry
*entry
;
390 for (entry
= list
->head
; entry
; entry
= entry
->next
)
393 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
395 if (entry
->style
== COMMUNITY_LIST_STANDARD
)
397 if (community_include (entry
->u
.com
, COMMUNITY_INTERNET
))
398 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
400 if (community_match (com
, entry
->u
.com
))
401 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
403 else if (entry
->style
== COMMUNITY_LIST_EXPANDED
)
405 if (community_regexp_match (com
, entry
->reg
))
406 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
413 ecommunity_list_match (struct ecommunity
*ecom
, struct community_list
*list
)
415 struct community_entry
*entry
;
417 for (entry
= list
->head
; entry
; entry
= entry
->next
)
420 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
422 if (entry
->style
== EXTCOMMUNITY_LIST_STANDARD
)
424 if (ecommunity_match (ecom
, entry
->u
.ecom
))
425 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
427 else if (entry
->style
== EXTCOMMUNITY_LIST_EXPANDED
)
429 if (ecommunity_regexp_match (ecom
, entry
->reg
))
430 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
436 /* Perform exact matching. In case of expanded community-list, do
437 same thing as community_list_match(). */
439 community_list_exact_match (struct community
*com
, struct community_list
*list
)
441 struct community_entry
*entry
;
443 for (entry
= list
->head
; entry
; entry
= entry
->next
)
446 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
448 if (entry
->style
== COMMUNITY_LIST_STANDARD
)
450 if (community_include (entry
->u
.com
, COMMUNITY_INTERNET
))
451 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
453 if (community_cmp (com
, entry
->u
.com
))
454 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
456 else if (entry
->style
== COMMUNITY_LIST_EXPANDED
)
458 if (community_regexp_match (com
, entry
->reg
))
459 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
465 /* Do regular expression matching with single community val. */
467 comval_regexp_match (u_int32_t comval
, regex_t
*reg
)
469 /* Maximum is "65535:65535" + '\0'. */
475 case COMMUNITY_INTERNET
:
478 case COMMUNITY_NO_EXPORT
:
481 case COMMUNITY_NO_ADVERTISE
:
482 str
= "no-advertise";
484 case COMMUNITY_LOCAL_AS
:
488 snprintf (c
, sizeof c
,
489 "%d:%d", (comval
>> 16) & 0xFFFF, comval
& 0xFFFF);
494 if (regexec (reg
, str
, 0, NULL
, 0) == 0)
500 /* Delete all permitted communities in the list from com. */
502 community_list_match_delete (struct community
*com
,
503 struct community_list
*clist
)
507 struct community
*merge
;
508 struct community_entry
*entry
;
510 /* Empty community value check. */
514 /* Duplicate communities value. */
515 merge
= community_dup (com
);
517 /* For each communities value, we have to check each
519 for (i
= 0; i
< com
->size
; i
++)
521 /* Get one communities value. */
522 memcpy (&comval
, com_nthval (com
, i
), sizeof (u_int32_t
));
523 comval
= ntohl (comval
);
525 /* Loop community-list. */
526 for (entry
= clist
->head
; entry
; entry
= entry
->next
)
528 /* Various match condition check. */
530 || (entry
->style
== COMMUNITY_LIST_STANDARD
532 && community_include (entry
->u
.com
, comval
))
533 || (entry
->style
== COMMUNITY_LIST_EXPANDED
535 && comval_regexp_match (comval
, entry
->reg
)))
537 /* If the rule is "permit", delete this community value. */
538 if (entry
->direct
== COMMUNITY_PERMIT
)
539 community_del_val (merge
, com_nthval (com
, i
));
541 /* Exit community-list loop, goto next communities
550 /* To avoid duplicated entry in the community-list, this function
551 compares specified entry to existing entry. */
553 community_list_dup_check (struct community_list
*list
,
554 struct community_entry
*new)
556 struct community_entry
*entry
;
558 for (entry
= list
->head
; entry
; entry
= entry
->next
)
560 if (entry
->style
!= new->style
)
563 if (entry
->direct
!= new->direct
)
566 if (entry
->any
!= new->any
)
572 switch (entry
->style
)
574 case COMMUNITY_LIST_STANDARD
:
575 if (community_cmp (entry
->u
.com
, new->u
.com
))
578 case EXTCOMMUNITY_LIST_STANDARD
:
579 if (ecommunity_cmp (entry
->u
.ecom
, new->u
.ecom
))
582 case COMMUNITY_LIST_EXPANDED
:
583 case EXTCOMMUNITY_LIST_EXPANDED
:
584 if (strcmp (entry
->config
, new->config
) == 0)
594 /* Set community-list. */
596 community_list_set (struct community_list_handler
*ch
,
597 char *name
, char *str
, int direct
, int style
)
599 struct community_entry
*entry
= NULL
;
600 struct community_list
*list
;
601 struct community
*com
= NULL
;
602 regex_t
*regex
= NULL
;
604 /* Get community list. */
605 list
= community_list_get (ch
, name
, COMMUNITY_LIST_MASTER
);
607 /* When community-list already has entry, new entry should have same
608 style. If you want to have mixed style community-list, you can
609 comment out this check. */
610 if (! community_list_empty_p (list
))
612 struct community_entry
*first
;
616 if (style
!= first
->style
)
618 return (first
->style
== COMMUNITY_LIST_STANDARD
619 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
620 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT
);
626 if (style
== COMMUNITY_LIST_STANDARD
)
627 com
= community_str2com (str
);
629 regex
= bgp_regcomp (str
);
631 if (! com
&& ! regex
)
632 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
635 entry
= community_entry_new ();
636 entry
->direct
= direct
;
637 entry
->style
= style
;
638 entry
->any
= (str
? 0 : 1);
641 entry
->config
= (regex
? XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG
, str
) : NULL
);
643 /* Do not put duplicated community entry. */
644 if (community_list_dup_check (list
, entry
))
645 community_entry_free (entry
);
647 community_list_entry_add (list
, entry
);
652 /* Unset community-list. When str is NULL, delete all of
653 community-list entry belongs to the specified name. */
655 community_list_unset (struct community_list_handler
*ch
,
656 char *name
, char *str
, int direct
, int style
)
658 struct community_entry
*entry
= NULL
;
659 struct community_list
*list
;
660 struct community
*com
= NULL
;
661 regex_t
*regex
= NULL
;
663 /* Lookup community list. */
664 list
= community_list_lookup (ch
, name
, COMMUNITY_LIST_MASTER
);
666 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
668 /* Delete all of entry belongs to this community-list. */
671 community_list_delete (list
);
675 if (style
== COMMUNITY_LIST_STANDARD
)
676 com
= community_str2com (str
);
678 regex
= bgp_regcomp (str
);
680 if (! com
&& ! regex
)
681 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
684 entry
= community_list_entry_lookup (list
, com
, direct
);
686 entry
= community_list_entry_lookup (list
, str
, direct
);
689 community_free (com
);
691 bgp_regex_free (regex
);
694 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
696 community_list_entry_delete (list
, entry
, style
);
701 /* Set extcommunity-list. */
703 extcommunity_list_set (struct community_list_handler
*ch
,
704 char *name
, char *str
, int direct
, int style
)
706 struct community_entry
*entry
= NULL
;
707 struct community_list
*list
;
708 struct ecommunity
*ecom
= NULL
;
709 regex_t
*regex
= NULL
;
713 /* Get community list. */
714 list
= community_list_get (ch
, name
, EXTCOMMUNITY_LIST_MASTER
);
716 /* When community-list already has entry, new entry should have same
717 style. If you want to have mixed style community-list, you can
718 comment out this check. */
719 if (! community_list_empty_p (list
))
721 struct community_entry
*first
;
725 if (style
!= first
->style
)
727 return (first
->style
== EXTCOMMUNITY_LIST_STANDARD
728 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
729 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT
);
735 if (style
== EXTCOMMUNITY_LIST_STANDARD
)
736 ecom
= ecommunity_str2com (str
, 0, 1);
738 regex
= bgp_regcomp (str
);
740 if (! ecom
&& ! regex
)
741 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
745 ecom
->str
= ecommunity_ecom2str (ecom
, ECOMMUNITY_FORMAT_DISPLAY
);
747 entry
= community_entry_new ();
748 entry
->direct
= direct
;
749 entry
->style
= style
;
750 entry
->any
= (str
? 0 : 1);
752 entry
->config
= ecommunity_ecom2str (ecom
, ECOMMUNITY_FORMAT_CONFIG
);
754 entry
->config
= XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG
, str
);
756 entry
->config
= NULL
;
757 entry
->u
.ecom
= ecom
;
760 /* Do not put duplicated community entry. */
761 if (community_list_dup_check (list
, entry
))
762 community_entry_free (entry
);
764 community_list_entry_add (list
, entry
);
769 /* Unset extcommunity-list. When str is NULL, delete all of
770 extcommunity-list entry belongs to the specified name. */
772 extcommunity_list_unset (struct community_list_handler
*ch
,
773 char *name
, char *str
, int direct
, int style
)
775 struct community_entry
*entry
= NULL
;
776 struct community_list
*list
;
777 struct ecommunity
*ecom
= NULL
;
778 regex_t
*regex
= NULL
;
780 /* Lookup extcommunity list. */
781 list
= community_list_lookup (ch
, name
, EXTCOMMUNITY_LIST_MASTER
);
783 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
785 /* Delete all of entry belongs to this extcommunity-list. */
788 community_list_delete (list
);
792 if (style
== EXTCOMMUNITY_LIST_STANDARD
)
793 ecom
= ecommunity_str2com (str
, 0, 1);
795 regex
= bgp_regcomp (str
);
797 if (! ecom
&& ! regex
)
798 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
801 entry
= community_list_entry_lookup (list
, ecom
, direct
);
803 entry
= community_list_entry_lookup (list
, str
, direct
);
806 ecommunity_free (ecom
);
808 bgp_regex_free (regex
);
811 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
813 community_list_entry_delete (list
, entry
, style
);
818 /* Initializa community-list. Return community-list handler. */
819 struct community_list_handler
*
820 community_list_init ()
822 struct community_list_handler
*ch
;
823 ch
= XCALLOC (MTYPE_COMMUNITY_LIST_HANDLER
,
824 sizeof (struct community_list_handler
));
828 /* Terminate community-list. */
830 community_list_terminate (struct community_list_handler
*ch
)
832 struct community_list_master
*cm
;
833 struct community_list
*list
;
835 cm
= &ch
->community_list
;
836 while ((list
= cm
->num
.head
) != NULL
)
837 community_list_delete (list
);
838 while ((list
= cm
->str
.head
) != NULL
)
839 community_list_delete (list
);
841 cm
= &ch
->extcommunity_list
;
842 while ((list
= cm
->num
.head
) != NULL
)
843 community_list_delete (list
);
844 while ((list
= cm
->str
.head
) != NULL
)
845 community_list_delete (list
);
847 XFREE (MTYPE_COMMUNITY_LIST_HANDLER
, ch
);