From b80ebc2d8cf02f6530181d1ec260fdb1e714c1c1 Mon Sep 17 00:00:00 2001 From: Francois Dumontet Date: Mon, 22 May 2023 15:36:06 +0200 Subject: [PATCH 1/4] bgpd: add colored extended communities support add support of color extended community, conforming to RFC 9012. This extended community will be added to the existing one, RT,SOO and Node Target. The configuration will be made through the route-map service. find above a configuration example: 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 neighbor 192.168.1.3 remote-as external neighbor 192.168.1.4 remote-as external address-family ipv4 unicast network 10.10.10.10/24 route-map rmap exit-address-family ! route-map rmap permit 10 set extcommunity color 55555 200 exit Signed-off-by: Francois Dumontet --- bgpd/bgp_ecommunity.c | 97 +++++++++++++++++++++++++++++------ bgpd/bgp_ecommunity.h | 33 +++++++++++- bgpd/bgp_routemap.c | 93 +++++++++++++++++++++++++++++++++ bgpd/bgp_routemap_nb.c | 7 +++ bgpd/bgp_routemap_nb.h | 4 ++ bgpd/bgp_routemap_nb_config.c | 52 +++++++++++++++++++ lib/routemap.h | 3 ++ lib/routemap_cli.c | 5 ++ yang/frr-bgp-route-map.yang | 35 +++++++++++++ 9 files changed, 313 insertions(+), 16 deletions(-) diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index a555930137..ec860c5a1c 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; } diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 94a178bbb6..5e67e5015e 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,7 +345,7 @@ 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); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 7db110be93..c5de9928a7 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -3055,6 +3055,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); @@ -6522,6 +6560,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", @@ -7375,6 +7464,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); @@ -7478,6 +7568,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/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/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 From 9ec092c6a278d98dc5f36e8e8b2e04f3a9c3fd70 Mon Sep 17 00:00:00 2001 From: Francois Dumontet Date: Thu, 25 May 2023 18:23:48 +0200 Subject: [PATCH 2/4] tests: add bgp colored extended community test Signed-off-by: Francois Dumontet --- .../bgp_color_extcommunities/__init__.py | 0 .../bgp_color_extcommunities/r1/bgpd.conf | 17 +++ .../bgp_color_extcommunities/r1/zebra.conf | 3 + .../bgp_color_extcommunities/r2/bgpd.conf | 4 + .../bgp_color_extcommunities/r2/zebra.conf | 4 + .../test_bgp_color_extcommunities.py | 125 ++++++++++++++++++ 6 files changed, 153 insertions(+) create mode 100644 tests/topotests/bgp_color_extcommunities/__init__.py create mode 100644 tests/topotests/bgp_color_extcommunities/r1/bgpd.conf create mode 100644 tests/topotests/bgp_color_extcommunities/r1/zebra.conf create mode 100644 tests/topotests/bgp_color_extcommunities/r2/bgpd.conf create mode 100644 tests/topotests/bgp_color_extcommunities/r2/zebra.conf create mode 100644 tests/topotests/bgp_color_extcommunities/test_bgp_color_extcommunities.py 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)) From 442e2edcfaefa004bc3dff62dc35b03abd34ee71 Mon Sep 17 00:00:00 2001 From: Francois Dumontet Date: Fri, 9 Jun 2023 14:30:33 +0200 Subject: [PATCH 3/4] bgpd: add functions related to srte_color management Signed-off-by: Francois Dumontet --- bgpd/bgp_attr.c | 10 ++++++++++ bgpd/bgp_attr.h | 2 ++ bgpd/bgp_ecommunity.c | 23 +++++++++++++++++++++++ bgpd/bgp_ecommunity.h | 1 + bgpd/bgp_nht.c | 12 ++++++++---- bgpd/bgp_zebra.c | 2 +- 6 files changed, 45 insertions(+), 5 deletions(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index ec9f12d61a..e1b056ff45 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 1c7e88a4f9..cf3e8d7d7f 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 ec860c5a1c..0fea9074c5 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -1420,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 5e67e5015e..31196e9ca9 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -349,6 +349,7 @@ 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 d7b1429881..1d99e5e829 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; @@ -321,7 +322,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 @@ -1238,9 +1242,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_zebra.c b/bgpd/bgp_zebra.c index 1965cd2704..7e773b6cd7 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1440,7 +1440,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) { From fc4478721150af6177a5101bdee8359acfa300d8 Mon Sep 17 00:00:00 2001 From: Francois Dumontet Date: Sat, 10 Jun 2023 18:27:35 +0200 Subject: [PATCH 4/4] doc: extcommunity color Signed-off-by: Francois Dumontet --- doc/user/bgp.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index a2585f3a57..93a1e19413 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2611,6 +2611,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