Merge pull request #13310 from opensourcerouting/feature/bgpd_node_target_extended_community

bgpd: Add Node Target Extended Communities support
This commit is contained in:
Russ White 2023-04-25 11:06:23 -04:00 committed by GitHub
commit 4855ca5e56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 498 additions and 30 deletions

View File

@ -371,6 +371,7 @@ void ecommunity_finish(void)
enum ecommunity_token { enum ecommunity_token {
ecommunity_token_unknown = 0, ecommunity_token_unknown = 0,
ecommunity_token_rt, ecommunity_token_rt,
ecommunity_token_nt,
ecommunity_token_soo, ecommunity_token_soo,
ecommunity_token_val, ecommunity_token_val,
ecommunity_token_rt6, ecommunity_token_rt6,
@ -415,6 +416,53 @@ static void ecommunity_origin_validation_state_str(char *buf, size_t bufsz,
(void)ptr; /* consume value */ (void)ptr; /* consume value */
} }
bool ecommunity_node_target_match(struct ecommunity *ecom,
struct in_addr *local_id)
{
uint32_t i;
bool match = false;
if (!ecom || !ecom->size)
return NULL;
for (i = 0; i < ecom->size; i++) {
const uint8_t *pnt;
uint8_t type, sub_type;
pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
type = *pnt++;
sub_type = *pnt++;
if (type == ECOMMUNITY_ENCODE_IP &&
sub_type == ECOMMUNITY_NODE_TARGET) {
/* Node Target ID is encoded as A.B.C.D:0 */
if (IPV4_ADDR_SAME((struct in_addr *)pnt, local_id))
match = true;
(void)pnt;
}
}
return match;
}
static void ecommunity_node_target_str(char *buf, size_t bufsz, uint8_t *ptr)
{
/*
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | 0x01 or 0x41 | Sub-Type(0x09) | Target BGP Identifier |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Target BGP Identifier (cont.) | Reserved |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
struct in_addr node_id = {};
IPV4_ADDR_COPY(&node_id, (struct in_addr *)ptr);
snprintfrr(buf, bufsz, "NT:%pI4", &node_id);
(void)ptr; /* consume value */
}
static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type, static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type,
int trans, as_t as, int trans, as_t as,
struct in_addr *ip, struct in_addr *ip,
@ -446,28 +494,19 @@ static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type,
eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE; eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
eval->val[1] = sub_type; eval->val[1] = sub_type;
if (type == ECOMMUNITY_ENCODE_AS) { if (type == ECOMMUNITY_ENCODE_AS) {
eval->val[2] = (as >> 8) & 0xff; encode_route_target_as(as, val, eval, trans);
eval->val[3] = as & 0xff;
eval->val[4] = (val >> 24) & 0xff;
eval->val[5] = (val >> 16) & 0xff;
eval->val[6] = (val >> 8) & 0xff;
eval->val[7] = val & 0xff;
} else if (type == ECOMMUNITY_ENCODE_IP) { } else if (type == ECOMMUNITY_ENCODE_IP) {
memcpy(&eval->val[2], ip, sizeof(struct in_addr)); if (sub_type == ECOMMUNITY_NODE_TARGET)
eval->val[6] = (val >> 8) & 0xff; encode_node_target(ip, eval, trans);
eval->val[7] = val & 0xff; else
encode_route_target_ip(ip, val, eval, trans);
} else if (type == ECOMMUNITY_ENCODE_TRANS_EXP && } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP &&
sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) { sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) {
memcpy(&eval6->val[2], ip6, sizeof(struct in6_addr)); memcpy(&eval6->val[2], ip6, sizeof(struct in6_addr));
eval6->val[18] = (val >> 8) & 0xff; eval6->val[18] = (val >> 8) & 0xff;
eval6->val[19] = val & 0xff; eval6->val[19] = val & 0xff;
} else { } else {
eval->val[2] = (as >> 24) & 0xff; encode_route_target_as4(as, val, eval, trans);
eval->val[3] = (as >> 16) & 0xff;
eval->val[4] = (as >> 8) & 0xff;
eval->val[5] = as & 0xff;
eval->val[6] = (val >> 8) & 0xff;
eval->val[7] = val & 0xff;
} }
return 0; return 0;
@ -486,9 +525,8 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
} }
/* Get next Extended Communities token from the string. */ /* Get next Extended Communities token from the string. */
static const char *ecommunity_gettoken(const char *str, static const char *ecommunity_gettoken(const char *str, void *eval_ptr,
void *eval_ptr, enum ecommunity_token *token, int type)
enum ecommunity_token *token)
{ {
int ret; int ret;
int dot = 0; int dot = 0;
@ -515,7 +553,7 @@ static const char *ecommunity_gettoken(const char *str,
if (*p == '\0') if (*p == '\0')
return NULL; return NULL;
/* "rt" and "soo" keyword parse. */ /* "rt", "nt", and "soo" keyword parse. */
if (!isdigit((unsigned char)*p)) { if (!isdigit((unsigned char)*p)) {
/* "rt" match check. */ /* "rt" match check. */
if (tolower((unsigned char)*p) == 'r') { if (tolower((unsigned char)*p) == 'r') {
@ -534,6 +572,20 @@ static const char *ecommunity_gettoken(const char *str,
} }
goto error; goto error;
} }
/* "nt" match check. */
if (tolower((unsigned char)*p) == 'n') {
p++;
if (tolower((unsigned char)*p) == 't') {
p++;
*token = ecommunity_token_nt;
return p;
}
if (isspace((unsigned char)*p) || *p == '\0') {
*token = ecommunity_token_nt;
return p;
}
goto error;
}
/* "soo" match check. */ /* "soo" match check. */
else if (tolower((unsigned char)*p) == 's') { else if (tolower((unsigned char)*p) == 's') {
p++; p++;
@ -679,7 +731,7 @@ static const char *ecommunity_gettoken(const char *str,
ecomm_type = ECOMMUNITY_ENCODE_AS4; ecomm_type = ECOMMUNITY_ENCODE_AS4;
else else
ecomm_type = ECOMMUNITY_ENCODE_AS; ecomm_type = ECOMMUNITY_ENCODE_AS;
if (ecommunity_encode(ecomm_type, 0, 1, as, ip, val, eval)) if (ecommunity_encode(ecomm_type, type, 1, as, ip, val, eval))
goto error; goto error;
*token = ecommunity_token_val; *token = ecommunity_token_val;
return p; return p;
@ -700,9 +752,10 @@ static struct ecommunity *ecommunity_str2com_internal(const char *str, int type,
if (is_ipv6_extcomm) if (is_ipv6_extcomm)
token = ecommunity_token_rt6; token = ecommunity_token_rt6;
while ((str = ecommunity_gettoken(str, (void *)&eval, &token))) { while ((str = ecommunity_gettoken(str, (void *)&eval, &token, type))) {
switch (token) { switch (token) {
case ecommunity_token_rt: case ecommunity_token_rt:
case ecommunity_token_nt:
case ecommunity_token_rt6: case ecommunity_token_rt6:
case ecommunity_token_soo: case ecommunity_token_soo:
if (!keyword_included || keyword) { if (!keyword_included || keyword) {
@ -719,6 +772,9 @@ static struct ecommunity *ecommunity_str2com_internal(const char *str, int type,
if (token == ecommunity_token_soo) { if (token == ecommunity_token_soo) {
type = ECOMMUNITY_SITE_ORIGIN; type = ECOMMUNITY_SITE_ORIGIN;
} }
if (token == ecommunity_token_nt) {
type = ECOMMUNITY_NODE_TARGET;
}
break; break;
case ecommunity_token_val: case ecommunity_token_val:
if (keyword_included) { if (keyword_included) {
@ -1000,6 +1056,10 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
ecommunity_lb_str( ecommunity_lb_str(
encbuf, sizeof(encbuf), pnt, encbuf, sizeof(encbuf), pnt,
ecom->disable_ieee_floating); ecom->disable_ieee_floating);
} else if (sub_type == ECOMMUNITY_NODE_TARGET &&
type == ECOMMUNITY_ENCODE_IP) {
ecommunity_node_target_str(
encbuf, sizeof(encbuf), pnt);
} else } else
unk_ecom = 1; unk_ecom = 1;
} else { } else {
@ -1209,6 +1269,13 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
ecom->disable_ieee_floating); ecom->disable_ieee_floating);
else else
unk_ecom = 1; unk_ecom = 1;
} else if (type == ECOMMUNITY_ENCODE_IP_NON_TRANS) {
sub_type = *pnt++;
if (sub_type == ECOMMUNITY_NODE_TARGET)
ecommunity_node_target_str(encbuf,
sizeof(encbuf), pnt);
else
unk_ecom = 1;
} else if (type == ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS) { } else if (type == ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS) {
sub_type = *pnt++; sub_type = *pnt++;
if (sub_type == ECOMMUNITY_ORIGIN_VALIDATION_STATE) if (sub_type == ECOMMUNITY_ORIGIN_VALIDATION_STATE)

View File

@ -101,6 +101,10 @@ enum ecommunity_origin_validation_states {
/* Extended Community readable string length */ /* Extended Community readable string length */
#define ECOMMUNITY_STRLEN 64 #define ECOMMUNITY_STRLEN 64
/* Node Target Extended Communities */
#define ECOMMUNITY_NODE_TARGET 0x09
#define ECOMMUNITY_NODE_TARGET_RESERVED 0
/* Extended Communities attribute. */ /* Extended Communities attribute. */
struct ecommunity { struct ecommunity {
/* Reference counter. */ /* Reference counter. */
@ -155,9 +159,12 @@ struct ecommunity_val_ipv6 {
* Encode BGP Route Target AS:nn. * Encode BGP Route Target AS:nn.
*/ */
static inline void encode_route_target_as(as_t as, uint32_t val, static inline void encode_route_target_as(as_t as, uint32_t val,
struct ecommunity_val *eval) struct ecommunity_val *eval,
bool trans)
{ {
eval->val[0] = ECOMMUNITY_ENCODE_AS; eval->val[0] = ECOMMUNITY_ENCODE_AS;
if (!trans)
eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
eval->val[1] = ECOMMUNITY_ROUTE_TARGET; eval->val[1] = ECOMMUNITY_ROUTE_TARGET;
eval->val[2] = (as >> 8) & 0xff; eval->val[2] = (as >> 8) & 0xff;
eval->val[3] = as & 0xff; eval->val[3] = as & 0xff;
@ -170,12 +177,15 @@ static inline void encode_route_target_as(as_t as, uint32_t val,
/* /*
* Encode BGP Route Target IP:nn. * Encode BGP Route Target IP:nn.
*/ */
static inline void encode_route_target_ip(struct in_addr ip, uint16_t val, static inline void encode_route_target_ip(struct in_addr *ip, uint16_t val,
struct ecommunity_val *eval) struct ecommunity_val *eval,
bool trans)
{ {
eval->val[0] = ECOMMUNITY_ENCODE_IP; eval->val[0] = ECOMMUNITY_ENCODE_IP;
if (!trans)
eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
eval->val[1] = ECOMMUNITY_ROUTE_TARGET; eval->val[1] = ECOMMUNITY_ROUTE_TARGET;
memcpy(&eval->val[2], &ip, sizeof(struct in_addr)); memcpy(&eval->val[2], ip, sizeof(struct in_addr));
eval->val[6] = (val >> 8) & 0xff; eval->val[6] = (val >> 8) & 0xff;
eval->val[7] = val & 0xff; eval->val[7] = val & 0xff;
} }
@ -184,9 +194,12 @@ static inline void encode_route_target_ip(struct in_addr ip, uint16_t val,
* Encode BGP Route Target AS4:nn. * Encode BGP Route Target AS4:nn.
*/ */
static inline void encode_route_target_as4(as_t as, uint16_t val, static inline void encode_route_target_as4(as_t as, uint16_t val,
struct ecommunity_val *eval) struct ecommunity_val *eval,
bool trans)
{ {
eval->val[0] = ECOMMUNITY_ENCODE_AS4; eval->val[0] = ECOMMUNITY_ENCODE_AS4;
if (!trans)
eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
eval->val[1] = ECOMMUNITY_ROUTE_TARGET; eval->val[1] = ECOMMUNITY_ROUTE_TARGET;
eval->val[2] = (as >> 24) & 0xff; eval->val[2] = (as >> 24) & 0xff;
eval->val[3] = (as >> 16) & 0xff; eval->val[3] = (as >> 16) & 0xff;
@ -257,6 +270,26 @@ static inline void encode_origin_validation_state(enum rpki_states state,
eval->val[7] = ovs_state; eval->val[7] = ovs_state;
} }
static inline void encode_node_target(struct in_addr *node_id,
struct ecommunity_val *eval, bool trans)
{
/*
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | 0x01 or 0x41 | Sub-Type(0x09) | Target BGP Identifier |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Target BGP Identifier (cont.) | Reserved |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
memset(eval, 0, sizeof(*eval));
eval->val[0] = ECOMMUNITY_ENCODE_IP;
if (!trans)
eval->val[0] |= ECOMMUNITY_ENCODE_IP_NON_TRANS;
eval->val[1] = ECOMMUNITY_NODE_TARGET;
memcpy(&eval->val[2], node_id, sizeof(*node_id));
eval->val[6] = ECOMMUNITY_NODE_TARGET_RESERVED;
eval->val[7] = ECOMMUNITY_NODE_TARGET_RESERVED;
}
extern void ecommunity_init(void); extern void ecommunity_init(void);
extern void ecommunity_finish(void); extern void ecommunity_finish(void);
extern void ecommunity_free(struct ecommunity **); extern void ecommunity_free(struct ecommunity **);
@ -338,4 +371,9 @@ static inline void ecommunity_strip_rts(struct ecommunity *ecom)
extern struct ecommunity * extern struct ecommunity *
ecommunity_add_origin_validation_state(enum rpki_states rpki_state, ecommunity_add_origin_validation_state(enum rpki_states rpki_state,
struct ecommunity *ecom); struct ecommunity *ecom);
extern struct ecommunity *ecommunity_add_node_target(struct in_addr *node_id,
struct ecommunity *old,
bool non_trans);
extern bool ecommunity_node_target_match(struct ecommunity *ecomm,
struct in_addr *local_id);
#endif /* _QUAGGA_BGP_ECOMMUNITY_H */ #endif /* _QUAGGA_BGP_ECOMMUNITY_H */

View File

@ -577,7 +577,7 @@ static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl,
if (bgp->advertise_autort_rfc8365) if (bgp->advertise_autort_rfc8365)
vni |= EVPN_AUTORT_VXLAN; vni |= EVPN_AUTORT_VXLAN;
encode_route_target_as((bgp->as & 0xFFFF), vni, &eval); encode_route_target_as((bgp->as & 0xFFFF), vni, &eval, true);
ecomadd = ecommunity_new(); ecomadd = ecommunity_new();
ecommunity_add_val(ecomadd, &eval, false, false); ecommunity_add_val(ecomadd, &eval, false, false);
@ -5163,7 +5163,7 @@ void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl,
if (bgp->advertise_autort_rfc8365) if (bgp->advertise_autort_rfc8365)
vni |= EVPN_AUTORT_VXLAN; vni |= EVPN_AUTORT_VXLAN;
encode_route_target_as((bgp->as & 0xFFFF), vni, &eval); encode_route_target_as((bgp->as & 0xFFFF), vni, &eval, true);
ecom_auto = ecommunity_new(); ecom_auto = ecommunity_new();
ecommunity_add_val(ecom_auto, &eval, false, false); ecommunity_add_val(ecom_auto, &eval, false, false);

View File

@ -4173,6 +4173,21 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
goto filtered; goto filtered;
} }
/* If the route has Node Target Extended Communities, check
* if it's allowed to be installed locally.
*/
if ((attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) {
struct ecommunity *ecomm = bgp_attr_get_ecommunity(attr);
if (ecommunity_lookup(ecomm, ECOMMUNITY_ENCODE_IP,
ECOMMUNITY_NODE_TARGET) &&
!ecommunity_node_target_match(ecomm, &peer->local_id)) {
reason =
"Node-Target Extended Communities do not contain own BGP Identifier;";
goto filtered;
}
}
/* RFC 8212 to prevent route leaks. /* RFC 8212 to prevent route leaks.
* This specification intends to improve this situation by requiring the * This specification intends to improve this situation by requiring the
* explicit configuration of both BGP Import and Export Policies for any * explicit configuration of both BGP Import and Export Policies for any

View File

@ -2867,6 +2867,29 @@ static const struct route_map_rule_cmd route_set_ecommunity_soo_cmd = {
route_set_ecommunity_free, route_set_ecommunity_free,
}; };
static void *route_set_ecommunity_nt_compile(const char *arg)
{
struct rmap_ecom_set *rcs;
struct ecommunity *ecom;
ecom = ecommunity_str2com(arg, ECOMMUNITY_NODE_TARGET, 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_nt_cmd = {
"extcommunity nt",
route_set_ecommunity,
route_set_ecommunity_nt_compile,
route_set_ecommunity_free,
};
/* `set extcommunity bandwidth' */ /* `set extcommunity bandwidth' */
struct rmap_ecomm_lb_set { struct rmap_ecomm_lb_set {
@ -6418,6 +6441,55 @@ ALIAS_YANG (no_set_ecommunity_lb,
"BGP extended community attribute\n" "BGP extended community attribute\n"
"Link bandwidth extended community\n") "Link bandwidth extended community\n")
DEFPY_YANG (set_ecommunity_nt,
set_ecommunity_nt_cmd,
"set extcommunity nt RTLIST...",
SET_STR
"BGP extended community attribute\n"
"Node Target extended community\n"
"Node Target ID\n")
{
int idx_nt = 3;
char *str;
int ret;
const char *xpath =
"./set-action[action='frr-bgp-route-map:set-extcommunity-nt']";
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-nt", xpath);
str = argv_concat(argv, argc, idx_nt);
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_nt,
no_set_ecommunity_nt_cmd,
"no set extcommunity nt RTLIST...",
NO_STR
SET_STR
"BGP extended community attribute\n"
"Node Target extended community\n"
"Node Target ID\n")
{
const char *xpath =
"./set-action[action='frr-bgp-route-map:set-extcommunity-nt']";
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",
NO_STR
SET_STR
"BGP extended community attribute\n"
"Node Target extended community\n")
DEFUN_YANG (set_origin, DEFUN_YANG (set_origin,
set_origin_cmd, set_origin_cmd,
"set origin <egp|igp|incomplete>", "set origin <egp|igp|incomplete>",
@ -7223,6 +7295,7 @@ void bgp_route_map_init(void)
route_map_install_set(&route_set_vpnv6_nexthop_cmd); route_map_install_set(&route_set_vpnv6_nexthop_cmd);
route_map_install_set(&route_set_originator_id_cmd); route_map_install_set(&route_set_originator_id_cmd);
route_map_install_set(&route_set_ecommunity_rt_cmd); route_map_install_set(&route_set_ecommunity_rt_cmd);
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_soo_cmd);
route_map_install_set(&route_set_ecommunity_lb_cmd); route_map_install_set(&route_set_ecommunity_lb_cmd);
route_map_install_set(&route_set_ecommunity_none_cmd); route_map_install_set(&route_set_ecommunity_none_cmd);
@ -7325,6 +7398,9 @@ void bgp_route_map_init(void)
install_element(RMAP_NODE, &no_set_ecommunity_lb_short_cmd); install_element(RMAP_NODE, &no_set_ecommunity_lb_short_cmd);
install_element(RMAP_NODE, &set_ecommunity_none_cmd); install_element(RMAP_NODE, &set_ecommunity_none_cmd);
install_element(RMAP_NODE, &no_set_ecommunity_none_cmd); install_element(RMAP_NODE, &no_set_ecommunity_none_cmd);
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);
#ifdef KEEP_OLD_VPN_COMMANDS #ifdef KEEP_OLD_VPN_COMMANDS
install_element(RMAP_NODE, &set_vpn_nexthop_cmd); install_element(RMAP_NODE, &set_vpn_nexthop_cmd);
install_element(RMAP_NODE, &no_set_vpn_nexthop_cmd); install_element(RMAP_NODE, &no_set_vpn_nexthop_cmd);

View File

@ -185,6 +185,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = {
.destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy, .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy,
} }
}, },
{
.xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-nt",
.cbs = {
.modify = lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_modify,
.destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_destroy,
}
},
{ {
.xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-soo", .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-soo",
.cbs = { .cbs = {

View File

@ -69,6 +69,10 @@ int lib_route_map_entry_set_action_rmap_set_action_distance_modify(struct nb_cb_
int lib_route_map_entry_set_action_rmap_set_action_distance_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_set_action_rmap_set_action_distance_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_modify(struct nb_cb_modify_args *args);
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_modify(
struct nb_cb_modify_args *args);
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_destroy(
struct nb_cb_destroy_args *args);
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_modify(struct nb_cb_modify_args *args);
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_set_action_rmap_set_action_ipv4_address_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_set_action_rmap_set_action_ipv4_address_modify(struct nb_cb_modify_args *args);

View File

@ -1413,6 +1413,58 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy(
return NB_OK; return NB_OK;
} }
/*
* XPath:
* /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-nt
*/
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_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 nt";
rhc->rhc_event = RMAP_EVENT_SET_DELETED;
rv = generic_set_add(rhc->rhc_rmi, "extcommunity nt", 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_nt_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: * XPath:
* /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-soo * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-soo

View File

@ -2584,11 +2584,19 @@ BGP Extended Communities in Route Map
.. clicmd:: set extcommunity rt EXTCOMMUNITY .. clicmd:: set extcommunity rt EXTCOMMUNITY
This command set Route Target value. This command sets Route Target value.
.. clicmd:: set extcommunity nt EXTCOMMUNITY
This command sets Node Target value.
If the receiving BGP router supports Node Target Extended Communities,
it will install the route with the community that contains it's own
local BGP Identifier. Otherwise, it's not installed.
.. clicmd:: set extcommunity soo EXTCOMMUNITY .. clicmd:: set extcommunity soo EXTCOMMUNITY
This command set Site of Origin value. This command sets Site of Origin value.
.. clicmd:: set extcommunity bandwidth <(1-25600) | cumulative | num-multipaths> [non-transitive] .. clicmd:: set extcommunity bandwidth <(1-25600) | cumulative | num-multipaths> [non-transitive]

View File

@ -354,6 +354,8 @@ DECLARE_QOBJ_TYPE(route_map);
(strmatch(A, "frr-bgp-route-map:set-extcommunity-none")) (strmatch(A, "frr-bgp-route-map:set-extcommunity-none"))
#define IS_SET_EXTCOMMUNITY_RT(A) \ #define IS_SET_EXTCOMMUNITY_RT(A) \
(strmatch(A, "frr-bgp-route-map:set-extcommunity-rt")) (strmatch(A, "frr-bgp-route-map:set-extcommunity-rt"))
#define IS_SET_EXTCOMMUNITY_NT(A) \
(strmatch(A, "frr-bgp-route-map:set-extcommunity-nt"))
#define IS_SET_EXTCOMMUNITY_SOO(A) \ #define IS_SET_EXTCOMMUNITY_SOO(A) \
(strmatch(A, "frr-bgp-route-map:set-extcommunity-soo")) (strmatch(A, "frr-bgp-route-map:set-extcommunity-soo"))
#define IS_SET_EXTCOMMUNITY_LB(A) \ #define IS_SET_EXTCOMMUNITY_LB(A) \

View File

@ -1218,6 +1218,11 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *dnode,
yang_dnode_get_string( yang_dnode_get_string(
dnode, dnode,
"./rmap-set-action/frr-bgp-route-map:extcommunity-rt")); "./rmap-set-action/frr-bgp-route-map:extcommunity-rt"));
} else if (IS_SET_EXTCOMMUNITY_NT(action)) {
vty_out(vty, " set extcommunity nt %s\n",
yang_dnode_get_string(
dnode,
"./rmap-set-action/frr-bgp-route-map:extcommunity-nt"));
} else if (IS_SET_EXTCOMMUNITY_SOO(action)) { } else if (IS_SET_EXTCOMMUNITY_SOO(action)) {
vty_out(vty, " set extcommunity soo %s\n", vty_out(vty, " set extcommunity soo %s\n",
yang_dnode_get_string( yang_dnode_get_string(

View File

@ -0,0 +1,21 @@
!
int r1-eth0
ip address 192.168.1.1/24
!
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/32
neighbor 192.168.1.2 route-map rmap out
neighbor 192.168.1.3 route-map rmap out
neighbor 192.168.1.4 route-map rmap out
exit-address-family
!
route-map rmap permit 10
set extcommunity nt 192.168.1.3:0 192.168.1.4:0
exit

View File

@ -0,0 +1,8 @@
!
int r2-eth0
ip address 192.168.1.2/24
!
router bgp 65002
bgp router-id 192.168.1.2
no bgp ebgp-requires-policy
neighbor 192.168.1.1 remote-as external

View File

@ -0,0 +1,8 @@
!
int r3-eth0
ip address 192.168.1.3/24
!
router bgp 65003
bgp router-id 192.168.1.3
no bgp ebgp-requires-policy
neighbor 192.168.1.1 remote-as external

View File

@ -0,0 +1,8 @@
!
int r4-eth0
ip address 192.168.1.4/24
!
router bgp 65004
bgp router-id 192.168.1.4
no bgp ebgp-requires-policy
neighbor 192.168.1.1 remote-as external

View File

@ -0,0 +1,132 @@
#!/usr/bin/env python
# SPDX-License-Identifier: ISC
# Copyright (c) 2023 by
# Donatas Abraitis <donatas@opensourcerouting.org>
#
"""
Test if Node Target Extended Communities works.
At r1 we set NT to 192.168.1.3 and 192.168.1.4 (this is the R3/R4 router-id),
and that means 10.10.10.10/32 MUST be installed on R3 and R4, but not on R2,
because this route does not have NT:192.168.1.2.
"""
import os
import sys
import json
import pytest
import functools
CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(CWD, "../"))
# pylint: disable=C0413
from lib import topotest
from lib.topogen import Topogen, TopoRouter, get_topogen
pytestmark = [pytest.mark.bgpd]
def build_topo(tgen):
for routern in range(1, 5):
tgen.add_router("r{}".format(routern))
switch = tgen.add_switch("s1")
switch.add_link(tgen.gears["r1"])
switch.add_link(tgen.gears["r2"])
switch.add_link(tgen.gears["r3"])
switch.add_link(tgen.gears["r4"])
def setup_module(mod):
tgen = Topogen(build_topo, mod.__name__)
tgen.start_topology()
router_list = tgen.routers()
for i, (rname, router) in enumerate(router_list.items(), 1):
router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
tgen.start_router()
def teardown_module(mod):
tgen = get_topogen()
tgen.stop_topology()
def test_bgp_node_target_extended_communities():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
r1 = tgen.gears["r1"]
r2 = tgen.gears["r2"]
r3 = tgen.gears["r3"]
r4 = tgen.gears["r4"]
def _bgp_converge():
output = json.loads(r1.vtysh_cmd("show bgp summary json"))
expected = {
"ipv4Unicast": {
"peers": {
"192.168.1.2": {
"pfxSnt": 1,
"state": "Established",
},
"192.168.1.3": {
"pfxSnt": 1,
"state": "Established",
},
"192.168.1.4": {
"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, r3, and r4"
def _bgp_check_route(router, exists):
output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json"))
if exists:
expected = {
"routes": {
"10.10.10.10/32": [
{
"valid": True,
}
]
}
}
else:
expected = {
"routes": {
"10.10.10.10/32": None,
}
}
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_check_route, r3, True)
_, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
assert result is None, "10.10.10.10/32 is not installed, but SHOULD be"
test_func = functools.partial(_bgp_check_route, r4, True)
_, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
assert result is None, "10.10.10.10/32 is not installed, but SHOULD be"
test_func = functools.partial(_bgp_check_route, r2, False)
_, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
assert result is None, "10.10.10.10/32 is installed, but SHOULD NOT be"
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))

View File

@ -186,6 +186,12 @@ module frr-bgp-route-map {
"Set BGP extended community attribute"; "Set BGP extended community attribute";
} }
identity set-extcommunity-nt {
base frr-route-map:rmap-set-type;
description
"Set BGP extended community attribute";
}
identity set-extcommunity-soo { identity set-extcommunity-soo {
base frr-route-map:rmap-set-type; base frr-route-map:rmap-set-type;
description description
@ -786,6 +792,17 @@ module frr-bgp-route-map {
} }
} }
case extcommunity-nt {
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-nt')";
description
"Value of the ext-community";
leaf extcommunity-nt {
type string;
description
"Set BGP ext-community node-target attribute";
}
}
case extcommunity-soo { case extcommunity-soo {
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-soo')"; 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-soo')";
description description