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 case COMMUNITY_INTERNET
:
453 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "internet");
455 case COMMUNITY_GSHUT
:
456 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "graceful-shutdown");
458 case COMMUNITY_ACCEPT_OWN
:
459 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "accept-own");
461 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4
:
462 str
= XSTRDUP(MTYPE_COMMUNITY_STR
,
463 "route-filter-translated-v4");
465 case COMMUNITY_ROUTE_FILTER_v4
:
466 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "route-filter-v4");
468 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6
:
469 str
= XSTRDUP(MTYPE_COMMUNITY_STR
,
470 "route-filter-translated-v6");
472 case COMMUNITY_ROUTE_FILTER_v6
:
473 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "route-filter-v6");
475 case COMMUNITY_LLGR_STALE
:
476 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "llgr-stale");
478 case COMMUNITY_NO_LLGR
:
479 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "no-llgr");
481 case COMMUNITY_ACCEPT_OWN_NEXTHOP
:
482 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "accept-own-nexthop");
484 case COMMUNITY_BLACKHOLE
:
485 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "blackhole");
487 case COMMUNITY_NO_EXPORT
:
488 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "no-export");
490 case COMMUNITY_NO_ADVERTISE
:
491 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "no-advertise");
493 case COMMUNITY_LOCAL_AS
:
494 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "local-AS");
496 case COMMUNITY_NO_PEER
:
497 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "no-peer");
500 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "65536:65535");
501 as
= (comval
>> 16) & 0xFFFF;
502 val
= comval
& 0xFFFF;
503 snprintf(str
, strlen(str
), "%u:%d", as
, val
);
510 /* Internal function to perform regular expression match for
511 * a single community. */
512 static bool community_regexp_include(regex_t
*reg
, struct community
*com
, int i
)
517 /* When there is no communities attribute it is treated as empty string.
519 if (com
== NULL
|| com
->size
== 0)
520 str
= XSTRDUP(MTYPE_COMMUNITY_STR
, "");
522 str
= community_str_get(com
, i
);
524 /* Regular expression match. */
525 rv
= regexec(reg
, str
, 0, NULL
, 0);
527 XFREE(MTYPE_COMMUNITY_STR
, str
);
532 /* Internal function to perform regular expression match for community
534 static bool community_regexp_match(struct community
*com
, regex_t
*reg
)
540 /* When there is no communities attribute it is treated as empty
542 if (com
== NULL
|| com
->size
== 0)
545 str
= community_str(com
, false, true);
547 regstr
= bgp_alias2community_str(str
);
549 /* Regular expression match. */
550 rv
= regexec(reg
, regstr
, 0, NULL
, 0);
552 XFREE(MTYPE_TMP
, regstr
);
557 static char *lcommunity_str_get(struct lcommunity
*lcom
, int i
)
559 struct lcommunity_val lcomval
;
560 uint32_t globaladmin
;
566 ptr
= lcom
->val
+ (i
* LCOMMUNITY_SIZE
);
568 memcpy(&lcomval
, ptr
, LCOMMUNITY_SIZE
);
570 /* Allocate memory. 48 bytes taken off bgp_lcommunity.c */
571 ptr
= (uint8_t *)lcomval
.val
;
572 ptr
= ptr_get_be32(ptr
, &globaladmin
);
573 ptr
= ptr_get_be32(ptr
, &localdata1
);
574 ptr
= ptr_get_be32(ptr
, &localdata2
);
575 (void)ptr
; /* consume value */
577 str
= XMALLOC(MTYPE_LCOMMUNITY_STR
, 48);
578 snprintf(str
, 48, "%u:%u:%u", globaladmin
, localdata1
, localdata2
);
583 /* Internal function to perform regular expression match for
584 * a single community. */
585 static bool lcommunity_regexp_include(regex_t
*reg
, struct lcommunity
*lcom
,
590 /* When there is no communities attribute it is treated as empty string.
592 if (lcom
== NULL
|| lcom
->size
== 0)
593 str
= XSTRDUP(MTYPE_LCOMMUNITY_STR
, "");
595 str
= lcommunity_str_get(lcom
, i
);
597 /* Regular expression match. */
598 if (regexec(reg
, str
, 0, NULL
, 0) == 0) {
599 XFREE(MTYPE_LCOMMUNITY_STR
, str
);
603 XFREE(MTYPE_LCOMMUNITY_STR
, str
);
608 static bool lcommunity_regexp_match(struct lcommunity
*com
, regex_t
*reg
)
614 /* When there is no communities attribute it is treated as empty
616 if (com
== NULL
|| com
->size
== 0)
619 str
= lcommunity_str(com
, false, true);
621 regstr
= bgp_alias2community_str(str
);
623 /* Regular expression match. */
624 rv
= regexec(reg
, regstr
, 0, NULL
, 0);
626 XFREE(MTYPE_TMP
, regstr
);
632 static bool ecommunity_regexp_match(struct ecommunity
*ecom
, regex_t
*reg
)
636 /* When there is no communities attribute it is treated as empty
638 if (ecom
== NULL
|| ecom
->size
== 0)
641 str
= ecommunity_str(ecom
);
643 /* Regular expression match. */
644 if (regexec(reg
, str
, 0, NULL
, 0) == 0)
651 /* When given community attribute matches to the community-list return
653 bool community_list_match(struct community
*com
, struct community_list
*list
)
655 struct community_entry
*entry
;
657 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
659 return entry
->direct
== COMMUNITY_PERMIT
;
661 if (entry
->style
== COMMUNITY_LIST_STANDARD
) {
662 if (community_include(entry
->u
.com
, COMMUNITY_INTERNET
))
663 return entry
->direct
== COMMUNITY_PERMIT
;
665 if (community_match(com
, entry
->u
.com
))
666 return entry
->direct
== COMMUNITY_PERMIT
;
667 } else if (entry
->style
== COMMUNITY_LIST_EXPANDED
) {
668 if (community_regexp_match(com
, entry
->reg
))
669 return entry
->direct
== COMMUNITY_PERMIT
;
675 bool lcommunity_list_match(struct lcommunity
*lcom
, struct community_list
*list
)
677 struct community_entry
*entry
;
679 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
681 return entry
->direct
== COMMUNITY_PERMIT
;
683 if (entry
->style
== LARGE_COMMUNITY_LIST_STANDARD
) {
684 if (lcommunity_match(lcom
, entry
->u
.lcom
))
685 return entry
->direct
== COMMUNITY_PERMIT
;
686 } else if (entry
->style
== LARGE_COMMUNITY_LIST_EXPANDED
) {
687 if (lcommunity_regexp_match(lcom
, entry
->reg
))
688 return entry
->direct
== COMMUNITY_PERMIT
;
695 /* Perform exact matching. In case of expanded large-community-list, do
696 * same thing as lcommunity_list_match().
698 bool lcommunity_list_exact_match(struct lcommunity
*lcom
,
699 struct community_list
*list
)
701 struct community_entry
*entry
;
703 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
705 return entry
->direct
== COMMUNITY_PERMIT
;
707 if (entry
->style
== LARGE_COMMUNITY_LIST_STANDARD
) {
708 if (lcommunity_cmp(lcom
, entry
->u
.lcom
))
709 return entry
->direct
== COMMUNITY_PERMIT
;
710 } else if (entry
->style
== LARGE_COMMUNITY_LIST_EXPANDED
) {
711 if (lcommunity_regexp_match(lcom
, entry
->reg
))
712 return entry
->direct
== COMMUNITY_PERMIT
;
718 bool ecommunity_list_match(struct ecommunity
*ecom
, struct community_list
*list
)
720 struct community_entry
*entry
;
722 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
724 return entry
->direct
== COMMUNITY_PERMIT
;
726 if (entry
->style
== EXTCOMMUNITY_LIST_STANDARD
) {
727 if (ecommunity_match(ecom
, entry
->u
.ecom
))
728 return entry
->direct
== COMMUNITY_PERMIT
;
729 } else if (entry
->style
== EXTCOMMUNITY_LIST_EXPANDED
) {
730 if (ecommunity_regexp_match(ecom
, entry
->reg
))
731 return entry
->direct
== COMMUNITY_PERMIT
;
737 /* Perform exact matching. In case of expanded community-list, do
738 same thing as community_list_match(). */
739 bool community_list_exact_match(struct community
*com
,
740 struct community_list
*list
)
742 struct community_entry
*entry
;
744 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
746 return entry
->direct
== COMMUNITY_PERMIT
;
748 if (entry
->style
== COMMUNITY_LIST_STANDARD
) {
749 if (community_include(entry
->u
.com
, COMMUNITY_INTERNET
))
750 return entry
->direct
== COMMUNITY_PERMIT
;
752 if (community_cmp(com
, entry
->u
.com
))
753 return entry
->direct
== COMMUNITY_PERMIT
;
754 } else if (entry
->style
== COMMUNITY_LIST_EXPANDED
) {
755 if (community_regexp_match(com
, entry
->reg
))
756 return entry
->direct
== COMMUNITY_PERMIT
;
762 /* Delete all permitted communities in the list from com. */
763 struct community
*community_list_match_delete(struct community
*com
,
764 struct community_list
*list
)
766 struct community_entry
*entry
;
768 uint32_t com_index_to_delete
[com
->size
];
769 int delete_index
= 0;
772 /* Loop over each community value and evaluate each against the
773 * community-list. If we need to delete a community value add its index
774 * to com_index_to_delete.
776 for (i
= 0; i
< com
->size
; i
++) {
777 val
= community_val_get(com
, i
);
779 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
781 if (entry
->direct
== COMMUNITY_PERMIT
) {
782 com_index_to_delete
[delete_index
] = i
;
788 else if ((entry
->style
== COMMUNITY_LIST_STANDARD
)
789 && (community_include(entry
->u
.com
,
791 || community_include(entry
->u
.com
, val
))) {
792 if (entry
->direct
== COMMUNITY_PERMIT
) {
793 com_index_to_delete
[delete_index
] = i
;
799 else if ((entry
->style
== COMMUNITY_LIST_EXPANDED
)
800 && community_regexp_include(entry
->reg
, com
,
802 if (entry
->direct
== COMMUNITY_PERMIT
) {
803 com_index_to_delete
[delete_index
] = i
;
811 /* Delete all of the communities we flagged for deletion */
812 for (i
= delete_index
- 1; i
>= 0; i
--) {
813 val
= community_val_get(com
, com_index_to_delete
[i
]);
815 community_del_val(com
, &val
);
821 /* To avoid duplicated entry in the community-list, this function
822 compares specified entry to existing entry. */
823 static bool community_list_dup_check(struct community_list
*list
,
824 struct community_entry
*new)
826 struct community_entry
*entry
;
828 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
829 if (entry
->style
!= new->style
)
832 if (entry
->direct
!= new->direct
)
835 if (entry
->any
!= new->any
)
841 switch (entry
->style
) {
842 case COMMUNITY_LIST_STANDARD
:
843 if (community_cmp(entry
->u
.com
, new->u
.com
))
846 case LARGE_COMMUNITY_LIST_STANDARD
:
847 if (lcommunity_cmp(entry
->u
.lcom
, new->u
.lcom
))
850 case EXTCOMMUNITY_LIST_STANDARD
:
851 if (ecommunity_cmp(entry
->u
.ecom
, new->u
.ecom
))
854 case COMMUNITY_LIST_EXPANDED
:
855 case EXTCOMMUNITY_LIST_EXPANDED
:
856 case LARGE_COMMUNITY_LIST_EXPANDED
:
857 if (strcmp(entry
->config
, new->config
) == 0)
867 /* Set community-list. */
868 int community_list_set(struct community_list_handler
*ch
, const char *name
,
869 const char *str
, const char *seq
, int direct
, int style
)
871 struct community_entry
*entry
= NULL
;
872 struct community_list
*list
;
873 struct community
*com
= NULL
;
874 regex_t
*regex
= NULL
;
875 int64_t seqnum
= COMMUNITY_SEQ_NUMBER_AUTO
;
878 seqnum
= (int64_t)atol(seq
);
880 /* Get community list. */
881 list
= community_list_get(ch
, name
, COMMUNITY_LIST_MASTER
);
883 /* When community-list already has entry, new entry should have same
884 style. If you want to have mixed style community-list, you can
885 comment out this check. */
886 if (!community_list_empty_p(list
)) {
887 struct community_entry
*first
;
891 if (style
!= first
->style
) {
892 return (first
->style
== COMMUNITY_LIST_STANDARD
893 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
894 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT
);
899 if (style
== COMMUNITY_LIST_STANDARD
)
900 com
= community_str2com(str
);
902 regex
= bgp_regcomp(str
);
905 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
908 entry
= community_entry_new();
909 entry
->direct
= direct
;
910 entry
->style
= style
;
911 entry
->any
= (str
? false : true);
916 (regex
? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG
, str
) : NULL
);
918 /* Do not put duplicated community entry. */
919 if (community_list_dup_check(list
, entry
))
920 community_entry_free(entry
);
922 community_list_entry_add(list
, entry
, ch
,
923 COMMUNITY_LIST_MASTER
);
924 route_map_notify_dependencies(name
, RMAP_EVENT_CLIST_ADDED
);
930 /* Unset community-list */
931 int community_list_unset(struct community_list_handler
*ch
, const char *name
,
932 const char *str
, const char *seq
, int direct
,
935 struct community_list_master
*cm
= NULL
;
936 struct community_entry
*entry
= NULL
;
937 struct community_list
*list
;
938 struct community
*com
= NULL
;
940 /* Lookup community list. */
941 list
= community_list_lookup(ch
, name
, 0, COMMUNITY_LIST_MASTER
);
943 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
945 cm
= community_list_master_lookup(ch
, COMMUNITY_LIST_MASTER
);
946 /* Delete all of entry belongs to this community-list. */
948 community_list_delete(cm
, list
);
949 route_map_notify_dependencies(name
, RMAP_EVENT_CLIST_DELETED
);
953 if (style
== COMMUNITY_LIST_STANDARD
)
954 com
= community_str2com(str
);
957 entry
= community_list_entry_lookup(list
, com
, direct
);
958 community_free(&com
);
960 entry
= community_list_entry_lookup(list
, str
, direct
);
963 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
965 community_list_entry_delete(cm
, list
, entry
);
966 route_map_notify_dependencies(name
, RMAP_EVENT_CLIST_DELETED
);
971 /* Delete all permitted large communities in the list from com. */
972 struct lcommunity
*lcommunity_list_match_delete(struct lcommunity
*lcom
,
973 struct community_list
*list
)
975 struct community_entry
*entry
;
976 uint32_t com_index_to_delete
[lcom
->size
];
978 int delete_index
= 0;
981 /* Loop over each lcommunity value and evaluate each against the
982 * community-list. If we need to delete a community value add its index
983 * to com_index_to_delete.
985 for (i
= 0; i
< lcom
->size
; i
++) {
986 ptr
= lcom
->val
+ (i
* LCOMMUNITY_SIZE
);
987 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
989 if (entry
->direct
== COMMUNITY_PERMIT
) {
990 com_index_to_delete
[delete_index
] = i
;
996 else if ((entry
->style
== LARGE_COMMUNITY_LIST_STANDARD
)
997 && lcommunity_include(entry
->u
.lcom
, ptr
)) {
998 if (entry
->direct
== COMMUNITY_PERMIT
) {
999 com_index_to_delete
[delete_index
] = i
;
1005 else if ((entry
->style
== LARGE_COMMUNITY_LIST_EXPANDED
)
1006 && lcommunity_regexp_include(entry
->reg
, lcom
,
1008 if (entry
->direct
== COMMUNITY_PERMIT
) {
1009 com_index_to_delete
[delete_index
] = i
;
1017 /* Delete all of the communities we flagged for deletion */
1018 for (i
= delete_index
- 1; i
>= 0; i
--) {
1019 ptr
= lcom
->val
+ (com_index_to_delete
[i
] * LCOMMUNITY_SIZE
);
1020 lcommunity_del_val(lcom
, ptr
);
1026 /* Helper to check if every octet do not exceed UINT_MAX */
1027 bool lcommunity_list_valid(const char *community
, int style
)
1030 char **splits
, **communities
;
1032 int num
, num_communities
;
1036 frrstr_split(community
, " ", &communities
, &num_communities
);
1038 for (int j
= 0; j
< num_communities
; j
++) {
1040 frrstr_split(communities
[j
], ":", &splits
, &num
);
1042 for (int i
= 0; i
< num
; i
++) {
1043 if (strlen(splits
[i
]) == 0)
1044 /* There is no digit to check */
1047 if (style
== LARGE_COMMUNITY_LIST_STANDARD
) {
1048 if (*splits
[i
] == '-')
1049 /* Must not be negative */
1051 else if (strtoul(splits
[i
], &endptr
, 10)
1053 /* Larger than 4 octets */
1056 /* Not all characters were digits */
1059 regres
= bgp_regcomp(communities
[j
]);
1061 /* malformed regex */
1064 bgp_regex_free(regres
);
1068 XFREE(MTYPE_TMP
, splits
[i
]);
1070 XFREE(MTYPE_TMP
, splits
);
1075 XFREE(MTYPE_TMP
, communities
[j
]);
1077 XFREE(MTYPE_TMP
, communities
);
1079 return (invalid
> 0) ? false : true;
1082 /* Set lcommunity-list. */
1083 int lcommunity_list_set(struct community_list_handler
*ch
, const char *name
,
1084 const char *str
, const char *seq
, int direct
, int style
)
1086 struct community_entry
*entry
= NULL
;
1087 struct community_list
*list
;
1088 struct lcommunity
*lcom
= NULL
;
1089 regex_t
*regex
= NULL
;
1090 int64_t seqnum
= COMMUNITY_SEQ_NUMBER_AUTO
;
1093 seqnum
= (int64_t)atol(seq
);
1095 /* Get community list. */
1096 list
= community_list_get(ch
, name
, LARGE_COMMUNITY_LIST_MASTER
);
1098 /* When community-list already has entry, new entry should have same
1099 style. If you want to have mixed style community-list, you can
1100 comment out this check. */
1101 if (!community_list_empty_p(list
)) {
1102 struct community_entry
*first
;
1106 if (style
!= first
->style
) {
1107 return (first
->style
== COMMUNITY_LIST_STANDARD
1108 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
1109 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT
);
1114 if (style
== LARGE_COMMUNITY_LIST_STANDARD
)
1115 lcom
= lcommunity_str2com(str
);
1117 regex
= bgp_regcomp(str
);
1119 if (!lcom
&& !regex
)
1120 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
1123 entry
= community_entry_new();
1124 entry
->direct
= direct
;
1125 entry
->style
= style
;
1126 entry
->any
= (str
? false : true);
1127 entry
->u
.lcom
= lcom
;
1129 entry
->seq
= seqnum
;
1131 (regex
? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG
, str
) : NULL
);
1133 /* Do not put duplicated community entry. */
1134 if (community_list_dup_check(list
, entry
))
1135 community_entry_free(entry
);
1137 community_list_entry_add(list
, entry
, ch
,
1138 LARGE_COMMUNITY_LIST_MASTER
);
1139 route_map_notify_dependencies(name
, RMAP_EVENT_LLIST_ADDED
);
1145 /* Unset community-list. When str is NULL, delete all of
1146 community-list entry belongs to the specified name. */
1147 int lcommunity_list_unset(struct community_list_handler
*ch
, const char *name
,
1148 const char *str
, const char *seq
, int direct
,
1151 struct community_list_master
*cm
= NULL
;
1152 struct community_entry
*entry
= NULL
;
1153 struct community_list
*list
;
1154 struct lcommunity
*lcom
= NULL
;
1155 regex_t
*regex
= NULL
;
1157 /* Lookup community list. */
1158 list
= community_list_lookup(ch
, name
, 0, LARGE_COMMUNITY_LIST_MASTER
);
1160 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
1162 cm
= community_list_master_lookup(ch
, LARGE_COMMUNITY_LIST_MASTER
);
1163 /* Delete all of entry belongs to this community-list. */
1165 community_list_delete(cm
, list
);
1166 route_map_notify_dependencies(name
, RMAP_EVENT_LLIST_DELETED
);
1170 if (style
== LARGE_COMMUNITY_LIST_STANDARD
)
1171 lcom
= lcommunity_str2com(str
);
1173 regex
= bgp_regcomp(str
);
1175 if (!lcom
&& !regex
)
1176 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
1179 entry
= community_list_entry_lookup(list
, lcom
, direct
);
1181 entry
= community_list_entry_lookup(list
, str
, direct
);
1184 lcommunity_free(&lcom
);
1186 bgp_regex_free(regex
);
1189 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
1191 community_list_entry_delete(cm
, list
, entry
);
1192 route_map_notify_dependencies(name
, RMAP_EVENT_LLIST_DELETED
);
1197 /* Set extcommunity-list. */
1198 int extcommunity_list_set(struct community_list_handler
*ch
, const char *name
,
1199 const char *str
, const char *seq
, int direct
,
1202 struct community_entry
*entry
= NULL
;
1203 struct community_list
*list
;
1204 struct ecommunity
*ecom
= NULL
;
1205 regex_t
*regex
= NULL
;
1206 int64_t seqnum
= COMMUNITY_SEQ_NUMBER_AUTO
;
1209 seqnum
= (int64_t)atol(seq
);
1212 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
1214 /* Get community list. */
1215 list
= community_list_get(ch
, name
, EXTCOMMUNITY_LIST_MASTER
);
1217 /* When community-list already has entry, new entry should have same
1218 style. If you want to have mixed style community-list, you can
1219 comment out this check. */
1220 if (!community_list_empty_p(list
)) {
1221 struct community_entry
*first
;
1225 if (style
!= first
->style
) {
1226 return (first
->style
== EXTCOMMUNITY_LIST_STANDARD
1227 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
1228 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT
);
1232 if (style
== EXTCOMMUNITY_LIST_STANDARD
)
1233 ecom
= ecommunity_str2com(str
, 0, 1);
1235 regex
= bgp_regcomp(str
);
1237 if (!ecom
&& !regex
)
1238 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
1242 ecommunity_ecom2str(ecom
, ECOMMUNITY_FORMAT_DISPLAY
, 0);
1244 entry
= community_entry_new();
1245 entry
->direct
= direct
;
1246 entry
->style
= style
;
1249 entry
->config
= ecommunity_ecom2str(
1250 ecom
, ECOMMUNITY_FORMAT_COMMUNITY_LIST
, 0);
1252 entry
->config
= XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG
, str
);
1254 entry
->u
.ecom
= ecom
;
1256 entry
->seq
= seqnum
;
1258 /* Do not put duplicated community entry. */
1259 if (community_list_dup_check(list
, entry
))
1260 community_entry_free(entry
);
1262 community_list_entry_add(list
, entry
, ch
,
1263 EXTCOMMUNITY_LIST_MASTER
);
1264 route_map_notify_dependencies(name
, RMAP_EVENT_ECLIST_ADDED
);
1270 /* Unset extcommunity-list.
1272 * When str is NULL, delete all extcommunity-list entries belonging to the
1275 int extcommunity_list_unset(struct community_list_handler
*ch
, const char *name
,
1276 const char *str
, const char *seq
, int direct
,
1279 struct community_list_master
*cm
= NULL
;
1280 struct community_entry
*entry
= NULL
;
1281 struct community_list
*list
;
1282 struct ecommunity
*ecom
= NULL
;
1284 /* Lookup extcommunity list. */
1285 list
= community_list_lookup(ch
, name
, 0, EXTCOMMUNITY_LIST_MASTER
);
1287 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
1289 cm
= community_list_master_lookup(ch
, EXTCOMMUNITY_LIST_MASTER
);
1290 /* Delete all of entry belongs to this extcommunity-list. */
1292 community_list_delete(cm
, list
);
1293 route_map_notify_dependencies(name
, RMAP_EVENT_ECLIST_DELETED
);
1297 if (style
== EXTCOMMUNITY_LIST_STANDARD
)
1298 ecom
= ecommunity_str2com(str
, 0, 1);
1301 entry
= community_list_entry_lookup(list
, ecom
, direct
);
1302 ecommunity_free(&ecom
);
1304 entry
= community_list_entry_lookup(list
, str
, direct
);
1307 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
1309 community_list_entry_delete(cm
, list
, entry
);
1310 route_map_notify_dependencies(name
, RMAP_EVENT_ECLIST_DELETED
);
1315 /* Initializa community-list. Return community-list handler. */
1316 struct community_list_handler
*community_list_init(void)
1318 struct community_list_handler
*ch
;
1319 ch
= XCALLOC(MTYPE_COMMUNITY_LIST_HANDLER
,
1320 sizeof(struct community_list_handler
));
1322 ch
->community_list
.hash
=
1323 hash_create_size(4, bgp_clist_hash_key_community_list
,
1324 bgp_clist_hash_cmp_community_list
,
1325 "Community List Number Quick Lookup");
1327 ch
->extcommunity_list
.hash
=
1328 hash_create_size(4, bgp_clist_hash_key_community_list
,
1329 bgp_clist_hash_cmp_community_list
,
1330 "Extended Community List Quick Lookup");
1332 ch
->lcommunity_list
.hash
=
1333 hash_create_size(4, bgp_clist_hash_key_community_list
,
1334 bgp_clist_hash_cmp_community_list
,
1335 "Large Community List Quick Lookup");
1340 /* Terminate community-list. */
1341 void community_list_terminate(struct community_list_handler
*ch
)
1343 struct community_list_master
*cm
;
1344 struct community_list
*list
;
1346 cm
= &ch
->community_list
;
1347 while ((list
= cm
->num
.head
) != NULL
)
1348 community_list_delete(cm
, list
);
1349 while ((list
= cm
->str
.head
) != NULL
)
1350 community_list_delete(cm
, list
);
1351 hash_free(cm
->hash
);
1353 cm
= &ch
->lcommunity_list
;
1354 while ((list
= cm
->num
.head
) != NULL
)
1355 community_list_delete(cm
, list
);
1356 while ((list
= cm
->str
.head
) != NULL
)
1357 community_list_delete(cm
, list
);
1358 hash_free(cm
->hash
);
1360 cm
= &ch
->extcommunity_list
;
1361 while ((list
= cm
->num
.head
) != NULL
)
1362 community_list_delete(cm
, list
);
1363 while ((list
= cm
->str
.head
) != NULL
)
1364 community_list_delete(cm
, list
);
1365 hash_free(cm
->hash
);
1367 XFREE(MTYPE_COMMUNITY_LIST_HANDLER
, ch
);
1370 static int bgp_community_list_vector_walker(struct hash_bucket
*bucket
,
1373 vector
*comps
= data
;
1374 struct community_list
*list
= bucket
->data
;
1376 vector_set(*comps
, XSTRDUP(MTYPE_COMPLETION
, list
->name
));
1381 static void bgp_community_list_cmd_completion(vector comps
,
1382 struct cmd_token
*token
)
1384 struct community_list_master
*cm
;
1386 cm
= community_list_master_lookup(bgp_clist
, COMMUNITY_LIST_MASTER
);
1388 hash_walk(cm
->hash
, bgp_community_list_vector_walker
, &comps
);
1391 static void bgp_lcommunity_list_cmd_completion(vector comps
,
1392 struct cmd_token
*token
)
1394 struct community_list_master
*cm
;
1396 cm
= community_list_master_lookup(bgp_clist
,
1397 LARGE_COMMUNITY_LIST_MASTER
);
1399 hash_walk(cm
->hash
, bgp_community_list_vector_walker
, &comps
);
1402 static void bgp_extcommunity_list_cmd_completion(vector comps
,
1403 struct cmd_token
*token
)
1405 struct community_list_master
*cm
;
1407 cm
= community_list_master_lookup(bgp_clist
, EXTCOMMUNITY_LIST_MASTER
);
1409 hash_walk(cm
->hash
, bgp_community_list_vector_walker
, &comps
);
1412 static const struct cmd_variable_handler community_list_handlers
[] = {
1413 {.tokenname
= "COMMUNITY_LIST_NAME",
1414 .completions
= bgp_community_list_cmd_completion
},
1415 {.completions
= NULL
}};
1417 static const struct cmd_variable_handler lcommunity_list_handlers
[] = {
1418 {.tokenname
= "LCOMMUNITY_LIST_NAME",
1419 .completions
= bgp_lcommunity_list_cmd_completion
},
1420 {.completions
= NULL
}};
1422 static const struct cmd_variable_handler extcommunity_list_handlers
[] = {
1423 {.tokenname
= "EXTCOMMUNITY_LIST_NAME",
1424 .completions
= bgp_extcommunity_list_cmd_completion
},
1425 {.completions
= NULL
}};
1427 void bgp_community_list_command_completion_setup(void)
1429 cmd_variable_handler_register(community_list_handlers
);
1430 cmd_variable_handler_register(lcommunity_list_handlers
);
1431 cmd_variable_handler_register(extcommunity_list_handlers
);