1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* BGP community-list and extcommunity-list.
3 * Copyright (C) 1999 Kunihiro Ishiguro
17 #include "bgpd/bgpd.h"
18 #include "bgpd/bgp_community.h"
19 #include "bgpd/bgp_ecommunity.h"
20 #include "bgpd/bgp_lcommunity.h"
21 #include "bgpd/bgp_community_alias.h"
22 #include "bgpd/bgp_aspath.h"
23 #include "bgpd/bgp_regex.h"
24 #include "bgpd/bgp_clist.h"
26 /* Calculate new sequential number. */
27 static int64_t bgp_clist_new_seq_get(struct community_list
*list
)
31 struct community_entry
*entry
;
35 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
36 if (maxseq
< entry
->seq
)
40 newseq
= ((maxseq
/ 5) * 5) + 5;
42 return (newseq
> UINT_MAX
) ? UINT_MAX
: newseq
;
45 /* Return community-list entry which has same seq number. */
46 static struct community_entry
*bgp_clist_seq_check(struct community_list
*list
,
49 struct community_entry
*entry
;
51 for (entry
= list
->head
; entry
; entry
= entry
->next
)
52 if (entry
->seq
== seq
)
57 static uint32_t bgp_clist_hash_key_community_list(const void *data
)
59 struct community_list
*cl
= (struct community_list
*) data
;
64 cl
->name_hash
= bgp_clist_hash_key(cl
->name
);
68 static bool bgp_clist_hash_cmp_community_list(const void *a1
, const void *a2
)
70 const struct community_list
*cl1
= a1
;
71 const struct community_list
*cl2
= a2
;
73 if (cl1
->name_hash
!= cl2
->name_hash
)
76 if (strcmp(cl1
->name
, cl2
->name
) == 0)
82 /* Lookup master structure for community-list or
84 struct community_list_master
*
85 community_list_master_lookup(struct community_list_handler
*ch
, int master
)
89 case COMMUNITY_LIST_MASTER
:
90 return &ch
->community_list
;
91 case EXTCOMMUNITY_LIST_MASTER
:
92 return &ch
->extcommunity_list
;
93 case LARGE_COMMUNITY_LIST_MASTER
:
94 return &ch
->lcommunity_list
;
99 /* Allocate a new community list entry. */
100 static struct community_entry
*community_entry_new(void)
102 return XCALLOC(MTYPE_COMMUNITY_LIST_ENTRY
,
103 sizeof(struct community_entry
));
106 /* Free community list entry. */
107 static void community_entry_free(struct community_entry
*entry
)
109 switch (entry
->style
) {
110 case COMMUNITY_LIST_STANDARD
:
112 community_free(&entry
->u
.com
);
114 case LARGE_COMMUNITY_LIST_STANDARD
:
116 lcommunity_free(&entry
->u
.lcom
);
118 case EXTCOMMUNITY_LIST_STANDARD
:
119 /* In case of standard extcommunity-list, configuration string
120 is made by ecommunity_ecom2str(). */
121 XFREE(MTYPE_ECOMMUNITY_STR
, entry
->config
);
123 ecommunity_free(&entry
->u
.ecom
);
125 case COMMUNITY_LIST_EXPANDED
:
126 case EXTCOMMUNITY_LIST_EXPANDED
:
127 case LARGE_COMMUNITY_LIST_EXPANDED
:
128 XFREE(MTYPE_COMMUNITY_LIST_CONFIG
, entry
->config
);
130 bgp_regex_free(entry
->reg
);
134 XFREE(MTYPE_COMMUNITY_LIST_ENTRY
, entry
);
137 /* Allocate a new community-list. */
138 static struct community_list
*community_list_new(void)
140 return XCALLOC(MTYPE_COMMUNITY_LIST
, sizeof(struct community_list
));
143 /* Free community-list. */
144 static void community_list_free(struct community_list
*list
)
146 XFREE(MTYPE_COMMUNITY_LIST_NAME
, list
->name
);
147 XFREE(MTYPE_COMMUNITY_LIST
, list
);
150 static struct community_list
*
151 community_list_insert(struct community_list_handler
*ch
, const char *name
,
156 struct community_list
*new;
157 struct community_list
*point
;
158 struct community_list_list
*list
;
159 struct community_list_master
*cm
;
161 /* Lookup community-list master. */
162 cm
= community_list_master_lookup(ch
, master
);
166 /* Allocate new community_list and copy given name. */
167 new = community_list_new();
168 new->name
= XSTRDUP(MTYPE_COMMUNITY_LIST_NAME
, name
);
169 new->name_hash
= bgp_clist_hash_key_community_list(new);
172 (void)hash_get(cm
->hash
, new, hash_alloc_intern
);
174 /* If name is made by all digit character. We treat it as
176 for (number
= 0, i
= 0; i
< strlen(name
); i
++) {
177 if (isdigit((unsigned char)name
[i
]))
178 number
= (number
* 10) + (name
[i
] - '0');
183 /* In case of name is all digit character */
184 if (i
== strlen(name
)) {
185 new->sort
= COMMUNITY_LIST_NUMBER
;
187 /* Set access_list to number list. */
190 for (point
= list
->head
; point
; point
= point
->next
)
191 if (atol(point
->name
) >= number
)
194 new->sort
= COMMUNITY_LIST_STRING
;
196 /* Set access_list to string list. */
199 /* Set point to insertion point. */
200 for (point
= list
->head
; point
; point
= point
->next
)
201 if (strcmp(point
->name
, name
) >= 0)
205 /* Link to upper list. */
208 /* In case of this is the first element of master. */
209 if (list
->head
== NULL
) {
210 list
->head
= list
->tail
= new;
214 /* In case of insertion is made at the tail of access_list. */
216 new->prev
= list
->tail
;
217 list
->tail
->next
= new;
222 /* In case of insertion is made at the head of access_list. */
223 if (point
== list
->head
) {
224 new->next
= list
->head
;
225 list
->head
->prev
= new;
230 /* Insertion is made at middle of the access_list. */
232 new->prev
= point
->prev
;
235 point
->prev
->next
= new;
241 struct community_list
*community_list_lookup(struct community_list_handler
*ch
,
246 struct community_list lookup
;
247 struct community_list_master
*cm
;
252 cm
= community_list_master_lookup(ch
, master
);
256 lookup
.name
= (char *)name
;
257 lookup
.name_hash
= name_hash
;
258 return hash_get(cm
->hash
, &lookup
, NULL
);
261 static struct community_list
*
262 community_list_get(struct community_list_handler
*ch
, const char *name
,
265 struct community_list
*list
;
267 list
= community_list_lookup(ch
, name
, 0, master
);
269 list
= community_list_insert(ch
, name
, master
);
273 static void community_list_delete(struct community_list_master
*cm
,
274 struct community_list
*list
)
276 struct community_list_list
*clist
;
277 struct community_entry
*entry
, *next
;
279 for (entry
= list
->head
; entry
; entry
= next
) {
281 community_entry_free(entry
);
284 clist
= list
->parent
;
287 list
->next
->prev
= list
->prev
;
289 clist
->tail
= list
->prev
;
292 list
->prev
->next
= list
->next
;
294 clist
->head
= list
->next
;
296 hash_release(cm
->hash
, list
);
297 community_list_free(list
);
300 static bool community_list_empty_p(struct community_list
*list
)
302 return list
->head
== NULL
&& list
->tail
== NULL
;
305 /* Delete community-list entry from the list. */
306 static void community_list_entry_delete(struct community_list_master
*cm
,
307 struct community_list
*list
,
308 struct community_entry
*entry
)
311 entry
->next
->prev
= entry
->prev
;
313 list
->tail
= entry
->prev
;
316 entry
->prev
->next
= entry
->next
;
318 list
->head
= entry
->next
;
320 community_entry_free(entry
);
322 if (community_list_empty_p(list
))
323 community_list_delete(cm
, list
);
327 * Replace community-list entry in the list. Note that entry is the new one
328 * and replace is one one being replaced.
330 static void community_list_entry_replace(struct community_list
*list
,
331 struct community_entry
*replace
,
332 struct community_entry
*entry
)
335 entry
->next
= replace
->next
;
336 replace
->next
->prev
= entry
;
343 entry
->prev
= replace
->prev
;
344 replace
->prev
->next
= entry
;
350 community_entry_free(replace
);
353 /* Add community-list entry to the list. */
354 static void community_list_entry_add(struct community_list
*list
,
355 struct community_entry
*entry
,
356 struct community_list_handler
*ch
,
359 struct community_entry
*replace
;
360 struct community_entry
*point
;
362 /* Automatic assignment of seq no. */
363 if (entry
->seq
== COMMUNITY_SEQ_NUMBER_AUTO
)
364 entry
->seq
= bgp_clist_new_seq_get(list
);
366 if (list
->tail
&& entry
->seq
> list
->tail
->seq
)
369 replace
= bgp_clist_seq_check(list
, entry
->seq
);
371 community_list_entry_replace(list
, replace
, entry
);
375 /* Check insert point. */
376 for (point
= list
->head
; point
; point
= point
->next
)
377 if (point
->seq
>= entry
->seq
)
381 /* In case of this is the first element of the list. */
386 point
->prev
->next
= entry
;
390 entry
->prev
= point
->prev
;
394 list
->tail
->next
= entry
;
398 entry
->prev
= list
->tail
;
403 /* Lookup community-list entry from the list. */
404 static struct community_entry
*
405 community_list_entry_lookup(struct community_list
*list
, const void *arg
,
408 struct community_entry
*entry
;
410 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
411 switch (entry
->style
) {
412 case COMMUNITY_LIST_STANDARD
:
413 if (entry
->direct
== direct
414 && community_cmp(entry
->u
.com
, arg
))
417 case EXTCOMMUNITY_LIST_STANDARD
:
418 if (entry
->direct
== direct
419 && ecommunity_cmp(entry
->u
.ecom
, arg
))
422 case LARGE_COMMUNITY_LIST_STANDARD
:
423 if (entry
->direct
== direct
424 && lcommunity_cmp(entry
->u
.lcom
, arg
))
427 case COMMUNITY_LIST_EXPANDED
:
428 case EXTCOMMUNITY_LIST_EXPANDED
:
429 case LARGE_COMMUNITY_LIST_EXPANDED
:
430 if (entry
->direct
== direct
431 && strcmp(entry
->config
, arg
) == 0)
441 static char *community_str_get(struct community
*com
, int i
)
448 memcpy(&comval
, com_nthval(com
, i
), sizeof(uint32_t));
449 comval
= ntohl(comval
);
452 #if CONFDATE > 20230801
453 CPP_NOTICE("Deprecate COMMUNITY_INTERNET BGP community")
455 case COMMUNITY_INTERNET
:
456 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "internet");
457 zlog_warn("`internet` community is deprecated");
459 case COMMUNITY_GSHUT
:
460 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "graceful-shutdown");
462 case COMMUNITY_ACCEPT_OWN
:
463 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "accept-own");
465 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4
:
466 str
= XSTRDUP(MTYPE_COMMUNITY_STR
,
467 "route-filter-translated-v4");
469 case COMMUNITY_ROUTE_FILTER_v4
:
470 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "route-filter-v4");
472 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6
:
473 str
= XSTRDUP(MTYPE_COMMUNITY_STR
,
474 "route-filter-translated-v6");
476 case COMMUNITY_ROUTE_FILTER_v6
:
477 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "route-filter-v6");
479 case COMMUNITY_LLGR_STALE
:
480 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "llgr-stale");
482 case COMMUNITY_NO_LLGR
:
483 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "no-llgr");
485 case COMMUNITY_ACCEPT_OWN_NEXTHOP
:
486 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "accept-own-nexthop");
488 case COMMUNITY_BLACKHOLE
:
489 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "blackhole");
491 case COMMUNITY_NO_EXPORT
:
492 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "no-export");
494 case COMMUNITY_NO_ADVERTISE
:
495 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "no-advertise");
497 case COMMUNITY_LOCAL_AS
:
498 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "local-AS");
500 case COMMUNITY_NO_PEER
:
501 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "no-peer");
504 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "65536:65535");
505 as
= (comval
>> 16) & 0xFFFF;
506 val
= comval
& 0xFFFF;
507 snprintf(str
, strlen(str
), "%u:%d", as
, val
);
514 /* Internal function to perform regular expression match for
515 * a single community. */
516 static bool community_regexp_include(regex_t
*reg
, struct community
*com
, int i
)
521 /* When there is no communities attribute it is treated as empty string.
523 if (com
== NULL
|| com
->size
== 0)
524 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "");
526 str
= community_str_get(com
, i
);
528 /* Regular expression match. */
529 rv
= regexec(reg
, str
, 0, NULL
, 0);
531 XFREE(MTYPE_COMMUNITY_STR
, str
);
536 /* Internal function to perform regular expression match for community
538 static bool community_regexp_match(struct community
*com
, regex_t
*reg
)
544 /* When there is no communities attribute it is treated as empty
546 if (com
== NULL
|| com
->size
== 0)
549 str
= community_str(com
, false, true);
551 regstr
= bgp_alias2community_str(str
);
553 /* Regular expression match. */
554 rv
= regexec(reg
, regstr
, 0, NULL
, 0);
556 XFREE(MTYPE_TMP
, regstr
);
561 static char *lcommunity_str_get(struct lcommunity
*lcom
, int i
)
563 struct lcommunity_val lcomval
;
564 uint32_t globaladmin
;
570 ptr
= lcom
->val
+ (i
* LCOMMUNITY_SIZE
);
572 memcpy(&lcomval
, ptr
, LCOMMUNITY_SIZE
);
574 /* Allocate memory. 48 bytes taken off bgp_lcommunity.c */
575 ptr
= (uint8_t *)lcomval
.val
;
576 ptr
= ptr_get_be32(ptr
, &globaladmin
);
577 ptr
= ptr_get_be32(ptr
, &localdata1
);
578 ptr
= ptr_get_be32(ptr
, &localdata2
);
579 (void)ptr
; /* consume value */
581 str
= XMALLOC(MTYPE_LCOMMUNITY_STR
, 48);
582 snprintf(str
, 48, "%u:%u:%u", globaladmin
, localdata1
, localdata2
);
587 /* Internal function to perform regular expression match for
588 * a single community. */
589 static bool lcommunity_regexp_include(regex_t
*reg
, struct lcommunity
*lcom
,
594 /* When there is no communities attribute it is treated as empty string.
596 if (lcom
== NULL
|| lcom
->size
== 0)
597 str
= XSTRDUP(MTYPE_LCOMMUNITY_STR
, "");
599 str
= lcommunity_str_get(lcom
, i
);
601 /* Regular expression match. */
602 if (regexec(reg
, str
, 0, NULL
, 0) == 0) {
603 XFREE(MTYPE_LCOMMUNITY_STR
, str
);
607 XFREE(MTYPE_LCOMMUNITY_STR
, str
);
612 static bool lcommunity_regexp_match(struct lcommunity
*com
, regex_t
*reg
)
618 /* When there is no communities attribute it is treated as empty
620 if (com
== NULL
|| com
->size
== 0)
623 str
= lcommunity_str(com
, false, true);
625 regstr
= bgp_alias2community_str(str
);
627 /* Regular expression match. */
628 rv
= regexec(reg
, regstr
, 0, NULL
, 0);
630 XFREE(MTYPE_TMP
, regstr
);
636 static bool ecommunity_regexp_match(struct ecommunity
*ecom
, regex_t
*reg
)
640 /* When there is no communities attribute it is treated as empty
642 if (ecom
== NULL
|| ecom
->size
== 0)
645 str
= ecommunity_str(ecom
);
647 /* Regular expression match. */
648 if (regexec(reg
, str
, 0, NULL
, 0) == 0)
655 /* When given community attribute matches to the community-list return
657 bool community_list_match(struct community
*com
, struct community_list
*list
)
659 struct community_entry
*entry
;
661 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
663 return entry
->direct
== COMMUNITY_PERMIT
;
665 if (entry
->style
== COMMUNITY_LIST_STANDARD
) {
666 if (community_include(entry
->u
.com
, COMMUNITY_INTERNET
))
667 return entry
->direct
== COMMUNITY_PERMIT
;
669 if (community_match(com
, entry
->u
.com
))
670 return entry
->direct
== COMMUNITY_PERMIT
;
671 } else if (entry
->style
== COMMUNITY_LIST_EXPANDED
) {
672 if (community_regexp_match(com
, entry
->reg
))
673 return entry
->direct
== COMMUNITY_PERMIT
;
679 bool lcommunity_list_match(struct lcommunity
*lcom
, struct community_list
*list
)
681 struct community_entry
*entry
;
683 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
685 return entry
->direct
== COMMUNITY_PERMIT
;
687 if (entry
->style
== LARGE_COMMUNITY_LIST_STANDARD
) {
688 if (lcommunity_match(lcom
, entry
->u
.lcom
))
689 return entry
->direct
== COMMUNITY_PERMIT
;
690 } else if (entry
->style
== LARGE_COMMUNITY_LIST_EXPANDED
) {
691 if (lcommunity_regexp_match(lcom
, entry
->reg
))
692 return entry
->direct
== COMMUNITY_PERMIT
;
699 /* Perform exact matching. In case of expanded large-community-list, do
700 * same thing as lcommunity_list_match().
702 bool lcommunity_list_exact_match(struct lcommunity
*lcom
,
703 struct community_list
*list
)
705 struct community_entry
*entry
;
707 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
709 return entry
->direct
== COMMUNITY_PERMIT
;
711 if (entry
->style
== LARGE_COMMUNITY_LIST_STANDARD
) {
712 if (lcommunity_cmp(lcom
, entry
->u
.lcom
))
713 return entry
->direct
== COMMUNITY_PERMIT
;
714 } else if (entry
->style
== LARGE_COMMUNITY_LIST_EXPANDED
) {
715 if (lcommunity_regexp_match(lcom
, entry
->reg
))
716 return entry
->direct
== COMMUNITY_PERMIT
;
722 bool ecommunity_list_match(struct ecommunity
*ecom
, struct community_list
*list
)
724 struct community_entry
*entry
;
726 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
728 return entry
->direct
== COMMUNITY_PERMIT
;
730 if (entry
->style
== EXTCOMMUNITY_LIST_STANDARD
) {
731 if (ecommunity_match(ecom
, entry
->u
.ecom
))
732 return entry
->direct
== COMMUNITY_PERMIT
;
733 } else if (entry
->style
== EXTCOMMUNITY_LIST_EXPANDED
) {
734 if (ecommunity_regexp_match(ecom
, entry
->reg
))
735 return entry
->direct
== COMMUNITY_PERMIT
;
741 /* Perform exact matching. In case of expanded community-list, do
742 same thing as community_list_match(). */
743 bool community_list_exact_match(struct community
*com
,
744 struct community_list
*list
)
746 struct community_entry
*entry
;
748 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
750 return entry
->direct
== COMMUNITY_PERMIT
;
752 if (entry
->style
== COMMUNITY_LIST_STANDARD
) {
753 if (community_include(entry
->u
.com
, COMMUNITY_INTERNET
))
754 return entry
->direct
== COMMUNITY_PERMIT
;
756 if (community_cmp(com
, entry
->u
.com
))
757 return entry
->direct
== COMMUNITY_PERMIT
;
758 } else if (entry
->style
== COMMUNITY_LIST_EXPANDED
) {
759 if (community_regexp_match(com
, entry
->reg
))
760 return entry
->direct
== COMMUNITY_PERMIT
;
766 /* Delete all permitted communities in the list from com. */
767 struct community
*community_list_match_delete(struct community
*com
,
768 struct community_list
*list
)
770 struct community_entry
*entry
;
772 uint32_t com_index_to_delete
[com
->size
];
773 int delete_index
= 0;
776 /* Loop over each community value and evaluate each against the
777 * community-list. If we need to delete a community value add its index
778 * to com_index_to_delete.
780 for (i
= 0; i
< com
->size
; i
++) {
781 val
= community_val_get(com
, i
);
783 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
785 if (entry
->direct
== COMMUNITY_PERMIT
) {
786 com_index_to_delete
[delete_index
] = i
;
792 else if ((entry
->style
== COMMUNITY_LIST_STANDARD
)
793 && (community_include(entry
->u
.com
,
795 || community_include(entry
->u
.com
, val
))) {
796 if (entry
->direct
== COMMUNITY_PERMIT
) {
797 com_index_to_delete
[delete_index
] = i
;
803 else if ((entry
->style
== COMMUNITY_LIST_EXPANDED
)
804 && community_regexp_include(entry
->reg
, com
,
806 if (entry
->direct
== COMMUNITY_PERMIT
) {
807 com_index_to_delete
[delete_index
] = i
;
815 /* Delete all of the communities we flagged for deletion */
816 for (i
= delete_index
- 1; i
>= 0; i
--) {
817 val
= community_val_get(com
, com_index_to_delete
[i
]);
819 community_del_val(com
, &val
);
825 /* To avoid duplicated entry in the community-list, this function
826 compares specified entry to existing entry. */
827 static bool community_list_dup_check(struct community_list
*list
,
828 struct community_entry
*new)
830 struct community_entry
*entry
;
832 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
833 if (entry
->style
!= new->style
)
836 if (entry
->direct
!= new->direct
)
839 if (entry
->any
!= new->any
)
845 switch (entry
->style
) {
846 case COMMUNITY_LIST_STANDARD
:
847 if (community_cmp(entry
->u
.com
, new->u
.com
))
850 case LARGE_COMMUNITY_LIST_STANDARD
:
851 if (lcommunity_cmp(entry
->u
.lcom
, new->u
.lcom
))
854 case EXTCOMMUNITY_LIST_STANDARD
:
855 if (ecommunity_cmp(entry
->u
.ecom
, new->u
.ecom
))
858 case COMMUNITY_LIST_EXPANDED
:
859 case EXTCOMMUNITY_LIST_EXPANDED
:
860 case LARGE_COMMUNITY_LIST_EXPANDED
:
861 if (strcmp(entry
->config
, new->config
) == 0)
871 /* Set community-list. */
872 int community_list_set(struct community_list_handler
*ch
, const char *name
,
873 const char *str
, const char *seq
, int direct
, int style
)
875 struct community_entry
*entry
= NULL
;
876 struct community_list
*list
;
877 struct community
*com
= NULL
;
878 regex_t
*regex
= NULL
;
879 int64_t seqnum
= COMMUNITY_SEQ_NUMBER_AUTO
;
882 seqnum
= (int64_t)atol(seq
);
884 /* Get community list. */
885 list
= community_list_get(ch
, name
, COMMUNITY_LIST_MASTER
);
887 /* When community-list already has entry, new entry should have same
888 style. If you want to have mixed style community-list, you can
889 comment out this check. */
890 if (!community_list_empty_p(list
)) {
891 struct community_entry
*first
;
895 if (style
!= first
->style
) {
896 return (first
->style
== COMMUNITY_LIST_STANDARD
897 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
898 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT
);
903 if (style
== COMMUNITY_LIST_STANDARD
)
904 com
= community_str2com(str
);
906 regex
= bgp_regcomp(str
);
909 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
912 entry
= community_entry_new();
913 entry
->direct
= direct
;
914 entry
->style
= style
;
915 entry
->any
= (str
? false : true);
920 (regex
? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG
, str
) : NULL
);
922 /* Do not put duplicated community entry. */
923 if (community_list_dup_check(list
, entry
))
924 community_entry_free(entry
);
926 community_list_entry_add(list
, entry
, ch
,
927 COMMUNITY_LIST_MASTER
);
928 route_map_notify_dependencies(name
, RMAP_EVENT_CLIST_ADDED
);
934 /* Unset community-list */
935 int community_list_unset(struct community_list_handler
*ch
, const char *name
,
936 const char *str
, const char *seq
, int direct
,
939 struct community_list_master
*cm
= NULL
;
940 struct community_entry
*entry
= NULL
;
941 struct community_list
*list
;
942 struct community
*com
= NULL
;
944 /* Lookup community list. */
945 list
= community_list_lookup(ch
, name
, 0, COMMUNITY_LIST_MASTER
);
947 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
949 cm
= community_list_master_lookup(ch
, COMMUNITY_LIST_MASTER
);
950 /* Delete all of entry belongs to this community-list. */
952 community_list_delete(cm
, list
);
953 route_map_notify_dependencies(name
, RMAP_EVENT_CLIST_DELETED
);
957 if (style
== COMMUNITY_LIST_STANDARD
)
958 com
= community_str2com(str
);
961 entry
= community_list_entry_lookup(list
, com
, direct
);
962 community_free(&com
);
964 entry
= community_list_entry_lookup(list
, str
, direct
);
967 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
969 community_list_entry_delete(cm
, list
, entry
);
970 route_map_notify_dependencies(name
, RMAP_EVENT_CLIST_DELETED
);
975 /* Delete all permitted large communities in the list from com. */
976 struct lcommunity
*lcommunity_list_match_delete(struct lcommunity
*lcom
,
977 struct community_list
*list
)
979 struct community_entry
*entry
;
980 uint32_t com_index_to_delete
[lcom
->size
];
982 int delete_index
= 0;
985 /* Loop over each lcommunity value and evaluate each against the
986 * community-list. If we need to delete a community value add its index
987 * to com_index_to_delete.
989 for (i
= 0; i
< lcom
->size
; i
++) {
990 ptr
= lcom
->val
+ (i
* LCOMMUNITY_SIZE
);
991 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
993 if (entry
->direct
== COMMUNITY_PERMIT
) {
994 com_index_to_delete
[delete_index
] = i
;
1000 else if ((entry
->style
== LARGE_COMMUNITY_LIST_STANDARD
)
1001 && lcommunity_include(entry
->u
.lcom
, ptr
)) {
1002 if (entry
->direct
== COMMUNITY_PERMIT
) {
1003 com_index_to_delete
[delete_index
] = i
;
1009 else if ((entry
->style
== LARGE_COMMUNITY_LIST_EXPANDED
)
1010 && lcommunity_regexp_include(entry
->reg
, lcom
,
1012 if (entry
->direct
== COMMUNITY_PERMIT
) {
1013 com_index_to_delete
[delete_index
] = i
;
1021 /* Delete all of the communities we flagged for deletion */
1022 for (i
= delete_index
- 1; i
>= 0; i
--) {
1023 ptr
= lcom
->val
+ (com_index_to_delete
[i
] * LCOMMUNITY_SIZE
);
1024 lcommunity_del_val(lcom
, ptr
);
1030 /* Helper to check if every octet do not exceed UINT_MAX */
1031 bool lcommunity_list_valid(const char *community
, int style
)
1034 char **splits
, **communities
;
1036 int num
, num_communities
;
1040 frrstr_split(community
, " ", &communities
, &num_communities
);
1042 for (int j
= 0; j
< num_communities
; j
++) {
1044 frrstr_split(communities
[j
], ":", &splits
, &num
);
1046 for (int i
= 0; i
< num
; i
++) {
1047 if (strlen(splits
[i
]) == 0)
1048 /* There is no digit to check */
1051 if (style
== LARGE_COMMUNITY_LIST_STANDARD
) {
1052 if (*splits
[i
] == '-')
1053 /* Must not be negative */
1055 else if (strtoul(splits
[i
], &endptr
, 10)
1057 /* Larger than 4 octets */
1060 /* Not all characters were digits */
1063 regres
= bgp_regcomp(communities
[j
]);
1065 /* malformed regex */
1068 bgp_regex_free(regres
);
1072 XFREE(MTYPE_TMP
, splits
[i
]);
1074 XFREE(MTYPE_TMP
, splits
);
1079 XFREE(MTYPE_TMP
, communities
[j
]);
1081 XFREE(MTYPE_TMP
, communities
);
1083 return (invalid
> 0) ? false : true;
1086 /* Set lcommunity-list. */
1087 int lcommunity_list_set(struct community_list_handler
*ch
, const char *name
,
1088 const char *str
, const char *seq
, int direct
, int style
)
1090 struct community_entry
*entry
= NULL
;
1091 struct community_list
*list
;
1092 struct lcommunity
*lcom
= NULL
;
1093 regex_t
*regex
= NULL
;
1094 int64_t seqnum
= COMMUNITY_SEQ_NUMBER_AUTO
;
1097 seqnum
= (int64_t)atol(seq
);
1099 /* Get community list. */
1100 list
= community_list_get(ch
, name
, LARGE_COMMUNITY_LIST_MASTER
);
1102 /* When community-list already has entry, new entry should have same
1103 style. If you want to have mixed style community-list, you can
1104 comment out this check. */
1105 if (!community_list_empty_p(list
)) {
1106 struct community_entry
*first
;
1110 if (style
!= first
->style
) {
1111 return (first
->style
== COMMUNITY_LIST_STANDARD
1112 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
1113 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT
);
1118 if (style
== LARGE_COMMUNITY_LIST_STANDARD
)
1119 lcom
= lcommunity_str2com(str
);
1121 regex
= bgp_regcomp(str
);
1123 if (!lcom
&& !regex
)
1124 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
1127 entry
= community_entry_new();
1128 entry
->direct
= direct
;
1129 entry
->style
= style
;
1130 entry
->any
= (str
? false : true);
1131 entry
->u
.lcom
= lcom
;
1133 entry
->seq
= seqnum
;
1135 (regex
? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG
, str
) : NULL
);
1137 /* Do not put duplicated community entry. */
1138 if (community_list_dup_check(list
, entry
))
1139 community_entry_free(entry
);
1141 community_list_entry_add(list
, entry
, ch
,
1142 LARGE_COMMUNITY_LIST_MASTER
);
1143 route_map_notify_dependencies(name
, RMAP_EVENT_LLIST_ADDED
);
1149 /* Unset community-list. When str is NULL, delete all of
1150 community-list entry belongs to the specified name. */
1151 int lcommunity_list_unset(struct community_list_handler
*ch
, const char *name
,
1152 const char *str
, const char *seq
, int direct
,
1155 struct community_list_master
*cm
= NULL
;
1156 struct community_entry
*entry
= NULL
;
1157 struct community_list
*list
;
1158 struct lcommunity
*lcom
= NULL
;
1159 regex_t
*regex
= NULL
;
1161 /* Lookup community list. */
1162 list
= community_list_lookup(ch
, name
, 0, LARGE_COMMUNITY_LIST_MASTER
);
1164 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
1166 cm
= community_list_master_lookup(ch
, LARGE_COMMUNITY_LIST_MASTER
);
1167 /* Delete all of entry belongs to this community-list. */
1169 community_list_delete(cm
, list
);
1170 route_map_notify_dependencies(name
, RMAP_EVENT_LLIST_DELETED
);
1174 if (style
== LARGE_COMMUNITY_LIST_STANDARD
)
1175 lcom
= lcommunity_str2com(str
);
1177 regex
= bgp_regcomp(str
);
1179 if (!lcom
&& !regex
)
1180 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
1183 entry
= community_list_entry_lookup(list
, lcom
, direct
);
1185 entry
= community_list_entry_lookup(list
, str
, direct
);
1188 lcommunity_free(&lcom
);
1190 bgp_regex_free(regex
);
1193 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
1195 community_list_entry_delete(cm
, list
, entry
);
1196 route_map_notify_dependencies(name
, RMAP_EVENT_LLIST_DELETED
);
1201 /* Set extcommunity-list. */
1202 int extcommunity_list_set(struct community_list_handler
*ch
, const char *name
,
1203 const char *str
, const char *seq
, int direct
,
1206 struct community_entry
*entry
= NULL
;
1207 struct community_list
*list
;
1208 struct ecommunity
*ecom
= NULL
;
1209 regex_t
*regex
= NULL
;
1210 int64_t seqnum
= COMMUNITY_SEQ_NUMBER_AUTO
;
1213 seqnum
= (int64_t)atol(seq
);
1216 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
1218 /* Get community list. */
1219 list
= community_list_get(ch
, name
, EXTCOMMUNITY_LIST_MASTER
);
1221 /* When community-list already has entry, new entry should have same
1222 style. If you want to have mixed style community-list, you can
1223 comment out this check. */
1224 if (!community_list_empty_p(list
)) {
1225 struct community_entry
*first
;
1229 if (style
!= first
->style
) {
1230 return (first
->style
== EXTCOMMUNITY_LIST_STANDARD
1231 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
1232 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT
);
1236 if (style
== EXTCOMMUNITY_LIST_STANDARD
)
1237 ecom
= ecommunity_str2com(str
, 0, 1);
1239 regex
= bgp_regcomp(str
);
1241 if (!ecom
&& !regex
)
1242 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
1246 ecommunity_ecom2str(ecom
, ECOMMUNITY_FORMAT_DISPLAY
, 0);
1248 entry
= community_entry_new();
1249 entry
->direct
= direct
;
1250 entry
->style
= style
;
1253 entry
->config
= ecommunity_ecom2str(
1254 ecom
, ECOMMUNITY_FORMAT_COMMUNITY_LIST
, 0);
1256 entry
->config
= XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG
, str
);
1258 entry
->u
.ecom
= ecom
;
1260 entry
->seq
= seqnum
;
1262 /* Do not put duplicated community entry. */
1263 if (community_list_dup_check(list
, entry
))
1264 community_entry_free(entry
);
1266 community_list_entry_add(list
, entry
, ch
,
1267 EXTCOMMUNITY_LIST_MASTER
);
1268 route_map_notify_dependencies(name
, RMAP_EVENT_ECLIST_ADDED
);
1274 /* Unset extcommunity-list.
1276 * When str is NULL, delete all extcommunity-list entries belonging to the
1279 int extcommunity_list_unset(struct community_list_handler
*ch
, const char *name
,
1280 const char *str
, const char *seq
, int direct
,
1283 struct community_list_master
*cm
= NULL
;
1284 struct community_entry
*entry
= NULL
;
1285 struct community_list
*list
;
1286 struct ecommunity
*ecom
= NULL
;
1288 /* Lookup extcommunity list. */
1289 list
= community_list_lookup(ch
, name
, 0, EXTCOMMUNITY_LIST_MASTER
);
1291 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
1293 cm
= community_list_master_lookup(ch
, EXTCOMMUNITY_LIST_MASTER
);
1294 /* Delete all of entry belongs to this extcommunity-list. */
1296 community_list_delete(cm
, list
);
1297 route_map_notify_dependencies(name
, RMAP_EVENT_ECLIST_DELETED
);
1301 if (style
== EXTCOMMUNITY_LIST_STANDARD
)
1302 ecom
= ecommunity_str2com(str
, 0, 1);
1305 entry
= community_list_entry_lookup(list
, ecom
, direct
);
1306 ecommunity_free(&ecom
);
1308 entry
= community_list_entry_lookup(list
, str
, direct
);
1311 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
1313 community_list_entry_delete(cm
, list
, entry
);
1314 route_map_notify_dependencies(name
, RMAP_EVENT_ECLIST_DELETED
);
1319 /* Initializa community-list. Return community-list handler. */
1320 struct community_list_handler
*community_list_init(void)
1322 struct community_list_handler
*ch
;
1323 ch
= XCALLOC(MTYPE_COMMUNITY_LIST_HANDLER
,
1324 sizeof(struct community_list_handler
));
1326 ch
->community_list
.hash
=
1327 hash_create_size(4, bgp_clist_hash_key_community_list
,
1328 bgp_clist_hash_cmp_community_list
,
1329 "Community List Number Quick Lookup");
1331 ch
->extcommunity_list
.hash
=
1332 hash_create_size(4, bgp_clist_hash_key_community_list
,
1333 bgp_clist_hash_cmp_community_list
,
1334 "Extended Community List Quick Lookup");
1336 ch
->lcommunity_list
.hash
=
1337 hash_create_size(4, bgp_clist_hash_key_community_list
,
1338 bgp_clist_hash_cmp_community_list
,
1339 "Large Community List Quick Lookup");
1344 /* Terminate community-list. */
1345 void community_list_terminate(struct community_list_handler
*ch
)
1347 struct community_list_master
*cm
;
1348 struct community_list
*list
;
1350 cm
= &ch
->community_list
;
1351 while ((list
= cm
->num
.head
) != NULL
)
1352 community_list_delete(cm
, list
);
1353 while ((list
= cm
->str
.head
) != NULL
)
1354 community_list_delete(cm
, list
);
1355 hash_free(cm
->hash
);
1357 cm
= &ch
->lcommunity_list
;
1358 while ((list
= cm
->num
.head
) != NULL
)
1359 community_list_delete(cm
, list
);
1360 while ((list
= cm
->str
.head
) != NULL
)
1361 community_list_delete(cm
, list
);
1362 hash_free(cm
->hash
);
1364 cm
= &ch
->extcommunity_list
;
1365 while ((list
= cm
->num
.head
) != NULL
)
1366 community_list_delete(cm
, list
);
1367 while ((list
= cm
->str
.head
) != NULL
)
1368 community_list_delete(cm
, list
);
1369 hash_free(cm
->hash
);
1371 XFREE(MTYPE_COMMUNITY_LIST_HANDLER
, ch
);
1374 static int bgp_community_list_vector_walker(struct hash_bucket
*bucket
,
1377 vector
*comps
= data
;
1378 struct community_list
*list
= bucket
->data
;
1380 vector_set(*comps
, XSTRDUP(MTYPE_COMPLETION
, list
->name
));
1385 static void bgp_community_list_cmd_completion(vector comps
,
1386 struct cmd_token
*token
)
1388 struct community_list_master
*cm
;
1390 cm
= community_list_master_lookup(bgp_clist
, COMMUNITY_LIST_MASTER
);
1392 hash_walk(cm
->hash
, bgp_community_list_vector_walker
, &comps
);
1395 static void bgp_lcommunity_list_cmd_completion(vector comps
,
1396 struct cmd_token
*token
)
1398 struct community_list_master
*cm
;
1400 cm
= community_list_master_lookup(bgp_clist
,
1401 LARGE_COMMUNITY_LIST_MASTER
);
1403 hash_walk(cm
->hash
, bgp_community_list_vector_walker
, &comps
);
1406 static void bgp_extcommunity_list_cmd_completion(vector comps
,
1407 struct cmd_token
*token
)
1409 struct community_list_master
*cm
;
1411 cm
= community_list_master_lookup(bgp_clist
, EXTCOMMUNITY_LIST_MASTER
);
1413 hash_walk(cm
->hash
, bgp_community_list_vector_walker
, &comps
);
1416 static const struct cmd_variable_handler community_list_handlers
[] = {
1417 {.tokenname
= "COMMUNITY_LIST_NAME",
1418 .completions
= bgp_community_list_cmd_completion
},
1419 {.completions
= NULL
}};
1421 static const struct cmd_variable_handler lcommunity_list_handlers
[] = {
1422 {.tokenname
= "LCOMMUNITY_LIST_NAME",
1423 .completions
= bgp_lcommunity_list_cmd_completion
},
1424 {.completions
= NULL
}};
1426 static const struct cmd_variable_handler extcommunity_list_handlers
[] = {
1427 {.tokenname
= "EXTCOMMUNITY_LIST_NAME",
1428 .completions
= bgp_extcommunity_list_cmd_completion
},
1429 {.completions
= NULL
}};
1431 void bgp_community_list_command_completion_setup(void)
1433 cmd_variable_handler_register(community_list_handlers
);
1434 cmd_variable_handler_register(lcommunity_list_handlers
);
1435 cmd_variable_handler_register(extcommunity_list_handlers
);