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 along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
32 #include "bgpd/bgpd.h"
33 #include "bgpd/bgp_community.h"
34 #include "bgpd/bgp_ecommunity.h"
35 #include "bgpd/bgp_lcommunity.h"
36 #include "bgpd/bgp_community_alias.h"
37 #include "bgpd/bgp_aspath.h"
38 #include "bgpd/bgp_regex.h"
39 #include "bgpd/bgp_clist.h"
41 /* Calculate new sequential number. */
42 static int64_t bgp_clist_new_seq_get(struct community_list
*list
)
46 struct community_entry
*entry
;
50 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
51 if (maxseq
< entry
->seq
)
55 newseq
= ((maxseq
/ 5) * 5) + 5;
57 return (newseq
> UINT_MAX
) ? UINT_MAX
: newseq
;
60 /* Return community-list entry which has same seq number. */
61 static struct community_entry
*bgp_clist_seq_check(struct community_list
*list
,
64 struct community_entry
*entry
;
66 for (entry
= list
->head
; entry
; entry
= entry
->next
)
67 if (entry
->seq
== seq
)
72 static uint32_t bgp_clist_hash_key_community_list(const void *data
)
74 struct community_list
*cl
= (struct community_list
*) data
;
79 cl
->name_hash
= bgp_clist_hash_key(cl
->name
);
83 static bool bgp_clist_hash_cmp_community_list(const void *a1
, const void *a2
)
85 const struct community_list
*cl1
= a1
;
86 const struct community_list
*cl2
= a2
;
88 if (cl1
->name_hash
!= cl2
->name_hash
)
91 if (strcmp(cl1
->name
, cl2
->name
) == 0)
97 /* Lookup master structure for community-list or
99 struct community_list_master
*
100 community_list_master_lookup(struct community_list_handler
*ch
, int master
)
104 case COMMUNITY_LIST_MASTER
:
105 return &ch
->community_list
;
106 case EXTCOMMUNITY_LIST_MASTER
:
107 return &ch
->extcommunity_list
;
108 case LARGE_COMMUNITY_LIST_MASTER
:
109 return &ch
->lcommunity_list
;
114 /* Allocate a new community list entry. */
115 static struct community_entry
*community_entry_new(void)
117 return XCALLOC(MTYPE_COMMUNITY_LIST_ENTRY
,
118 sizeof(struct community_entry
));
121 /* Free community list entry. */
122 static void community_entry_free(struct community_entry
*entry
)
124 switch (entry
->style
) {
125 case COMMUNITY_LIST_STANDARD
:
127 community_free(&entry
->u
.com
);
129 case LARGE_COMMUNITY_LIST_STANDARD
:
131 lcommunity_free(&entry
->u
.lcom
);
133 case EXTCOMMUNITY_LIST_STANDARD
:
134 /* In case of standard extcommunity-list, configuration string
135 is made by ecommunity_ecom2str(). */
136 XFREE(MTYPE_ECOMMUNITY_STR
, entry
->config
);
138 ecommunity_free(&entry
->u
.ecom
);
140 case COMMUNITY_LIST_EXPANDED
:
141 case EXTCOMMUNITY_LIST_EXPANDED
:
142 case LARGE_COMMUNITY_LIST_EXPANDED
:
143 XFREE(MTYPE_COMMUNITY_LIST_CONFIG
, entry
->config
);
145 bgp_regex_free(entry
->reg
);
149 XFREE(MTYPE_COMMUNITY_LIST_ENTRY
, entry
);
152 /* Allocate a new community-list. */
153 static struct community_list
*community_list_new(void)
155 return XCALLOC(MTYPE_COMMUNITY_LIST
, sizeof(struct community_list
));
158 /* Free community-list. */
159 static void community_list_free(struct community_list
*list
)
161 XFREE(MTYPE_COMMUNITY_LIST_NAME
, list
->name
);
162 XFREE(MTYPE_COMMUNITY_LIST
, list
);
165 static struct community_list
*
166 community_list_insert(struct community_list_handler
*ch
, const char *name
,
171 struct community_list
*new;
172 struct community_list
*point
;
173 struct community_list_list
*list
;
174 struct community_list_master
*cm
;
176 /* Lookup community-list master. */
177 cm
= community_list_master_lookup(ch
, master
);
181 /* Allocate new community_list and copy given name. */
182 new = community_list_new();
183 new->name
= XSTRDUP(MTYPE_COMMUNITY_LIST_NAME
, name
);
184 new->name_hash
= bgp_clist_hash_key_community_list(new);
187 hash_get(cm
->hash
, new, hash_alloc_intern
);
189 /* If name is made by all digit character. We treat it as
191 for (number
= 0, i
= 0; i
< strlen(name
); i
++) {
192 if (isdigit((unsigned char)name
[i
]))
193 number
= (number
* 10) + (name
[i
] - '0');
198 /* In case of name is all digit character */
199 if (i
== strlen(name
)) {
200 new->sort
= COMMUNITY_LIST_NUMBER
;
202 /* Set access_list to number list. */
205 for (point
= list
->head
; point
; point
= point
->next
)
206 if (atol(point
->name
) >= number
)
209 new->sort
= COMMUNITY_LIST_STRING
;
211 /* Set access_list to string list. */
214 /* Set point to insertion point. */
215 for (point
= list
->head
; point
; point
= point
->next
)
216 if (strcmp(point
->name
, name
) >= 0)
220 /* Link to upper list. */
223 /* In case of this is the first element of master. */
224 if (list
->head
== NULL
) {
225 list
->head
= list
->tail
= new;
229 /* In case of insertion is made at the tail of access_list. */
231 new->prev
= list
->tail
;
232 list
->tail
->next
= new;
237 /* In case of insertion is made at the head of access_list. */
238 if (point
== list
->head
) {
239 new->next
= list
->head
;
240 list
->head
->prev
= new;
245 /* Insertion is made at middle of the access_list. */
247 new->prev
= point
->prev
;
250 point
->prev
->next
= new;
256 struct community_list
*community_list_lookup(struct community_list_handler
*ch
,
261 struct community_list lookup
;
262 struct community_list_master
*cm
;
267 cm
= community_list_master_lookup(ch
, master
);
271 lookup
.name
= (char *)name
;
272 lookup
.name_hash
= name_hash
;
273 return hash_get(cm
->hash
, &lookup
, NULL
);
276 static struct community_list
*
277 community_list_get(struct community_list_handler
*ch
, const char *name
,
280 struct community_list
*list
;
282 list
= community_list_lookup(ch
, name
, 0, master
);
284 list
= community_list_insert(ch
, name
, master
);
288 static void community_list_delete(struct community_list_master
*cm
,
289 struct community_list
*list
)
291 struct community_list_list
*clist
;
292 struct community_entry
*entry
, *next
;
294 for (entry
= list
->head
; entry
; entry
= next
) {
296 community_entry_free(entry
);
299 clist
= list
->parent
;
302 list
->next
->prev
= list
->prev
;
304 clist
->tail
= list
->prev
;
307 list
->prev
->next
= list
->next
;
309 clist
->head
= list
->next
;
311 hash_release(cm
->hash
, list
);
312 community_list_free(list
);
315 static bool community_list_empty_p(struct community_list
*list
)
317 return list
->head
== NULL
&& list
->tail
== NULL
;
320 /* Delete community-list entry from the list. */
321 static void community_list_entry_delete(struct community_list_master
*cm
,
322 struct community_list
*list
,
323 struct community_entry
*entry
)
326 entry
->next
->prev
= entry
->prev
;
328 list
->tail
= entry
->prev
;
331 entry
->prev
->next
= entry
->next
;
333 list
->head
= entry
->next
;
335 community_entry_free(entry
);
337 if (community_list_empty_p(list
))
338 community_list_delete(cm
, list
);
342 * Replace community-list entry in the list. Note that entry is the new one
343 * and replace is one one being replaced.
345 static void community_list_entry_replace(struct community_list
*list
,
346 struct community_entry
*replace
,
347 struct community_entry
*entry
)
350 entry
->next
= replace
->next
;
351 replace
->next
->prev
= entry
;
358 entry
->prev
= replace
->prev
;
359 replace
->prev
->next
= entry
;
365 community_entry_free(replace
);
368 /* Add community-list entry to the list. */
369 static void community_list_entry_add(struct community_list
*list
,
370 struct community_entry
*entry
,
371 struct community_list_handler
*ch
,
374 struct community_entry
*replace
;
375 struct community_entry
*point
;
377 /* Automatic assignment of seq no. */
378 if (entry
->seq
== COMMUNITY_SEQ_NUMBER_AUTO
)
379 entry
->seq
= bgp_clist_new_seq_get(list
);
381 if (list
->tail
&& entry
->seq
> list
->tail
->seq
)
384 replace
= bgp_clist_seq_check(list
, entry
->seq
);
386 community_list_entry_replace(list
, replace
, entry
);
390 /* Check insert point. */
391 for (point
= list
->head
; point
; point
= point
->next
)
392 if (point
->seq
>= entry
->seq
)
396 /* In case of this is the first element of the list. */
401 point
->prev
->next
= entry
;
405 entry
->prev
= point
->prev
;
409 list
->tail
->next
= entry
;
413 entry
->prev
= list
->tail
;
418 /* Lookup community-list entry from the list. */
419 static struct community_entry
*
420 community_list_entry_lookup(struct community_list
*list
, const void *arg
,
423 struct community_entry
*entry
;
425 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
426 switch (entry
->style
) {
427 case COMMUNITY_LIST_STANDARD
:
428 if (entry
->direct
== direct
429 && community_cmp(entry
->u
.com
, arg
))
432 case EXTCOMMUNITY_LIST_STANDARD
:
433 if (entry
->direct
== direct
434 && ecommunity_cmp(entry
->u
.ecom
, arg
))
437 case LARGE_COMMUNITY_LIST_STANDARD
:
438 if (entry
->direct
== direct
439 && lcommunity_cmp(entry
->u
.lcom
, arg
))
442 case COMMUNITY_LIST_EXPANDED
:
443 case EXTCOMMUNITY_LIST_EXPANDED
:
444 case LARGE_COMMUNITY_LIST_EXPANDED
:
445 if (entry
->direct
== direct
446 && strcmp(entry
->config
, arg
) == 0)
456 static char *community_str_get(struct community
*com
, int i
)
463 memcpy(&comval
, com_nthval(com
, i
), sizeof(uint32_t));
464 comval
= ntohl(comval
);
467 case COMMUNITY_INTERNET
:
468 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "internet");
470 case COMMUNITY_GSHUT
:
471 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "graceful-shutdown");
473 case COMMUNITY_ACCEPT_OWN
:
474 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "accept-own");
476 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4
:
477 str
= XSTRDUP(MTYPE_COMMUNITY_STR
,
478 "route-filter-translated-v4");
480 case COMMUNITY_ROUTE_FILTER_v4
:
481 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "route-filter-v4");
483 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6
:
484 str
= XSTRDUP(MTYPE_COMMUNITY_STR
,
485 "route-filter-translated-v6");
487 case COMMUNITY_ROUTE_FILTER_v6
:
488 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "route-filter-v6");
490 case COMMUNITY_LLGR_STALE
:
491 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "llgr-stale");
493 case COMMUNITY_NO_LLGR
:
494 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "no-llgr");
496 case COMMUNITY_ACCEPT_OWN_NEXTHOP
:
497 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "accept-own-nexthop");
499 case COMMUNITY_BLACKHOLE
:
500 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "blackhole");
502 case COMMUNITY_NO_EXPORT
:
503 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "no-export");
505 case COMMUNITY_NO_ADVERTISE
:
506 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "no-advertise");
508 case COMMUNITY_LOCAL_AS
:
509 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "local-AS");
511 case COMMUNITY_NO_PEER
:
512 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "no-peer");
515 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "65536:65535");
516 as
= (comval
>> 16) & 0xFFFF;
517 val
= comval
& 0xFFFF;
518 snprintf(str
, strlen(str
), "%u:%d", as
, val
);
525 /* Internal function to perform regular expression match for
526 * a single community. */
527 static bool community_regexp_include(regex_t
*reg
, struct community
*com
, int i
)
532 /* When there is no communities attribute it is treated as empty string.
534 if (com
== NULL
|| com
->size
== 0)
535 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "");
537 str
= community_str_get(com
, i
);
539 /* Regular expression match. */
540 rv
= regexec(reg
, str
, 0, NULL
, 0);
542 XFREE(MTYPE_COMMUNITY_STR
, str
);
547 /* Internal function to perform regular expression match for community
549 static bool community_regexp_match(struct community
*com
, regex_t
*reg
)
555 /* When there is no communities attribute it is treated as empty
557 if (com
== NULL
|| com
->size
== 0)
560 str
= community_str(com
, false);
562 regstr
= bgp_alias2community_str(str
);
564 /* Regular expression match. */
565 rv
= regexec(reg
, regstr
, 0, NULL
, 0);
567 XFREE(MTYPE_TMP
, regstr
);
572 static char *lcommunity_str_get(struct lcommunity
*lcom
, int i
)
574 struct lcommunity_val lcomval
;
575 uint32_t globaladmin
;
581 ptr
= lcom
->val
+ (i
* LCOMMUNITY_SIZE
);
583 memcpy(&lcomval
, ptr
, LCOMMUNITY_SIZE
);
585 /* Allocate memory. 48 bytes taken off bgp_lcommunity.c */
586 ptr
= (uint8_t *)lcomval
.val
;
587 ptr
= ptr_get_be32(ptr
, &globaladmin
);
588 ptr
= ptr_get_be32(ptr
, &localdata1
);
589 ptr
= ptr_get_be32(ptr
, &localdata2
);
590 (void)ptr
; /* consume value */
592 str
= XMALLOC(MTYPE_LCOMMUNITY_STR
, 48);
593 snprintf(str
, 48, "%u:%u:%u", globaladmin
, localdata1
, localdata2
);
598 /* Internal function to perform regular expression match for
599 * a single community. */
600 static bool lcommunity_regexp_include(regex_t
*reg
, struct lcommunity
*lcom
,
605 /* When there is no communities attribute it is treated as empty string.
607 if (lcom
== NULL
|| lcom
->size
== 0)
608 str
= XSTRDUP(MTYPE_LCOMMUNITY_STR
, "");
610 str
= lcommunity_str_get(lcom
, i
);
612 /* Regular expression match. */
613 if (regexec(reg
, str
, 0, NULL
, 0) == 0) {
614 XFREE(MTYPE_LCOMMUNITY_STR
, str
);
618 XFREE(MTYPE_LCOMMUNITY_STR
, str
);
623 static bool lcommunity_regexp_match(struct lcommunity
*com
, regex_t
*reg
)
629 /* When there is no communities attribute it is treated as empty
631 if (com
== NULL
|| com
->size
== 0)
634 str
= lcommunity_str(com
, false);
636 regstr
= bgp_alias2community_str(str
);
638 /* Regular expression match. */
639 rv
= regexec(reg
, regstr
, 0, NULL
, 0);
641 XFREE(MTYPE_TMP
, regstr
);
647 static bool ecommunity_regexp_match(struct ecommunity
*ecom
, regex_t
*reg
)
651 /* When there is no communities attribute it is treated as empty
653 if (ecom
== NULL
|| ecom
->size
== 0)
656 str
= ecommunity_str(ecom
);
658 /* Regular expression match. */
659 if (regexec(reg
, str
, 0, NULL
, 0) == 0)
666 /* When given community attribute matches to the community-list return
668 bool community_list_match(struct community
*com
, struct community_list
*list
)
670 struct community_entry
*entry
;
672 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
674 return entry
->direct
== COMMUNITY_PERMIT
;
676 if (entry
->style
== COMMUNITY_LIST_STANDARD
) {
677 if (community_include(entry
->u
.com
, COMMUNITY_INTERNET
))
678 return entry
->direct
== COMMUNITY_PERMIT
;
680 if (community_match(com
, entry
->u
.com
))
681 return entry
->direct
== COMMUNITY_PERMIT
;
682 } else if (entry
->style
== COMMUNITY_LIST_EXPANDED
) {
683 if (community_regexp_match(com
, entry
->reg
))
684 return entry
->direct
== COMMUNITY_PERMIT
;
690 bool lcommunity_list_match(struct lcommunity
*lcom
, struct community_list
*list
)
692 struct community_entry
*entry
;
694 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
696 return entry
->direct
== COMMUNITY_PERMIT
;
698 if (entry
->style
== LARGE_COMMUNITY_LIST_STANDARD
) {
699 if (lcommunity_match(lcom
, entry
->u
.lcom
))
700 return entry
->direct
== COMMUNITY_PERMIT
;
701 } else if (entry
->style
== LARGE_COMMUNITY_LIST_EXPANDED
) {
702 if (lcommunity_regexp_match(lcom
, entry
->reg
))
703 return entry
->direct
== COMMUNITY_PERMIT
;
710 /* Perform exact matching. In case of expanded large-community-list, do
711 * same thing as lcommunity_list_match().
713 bool lcommunity_list_exact_match(struct lcommunity
*lcom
,
714 struct community_list
*list
)
716 struct community_entry
*entry
;
718 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
720 return entry
->direct
== COMMUNITY_PERMIT
;
722 if (entry
->style
== LARGE_COMMUNITY_LIST_STANDARD
) {
723 if (lcommunity_cmp(lcom
, entry
->u
.lcom
))
724 return entry
->direct
== COMMUNITY_PERMIT
;
725 } else if (entry
->style
== LARGE_COMMUNITY_LIST_EXPANDED
) {
726 if (lcommunity_regexp_match(lcom
, entry
->reg
))
727 return entry
->direct
== COMMUNITY_PERMIT
;
733 bool ecommunity_list_match(struct ecommunity
*ecom
, struct community_list
*list
)
735 struct community_entry
*entry
;
737 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
739 return entry
->direct
== COMMUNITY_PERMIT
;
741 if (entry
->style
== EXTCOMMUNITY_LIST_STANDARD
) {
742 if (ecommunity_match(ecom
, entry
->u
.ecom
))
743 return entry
->direct
== COMMUNITY_PERMIT
;
744 } else if (entry
->style
== EXTCOMMUNITY_LIST_EXPANDED
) {
745 if (ecommunity_regexp_match(ecom
, entry
->reg
))
746 return entry
->direct
== COMMUNITY_PERMIT
;
752 /* Perform exact matching. In case of expanded community-list, do
753 same thing as community_list_match(). */
754 bool community_list_exact_match(struct community
*com
,
755 struct community_list
*list
)
757 struct community_entry
*entry
;
759 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
761 return entry
->direct
== COMMUNITY_PERMIT
;
763 if (entry
->style
== COMMUNITY_LIST_STANDARD
) {
764 if (community_include(entry
->u
.com
, COMMUNITY_INTERNET
))
765 return entry
->direct
== COMMUNITY_PERMIT
;
767 if (community_cmp(com
, entry
->u
.com
))
768 return entry
->direct
== COMMUNITY_PERMIT
;
769 } else if (entry
->style
== COMMUNITY_LIST_EXPANDED
) {
770 if (community_regexp_match(com
, entry
->reg
))
771 return entry
->direct
== COMMUNITY_PERMIT
;
777 /* Delete all permitted communities in the list from com. */
778 struct community
*community_list_match_delete(struct community
*com
,
779 struct community_list
*list
)
781 struct community_entry
*entry
;
783 uint32_t com_index_to_delete
[com
->size
];
784 int delete_index
= 0;
787 /* Loop over each community value and evaluate each against the
788 * community-list. If we need to delete a community value add its index
789 * to com_index_to_delete.
791 for (i
= 0; i
< com
->size
; i
++) {
792 val
= community_val_get(com
, i
);
794 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
796 if (entry
->direct
== COMMUNITY_PERMIT
) {
797 com_index_to_delete
[delete_index
] = i
;
803 else if ((entry
->style
== COMMUNITY_LIST_STANDARD
)
804 && (community_include(entry
->u
.com
,
806 || community_include(entry
->u
.com
, val
))) {
807 if (entry
->direct
== COMMUNITY_PERMIT
) {
808 com_index_to_delete
[delete_index
] = i
;
814 else if ((entry
->style
== COMMUNITY_LIST_EXPANDED
)
815 && community_regexp_include(entry
->reg
, com
,
817 if (entry
->direct
== COMMUNITY_PERMIT
) {
818 com_index_to_delete
[delete_index
] = i
;
826 /* Delete all of the communities we flagged for deletion */
827 for (i
= delete_index
- 1; i
>= 0; i
--) {
828 val
= community_val_get(com
, com_index_to_delete
[i
]);
830 community_del_val(com
, &val
);
836 /* To avoid duplicated entry in the community-list, this function
837 compares specified entry to existing entry. */
838 static bool community_list_dup_check(struct community_list
*list
,
839 struct community_entry
*new)
841 struct community_entry
*entry
;
843 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
844 if (entry
->style
!= new->style
)
847 if (entry
->direct
!= new->direct
)
850 if (entry
->any
!= new->any
)
856 switch (entry
->style
) {
857 case COMMUNITY_LIST_STANDARD
:
858 if (community_cmp(entry
->u
.com
, new->u
.com
))
861 case LARGE_COMMUNITY_LIST_STANDARD
:
862 if (lcommunity_cmp(entry
->u
.lcom
, new->u
.lcom
))
865 case EXTCOMMUNITY_LIST_STANDARD
:
866 if (ecommunity_cmp(entry
->u
.ecom
, new->u
.ecom
))
869 case COMMUNITY_LIST_EXPANDED
:
870 case EXTCOMMUNITY_LIST_EXPANDED
:
871 case LARGE_COMMUNITY_LIST_EXPANDED
:
872 if (strcmp(entry
->config
, new->config
) == 0)
882 /* Set community-list. */
883 int community_list_set(struct community_list_handler
*ch
, const char *name
,
884 const char *str
, const char *seq
, int direct
, int style
)
886 struct community_entry
*entry
= NULL
;
887 struct community_list
*list
;
888 struct community
*com
= NULL
;
889 regex_t
*regex
= NULL
;
890 int64_t seqnum
= COMMUNITY_SEQ_NUMBER_AUTO
;
893 seqnum
= (int64_t)atol(seq
);
895 /* Get community list. */
896 list
= community_list_get(ch
, name
, COMMUNITY_LIST_MASTER
);
898 /* When community-list already has entry, new entry should have same
899 style. If you want to have mixed style community-list, you can
900 comment out this check. */
901 if (!community_list_empty_p(list
)) {
902 struct community_entry
*first
;
906 if (style
!= first
->style
) {
907 return (first
->style
== COMMUNITY_LIST_STANDARD
908 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
909 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT
);
914 if (style
== COMMUNITY_LIST_STANDARD
)
915 com
= community_str2com(str
);
917 regex
= bgp_regcomp(str
);
920 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
923 entry
= community_entry_new();
924 entry
->direct
= direct
;
925 entry
->style
= style
;
926 entry
->any
= (str
? false : true);
931 (regex
? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG
, str
) : NULL
);
933 /* Do not put duplicated community entry. */
934 if (community_list_dup_check(list
, entry
))
935 community_entry_free(entry
);
937 community_list_entry_add(list
, entry
, ch
,
938 COMMUNITY_LIST_MASTER
);
939 route_map_notify_dependencies(name
, RMAP_EVENT_CLIST_ADDED
);
945 /* Unset community-list */
946 int community_list_unset(struct community_list_handler
*ch
, const char *name
,
947 const char *str
, const char *seq
, int direct
,
950 struct community_list_master
*cm
= NULL
;
951 struct community_entry
*entry
= NULL
;
952 struct community_list
*list
;
953 struct community
*com
= NULL
;
955 /* Lookup community list. */
956 list
= community_list_lookup(ch
, name
, 0, COMMUNITY_LIST_MASTER
);
958 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
960 cm
= community_list_master_lookup(ch
, COMMUNITY_LIST_MASTER
);
961 /* Delete all of entry belongs to this community-list. */
963 community_list_delete(cm
, list
);
964 route_map_notify_dependencies(name
, RMAP_EVENT_CLIST_DELETED
);
968 if (style
== COMMUNITY_LIST_STANDARD
)
969 com
= community_str2com(str
);
972 entry
= community_list_entry_lookup(list
, com
, direct
);
973 community_free(&com
);
975 entry
= community_list_entry_lookup(list
, str
, direct
);
978 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
980 community_list_entry_delete(cm
, list
, entry
);
981 route_map_notify_dependencies(name
, RMAP_EVENT_CLIST_DELETED
);
986 /* Delete all permitted large communities in the list from com. */
987 struct lcommunity
*lcommunity_list_match_delete(struct lcommunity
*lcom
,
988 struct community_list
*list
)
990 struct community_entry
*entry
;
991 uint32_t com_index_to_delete
[lcom
->size
];
993 int delete_index
= 0;
996 /* Loop over each lcommunity value and evaluate each against the
997 * community-list. If we need to delete a community value add its index
998 * to com_index_to_delete.
1000 for (i
= 0; i
< lcom
->size
; i
++) {
1001 ptr
= lcom
->val
+ (i
* LCOMMUNITY_SIZE
);
1002 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
1004 if (entry
->direct
== COMMUNITY_PERMIT
) {
1005 com_index_to_delete
[delete_index
] = i
;
1011 else if ((entry
->style
== LARGE_COMMUNITY_LIST_STANDARD
)
1012 && lcommunity_include(entry
->u
.lcom
, ptr
)) {
1013 if (entry
->direct
== COMMUNITY_PERMIT
) {
1014 com_index_to_delete
[delete_index
] = i
;
1020 else if ((entry
->style
== LARGE_COMMUNITY_LIST_EXPANDED
)
1021 && lcommunity_regexp_include(entry
->reg
, lcom
,
1023 if (entry
->direct
== COMMUNITY_PERMIT
) {
1024 com_index_to_delete
[delete_index
] = i
;
1032 /* Delete all of the communities we flagged for deletion */
1033 for (i
= delete_index
- 1; i
>= 0; i
--) {
1034 ptr
= lcom
->val
+ (com_index_to_delete
[i
] * LCOMMUNITY_SIZE
);
1035 lcommunity_del_val(lcom
, ptr
);
1041 /* Helper to check if every octet do not exceed UINT_MAX */
1042 bool lcommunity_list_valid(const char *community
, int style
)
1045 char **splits
, **communities
;
1047 int num
, num_communities
;
1051 frrstr_split(community
, " ", &communities
, &num_communities
);
1053 for (int j
= 0; j
< num_communities
; j
++) {
1055 frrstr_split(communities
[j
], ":", &splits
, &num
);
1057 for (int i
= 0; i
< num
; i
++) {
1058 if (strlen(splits
[i
]) == 0)
1059 /* There is no digit to check */
1062 if (style
== LARGE_COMMUNITY_LIST_STANDARD
) {
1063 if (*splits
[i
] == '-')
1064 /* Must not be negative */
1066 else if (strtoul(splits
[i
], &endptr
, 10)
1068 /* Larger than 4 octets */
1071 /* Not all characters were digits */
1074 regres
= bgp_regcomp(communities
[j
]);
1076 /* malformed regex */
1079 bgp_regex_free(regres
);
1083 XFREE(MTYPE_TMP
, splits
[i
]);
1085 XFREE(MTYPE_TMP
, splits
);
1090 XFREE(MTYPE_TMP
, communities
[j
]);
1092 XFREE(MTYPE_TMP
, communities
);
1094 return (invalid
> 0) ? false : true;
1097 /* Set lcommunity-list. */
1098 int lcommunity_list_set(struct community_list_handler
*ch
, const char *name
,
1099 const char *str
, const char *seq
, int direct
, int style
)
1101 struct community_entry
*entry
= NULL
;
1102 struct community_list
*list
;
1103 struct lcommunity
*lcom
= NULL
;
1104 regex_t
*regex
= NULL
;
1105 int64_t seqnum
= COMMUNITY_SEQ_NUMBER_AUTO
;
1108 seqnum
= (int64_t)atol(seq
);
1110 /* Get community list. */
1111 list
= community_list_get(ch
, name
, LARGE_COMMUNITY_LIST_MASTER
);
1113 /* When community-list already has entry, new entry should have same
1114 style. If you want to have mixed style community-list, you can
1115 comment out this check. */
1116 if (!community_list_empty_p(list
)) {
1117 struct community_entry
*first
;
1121 if (style
!= first
->style
) {
1122 return (first
->style
== COMMUNITY_LIST_STANDARD
1123 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
1124 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT
);
1129 if (style
== LARGE_COMMUNITY_LIST_STANDARD
)
1130 lcom
= lcommunity_str2com(str
);
1132 regex
= bgp_regcomp(str
);
1134 if (!lcom
&& !regex
)
1135 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
1138 entry
= community_entry_new();
1139 entry
->direct
= direct
;
1140 entry
->style
= style
;
1141 entry
->any
= (str
? false : true);
1142 entry
->u
.lcom
= lcom
;
1144 entry
->seq
= seqnum
;
1146 (regex
? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG
, str
) : NULL
);
1148 /* Do not put duplicated community entry. */
1149 if (community_list_dup_check(list
, entry
))
1150 community_entry_free(entry
);
1152 community_list_entry_add(list
, entry
, ch
,
1153 LARGE_COMMUNITY_LIST_MASTER
);
1154 route_map_notify_dependencies(name
, RMAP_EVENT_LLIST_ADDED
);
1160 /* Unset community-list. When str is NULL, delete all of
1161 community-list entry belongs to the specified name. */
1162 int lcommunity_list_unset(struct community_list_handler
*ch
, const char *name
,
1163 const char *str
, const char *seq
, int direct
,
1166 struct community_list_master
*cm
= NULL
;
1167 struct community_entry
*entry
= NULL
;
1168 struct community_list
*list
;
1169 struct lcommunity
*lcom
= NULL
;
1170 regex_t
*regex
= NULL
;
1172 /* Lookup community list. */
1173 list
= community_list_lookup(ch
, name
, 0, LARGE_COMMUNITY_LIST_MASTER
);
1175 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
1177 cm
= community_list_master_lookup(ch
, LARGE_COMMUNITY_LIST_MASTER
);
1178 /* Delete all of entry belongs to this community-list. */
1180 community_list_delete(cm
, list
);
1181 route_map_notify_dependencies(name
, RMAP_EVENT_LLIST_DELETED
);
1185 if (style
== LARGE_COMMUNITY_LIST_STANDARD
)
1186 lcom
= lcommunity_str2com(str
);
1188 regex
= bgp_regcomp(str
);
1190 if (!lcom
&& !regex
)
1191 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
1194 entry
= community_list_entry_lookup(list
, lcom
, direct
);
1196 entry
= community_list_entry_lookup(list
, str
, direct
);
1199 lcommunity_free(&lcom
);
1201 bgp_regex_free(regex
);
1204 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
1206 community_list_entry_delete(cm
, list
, entry
);
1207 route_map_notify_dependencies(name
, RMAP_EVENT_LLIST_DELETED
);
1212 /* Set extcommunity-list. */
1213 int extcommunity_list_set(struct community_list_handler
*ch
, const char *name
,
1214 const char *str
, const char *seq
, int direct
,
1217 struct community_entry
*entry
= NULL
;
1218 struct community_list
*list
;
1219 struct ecommunity
*ecom
= NULL
;
1220 regex_t
*regex
= NULL
;
1221 int64_t seqnum
= COMMUNITY_SEQ_NUMBER_AUTO
;
1224 seqnum
= (int64_t)atol(seq
);
1227 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
1229 /* Get community list. */
1230 list
= community_list_get(ch
, name
, EXTCOMMUNITY_LIST_MASTER
);
1232 /* When community-list already has entry, new entry should have same
1233 style. If you want to have mixed style community-list, you can
1234 comment out this check. */
1235 if (!community_list_empty_p(list
)) {
1236 struct community_entry
*first
;
1240 if (style
!= first
->style
) {
1241 return (first
->style
== EXTCOMMUNITY_LIST_STANDARD
1242 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
1243 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT
);
1247 if (style
== EXTCOMMUNITY_LIST_STANDARD
)
1248 ecom
= ecommunity_str2com(str
, 0, 1);
1250 regex
= bgp_regcomp(str
);
1252 if (!ecom
&& !regex
)
1253 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
1257 ecommunity_ecom2str(ecom
, ECOMMUNITY_FORMAT_DISPLAY
, 0);
1259 entry
= community_entry_new();
1260 entry
->direct
= direct
;
1261 entry
->style
= style
;
1264 entry
->config
= ecommunity_ecom2str(
1265 ecom
, ECOMMUNITY_FORMAT_COMMUNITY_LIST
, 0);
1267 entry
->config
= XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG
, str
);
1269 entry
->u
.ecom
= ecom
;
1271 entry
->seq
= seqnum
;
1273 /* Do not put duplicated community entry. */
1274 if (community_list_dup_check(list
, entry
))
1275 community_entry_free(entry
);
1277 community_list_entry_add(list
, entry
, ch
,
1278 EXTCOMMUNITY_LIST_MASTER
);
1279 route_map_notify_dependencies(name
, RMAP_EVENT_ECLIST_ADDED
);
1285 /* Unset extcommunity-list.
1287 * When str is NULL, delete all extcommunity-list entries belonging to the
1290 int extcommunity_list_unset(struct community_list_handler
*ch
, const char *name
,
1291 const char *str
, const char *seq
, int direct
,
1294 struct community_list_master
*cm
= NULL
;
1295 struct community_entry
*entry
= NULL
;
1296 struct community_list
*list
;
1297 struct ecommunity
*ecom
= NULL
;
1299 /* Lookup extcommunity list. */
1300 list
= community_list_lookup(ch
, name
, 0, EXTCOMMUNITY_LIST_MASTER
);
1302 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
1304 cm
= community_list_master_lookup(ch
, EXTCOMMUNITY_LIST_MASTER
);
1305 /* Delete all of entry belongs to this extcommunity-list. */
1307 community_list_delete(cm
, list
);
1308 route_map_notify_dependencies(name
, RMAP_EVENT_ECLIST_DELETED
);
1312 if (style
== EXTCOMMUNITY_LIST_STANDARD
)
1313 ecom
= ecommunity_str2com(str
, 0, 1);
1316 entry
= community_list_entry_lookup(list
, ecom
, direct
);
1317 ecommunity_free(&ecom
);
1319 entry
= community_list_entry_lookup(list
, str
, direct
);
1322 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
1324 community_list_entry_delete(cm
, list
, entry
);
1325 route_map_notify_dependencies(name
, RMAP_EVENT_ECLIST_DELETED
);
1330 /* Initializa community-list. Return community-list handler. */
1331 struct community_list_handler
*community_list_init(void)
1333 struct community_list_handler
*ch
;
1334 ch
= XCALLOC(MTYPE_COMMUNITY_LIST_HANDLER
,
1335 sizeof(struct community_list_handler
));
1337 ch
->community_list
.hash
=
1338 hash_create_size(4, bgp_clist_hash_key_community_list
,
1339 bgp_clist_hash_cmp_community_list
,
1340 "Community List Number Quick Lookup");
1342 ch
->extcommunity_list
.hash
=
1343 hash_create_size(4, bgp_clist_hash_key_community_list
,
1344 bgp_clist_hash_cmp_community_list
,
1345 "Extended Community List Quick Lookup");
1347 ch
->lcommunity_list
.hash
=
1348 hash_create_size(4, bgp_clist_hash_key_community_list
,
1349 bgp_clist_hash_cmp_community_list
,
1350 "Large Community List Quick Lookup");
1355 /* Terminate community-list. */
1356 void community_list_terminate(struct community_list_handler
*ch
)
1358 struct community_list_master
*cm
;
1359 struct community_list
*list
;
1361 cm
= &ch
->community_list
;
1362 while ((list
= cm
->num
.head
) != NULL
)
1363 community_list_delete(cm
, list
);
1364 while ((list
= cm
->str
.head
) != NULL
)
1365 community_list_delete(cm
, list
);
1366 hash_free(cm
->hash
);
1368 cm
= &ch
->lcommunity_list
;
1369 while ((list
= cm
->num
.head
) != NULL
)
1370 community_list_delete(cm
, list
);
1371 while ((list
= cm
->str
.head
) != NULL
)
1372 community_list_delete(cm
, list
);
1373 hash_free(cm
->hash
);
1375 cm
= &ch
->extcommunity_list
;
1376 while ((list
= cm
->num
.head
) != NULL
)
1377 community_list_delete(cm
, list
);
1378 while ((list
= cm
->str
.head
) != NULL
)
1379 community_list_delete(cm
, list
);
1380 hash_free(cm
->hash
);
1382 XFREE(MTYPE_COMMUNITY_LIST_HANDLER
, ch
);
1385 static int bgp_community_list_vector_walker(struct hash_bucket
*bucket
,
1388 vector
*comps
= data
;
1389 struct community_list
*list
= bucket
->data
;
1391 vector_set(*comps
, XSTRDUP(MTYPE_COMPLETION
, list
->name
));
1396 static void bgp_community_list_cmd_completion(vector comps
,
1397 struct cmd_token
*token
)
1399 struct community_list_master
*cm
;
1401 cm
= community_list_master_lookup(bgp_clist
, COMMUNITY_LIST_MASTER
);
1403 hash_walk(cm
->hash
, bgp_community_list_vector_walker
, &comps
);
1406 static void bgp_lcommunity_list_cmd_completion(vector comps
,
1407 struct cmd_token
*token
)
1409 struct community_list_master
*cm
;
1411 cm
= community_list_master_lookup(bgp_clist
,
1412 LARGE_COMMUNITY_LIST_MASTER
);
1414 hash_walk(cm
->hash
, bgp_community_list_vector_walker
, &comps
);
1417 static void bgp_extcommunity_list_cmd_completion(vector comps
,
1418 struct cmd_token
*token
)
1420 struct community_list_master
*cm
;
1422 cm
= community_list_master_lookup(bgp_clist
, EXTCOMMUNITY_LIST_MASTER
);
1424 hash_walk(cm
->hash
, bgp_community_list_vector_walker
, &comps
);
1427 static const struct cmd_variable_handler community_list_handlers
[] = {
1428 {.tokenname
= "COMMUNITY_LIST_NAME",
1429 .completions
= bgp_community_list_cmd_completion
},
1430 {.completions
= NULL
}};
1432 static const struct cmd_variable_handler lcommunity_list_handlers
[] = {
1433 {.tokenname
= "LCOMMUNITY_LIST_NAME",
1434 .completions
= bgp_lcommunity_list_cmd_completion
},
1435 {.completions
= NULL
}};
1437 static const struct cmd_variable_handler extcommunity_list_handlers
[] = {
1438 {.tokenname
= "EXTCOMMUNITY_LIST_NAME",
1439 .completions
= bgp_extcommunity_list_cmd_completion
},
1440 {.completions
= NULL
}};
1442 void bgp_community_list_command_completion_setup(void)
1444 cmd_variable_handler_register(community_list_handlers
);
1445 cmd_variable_handler_register(lcommunity_list_handlers
);
1446 cmd_variable_handler_register(extcommunity_list_handlers
);