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 (void)
54 return XCALLOC (MTYPE_COMMUNITY_LIST_ENTRY
, sizeof (struct community_entry
));
57 /* Free community list entry. */
59 community_entry_free (struct community_entry
*entry
)
63 case COMMUNITY_LIST_STANDARD
:
65 community_free (entry
->u
.com
);
67 case EXTCOMMUNITY_LIST_STANDARD
:
68 /* In case of standard extcommunity-list, configuration string
69 is made by ecommunity_ecom2str(). */
71 XFREE (MTYPE_ECOMMUNITY_STR
, entry
->config
);
73 ecommunity_free (entry
->u
.ecom
);
75 case COMMUNITY_LIST_EXPANDED
:
76 case EXTCOMMUNITY_LIST_EXPANDED
:
78 XFREE (MTYPE_COMMUNITY_LIST_CONFIG
, entry
->config
);
80 bgp_regex_free (entry
->reg
);
84 XFREE (MTYPE_COMMUNITY_LIST_ENTRY
, entry
);
87 /* Allocate a new community-list. */
88 static struct community_list
*
89 community_list_new (void)
91 return XCALLOC (MTYPE_COMMUNITY_LIST
, sizeof (struct community_list
));
94 /* Free community-list. */
96 community_list_free (struct community_list
*list
)
99 XFREE (MTYPE_COMMUNITY_LIST_NAME
, list
->name
);
100 XFREE (MTYPE_COMMUNITY_LIST
, list
);
103 static struct community_list
*
104 community_list_insert (struct community_list_handler
*ch
,
105 const char *name
, int master
)
109 struct community_list
*new;
110 struct community_list
*point
;
111 struct community_list_list
*list
;
112 struct community_list_master
*cm
;
114 /* Lookup community-list master. */
115 cm
= community_list_master_lookup (ch
, master
);
119 /* Allocate new community_list and copy given name. */
120 new = community_list_new ();
121 new->name
= XSTRDUP (MTYPE_COMMUNITY_LIST_NAME
, name
);
123 /* If name is made by all digit character. We treat it as
125 for (number
= 0, i
= 0; i
< strlen (name
); i
++)
127 if (isdigit ((int) name
[i
]))
128 number
= (number
* 10) + (name
[i
] - '0');
133 /* In case of name is all digit character */
134 if (i
== strlen (name
))
136 new->sort
= COMMUNITY_LIST_NUMBER
;
138 /* Set access_list to number list. */
141 for (point
= list
->head
; point
; point
= point
->next
)
142 if (atol (point
->name
) >= number
)
147 new->sort
= COMMUNITY_LIST_STRING
;
149 /* Set access_list to string list. */
152 /* Set point to insertion point. */
153 for (point
= list
->head
; point
; point
= point
->next
)
154 if (strcmp (point
->name
, name
) >= 0)
158 /* Link to upper list. */
161 /* In case of this is the first element of master. */
162 if (list
->head
== NULL
)
164 list
->head
= list
->tail
= new;
168 /* In case of insertion is made at the tail of access_list. */
171 new->prev
= list
->tail
;
172 list
->tail
->next
= new;
177 /* In case of insertion is made at the head of access_list. */
178 if (point
== list
->head
)
180 new->next
= list
->head
;
181 list
->head
->prev
= new;
186 /* Insertion is made at middle of the access_list. */
188 new->prev
= point
->prev
;
191 point
->prev
->next
= new;
197 struct community_list
*
198 community_list_lookup (struct community_list_handler
*ch
,
199 const char *name
, int master
)
201 struct community_list
*list
;
202 struct community_list_master
*cm
;
207 cm
= community_list_master_lookup (ch
, master
);
211 for (list
= cm
->num
.head
; list
; list
= list
->next
)
212 if (strcmp (list
->name
, name
) == 0)
214 for (list
= cm
->str
.head
; list
; list
= list
->next
)
215 if (strcmp (list
->name
, name
) == 0)
221 static struct community_list
*
222 community_list_get (struct community_list_handler
*ch
,
223 const char *name
, int master
)
225 struct community_list
*list
;
227 list
= community_list_lookup (ch
, name
, master
);
229 list
= community_list_insert (ch
, name
, master
);
234 community_list_delete (struct community_list
*list
)
236 struct community_list_list
*clist
;
237 struct community_entry
*entry
, *next
;
239 for (entry
= list
->head
; entry
; entry
= next
)
242 community_entry_free (entry
);
245 clist
= list
->parent
;
248 list
->next
->prev
= list
->prev
;
250 clist
->tail
= list
->prev
;
253 list
->prev
->next
= list
->next
;
255 clist
->head
= list
->next
;
257 community_list_free (list
);
261 community_list_empty_p (struct community_list
*list
)
263 return (list
->head
== NULL
&& list
->tail
== NULL
) ? 1 : 0;
266 /* Add community-list entry to the list. */
268 community_list_entry_add (struct community_list
*list
,
269 struct community_entry
*entry
)
272 entry
->prev
= list
->tail
;
275 list
->tail
->next
= entry
;
281 /* Delete community-list entry from the list. */
283 community_list_entry_delete (struct community_list
*list
,
284 struct community_entry
*entry
, int style
)
287 entry
->next
->prev
= entry
->prev
;
289 list
->tail
= entry
->prev
;
292 entry
->prev
->next
= entry
->next
;
294 list
->head
= entry
->next
;
296 community_entry_free (entry
);
298 if (community_list_empty_p (list
))
299 community_list_delete (list
);
302 /* Lookup community-list entry from the list. */
303 static struct community_entry
*
304 community_list_entry_lookup (struct community_list
*list
, const void *arg
,
307 struct community_entry
*entry
;
309 for (entry
= list
->head
; entry
; entry
= entry
->next
)
311 switch (entry
->style
)
313 case COMMUNITY_LIST_STANDARD
:
314 if (community_cmp (entry
->u
.com
, arg
))
317 case EXTCOMMUNITY_LIST_STANDARD
:
318 if (ecommunity_cmp (entry
->u
.ecom
, arg
))
321 case COMMUNITY_LIST_EXPANDED
:
322 case EXTCOMMUNITY_LIST_EXPANDED
:
323 if (strcmp (entry
->config
, arg
) == 0)
333 /* Internal function to perform regular expression match for community
336 community_regexp_match (struct community
*com
, regex_t
* reg
)
340 /* When there is no communities attribute it is treated as empty
342 if (com
== NULL
|| com
->size
== 0)
345 str
= community_str (com
);
347 /* Regular expression match. */
348 if (regexec (reg
, str
, 0, NULL
, 0) == 0)
356 ecommunity_regexp_match (struct ecommunity
*ecom
, regex_t
* reg
)
360 /* When there is no communities attribute it is treated as empty
362 if (ecom
== NULL
|| ecom
->size
== 0)
365 str
= ecommunity_str (ecom
);
367 /* Regular expression match. */
368 if (regexec (reg
, str
, 0, NULL
, 0) == 0)
375 /* Delete community attribute using regular expression match. Return
376 modified communites attribute. */
377 static struct community
*
378 community_regexp_delete (struct community
*com
, regex_t
* reg
)
382 /* Maximum is "65535:65535" + '\0'. */
390 while (i
< com
->size
)
392 memcpy (&comval
, com_nthval (com
, i
), sizeof (u_int32_t
));
393 comval
= ntohl (comval
);
397 case COMMUNITY_INTERNET
:
400 case COMMUNITY_NO_EXPORT
:
403 case COMMUNITY_NO_ADVERTISE
:
404 str
= "no-advertise";
406 case COMMUNITY_LOCAL_AS
:
410 sprintf (c
, "%d:%d", (comval
>> 16) & 0xFFFF, comval
& 0xFFFF);
415 if (regexec (reg
, str
, 0, NULL
, 0) == 0)
416 community_del_val (com
, com_nthval (com
, i
));
423 /* When given community attribute matches to the community-list return
426 community_list_match (struct community
*com
, struct community_list
*list
)
428 struct community_entry
*entry
;
430 for (entry
= list
->head
; entry
; entry
= entry
->next
)
433 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
435 if (entry
->style
== COMMUNITY_LIST_STANDARD
)
437 if (community_include (entry
->u
.com
, COMMUNITY_INTERNET
))
438 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
440 if (community_match (com
, entry
->u
.com
))
441 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
443 else if (entry
->style
== COMMUNITY_LIST_EXPANDED
)
445 if (community_regexp_match (com
, entry
->reg
))
446 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
453 ecommunity_list_match (struct ecommunity
*ecom
, struct community_list
*list
)
455 struct community_entry
*entry
;
457 for (entry
= list
->head
; entry
; entry
= entry
->next
)
460 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
462 if (entry
->style
== EXTCOMMUNITY_LIST_STANDARD
)
464 if (ecommunity_match (ecom
, entry
->u
.ecom
))
465 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
467 else if (entry
->style
== EXTCOMMUNITY_LIST_EXPANDED
)
469 if (ecommunity_regexp_match (ecom
, entry
->reg
))
470 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
476 /* Perform exact matching. In case of expanded community-list, do
477 same thing as community_list_match(). */
479 community_list_exact_match (struct community
*com
,
480 struct community_list
*list
)
482 struct community_entry
*entry
;
484 for (entry
= list
->head
; entry
; entry
= entry
->next
)
487 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
489 if (entry
->style
== COMMUNITY_LIST_STANDARD
)
491 if (community_include (entry
->u
.com
, COMMUNITY_INTERNET
))
492 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
494 if (community_cmp (com
, entry
->u
.com
))
495 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
497 else if (entry
->style
== COMMUNITY_LIST_EXPANDED
)
499 if (community_regexp_match (com
, entry
->reg
))
500 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
506 /* Delete all permitted communities in the list from com. */
508 community_list_match_delete (struct community
*com
,
509 struct community_list
*list
)
511 struct community_entry
*entry
;
513 for (entry
= list
->head
; entry
; entry
= entry
->next
)
517 if (entry
->direct
== COMMUNITY_PERMIT
)
519 /* This is a tricky part. Currently only
520 * route_set_community_delete() uses this function. In the
521 * function com->size is zero, it free the community
529 if ((entry
->style
== COMMUNITY_LIST_STANDARD
)
530 && (community_include (entry
->u
.com
, COMMUNITY_INTERNET
)
531 || community_match (com
, entry
->u
.com
) ))
533 if (entry
->direct
== COMMUNITY_PERMIT
)
534 community_delete (com
, entry
->u
.com
);
538 else if ((entry
->style
== COMMUNITY_LIST_EXPANDED
)
539 && community_regexp_match (com
, entry
->reg
))
541 if (entry
->direct
== COMMUNITY_PERMIT
)
542 community_regexp_delete (com
, entry
->reg
);
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 const char *name
, const 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 const char *name
, const char *str
,
657 int direct
, int style
)
659 struct community_entry
*entry
= NULL
;
660 struct community_list
*list
;
661 struct community
*com
= NULL
;
662 regex_t
*regex
= NULL
;
664 /* Lookup community list. */
665 list
= community_list_lookup (ch
, name
, COMMUNITY_LIST_MASTER
);
667 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
669 /* Delete all of entry belongs to this community-list. */
672 community_list_delete (list
);
676 if (style
== COMMUNITY_LIST_STANDARD
)
677 com
= community_str2com (str
);
679 regex
= bgp_regcomp (str
);
681 if (! com
&& ! regex
)
682 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
685 entry
= community_list_entry_lookup (list
, com
, direct
);
687 entry
= community_list_entry_lookup (list
, str
, direct
);
690 community_free (com
);
692 bgp_regex_free (regex
);
695 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
697 community_list_entry_delete (list
, entry
, style
);
702 /* Set extcommunity-list. */
704 extcommunity_list_set (struct community_list_handler
*ch
,
705 const char *name
, const char *str
,
706 int direct
, int style
)
708 struct community_entry
*entry
= NULL
;
709 struct community_list
*list
;
710 struct ecommunity
*ecom
= NULL
;
711 regex_t
*regex
= NULL
;
715 /* Get community list. */
716 list
= community_list_get (ch
, name
, EXTCOMMUNITY_LIST_MASTER
);
718 /* When community-list already has entry, new entry should have same
719 style. If you want to have mixed style community-list, you can
720 comment out this check. */
721 if (!community_list_empty_p (list
))
723 struct community_entry
*first
;
727 if (style
!= first
->style
)
729 return (first
->style
== EXTCOMMUNITY_LIST_STANDARD
730 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
731 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT
);
737 if (style
== EXTCOMMUNITY_LIST_STANDARD
)
738 ecom
= ecommunity_str2com (str
, 0, 1);
740 regex
= bgp_regcomp (str
);
742 if (! ecom
&& ! regex
)
743 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
747 ecom
->str
= ecommunity_ecom2str (ecom
, ECOMMUNITY_FORMAT_DISPLAY
);
749 entry
= community_entry_new ();
750 entry
->direct
= direct
;
751 entry
->style
= style
;
752 entry
->any
= (str
? 0 : 1);
754 entry
->config
= ecommunity_ecom2str (ecom
, ECOMMUNITY_FORMAT_COMMUNITY_LIST
);
756 entry
->config
= XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG
, str
);
758 entry
->config
= NULL
;
759 entry
->u
.ecom
= ecom
;
762 /* Do not put duplicated community entry. */
763 if (community_list_dup_check (list
, entry
))
764 community_entry_free (entry
);
766 community_list_entry_add (list
, entry
);
771 /* Unset extcommunity-list. When str is NULL, delete all of
772 extcommunity-list entry belongs to the specified name. */
774 extcommunity_list_unset (struct community_list_handler
*ch
,
775 const char *name
, const char *str
,
776 int direct
, int style
)
778 struct community_entry
*entry
= NULL
;
779 struct community_list
*list
;
780 struct ecommunity
*ecom
= NULL
;
781 regex_t
*regex
= NULL
;
783 /* Lookup extcommunity list. */
784 list
= community_list_lookup (ch
, name
, EXTCOMMUNITY_LIST_MASTER
);
786 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
788 /* Delete all of entry belongs to this extcommunity-list. */
791 community_list_delete (list
);
795 if (style
== EXTCOMMUNITY_LIST_STANDARD
)
796 ecom
= ecommunity_str2com (str
, 0, 1);
798 regex
= bgp_regcomp (str
);
800 if (! ecom
&& ! regex
)
801 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
804 entry
= community_list_entry_lookup (list
, ecom
, direct
);
806 entry
= community_list_entry_lookup (list
, str
, direct
);
809 ecommunity_free (ecom
);
811 bgp_regex_free (regex
);
814 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
816 community_list_entry_delete (list
, entry
, style
);
821 /* Initializa community-list. Return community-list handler. */
822 struct community_list_handler
*
823 community_list_init (void)
825 struct community_list_handler
*ch
;
826 ch
= XCALLOC (MTYPE_COMMUNITY_LIST_HANDLER
,
827 sizeof (struct community_list_handler
));
831 /* Terminate community-list. */
833 community_list_terminate (struct community_list_handler
*ch
)
835 struct community_list_master
*cm
;
836 struct community_list
*list
;
838 cm
= &ch
->community_list
;
839 while ((list
= cm
->num
.head
) != NULL
)
840 community_list_delete (list
);
841 while ((list
= cm
->str
.head
) != NULL
)
842 community_list_delete (list
);
844 cm
= &ch
->extcommunity_list
;
845 while ((list
= cm
->num
.head
) != NULL
)
846 community_list_delete (list
);
847 while ((list
= cm
->str
.head
) != NULL
)
848 community_list_delete (list
);
850 XFREE (MTYPE_COMMUNITY_LIST_HANDLER
, ch
);