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 (void)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, true);
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, true);
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_match(com
, entry
->u
.com
))
678 return entry
->direct
== COMMUNITY_PERMIT
;
679 } else if (entry
->style
== COMMUNITY_LIST_EXPANDED
) {
680 if (community_regexp_match(com
, entry
->reg
))
681 return entry
->direct
== COMMUNITY_PERMIT
;
687 bool lcommunity_list_match(struct lcommunity
*lcom
, struct community_list
*list
)
689 struct community_entry
*entry
;
691 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
693 return entry
->direct
== COMMUNITY_PERMIT
;
695 if (entry
->style
== LARGE_COMMUNITY_LIST_STANDARD
) {
696 if (lcommunity_match(lcom
, entry
->u
.lcom
))
697 return entry
->direct
== COMMUNITY_PERMIT
;
698 } else if (entry
->style
== LARGE_COMMUNITY_LIST_EXPANDED
) {
699 if (lcommunity_regexp_match(lcom
, entry
->reg
))
700 return entry
->direct
== COMMUNITY_PERMIT
;
707 /* Perform exact matching. In case of expanded large-community-list, do
708 * same thing as lcommunity_list_match().
710 bool lcommunity_list_exact_match(struct lcommunity
*lcom
,
711 struct community_list
*list
)
713 struct community_entry
*entry
;
715 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
717 return entry
->direct
== COMMUNITY_PERMIT
;
719 if (entry
->style
== LARGE_COMMUNITY_LIST_STANDARD
) {
720 if (lcommunity_cmp(lcom
, entry
->u
.lcom
))
721 return entry
->direct
== COMMUNITY_PERMIT
;
722 } else if (entry
->style
== LARGE_COMMUNITY_LIST_EXPANDED
) {
723 if (lcommunity_regexp_match(lcom
, entry
->reg
))
724 return entry
->direct
== COMMUNITY_PERMIT
;
730 bool ecommunity_list_match(struct ecommunity
*ecom
, struct community_list
*list
)
732 struct community_entry
*entry
;
734 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
736 return entry
->direct
== COMMUNITY_PERMIT
;
738 if (entry
->style
== EXTCOMMUNITY_LIST_STANDARD
) {
739 if (ecommunity_match(ecom
, entry
->u
.ecom
))
740 return entry
->direct
== COMMUNITY_PERMIT
;
741 } else if (entry
->style
== EXTCOMMUNITY_LIST_EXPANDED
) {
742 if (ecommunity_regexp_match(ecom
, entry
->reg
))
743 return entry
->direct
== COMMUNITY_PERMIT
;
749 /* Perform exact matching. In case of expanded community-list, do
750 same thing as community_list_match(). */
751 bool community_list_exact_match(struct community
*com
,
752 struct community_list
*list
)
754 struct community_entry
*entry
;
756 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
758 return entry
->direct
== COMMUNITY_PERMIT
;
760 if (entry
->style
== COMMUNITY_LIST_STANDARD
) {
761 if (community_include(entry
->u
.com
, COMMUNITY_INTERNET
))
762 return entry
->direct
== COMMUNITY_PERMIT
;
764 if (community_cmp(com
, entry
->u
.com
))
765 return entry
->direct
== COMMUNITY_PERMIT
;
766 } else if (entry
->style
== COMMUNITY_LIST_EXPANDED
) {
767 if (community_regexp_match(com
, entry
->reg
))
768 return entry
->direct
== COMMUNITY_PERMIT
;
774 /* Delete all permitted communities in the list from com. */
775 struct community
*community_list_match_delete(struct community
*com
,
776 struct community_list
*list
)
778 struct community_entry
*entry
;
780 uint32_t com_index_to_delete
[com
->size
];
781 int delete_index
= 0;
784 /* Loop over each community value and evaluate each against the
785 * community-list. If we need to delete a community value add its index
786 * to com_index_to_delete.
788 for (i
= 0; i
< com
->size
; i
++) {
789 val
= community_val_get(com
, i
);
791 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
793 if (entry
->direct
== COMMUNITY_PERMIT
) {
794 com_index_to_delete
[delete_index
] = i
;
800 else if ((entry
->style
== COMMUNITY_LIST_STANDARD
)
801 && (community_include(entry
->u
.com
,
803 || community_include(entry
->u
.com
, val
))) {
804 if (entry
->direct
== COMMUNITY_PERMIT
) {
805 com_index_to_delete
[delete_index
] = i
;
811 else if ((entry
->style
== COMMUNITY_LIST_EXPANDED
)
812 && community_regexp_include(entry
->reg
, com
,
814 if (entry
->direct
== COMMUNITY_PERMIT
) {
815 com_index_to_delete
[delete_index
] = i
;
823 /* Delete all of the communities we flagged for deletion */
824 for (i
= delete_index
- 1; i
>= 0; i
--) {
825 val
= community_val_get(com
, com_index_to_delete
[i
]);
827 community_del_val(com
, &val
);
833 /* To avoid duplicated entry in the community-list, this function
834 compares specified entry to existing entry. */
835 static bool community_list_dup_check(struct community_list
*list
,
836 struct community_entry
*new)
838 struct community_entry
*entry
;
840 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
841 if (entry
->style
!= new->style
)
844 if (entry
->direct
!= new->direct
)
847 if (entry
->any
!= new->any
)
853 switch (entry
->style
) {
854 case COMMUNITY_LIST_STANDARD
:
855 if (community_cmp(entry
->u
.com
, new->u
.com
))
858 case LARGE_COMMUNITY_LIST_STANDARD
:
859 if (lcommunity_cmp(entry
->u
.lcom
, new->u
.lcom
))
862 case EXTCOMMUNITY_LIST_STANDARD
:
863 if (ecommunity_cmp(entry
->u
.ecom
, new->u
.ecom
))
866 case COMMUNITY_LIST_EXPANDED
:
867 case EXTCOMMUNITY_LIST_EXPANDED
:
868 case LARGE_COMMUNITY_LIST_EXPANDED
:
869 if (strcmp(entry
->config
, new->config
) == 0)
879 /* Set community-list. */
880 int community_list_set(struct community_list_handler
*ch
, const char *name
,
881 const char *str
, const char *seq
, int direct
, int style
)
883 struct community_entry
*entry
= NULL
;
884 struct community_list
*list
;
885 struct community
*com
= NULL
;
886 regex_t
*regex
= NULL
;
887 int64_t seqnum
= COMMUNITY_SEQ_NUMBER_AUTO
;
890 seqnum
= (int64_t)atol(seq
);
892 /* Get community list. */
893 list
= community_list_get(ch
, name
, COMMUNITY_LIST_MASTER
);
895 /* When community-list already has entry, new entry should have same
896 style. If you want to have mixed style community-list, you can
897 comment out this check. */
898 if (!community_list_empty_p(list
)) {
899 struct community_entry
*first
;
903 if (style
!= first
->style
) {
904 return (first
->style
== COMMUNITY_LIST_STANDARD
905 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
906 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT
);
911 if (style
== COMMUNITY_LIST_STANDARD
)
912 com
= community_str2com(str
);
914 regex
= bgp_regcomp(str
);
917 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
920 entry
= community_entry_new();
921 entry
->direct
= direct
;
922 entry
->style
= style
;
923 entry
->any
= (str
? false : true);
928 (regex
? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG
, str
) : NULL
);
930 /* Do not put duplicated community entry. */
931 if (community_list_dup_check(list
, entry
))
932 community_entry_free(entry
);
934 community_list_entry_add(list
, entry
, ch
,
935 COMMUNITY_LIST_MASTER
);
936 route_map_notify_dependencies(name
, RMAP_EVENT_CLIST_ADDED
);
942 /* Unset community-list */
943 int community_list_unset(struct community_list_handler
*ch
, const char *name
,
944 const char *str
, const char *seq
, int direct
,
947 struct community_list_master
*cm
= NULL
;
948 struct community_entry
*entry
= NULL
;
949 struct community_list
*list
;
950 struct community
*com
= NULL
;
952 /* Lookup community list. */
953 list
= community_list_lookup(ch
, name
, 0, COMMUNITY_LIST_MASTER
);
955 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
957 cm
= community_list_master_lookup(ch
, COMMUNITY_LIST_MASTER
);
958 /* Delete all of entry belongs to this community-list. */
960 community_list_delete(cm
, list
);
961 route_map_notify_dependencies(name
, RMAP_EVENT_CLIST_DELETED
);
965 if (style
== COMMUNITY_LIST_STANDARD
)
966 com
= community_str2com(str
);
969 entry
= community_list_entry_lookup(list
, com
, direct
);
970 community_free(&com
);
972 entry
= community_list_entry_lookup(list
, str
, direct
);
975 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
977 community_list_entry_delete(cm
, list
, entry
);
978 route_map_notify_dependencies(name
, RMAP_EVENT_CLIST_DELETED
);
983 /* Delete all permitted large communities in the list from com. */
984 struct lcommunity
*lcommunity_list_match_delete(struct lcommunity
*lcom
,
985 struct community_list
*list
)
987 struct community_entry
*entry
;
988 uint32_t com_index_to_delete
[lcom
->size
];
990 int delete_index
= 0;
993 /* Loop over each lcommunity value and evaluate each against the
994 * community-list. If we need to delete a community value add its index
995 * to com_index_to_delete.
997 for (i
= 0; i
< lcom
->size
; i
++) {
998 ptr
= lcom
->val
+ (i
* LCOMMUNITY_SIZE
);
999 for (entry
= list
->head
; entry
; entry
= entry
->next
) {
1001 if (entry
->direct
== COMMUNITY_PERMIT
) {
1002 com_index_to_delete
[delete_index
] = i
;
1008 else if ((entry
->style
== LARGE_COMMUNITY_LIST_STANDARD
)
1009 && lcommunity_include(entry
->u
.lcom
, ptr
)) {
1010 if (entry
->direct
== COMMUNITY_PERMIT
) {
1011 com_index_to_delete
[delete_index
] = i
;
1017 else if ((entry
->style
== LARGE_COMMUNITY_LIST_EXPANDED
)
1018 && lcommunity_regexp_include(entry
->reg
, lcom
,
1020 if (entry
->direct
== COMMUNITY_PERMIT
) {
1021 com_index_to_delete
[delete_index
] = i
;
1029 /* Delete all of the communities we flagged for deletion */
1030 for (i
= delete_index
- 1; i
>= 0; i
--) {
1031 ptr
= lcom
->val
+ (com_index_to_delete
[i
] * LCOMMUNITY_SIZE
);
1032 lcommunity_del_val(lcom
, ptr
);
1038 /* Helper to check if every octet do not exceed UINT_MAX */
1039 bool lcommunity_list_valid(const char *community
, int style
)
1042 char **splits
, **communities
;
1044 int num
, num_communities
;
1048 frrstr_split(community
, " ", &communities
, &num_communities
);
1050 for (int j
= 0; j
< num_communities
; j
++) {
1052 frrstr_split(communities
[j
], ":", &splits
, &num
);
1054 for (int i
= 0; i
< num
; i
++) {
1055 if (strlen(splits
[i
]) == 0)
1056 /* There is no digit to check */
1059 if (style
== LARGE_COMMUNITY_LIST_STANDARD
) {
1060 if (*splits
[i
] == '-')
1061 /* Must not be negative */
1063 else if (strtoul(splits
[i
], &endptr
, 10)
1065 /* Larger than 4 octets */
1068 /* Not all characters were digits */
1071 regres
= bgp_regcomp(communities
[j
]);
1073 /* malformed regex */
1076 bgp_regex_free(regres
);
1080 XFREE(MTYPE_TMP
, splits
[i
]);
1082 XFREE(MTYPE_TMP
, splits
);
1087 XFREE(MTYPE_TMP
, communities
[j
]);
1089 XFREE(MTYPE_TMP
, communities
);
1091 return (invalid
> 0) ? false : true;
1094 /* Set lcommunity-list. */
1095 int lcommunity_list_set(struct community_list_handler
*ch
, const char *name
,
1096 const char *str
, const char *seq
, int direct
, int style
)
1098 struct community_entry
*entry
= NULL
;
1099 struct community_list
*list
;
1100 struct lcommunity
*lcom
= NULL
;
1101 regex_t
*regex
= NULL
;
1102 int64_t seqnum
= COMMUNITY_SEQ_NUMBER_AUTO
;
1105 seqnum
= (int64_t)atol(seq
);
1107 /* Get community list. */
1108 list
= community_list_get(ch
, name
, LARGE_COMMUNITY_LIST_MASTER
);
1110 /* When community-list already has entry, new entry should have same
1111 style. If you want to have mixed style community-list, you can
1112 comment out this check. */
1113 if (!community_list_empty_p(list
)) {
1114 struct community_entry
*first
;
1118 if (style
!= first
->style
) {
1119 return (first
->style
== COMMUNITY_LIST_STANDARD
1120 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
1121 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT
);
1126 if (style
== LARGE_COMMUNITY_LIST_STANDARD
)
1127 lcom
= lcommunity_str2com(str
);
1129 regex
= bgp_regcomp(str
);
1131 if (!lcom
&& !regex
)
1132 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
1135 entry
= community_entry_new();
1136 entry
->direct
= direct
;
1137 entry
->style
= style
;
1138 entry
->any
= (str
? false : true);
1139 entry
->u
.lcom
= lcom
;
1141 entry
->seq
= seqnum
;
1143 (regex
? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG
, str
) : NULL
);
1145 /* Do not put duplicated community entry. */
1146 if (community_list_dup_check(list
, entry
))
1147 community_entry_free(entry
);
1149 community_list_entry_add(list
, entry
, ch
,
1150 LARGE_COMMUNITY_LIST_MASTER
);
1151 route_map_notify_dependencies(name
, RMAP_EVENT_LLIST_ADDED
);
1157 /* Unset community-list. When str is NULL, delete all of
1158 community-list entry belongs to the specified name. */
1159 int lcommunity_list_unset(struct community_list_handler
*ch
, const char *name
,
1160 const char *str
, const char *seq
, int direct
,
1163 struct community_list_master
*cm
= NULL
;
1164 struct community_entry
*entry
= NULL
;
1165 struct community_list
*list
;
1166 struct lcommunity
*lcom
= NULL
;
1167 regex_t
*regex
= NULL
;
1169 /* Lookup community list. */
1170 list
= community_list_lookup(ch
, name
, 0, LARGE_COMMUNITY_LIST_MASTER
);
1172 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
1174 cm
= community_list_master_lookup(ch
, LARGE_COMMUNITY_LIST_MASTER
);
1175 /* Delete all of entry belongs to this community-list. */
1177 community_list_delete(cm
, list
);
1178 route_map_notify_dependencies(name
, RMAP_EVENT_LLIST_DELETED
);
1182 if (style
== LARGE_COMMUNITY_LIST_STANDARD
)
1183 lcom
= lcommunity_str2com(str
);
1185 regex
= bgp_regcomp(str
);
1187 if (!lcom
&& !regex
)
1188 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
1191 entry
= community_list_entry_lookup(list
, lcom
, direct
);
1193 entry
= community_list_entry_lookup(list
, str
, direct
);
1196 lcommunity_free(&lcom
);
1198 bgp_regex_free(regex
);
1201 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
1203 community_list_entry_delete(cm
, list
, entry
);
1204 route_map_notify_dependencies(name
, RMAP_EVENT_LLIST_DELETED
);
1209 /* Set extcommunity-list. */
1210 int extcommunity_list_set(struct community_list_handler
*ch
, const char *name
,
1211 const char *str
, const char *seq
, int direct
,
1214 struct community_entry
*entry
= NULL
;
1215 struct community_list
*list
;
1216 struct ecommunity
*ecom
= NULL
;
1217 regex_t
*regex
= NULL
;
1218 int64_t seqnum
= COMMUNITY_SEQ_NUMBER_AUTO
;
1221 seqnum
= (int64_t)atol(seq
);
1224 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
1226 /* Get community list. */
1227 list
= community_list_get(ch
, name
, EXTCOMMUNITY_LIST_MASTER
);
1229 /* When community-list already has entry, new entry should have same
1230 style. If you want to have mixed style community-list, you can
1231 comment out this check. */
1232 if (!community_list_empty_p(list
)) {
1233 struct community_entry
*first
;
1237 if (style
!= first
->style
) {
1238 return (first
->style
== EXTCOMMUNITY_LIST_STANDARD
1239 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
1240 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT
);
1244 if (style
== EXTCOMMUNITY_LIST_STANDARD
)
1245 ecom
= ecommunity_str2com(str
, 0, 1);
1247 regex
= bgp_regcomp(str
);
1249 if (!ecom
&& !regex
)
1250 return COMMUNITY_LIST_ERR_MALFORMED_VAL
;
1254 ecommunity_ecom2str(ecom
, ECOMMUNITY_FORMAT_DISPLAY
, 0);
1256 entry
= community_entry_new();
1257 entry
->direct
= direct
;
1258 entry
->style
= style
;
1261 entry
->config
= ecommunity_ecom2str(
1262 ecom
, ECOMMUNITY_FORMAT_COMMUNITY_LIST
, 0);
1264 entry
->config
= XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG
, str
);
1266 entry
->u
.ecom
= ecom
;
1268 entry
->seq
= seqnum
;
1270 /* Do not put duplicated community entry. */
1271 if (community_list_dup_check(list
, entry
))
1272 community_entry_free(entry
);
1274 community_list_entry_add(list
, entry
, ch
,
1275 EXTCOMMUNITY_LIST_MASTER
);
1276 route_map_notify_dependencies(name
, RMAP_EVENT_ECLIST_ADDED
);
1282 /* Unset extcommunity-list.
1284 * When str is NULL, delete all extcommunity-list entries belonging to the
1287 int extcommunity_list_unset(struct community_list_handler
*ch
, const char *name
,
1288 const char *str
, const char *seq
, int direct
,
1291 struct community_list_master
*cm
= NULL
;
1292 struct community_entry
*entry
= NULL
;
1293 struct community_list
*list
;
1294 struct ecommunity
*ecom
= NULL
;
1296 /* Lookup extcommunity list. */
1297 list
= community_list_lookup(ch
, name
, 0, EXTCOMMUNITY_LIST_MASTER
);
1299 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
1301 cm
= community_list_master_lookup(ch
, EXTCOMMUNITY_LIST_MASTER
);
1302 /* Delete all of entry belongs to this extcommunity-list. */
1304 community_list_delete(cm
, list
);
1305 route_map_notify_dependencies(name
, RMAP_EVENT_ECLIST_DELETED
);
1309 if (style
== EXTCOMMUNITY_LIST_STANDARD
)
1310 ecom
= ecommunity_str2com(str
, 0, 1);
1313 entry
= community_list_entry_lookup(list
, ecom
, direct
);
1314 ecommunity_free(&ecom
);
1316 entry
= community_list_entry_lookup(list
, str
, direct
);
1319 return COMMUNITY_LIST_ERR_CANT_FIND_LIST
;
1321 community_list_entry_delete(cm
, list
, entry
);
1322 route_map_notify_dependencies(name
, RMAP_EVENT_ECLIST_DELETED
);
1327 /* Initializa community-list. Return community-list handler. */
1328 struct community_list_handler
*community_list_init(void)
1330 struct community_list_handler
*ch
;
1331 ch
= XCALLOC(MTYPE_COMMUNITY_LIST_HANDLER
,
1332 sizeof(struct community_list_handler
));
1334 ch
->community_list
.hash
=
1335 hash_create_size(4, bgp_clist_hash_key_community_list
,
1336 bgp_clist_hash_cmp_community_list
,
1337 "Community List Number Quick Lookup");
1339 ch
->extcommunity_list
.hash
=
1340 hash_create_size(4, bgp_clist_hash_key_community_list
,
1341 bgp_clist_hash_cmp_community_list
,
1342 "Extended Community List Quick Lookup");
1344 ch
->lcommunity_list
.hash
=
1345 hash_create_size(4, bgp_clist_hash_key_community_list
,
1346 bgp_clist_hash_cmp_community_list
,
1347 "Large Community List Quick Lookup");
1352 /* Terminate community-list. */
1353 void community_list_terminate(struct community_list_handler
*ch
)
1355 struct community_list_master
*cm
;
1356 struct community_list
*list
;
1358 cm
= &ch
->community_list
;
1359 while ((list
= cm
->num
.head
) != NULL
)
1360 community_list_delete(cm
, list
);
1361 while ((list
= cm
->str
.head
) != NULL
)
1362 community_list_delete(cm
, list
);
1363 hash_free(cm
->hash
);
1365 cm
= &ch
->lcommunity_list
;
1366 while ((list
= cm
->num
.head
) != NULL
)
1367 community_list_delete(cm
, list
);
1368 while ((list
= cm
->str
.head
) != NULL
)
1369 community_list_delete(cm
, list
);
1370 hash_free(cm
->hash
);
1372 cm
= &ch
->extcommunity_list
;
1373 while ((list
= cm
->num
.head
) != NULL
)
1374 community_list_delete(cm
, list
);
1375 while ((list
= cm
->str
.head
) != NULL
)
1376 community_list_delete(cm
, list
);
1377 hash_free(cm
->hash
);
1379 XFREE(MTYPE_COMMUNITY_LIST_HANDLER
, ch
);
1382 static int bgp_community_list_vector_walker(struct hash_bucket
*bucket
,
1385 vector
*comps
= data
;
1386 struct community_list
*list
= bucket
->data
;
1388 vector_set(*comps
, XSTRDUP(MTYPE_COMPLETION
, list
->name
));
1393 static void bgp_community_list_cmd_completion(vector comps
,
1394 struct cmd_token
*token
)
1396 struct community_list_master
*cm
;
1398 cm
= community_list_master_lookup(bgp_clist
, COMMUNITY_LIST_MASTER
);
1400 hash_walk(cm
->hash
, bgp_community_list_vector_walker
, &comps
);
1403 static void bgp_lcommunity_list_cmd_completion(vector comps
,
1404 struct cmd_token
*token
)
1406 struct community_list_master
*cm
;
1408 cm
= community_list_master_lookup(bgp_clist
,
1409 LARGE_COMMUNITY_LIST_MASTER
);
1411 hash_walk(cm
->hash
, bgp_community_list_vector_walker
, &comps
);
1414 static void bgp_extcommunity_list_cmd_completion(vector comps
,
1415 struct cmd_token
*token
)
1417 struct community_list_master
*cm
;
1419 cm
= community_list_master_lookup(bgp_clist
, EXTCOMMUNITY_LIST_MASTER
);
1421 hash_walk(cm
->hash
, bgp_community_list_vector_walker
, &comps
);
1424 static const struct cmd_variable_handler community_list_handlers
[] = {
1425 {.tokenname
= "COMMUNITY_LIST_NAME",
1426 .completions
= bgp_community_list_cmd_completion
},
1427 {.completions
= NULL
}};
1429 static const struct cmd_variable_handler lcommunity_list_handlers
[] = {
1430 {.tokenname
= "LCOMMUNITY_LIST_NAME",
1431 .completions
= bgp_lcommunity_list_cmd_completion
},
1432 {.completions
= NULL
}};
1434 static const struct cmd_variable_handler extcommunity_list_handlers
[] = {
1435 {.tokenname
= "EXTCOMMUNITY_LIST_NAME",
1436 .completions
= bgp_extcommunity_list_cmd_completion
},
1437 {.completions
= NULL
}};
1439 void bgp_community_list_command_completion_setup(void)
1441 cmd_variable_handler_register(community_list_handlers
);
1442 cmd_variable_handler_register(lcommunity_list_handlers
);
1443 cmd_variable_handler_register(extcommunity_list_handlers
);