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
29 #include "bgpd/bgpd.h"
30 #include "bgpd/bgp_community.h"
31 #include "bgpd/bgp_ecommunity.h"
32 #include "bgpd/bgp_aspath.h"
33 #include "bgpd/bgp_regex.h"
34 #include "bgpd/bgp_clist.h"
36 /* Lookup master structure for community-list or
38 struct community_list_master
*
39 community_list_master_lookup (struct community_list_handler
*ch
, int master
)
44 case COMMUNITY_LIST_MASTER
:
45 return &ch
->community_list
;
46 case EXTCOMMUNITY_LIST_MASTER
:
47 return &ch
->extcommunity_list
;
52 /* Allocate a new community list entry. */
53 static struct community_entry
*
54 community_entry_new (void)
56 return XCALLOC (MTYPE_COMMUNITY_LIST_ENTRY
, sizeof (struct community_entry
));
59 /* Free community list entry. */
61 community_entry_free (struct community_entry
*entry
)
65 case COMMUNITY_LIST_STANDARD
:
67 community_free (entry
->u
.com
);
69 case EXTCOMMUNITY_LIST_STANDARD
:
70 /* In case of standard extcommunity-list, configuration string
71 is made by ecommunity_ecom2str(). */
73 XFREE (MTYPE_ECOMMUNITY_STR
, entry
->config
);
75 ecommunity_free (&entry
->u
.ecom
);
77 case COMMUNITY_LIST_EXPANDED
:
78 case EXTCOMMUNITY_LIST_EXPANDED
:
80 XFREE (MTYPE_COMMUNITY_LIST_CONFIG
, entry
->config
);
82 bgp_regex_free (entry
->reg
);
86 XFREE (MTYPE_COMMUNITY_LIST_ENTRY
, entry
);
89 /* Allocate a new community-list. */
90 static struct community_list
*
91 community_list_new (void)
93 return XCALLOC (MTYPE_COMMUNITY_LIST
, sizeof (struct community_list
));
96 /* Free community-list. */
98 community_list_free (struct community_list
*list
)
101 XFREE (MTYPE_COMMUNITY_LIST_NAME
, list
->name
);
102 XFREE (MTYPE_COMMUNITY_LIST
, list
);
105 static struct community_list
*
106 community_list_insert (struct community_list_handler
*ch
,
107 const char *name
, int master
)
111 struct community_list
*new;
112 struct community_list
*point
;
113 struct community_list_list
*list
;
114 struct community_list_master
*cm
;
116 /* Lookup community-list master. */
117 cm
= community_list_master_lookup (ch
, master
);
121 /* Allocate new community_list and copy given name. */
122 new = community_list_new ();
123 new->name
= XSTRDUP (MTYPE_COMMUNITY_LIST_NAME
, name
);
125 /* If name is made by all digit character. We treat it as
127 for (number
= 0, i
= 0; i
< strlen (name
); i
++)
129 if (isdigit ((int) name
[i
]))
130 number
= (number
* 10) + (name
[i
] - '0');
135 /* In case of name is all digit character */
136 if (i
== strlen (name
))
138 new->sort
= COMMUNITY_LIST_NUMBER
;
140 /* Set access_list to number list. */
143 for (point
= list
->head
; point
; point
= point
->next
)
144 if (atol (point
->name
) >= number
)
149 new->sort
= COMMUNITY_LIST_STRING
;
151 /* Set access_list to string list. */
154 /* Set point to insertion point. */
155 for (point
= list
->head
; point
; point
= point
->next
)
156 if (strcmp (point
->name
, name
) >= 0)
160 /* Link to upper list. */
163 /* In case of this is the first element of master. */
164 if (list
->head
== NULL
)
166 list
->head
= list
->tail
= new;
170 /* In case of insertion is made at the tail of access_list. */
173 new->prev
= list
->tail
;
174 list
->tail
->next
= new;
179 /* In case of insertion is made at the head of access_list. */
180 if (point
== list
->head
)
182 new->next
= list
->head
;
183 list
->head
->prev
= new;
188 /* Insertion is made at middle of the access_list. */
190 new->prev
= point
->prev
;
193 point
->prev
->next
= new;
199 struct community_list
*
200 community_list_lookup (struct community_list_handler
*ch
,
201 const char *name
, int master
)
203 struct community_list
*list
;
204 struct community_list_master
*cm
;
209 cm
= community_list_master_lookup (ch
, master
);
213 for (list
= cm
->num
.head
; list
; list
= list
->next
)
214 if (strcmp (list
->name
, name
) == 0)
216 for (list
= cm
->str
.head
; list
; list
= list
->next
)
217 if (strcmp (list
->name
, name
) == 0)
223 static struct community_list
*
224 community_list_get (struct community_list_handler
*ch
,
225 const char *name
, int master
)
227 struct community_list
*list
;
229 list
= community_list_lookup (ch
, name
, master
);
231 list
= community_list_insert (ch
, name
, master
);
236 community_list_delete (struct community_list
*list
)
238 struct community_list_list
*clist
;
239 struct community_entry
*entry
, *next
;
241 for (entry
= list
->head
; entry
; entry
= next
)
244 community_entry_free (entry
);
247 clist
= list
->parent
;
250 list
->next
->prev
= list
->prev
;
252 clist
->tail
= list
->prev
;
255 list
->prev
->next
= list
->next
;
257 clist
->head
= list
->next
;
259 community_list_free (list
);
263 community_list_empty_p (struct community_list
*list
)
265 return (list
->head
== NULL
&& list
->tail
== NULL
) ? 1 : 0;
268 /* Add community-list entry to the list. */
270 community_list_entry_add (struct community_list
*list
,
271 struct community_entry
*entry
)
274 entry
->prev
= list
->tail
;
277 list
->tail
->next
= entry
;
283 /* Delete community-list entry from the list. */
285 community_list_entry_delete (struct community_list
*list
,
286 struct community_entry
*entry
, int style
)
289 entry
->next
->prev
= entry
->prev
;
291 list
->tail
= entry
->prev
;
294 entry
->prev
->next
= entry
->next
;
296 list
->head
= entry
->next
;
298 community_entry_free (entry
);
300 if (community_list_empty_p (list
))
301 community_list_delete (list
);
304 /* Lookup community-list entry from the list. */
305 static struct community_entry
*
306 community_list_entry_lookup (struct community_list
*list
, const void *arg
,
309 struct community_entry
*entry
;
311 for (entry
= list
->head
; entry
; entry
= entry
->next
)
313 switch (entry
->style
)
315 case COMMUNITY_LIST_STANDARD
:
316 if (entry
->direct
== direct
&& community_cmp (entry
->u
.com
, arg
))
319 case EXTCOMMUNITY_LIST_STANDARD
:
320 if (entry
->direct
== direct
&& ecommunity_cmp (entry
->u
.ecom
, arg
))
323 case COMMUNITY_LIST_EXPANDED
:
324 case EXTCOMMUNITY_LIST_EXPANDED
:
325 if (entry
->direct
== direct
&& strcmp (entry
->config
, arg
) == 0)
336 community_str_get (struct community
*com
, int i
)
345 memcpy (&comval
, com_nthval (com
, i
), sizeof (u_int32_t
));
346 comval
= ntohl (comval
);
350 case COMMUNITY_INTERNET
:
351 len
= strlen (" internet");
353 case COMMUNITY_NO_EXPORT
:
354 len
= strlen (" no-export");
356 case COMMUNITY_NO_ADVERTISE
:
357 len
= strlen (" no-advertise");
359 case COMMUNITY_LOCAL_AS
:
360 len
= strlen (" local-AS");
363 len
= strlen (" 65536:65535");
367 /* Allocate memory. */
368 str
= pnt
= XMALLOC (MTYPE_COMMUNITY_STR
, len
);
372 case COMMUNITY_INTERNET
:
373 strcpy (pnt
, "internet");
374 pnt
+= strlen ("internet");
376 case COMMUNITY_NO_EXPORT
:
377 strcpy (pnt
, "no-export");
378 pnt
+= strlen ("no-export");
380 case COMMUNITY_NO_ADVERTISE
:
381 strcpy (pnt
, "no-advertise");
382 pnt
+= strlen ("no-advertise");
384 case COMMUNITY_LOCAL_AS
:
385 strcpy (pnt
, "local-AS");
386 pnt
+= strlen ("local-AS");
389 as
= (comval
>> 16) & 0xFFFF;
390 val
= comval
& 0xFFFF;
391 sprintf (pnt
, "%u:%d", as
, val
);
401 /* Internal function to perform regular expression match for
402 * * a single community. */
404 community_regexp_include (regex_t
* reg
, struct community
*com
, int i
)
409 /* When there is no communities attribute it is treated as empty
411 if (com
== NULL
|| com
->size
== 0)
412 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "");
414 str
= community_str_get (com
, i
);
416 /* Regular expression match. */
417 rv
= regexec (reg
, str
, 0, NULL
, 0);
419 XFREE(MTYPE_COMMUNITY_STR
, str
);
428 /* Internal function to perform regular expression match for community
431 community_regexp_match (struct community
*com
, regex_t
* reg
)
435 /* When there is no communities attribute it is treated as empty
437 if (com
== NULL
|| com
->size
== 0)
440 str
= community_str (com
);
442 /* Regular expression match. */
443 if (regexec (reg
, str
, 0, NULL
, 0) == 0)
451 ecommunity_regexp_match (struct ecommunity
*ecom
, regex_t
* reg
)
455 /* When there is no communities attribute it is treated as empty
457 if (ecom
== NULL
|| ecom
->size
== 0)
460 str
= ecommunity_str (ecom
);
462 /* Regular expression match. */
463 if (regexec (reg
, str
, 0, NULL
, 0) == 0)
471 /* Delete community attribute using regular expression match. Return
472 modified communites attribute. */
473 static struct community
*
474 community_regexp_delete (struct community
*com
, regex_t
* reg
)
478 /* Maximum is "65535:65535" + '\0'. */
486 while (i
< com
->size
)
488 memcpy (&comval
, com_nthval (com
, i
), sizeof (u_int32_t
));
489 comval
= ntohl (comval
);
493 case COMMUNITY_INTERNET
:
496 case COMMUNITY_NO_EXPORT
:
499 case COMMUNITY_NO_ADVERTISE
:
500 str
= "no-advertise";
502 case COMMUNITY_LOCAL_AS
:
506 sprintf (c
, "%d:%d", (comval
>> 16) & 0xFFFF, comval
& 0xFFFF);
511 if (regexec (reg
, str
, 0, NULL
, 0) == 0)
512 community_del_val (com
, com_nthval (com
, i
));
520 /* When given community attribute matches to the community-list return
523 community_list_match (struct community
*com
, struct community_list
*list
)
525 struct community_entry
*entry
;
527 for (entry
= list
->head
; entry
; entry
= entry
->next
)
530 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
532 if (entry
->style
== COMMUNITY_LIST_STANDARD
)
534 if (community_include (entry
->u
.com
, COMMUNITY_INTERNET
))
535 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
537 if (community_match (com
, entry
->u
.com
))
538 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
540 else if (entry
->style
== COMMUNITY_LIST_EXPANDED
)
542 if (community_regexp_match (com
, entry
->reg
))
543 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
550 ecommunity_list_match (struct ecommunity
*ecom
, struct community_list
*list
)
552 struct community_entry
*entry
;
554 for (entry
= list
->head
; entry
; entry
= entry
->next
)
557 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
559 if (entry
->style
== EXTCOMMUNITY_LIST_STANDARD
)
561 if (ecommunity_match (ecom
, entry
->u
.ecom
))
562 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
564 else if (entry
->style
== EXTCOMMUNITY_LIST_EXPANDED
)
566 if (ecommunity_regexp_match (ecom
, entry
->reg
))
567 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
573 /* Perform exact matching. In case of expanded community-list, do
574 same thing as community_list_match(). */
576 community_list_exact_match (struct community
*com
,
577 struct community_list
*list
)
579 struct community_entry
*entry
;
581 for (entry
= list
->head
; entry
; entry
= entry
->next
)
584 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
586 if (entry
->style
== COMMUNITY_LIST_STANDARD
)
588 if (community_include (entry
->u
.com
, COMMUNITY_INTERNET
))
589 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
591 if (community_cmp (com
, entry
->u
.com
))
592 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
594 else if (entry
->style
== COMMUNITY_LIST_EXPANDED
)
596 if (community_regexp_match (com
, entry
->reg
))
597 return entry
->direct
== COMMUNITY_PERMIT
? 1 : 0;
603 /* Delete all permitted communities in the list from com. */
605 community_list_match_delete (struct community
*com
,
606 struct community_list
*list
)
608 struct community_entry
*entry
;
610 u_int32_t com_index_to_delete
[com
->size
];
611 int delete_index
= 0;
614 /* Loop over each community value and evaluate each against the
615 * community-list. If we need to delete a community value add its index to
616 * com_index_to_delete.
618 for (i
= 0; i
< com
->size
; i
++)
620 val
= community_val_get (com
, i
);
622 for (entry
= list
->head
; entry
; entry
= entry
->next
)
626 if (entry
->direct
== COMMUNITY_PERMIT
)
628 com_index_to_delete
[delete_index
] = i
;
634 else if ((entry
->style
== COMMUNITY_LIST_STANDARD
)
635 && (community_include (entry
->u
.com
, COMMUNITY_INTERNET
)
636 || community_include (entry
->u
.com
, val
) ))
638 if (entry
->direct
== COMMUNITY_PERMIT
)
640 com_index_to_delete
[delete_index
] = i
;
646 else if ((entry
->style
== COMMUNITY_LIST_EXPANDED
)
647 && community_regexp_include (entry
->reg
, com
, i
))
649 if (entry
->direct
== COMMUNITY_PERMIT
)
651 com_index_to_delete
[delete_index
] = i
;
659 /* Delete all of the communities we flagged for deletion */
660 for (i
= delete_index
-1; i
>= 0; i
--)
662 val
= community_val_get (com
, com_index_to_delete
[i
]);
663 community_del_val (com
, &val
);
669 /* To avoid duplicated entry in the community-list, this function
670 compares specified entry to existing entry. */
672 community_list_dup_check (struct community_list
*list
,
673 struct community_entry
*new)
675 struct community_entry
*entry
;
677 for (entry
= list
->head
; entry
; entry
= entry
->next
)
679 if (entry
->style
!= new->style
)
682 if (entry
->direct
!= new->direct
)
685 if (entry
->any
!= new->any
)
691 switch (entry
->style
)
693 case COMMUNITY_LIST_STANDARD
:
694 if (community_cmp (entry
->u
.com
, new->u
.com
))
697 case EXTCOMMUNITY_LIST_STANDARD
:
698 if (ecommunity_cmp (entry
->u
.ecom
, new->u
.ecom
))
701 case COMMUNITY_LIST_EXPANDED
:
702 case EXTCOMMUNITY_LIST_EXPANDED
:
703 if (strcmp (entry
->config
, new->config
) == 0)
713 /* Set community-list. */
715 community_list_set (struct community_list_handler
*ch
,
716 const char *name
, const char *str
, int direct
, int style
)
718 struct community_entry
*entry
= NULL
;
719 struct community_list
*list
;
720 struct community
*com
= NULL
;
721 regex_t
*regex
= NULL
;
723 /* Get community list. */
724 list
= community_list_get (ch
, name
, COMMUNITY_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
== COMMUNITY_LIST_STANDARD
738 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
739 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT
);
745 if (style
== COMMUNITY_LIST_STANDARD
)
746 com
= community_str2com (str
);
748 regex
= bgp_regcomp (str
);
750 if (! com
&& ! regex
)
751 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
754 entry
= community_entry_new ();
755 entry
->direct
= direct
;
756 entry
->style
= style
;
757 entry
->any
= (str
? 0 : 1);
760 entry
->config
= (regex
? XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG
, str
) : NULL
);
762 /* Do not put duplicated community entry. */
763 if (community_list_dup_check (list
, entry
))
764 community_entry_free (entry
);
767 community_list_entry_add (list
, entry
);
768 route_map_notify_dependencies(name
, RMAP_EVENT_CLIST_ADDED
);
774 /* Unset community-list */
776 community_list_unset (struct community_list_handler
*ch
,
777 const char *name
, const char *str
,
778 int direct
, int style
, int delete_all
)
780 struct community_entry
*entry
= NULL
;
781 struct community_list
*list
;
782 struct community
*com
= NULL
;
784 /* Lookup community list. */
785 list
= community_list_lookup (ch
, name
, COMMUNITY_LIST_MASTER
);
787 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
789 /* Delete all of entry belongs to this community-list. */
792 community_list_delete (list
);
793 route_map_notify_dependencies(name
, RMAP_EVENT_CLIST_DELETED
);
797 if (style
== COMMUNITY_LIST_STANDARD
)
800 com
= community_str2com (str
);
805 entry
= community_list_entry_lookup (list
, com
, direct
);
806 community_free (com
);
809 entry
= community_list_entry_lookup (list
, str
, direct
);
812 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
814 community_list_entry_delete (list
, entry
, style
);
815 route_map_notify_dependencies(name
, RMAP_EVENT_CLIST_DELETED
);
820 /* Set extcommunity-list. */
822 extcommunity_list_set (struct community_list_handler
*ch
,
823 const char *name
, const char *str
,
824 int direct
, int style
)
826 struct community_entry
*entry
= NULL
;
827 struct community_list
*list
;
828 struct ecommunity
*ecom
= NULL
;
829 regex_t
*regex
= NULL
;
833 /* Get community list. */
834 list
= community_list_get (ch
, name
, EXTCOMMUNITY_LIST_MASTER
);
836 /* When community-list already has entry, new entry should have same
837 style. If you want to have mixed style community-list, you can
838 comment out this check. */
839 if (!community_list_empty_p (list
))
841 struct community_entry
*first
;
845 if (style
!= first
->style
)
847 return (first
->style
== EXTCOMMUNITY_LIST_STANDARD
848 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
849 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT
);
855 if (style
== EXTCOMMUNITY_LIST_STANDARD
)
856 ecom
= ecommunity_str2com (str
, 0, 1);
858 regex
= bgp_regcomp (str
);
860 if (! ecom
&& ! regex
)
861 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
865 ecom
->str
= ecommunity_ecom2str (ecom
, ECOMMUNITY_FORMAT_DISPLAY
);
867 entry
= community_entry_new ();
868 entry
->direct
= direct
;
869 entry
->style
= style
;
870 entry
->any
= (str
? 0 : 1);
872 entry
->config
= ecommunity_ecom2str (ecom
, ECOMMUNITY_FORMAT_COMMUNITY_LIST
);
874 entry
->config
= XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG
, str
);
876 entry
->config
= NULL
;
877 entry
->u
.ecom
= ecom
;
880 /* Do not put duplicated community entry. */
881 if (community_list_dup_check (list
, entry
))
882 community_entry_free (entry
);
885 community_list_entry_add (list
, entry
);
886 route_map_notify_dependencies(name
, RMAP_EVENT_ECLIST_ADDED
);
892 /* Unset extcommunity-list. When str is NULL, delete all of
893 extcommunity-list entry belongs to the specified name. */
895 extcommunity_list_unset (struct community_list_handler
*ch
,
896 const char *name
, const char *str
,
897 int direct
, int style
, int delete_all
)
899 struct community_entry
*entry
= NULL
;
900 struct community_list
*list
;
901 struct ecommunity
*ecom
= NULL
;
903 /* Lookup extcommunity list. */
904 list
= community_list_lookup (ch
, name
, EXTCOMMUNITY_LIST_MASTER
);
906 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
908 /* Delete all of entry belongs to this extcommunity-list. */
911 community_list_delete (list
);
912 route_map_notify_dependencies(name
, RMAP_EVENT_ECLIST_DELETED
);
916 if (style
== EXTCOMMUNITY_LIST_STANDARD
)
919 ecom
= ecommunity_str2com (str
, 0, 1);
924 entry
= community_list_entry_lookup (list
, ecom
, direct
);
925 ecommunity_free (&ecom
);
928 entry
= community_list_entry_lookup (list
, str
, direct
);
931 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
933 community_list_entry_delete (list
, entry
, style
);
934 route_map_notify_dependencies(name
, RMAP_EVENT_ECLIST_DELETED
);
939 /* Initializa community-list. Return community-list handler. */
940 struct community_list_handler
*
941 community_list_init (void)
943 struct community_list_handler
*ch
;
944 ch
= XCALLOC (MTYPE_COMMUNITY_LIST_HANDLER
,
945 sizeof (struct community_list_handler
));
949 /* Terminate community-list. */
951 community_list_terminate (struct community_list_handler
*ch
)
953 struct community_list_master
*cm
;
954 struct community_list
*list
;
956 cm
= &ch
->community_list
;
957 while ((list
= cm
->num
.head
) != NULL
)
958 community_list_delete (list
);
959 while ((list
= cm
->str
.head
) != NULL
)
960 community_list_delete (list
);
962 cm
= &ch
->extcommunity_list
;
963 while ((list
= cm
->num
.head
) != NULL
)
964 community_list_delete (list
);
965 while ((list
= cm
->str
.head
) != NULL
)
966 community_list_delete (list
);
968 XFREE (MTYPE_COMMUNITY_LIST_HANDLER
, ch
);