diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 221605d985..34422bf514 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -2206,6 +2206,16 @@ cluster_list_ignore: return bgp_attr_ignore(peer, args->type); } +/* get locally configure or received srte-color value*/ +uint32_t bgp_attr_get_color(struct attr *attr) +{ + if (attr->srte_color) + return attr->srte_color; + if (attr->ecommunity) + return ecommunity_select_color(attr->ecommunity); + return 0; +} + /* Multiprotocol reachability information parse. */ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args, struct bgp_nlri *mp_update) diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 6cd34d301c..415df2ce53 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -466,6 +466,8 @@ extern void bgp_packet_mpunreach_end(struct stream *s, size_t attrlen_pnt); extern enum bgp_attr_parse_ret bgp_attr_nexthop_valid(struct peer *peer, struct attr *attr); +extern uint32_t bgp_attr_get_color(struct attr *attr); + static inline bool bgp_rmap_nhop_changed(uint32_t out_rmap_flags, uint32_t in_rmap_flags) { diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 29b2250747..e473468dbf 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -355,6 +355,22 @@ bool ecommunity_cmp(const void *arg1, const void *arg2) ecom1->unit_size) == 0); } +static void ecommunity_color_str(char *buf, size_t bufsz, uint8_t *ptr) +{ + /* + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x03 | Sub-Type(0x0b) | Flags | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Color Value | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + uint32_t colorid; + + memcpy(&colorid, ptr + 3, 4); + colorid = ntohl(colorid); + snprintf(buf, bufsz, "Color:%d", colorid); +} + /* Initialize Extended Comminities related hash. */ void ecommunity_init(void) { @@ -373,6 +389,7 @@ enum ecommunity_token { ecommunity_token_rt, ecommunity_token_nt, ecommunity_token_soo, + ecommunity_token_color, ecommunity_token_val, ecommunity_token_rt6, ecommunity_token_val6, @@ -510,6 +527,9 @@ static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type, memcpy(&eval6->val[2], ip6, sizeof(struct in6_addr)); eval6->val[18] = (val >> 8) & 0xff; eval6->val[19] = val & 0xff; + } else if (type == ECOMMUNITY_ENCODE_OPAQUE && + sub_type == ECOMMUNITY_COLOR) { + encode_color(val, eval); } else { encode_route_target_as4(as, val, eval, trans); } @@ -537,16 +557,22 @@ static const char *ecommunity_gettoken(const char *str, void *eval_ptr, int dot = 0; int digit = 0; int separator = 0; + int i; const char *p = str; char *endptr; struct in_addr ip; struct in6_addr ip6; as_t as = 0; uint32_t val = 0; - uint8_t ecomm_type; + uint32_t val_color = 0; + uint8_t ecomm_type = 0; + uint8_t sub_type = 0; char buf[INET_ADDRSTRLEN + 1]; struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr; uint64_t tmp_as = 0; + static const char str_color[5] = "color"; + const char *ptr_color; + bool val_color_set = false; /* Skip white space. */ while (isspace((unsigned char)*p)) { @@ -558,7 +584,7 @@ static const char *ecommunity_gettoken(const char *str, void *eval_ptr, if (*p == '\0') return NULL; - /* "rt", "nt", and "soo" keyword parse. */ + /* "rt", "nt", "soo", and "color" keyword parse. */ if (!isdigit((unsigned char)*p)) { /* "rt" match check. */ if (tolower((unsigned char)*p) == 'r') { @@ -612,10 +638,33 @@ static const char *ecommunity_gettoken(const char *str, void *eval_ptr, return p; } goto error; + } else if (tolower((unsigned char)*p) == 'c') { + /* "color" match check. + * 'c', 'co', 'col', 'colo' are also accepted + */ + for (i = 0; i < 5; i++) { + ptr_color = &str_color[0]; + if (tolower((unsigned char)*p) == *ptr_color) { + p++; + ptr_color++; + } else if (i > 0) { + if (isspace((unsigned char)*p) || + *p == '\0') { + *token = ecommunity_token_color; + return p; + } + goto error; + } + if (isspace((unsigned char)*p) || *p == '\0') { + *token = ecommunity_token_color; + return p; + } + goto error; + } + goto error; } goto error; } - /* What a mess, there are several possibilities: * * a) A.B.C.D:MN @@ -716,17 +765,24 @@ static const char *ecommunity_gettoken(const char *str, void *eval_ptr, } else { digit = 1; - /* We're past the IP/ASN part */ + /* We're past the IP/ASN part, + * or we have a color + */ if (separator) { val *= 10; val += (*p - '0'); + val_color_set = false; + } else { + val_color *= 10; + val_color += (*p - '0'); + val_color_set = true; } } p++; } /* Low digit part must be there. */ - if (!digit || !separator) + if (!digit && (!separator || !val_color_set)) goto error; /* Encode result into extended community. */ @@ -734,9 +790,15 @@ static const char *ecommunity_gettoken(const char *str, void *eval_ptr, ecomm_type = ECOMMUNITY_ENCODE_IP; else if (as > BGP_AS_MAX) ecomm_type = ECOMMUNITY_ENCODE_AS4; - else + else if (as > 0) ecomm_type = ECOMMUNITY_ENCODE_AS; - if (ecommunity_encode(ecomm_type, type, 1, as, ip, val, eval)) + else if (val_color) { + ecomm_type = ECOMMUNITY_ENCODE_OPAQUE; + sub_type = ECOMMUNITY_COLOR; + val = val_color; + } + + if (ecommunity_encode(ecomm_type, sub_type, 1, as, ip, val, eval)) goto error; *token = ecommunity_token_val; return p; @@ -763,6 +825,7 @@ static struct ecommunity *ecommunity_str2com_internal(const char *str, int type, case ecommunity_token_nt: case ecommunity_token_rt6: case ecommunity_token_soo: + case ecommunity_token_color: if (!keyword_included || keyword) { if (ecom) ecommunity_free(&ecom); @@ -771,15 +834,14 @@ static struct ecommunity *ecommunity_str2com_internal(const char *str, int type, keyword = 1; if (token == ecommunity_token_rt || - token == ecommunity_token_rt6) { + token == ecommunity_token_rt6) type = ECOMMUNITY_ROUTE_TARGET; - } - if (token == ecommunity_token_soo) { + if (token == ecommunity_token_soo) type = ECOMMUNITY_SITE_ORIGIN; - } - if (token == ecommunity_token_nt) { + if (token == ecommunity_token_nt) type = ECOMMUNITY_NODE_TARGET; - } + if (token == ecommunity_token_color) + type = ECOMMUNITY_COLOR; break; case ecommunity_token_val: if (keyword_included) { @@ -998,10 +1060,12 @@ static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt, "rt 100:1 100:2soo 100:3" extcommunity-list - "rt 100:1 rt 100:2 soo 100:3show [ip] bgp" and extcommunity-list regular expression matching + "rt 100:1 rt 100:2 soo 100:3 + show [ip] bgp" + and extcommunity-list regular expression matching "RT:100:1 RT:100:2 SoO:100:3" - For each formath please use below definition for format: + For each format please use below definition for format: ECOMMUNITY_FORMAT_ROUTE_MAP ECOMMUNITY_FORMAT_COMMUNITY_LIST @@ -1086,6 +1150,9 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW) { strlcpy(encbuf, "Default Gateway", sizeof(encbuf)); + } else if (*pnt == ECOMMUNITY_COLOR) { + ecommunity_color_str(encbuf, sizeof(encbuf), + pnt); } else { unk_ecom = 1; } @@ -1353,6 +1420,29 @@ bool ecommunity_match(const struct ecommunity *ecom1, return false; } +/* return last occurence of color */ +/* it will be the greatest color value */ +extern uint32_t ecommunity_select_color(const struct ecommunity *ecom) +{ + + uint32_t aux_color = 0; + uint8_t *p; + uint32_t c = 0; + + /* If the value already exists in the structure return 0. */ + + for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) { + if (p == NULL) + break; + + if (p[0] == ECOMMUNITY_ENCODE_OPAQUE && + p[1] == ECOMMUNITY_COLOR) + ptr_get_be32((const uint8_t *)&p[4], &aux_color); + } + return aux_color; +} + + /* return first occurence of type */ extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom, uint8_t type, uint8_t subtype) diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index d62dc2e84c..7dc04d206a 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -46,6 +46,8 @@ #define ECOMMUNITY_REDIRECT_VRF 0x08 #define ECOMMUNITY_TRAFFIC_MARKING 0x09 #define ECOMMUNITY_REDIRECT_IP_NH 0x00 +#define ECOMMUNITY_COLOR 0x0b /* RFC9012 - color */ + /* from IANA: bgp-extended-communities/bgp-extended-communities.xhtml * 0x0c Flow-spec Redirect to IPv4 - draft-ietf-idr-flowspec-redirect */ @@ -290,6 +292,35 @@ static inline void encode_node_target(struct in_addr *node_id, eval->val[7] = ECOMMUNITY_NODE_TARGET_RESERVED; } +/* + * Encode BGP Color extended community + * is's a transitive opaque Extended community (RFC 9012 4.3) + * flag is set to 0 + * RFC 9012 14.10: No values have currently been registered. + * 4.3: this field MUST be set to zero by the originator + * and ignored by the receiver; + * + */ +static inline void encode_color(uint32_t color_id, struct ecommunity_val *eval) +{ + /* + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x03 | Sub-Type(0x0b) | Flags | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Color Value | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + memset(eval, 0, sizeof(*eval)); + eval->val[0] = ECOMMUNITY_ENCODE_OPAQUE; + eval->val[1] = ECOMMUNITY_COLOR; + eval->val[2] = 0x00; + eval->val[3] = 0x00; + eval->val[4] = (color_id >> 24) & 0xff; + eval->val[5] = (color_id >> 16) & 0xff; + eval->val[6] = (color_id >> 8) & 0xff; + eval->val[7] = color_id & 0xff; +} + extern void ecommunity_init(void); extern void ecommunity_finish(void); extern void ecommunity_free(struct ecommunity **); @@ -314,10 +345,11 @@ extern void ecommunity_strfree(char **s); extern bool ecommunity_include(struct ecommunity *e1, struct ecommunity *e2); extern bool ecommunity_match(const struct ecommunity *, const struct ecommunity *); -extern char *ecommunity_str(struct ecommunity *); +extern char *ecommunity_str(struct ecommunity *ecom); extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *, uint8_t, uint8_t); +extern uint32_t ecommunity_select_color(const struct ecommunity *ecom); extern bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval, bool unique, bool overwrite); diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index ba5b0c7a7d..a46616803c 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -32,6 +32,7 @@ #include "bgpd/bgp_evpn.h" #include "bgpd/bgp_rd.h" #include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_ecommunity.h" extern struct zclient *zclient; @@ -322,7 +323,10 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, return 0; } - srte_color = pi->attr->srte_color; + if (CHECK_FLAG(pi->attr->flag, + ATTR_FLAG_BIT(BGP_ATTR_SRTE_COLOR))) + srte_color = bgp_attr_get_color(pi->attr); + } else if (peer) { /* * Gather the ifindex for if up/down events to be @@ -1249,9 +1253,9 @@ void evaluate_paths(struct bgp_nexthop_cache *bnc) else if (bpi_ultimate->extra) bpi_ultimate->extra->igpmetric = 0; - if (CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_METRIC_CHANGED) - || CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED) - || path->attr->srte_color != 0) + if (CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_METRIC_CHANGED) || + CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED) || + bgp_attr_get_color(path->attr)) SET_FLAG(path->flags, BGP_PATH_IGP_CHANGED); path_valid = CHECK_FLAG(path->flags, BGP_PATH_VALID); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index d29b91b48f..8b633f8402 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -3092,6 +3092,44 @@ static void *route_set_ecommunity_lb_compile(const char *arg) return rels; } +static enum route_map_cmd_result_t +route_set_ecommunity_color(void *rule, const struct prefix *prefix, + void *object) +{ + struct bgp_path_info *path; + + path = object; + + route_set_ecommunity(rule, prefix, object); + + path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_SRTE_COLOR); + return RMAP_OKAY; +} + +static void *route_set_ecommunity_color_compile(const char *arg) +{ + struct rmap_ecom_set *rcs; + struct ecommunity *ecom; + + ecom = ecommunity_str2com(arg, ECOMMUNITY_COLOR, 0); + if (!ecom) + return NULL; + + rcs = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_ecom_set)); + rcs->ecom = ecommunity_intern(ecom); + rcs->none = false; + + return rcs; +} + +static const struct route_map_rule_cmd route_set_ecommunity_color_cmd = { + "extcommunity color", + route_set_ecommunity_color, + route_set_ecommunity_color_compile, + route_set_ecommunity_free, +}; + + static void route_set_ecommunity_lb_free(void *rule) { XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); @@ -6585,6 +6623,57 @@ DEFPY_YANG (no_set_ecommunity_nt, return nb_cli_apply_changes(vty, NULL); } +DEFPY_YANG(set_ecommunity_color, set_ecommunity_color_cmd, + "set extcommunity color RTLIST...", + SET_STR + "BGP extended community attribute\n" + "Color extended community\n" + "Color ID\n") +{ + int idx_color = 3; + char *str; + int ret; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-extcommunity-color']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:extcommunity-color", + xpath); + str = argv_concat(argv, argc, idx_color); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, str); + ret = nb_cli_apply_changes(vty, NULL); + XFREE(MTYPE_TMP, str); + return ret; +} + +DEFPY_YANG(no_set_ecommunity_color_all, no_set_ecommunity_color_all_cmd, + "no set extcommunity color", + NO_STR SET_STR + "BGP extended community attribute\n" + "Color extended community\n") +{ + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-extcommunity-color']"; + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG(no_set_ecommunity_color, no_set_ecommunity_color_cmd, + "no set extcommunity color RTLIST...", + NO_STR SET_STR + "BGP extended community attribute\n" + "Color extended community\n" + "Color ID\n") +{ + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-extcommunity-color']"; + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + ALIAS_YANG (no_set_ecommunity_nt, no_set_ecommunity_nt_short_cmd, "no set extcommunity nt", @@ -7438,6 +7527,7 @@ void bgp_route_map_init(void) route_map_install_set(&route_set_ecommunity_nt_cmd); route_map_install_set(&route_set_ecommunity_soo_cmd); route_map_install_set(&route_set_ecommunity_lb_cmd); + route_map_install_set(&route_set_ecommunity_color_cmd); route_map_install_set(&route_set_ecommunity_none_cmd); route_map_install_set(&route_set_tag_cmd); route_map_install_set(&route_set_label_index_cmd); @@ -7542,6 +7632,9 @@ void bgp_route_map_init(void) install_element(RMAP_NODE, &set_ecommunity_nt_cmd); install_element(RMAP_NODE, &no_set_ecommunity_nt_cmd); install_element(RMAP_NODE, &no_set_ecommunity_nt_short_cmd); + install_element(RMAP_NODE, &set_ecommunity_color_cmd); + install_element(RMAP_NODE, &no_set_ecommunity_color_cmd); + install_element(RMAP_NODE, &no_set_ecommunity_color_all_cmd); #ifdef KEEP_OLD_VPN_COMMANDS install_element(RMAP_NODE, &set_vpn_nexthop_cmd); install_element(RMAP_NODE, &no_set_vpn_nexthop_cmd); diff --git a/bgpd/bgp_routemap_nb.c b/bgpd/bgp_routemap_nb.c index 282ebe9116..ae695a6f80 100644 --- a/bgpd/bgp_routemap_nb.c +++ b/bgpd/bgp_routemap_nb.c @@ -400,6 +400,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = { .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_destroy, } }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-color", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_extcommunity_color_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_color_destroy, + } + }, { .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb/two-octet-as-specific", .cbs = { diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h index 7066fdb419..3ff58f71a7 100644 --- a/bgpd/bgp_routemap_nb.h +++ b/bgpd/bgp_routemap_nb.h @@ -153,6 +153,10 @@ int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_modify( struct nb_cb_modify_args *args); int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy( struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_color_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_color_destroy( + struct nb_cb_destroy_args *args); int lib_route_map_entry_set_action_rmap_set_action_l3vpn_nexthop_encapsulation_modify( struct nb_cb_modify_args *args); int lib_route_map_entry_set_action_rmap_set_action_l3vpn_nexthop_encapsulation_destroy( diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c index 02564b0004..03b588a33b 100644 --- a/bgpd/bgp_routemap_nb_config.c +++ b/bgpd/bgp_routemap_nb_config.c @@ -2952,6 +2952,58 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_destroy return lib_route_map_entry_set_destroy(args); } +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-color + */ +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_color_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *str; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + str = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "extcommunity color"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "extcommunity color", str, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_color_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + /* * XPath: * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb/two-octet-as-specific diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 3e1fdc6284..4b2b31fd7b 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1442,7 +1442,7 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, if (CHECK_FLAG(info->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_SRTE_COLOR))) - api_nh->srte_color = info->attr->srte_color; + api_nh->srte_color = bgp_attr_get_color(info->attr); if (bgp_debug_zebra(&api.prefix)) { if (mpinfo->extra) { diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index df42e4d10a..46b22ff452 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2620,6 +2620,10 @@ BGP Extended Communities in Route Map This command sets Site of Origin value. +.. clicmd:: set extcomumnity color EXTCOMMUNITY + + This command sets colors values. + .. clicmd:: set extcommunity bandwidth <(1-25600) | cumulative | num-multipaths> [non-transitive] This command sets the BGP link-bandwidth extended community for the prefix diff --git a/lib/routemap.h b/lib/routemap.h index 7277744dc5..a83ef9c967 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -362,6 +362,9 @@ DECLARE_QOBJ_TYPE(route_map); (strmatch(A, "frr-bgp-route-map:set-extcommunity-soo")) #define IS_SET_EXTCOMMUNITY_LB(A) \ (strmatch(A, "frr-bgp-route-map:set-extcommunity-lb")) +#define IS_SET_EXTCOMMUNITY_COLOR(A) \ + (strmatch(A, "frr-bgp-route-map:set-extcommunity-color")) + #define IS_SET_AGGREGATOR(A) \ (strmatch(A, "frr-bgp-route-map:aggregator")) #define IS_SET_AS_PREPEND(A) \ diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index 0ccc78e838..c1bdd28eab 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -1259,6 +1259,11 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *dnode, strlcat(str, " non-transitive", sizeof(str)); vty_out(vty, " set extcommunity bandwidth %s\n", str); + } else if (IS_SET_EXTCOMMUNITY_COLOR(action)) { + vty_out(vty, " set extcommunity color %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:extcommunity-color")); } else if (IS_SET_EXTCOMMUNITY_NONE(action)) { if (yang_dnode_get_bool( dnode, diff --git a/tests/topotests/bgp_color_extcommunities/__init__.py b/tests/topotests/bgp_color_extcommunities/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_color_extcommunities/r1/bgpd.conf b/tests/topotests/bgp_color_extcommunities/r1/bgpd.conf new file mode 100644 index 0000000000..d4ca392b1a --- /dev/null +++ b/tests/topotests/bgp_color_extcommunities/r1/bgpd.conf @@ -0,0 +1,17 @@ +! +router bgp 65001 + bgp router-id 192.168.1.1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.1.2 remote-as external + address-family ipv4 unicast + network 10.10.10.10/24 route-map rmap + neighbor 192.168.1.2 route-map rmap out + neighbor 192.168.1.2 activate + exit-address-family +! +route-map rmap permit 10 + set extcommunity color 1 + set extcommunity rt 80:987 + set extcommunity color 100 55555 200 +exit diff --git a/tests/topotests/bgp_color_extcommunities/r1/zebra.conf b/tests/topotests/bgp_color_extcommunities/r1/zebra.conf new file mode 100644 index 0000000000..42a830372f --- /dev/null +++ b/tests/topotests/bgp_color_extcommunities/r1/zebra.conf @@ -0,0 +1,3 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 diff --git a/tests/topotests/bgp_color_extcommunities/r2/bgpd.conf b/tests/topotests/bgp_color_extcommunities/r2/bgpd.conf new file mode 100644 index 0000000000..2f83ada9d3 --- /dev/null +++ b/tests/topotests/bgp_color_extcommunities/r2/bgpd.conf @@ -0,0 +1,4 @@ +router bgp 65002 + bgp router-id 192.168.1.2 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external diff --git a/tests/topotests/bgp_color_extcommunities/r2/zebra.conf b/tests/topotests/bgp_color_extcommunities/r2/zebra.conf new file mode 100644 index 0000000000..cffe827363 --- /dev/null +++ b/tests/topotests/bgp_color_extcommunities/r2/zebra.conf @@ -0,0 +1,4 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_color_extcommunities/test_bgp_color_extcommunities.py b/tests/topotests/bgp_color_extcommunities/test_bgp_color_extcommunities.py new file mode 100644 index 0000000000..6d17cdb4d9 --- /dev/null +++ b/tests/topotests/bgp_color_extcommunities/test_bgp_color_extcommunities.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright 2022 6WIND S.A. +# Copyright 2023 6WIND S.A. +# François Dumontet +# + + +""" +test_bgp_color_extcommunity.py: Test the FRR BGP color extented +community feature +""" + +import os +import sys +import json +import functools +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + logger.info("setup_module") + + router_list = tgen.routers() + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_color_extended_communities(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp summary json")) + expected = { + "ipv4Unicast": { + "peers": { + "192.168.1.2": { + "pfxSnt": 1, + "state": "Established", + }, + } + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed announcing 10.10.10.10/32 to r2" + + def _bgp_check_route(router, exists): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast 10.10.10.10 json")) + if exists: + expected = { + "prefix": "10.10.10.0/24", + "paths": [ + { + "valid": True, + "extendedCommunity": { + "string": "RT:80:987 Color:100 Color:200 Color:55555" + }, + } + ], + } + else: + expected = {} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_route, r2, True) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "10.10.10.0/24 ext community is correctly not installed, but SHOULD be" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang index b557cabb22..4b6619739d 100644 --- a/yang/frr-bgp-route-map.yang +++ b/yang/frr-bgp-route-map.yang @@ -214,6 +214,12 @@ module frr-bgp-route-map { "Set BGP extended community attribute"; } +identity set-extcommunity-color { + base frr-route-map:rmap-set-type; + description + "Set BGP extended community attribute"; + } + identity set-ipv4-nexthop { base frr-route-map:rmap-set-type; description @@ -511,6 +517,22 @@ module frr-bgp-route-map { } } + typedef color-list { + type string { + pattern '((429496729[0-5]|42949672[0-8][0-9]|' + + '4294967[0-1][0-9]{2}|429496[0-6][0-9]{3}|' + + '42949[0-5][0-9]{4}|4294[0-8][0-9]{5}|' + + '429[0-3][0-9]{6}|42[0-8][0-9]{7}|' + + '4[0-1][0-9]{8}|[1-3][0-9]{9}|' + + '[1-9][0-9]{0,8})(\s*))+'; + } + description + "The color-list type represent a set of colors of value (1..4294967295) + values are separated by white spaces"; + reference + "RFC 9012 - The BGP Tunnel Encapsulation Attribute"; + } + augment "/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:rmap-match-condition/frr-route-map:match-condition" { case local-preference { when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'frr-bgp-route-map:match-local-preference')"; @@ -852,6 +874,19 @@ module frr-bgp-route-map { } } + case extcommunity-color { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'frr-bgp-route-map:set-extcommunity-color')"; + description + "Value of the ext-community"; + leaf extcommunity-color { + type color-list; + description + "Set BGP ext-community color attribute with a list of colors"; + reference + "RFC9012"; + } + } + case ipv4-address { when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'frr-bgp-route-map:ipv4-vpn-address')"; description