Skip to content

Commit c9a2561

Browse files
committed
bgpd: Implement Node Target Extended Communities
kttps://datatracker.ietf.org/doc/html/draft-ietf-idr-node-target-ext-comm unet> sh r1 vtysh -c 'sh ip bgp nei 192.168.1.2 adver' BGP table version is 1, local router ID is 192.168.1.1, vrf id 0 Default local pref 100, local AS 65001 Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path *> 10.10.10.10/32 0.0.0.0 0 32768 i Total number of prefixes 1 unet> sh r1 vtysh -c 'sh ip bgp nei 192.168.1.3 adver' BGP table version is 1, local router ID is 192.168.1.1, vrf id 0 Default local pref 100, local AS 65001 Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path *> 10.10.10.10/32 0.0.0.0 0 32768 i Total number of prefixes 1 unet> sh r2 vtysh -c 'show ip bgp 10.10.10.10/32' % Network not in table unet> sh r3 vtysh -c 'show ip bgp 10.10.10.10/32' BGP routing table entry for 10.10.10.10/32, version 1 Paths: (1 available, best #1, table default) Advertised to non peer-group peers: 192.168.1.1 65001 192.168.1.1 from 192.168.1.1 (192.168.1.1) Origin IGP, metric 0, valid, external, best (First path received) Extended Community: NT:192.168.1.3 NT:192.168.1.4 Last update: Tue Apr 11 23:19:33 2023 unet> Signed-off-by: Donatas Abraitis <[email protected]>
1 parent 068c4df commit c9a2561

10 files changed

+296
-9
lines changed

bgpd/bgp_ecommunity.c

+89-9
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ void ecommunity_finish(void)
371371
enum ecommunity_token {
372372
ecommunity_token_unknown = 0,
373373
ecommunity_token_rt,
374+
ecommunity_token_nt,
374375
ecommunity_token_soo,
375376
ecommunity_token_val,
376377
ecommunity_token_rt6,
@@ -415,6 +416,53 @@ static void ecommunity_origin_validation_state_str(char *buf, size_t bufsz,
415416
(void)ptr; /* consume value */
416417
}
417418

419+
bool ecommunity_node_target_match(struct ecommunity *ecom,
420+
struct in_addr *local_id)
421+
{
422+
uint32_t i;
423+
bool match = false;
424+
425+
if (!ecom || !ecom->size)
426+
return NULL;
427+
428+
for (i = 0; i < ecom->size; i++) {
429+
const uint8_t *pnt;
430+
uint8_t type, sub_type;
431+
432+
pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
433+
type = *pnt++;
434+
sub_type = *pnt++;
435+
436+
if (type == ECOMMUNITY_ENCODE_IP &&
437+
sub_type == ECOMMUNITY_NODE_TARGET) {
438+
/* Node Target ID is encoded as A.B.C.D:0 */
439+
if (IPV4_ADDR_SAME((struct in_addr *)pnt, local_id))
440+
match = true;
441+
(void)pnt;
442+
}
443+
}
444+
445+
return match;
446+
}
447+
448+
static void ecommunity_node_target_str(char *buf, size_t bufsz, uint8_t *ptr)
449+
{
450+
/*
451+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
452+
* | 0x01 or 0x41 | Sub-Type(0x09) | Target BGP Identifier |
453+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
454+
* | Target BGP Identifier (cont.) | Reserved |
455+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
456+
*/
457+
struct in_addr node_id = {};
458+
459+
IPV4_ADDR_COPY(&node_id, (struct in_addr *)ptr);
460+
461+
snprintfrr(buf, bufsz, "NT:%pI4", &node_id);
462+
463+
(void)ptr; /* consume value */
464+
}
465+
418466
static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type,
419467
int trans, as_t as,
420468
struct in_addr *ip,
@@ -453,9 +501,13 @@ static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type,
453501
eval->val[6] = (val >> 8) & 0xff;
454502
eval->val[7] = val & 0xff;
455503
} else if (type == ECOMMUNITY_ENCODE_IP) {
456-
memcpy(&eval->val[2], ip, sizeof(struct in_addr));
457-
eval->val[6] = (val >> 8) & 0xff;
458-
eval->val[7] = val & 0xff;
504+
if (sub_type == ECOMMUNITY_NODE_TARGET) {
505+
encode_node_target(ip, eval, trans);
506+
} else {
507+
memcpy(&eval->val[2], ip, sizeof(struct in_addr));
508+
eval->val[6] = (val >> 8) & 0xff;
509+
eval->val[7] = val & 0xff;
510+
}
459511
} else if (type == ECOMMUNITY_ENCODE_TRANS_EXP &&
460512
sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) {
461513
memcpy(&eval6->val[2], ip6, sizeof(struct in6_addr));
@@ -486,9 +538,8 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
486538
}
487539

488540
/* Get next Extended Communities token from the string. */
489-
static const char *ecommunity_gettoken(const char *str,
490-
void *eval_ptr,
491-
enum ecommunity_token *token)
541+
static const char *ecommunity_gettoken(const char *str, void *eval_ptr,
542+
enum ecommunity_token *token, int type)
492543
{
493544
int ret;
494545
int dot = 0;
@@ -515,7 +566,7 @@ static const char *ecommunity_gettoken(const char *str,
515566
if (*p == '\0')
516567
return NULL;
517568

518-
/* "rt" and "soo" keyword parse. */
569+
/* "rt", "nt", and "soo" keyword parse. */
519570
if (!isdigit((unsigned char)*p)) {
520571
/* "rt" match check. */
521572
if (tolower((unsigned char)*p) == 'r') {
@@ -534,6 +585,20 @@ static const char *ecommunity_gettoken(const char *str,
534585
}
535586
goto error;
536587
}
588+
/* "nt" match check. */
589+
if (tolower((unsigned char)*p) == 'n') {
590+
p++;
591+
if (tolower((unsigned char)*p) == 't') {
592+
p++;
593+
*token = ecommunity_token_nt;
594+
return p;
595+
}
596+
if (isspace((unsigned char)*p) || *p == '\0') {
597+
*token = ecommunity_token_nt;
598+
return p;
599+
}
600+
goto error;
601+
}
537602
/* "soo" match check. */
538603
else if (tolower((unsigned char)*p) == 's') {
539604
p++;
@@ -679,7 +744,7 @@ static const char *ecommunity_gettoken(const char *str,
679744
ecomm_type = ECOMMUNITY_ENCODE_AS4;
680745
else
681746
ecomm_type = ECOMMUNITY_ENCODE_AS;
682-
if (ecommunity_encode(ecomm_type, 0, 1, as, ip, val, eval))
747+
if (ecommunity_encode(ecomm_type, type, 1, as, ip, val, eval))
683748
goto error;
684749
*token = ecommunity_token_val;
685750
return p;
@@ -700,9 +765,10 @@ static struct ecommunity *ecommunity_str2com_internal(const char *str, int type,
700765

701766
if (is_ipv6_extcomm)
702767
token = ecommunity_token_rt6;
703-
while ((str = ecommunity_gettoken(str, (void *)&eval, &token))) {
768+
while ((str = ecommunity_gettoken(str, (void *)&eval, &token, type))) {
704769
switch (token) {
705770
case ecommunity_token_rt:
771+
case ecommunity_token_nt:
706772
case ecommunity_token_rt6:
707773
case ecommunity_token_soo:
708774
if (!keyword_included || keyword) {
@@ -719,6 +785,9 @@ static struct ecommunity *ecommunity_str2com_internal(const char *str, int type,
719785
if (token == ecommunity_token_soo) {
720786
type = ECOMMUNITY_SITE_ORIGIN;
721787
}
788+
if (token == ecommunity_token_nt) {
789+
type = ECOMMUNITY_NODE_TARGET;
790+
}
722791
break;
723792
case ecommunity_token_val:
724793
if (keyword_included) {
@@ -1000,6 +1069,10 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
10001069
ecommunity_lb_str(
10011070
encbuf, sizeof(encbuf), pnt,
10021071
ecom->disable_ieee_floating);
1072+
} else if (sub_type == ECOMMUNITY_NODE_TARGET &&
1073+
type == ECOMMUNITY_ENCODE_IP) {
1074+
ecommunity_node_target_str(
1075+
encbuf, sizeof(encbuf), pnt);
10031076
} else
10041077
unk_ecom = 1;
10051078
} else {
@@ -1209,6 +1282,13 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
12091282
ecom->disable_ieee_floating);
12101283
else
12111284
unk_ecom = 1;
1285+
} else if (type == ECOMMUNITY_ENCODE_IP_NON_TRANS) {
1286+
sub_type = *pnt++;
1287+
if (sub_type == ECOMMUNITY_NODE_TARGET)
1288+
ecommunity_node_target_str(encbuf,
1289+
sizeof(encbuf), pnt);
1290+
else
1291+
unk_ecom = 1;
12121292
} else if (type == ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS) {
12131293
sub_type = *pnt++;
12141294
if (sub_type == ECOMMUNITY_ORIGIN_VALIDATION_STATE)

bgpd/bgp_ecommunity.h

+29
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ enum ecommunity_origin_validation_states {
101101
/* Extended Community readable string length */
102102
#define ECOMMUNITY_STRLEN 64
103103

104+
/* Node Target Extended Communities */
105+
#define ECOMMUNITY_NODE_TARGET 0x09
106+
#define ECOMMUNITY_NODE_TARGET_RESERVED 0
107+
104108
/* Extended Communities attribute. */
105109
struct ecommunity {
106110
/* Reference counter. */
@@ -257,6 +261,26 @@ static inline void encode_origin_validation_state(enum rpki_states state,
257261
eval->val[7] = ovs_state;
258262
}
259263

264+
static inline void encode_node_target(struct in_addr *node_id,
265+
struct ecommunity_val *eval, bool trans)
266+
{
267+
/*
268+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
269+
* | 0x01 or 0x41 | Sub-Type(0x09) | Target BGP Identifier |
270+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
271+
* | Target BGP Identifier (cont.) | Reserved |
272+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
273+
*/
274+
memset(eval, 0, sizeof(*eval));
275+
eval->val[0] = ECOMMUNITY_ENCODE_IP;
276+
if (!trans)
277+
eval->val[0] |= ECOMMUNITY_ENCODE_IP_NON_TRANS;
278+
eval->val[1] = ECOMMUNITY_NODE_TARGET;
279+
memcpy(&eval->val[2], node_id, sizeof(*node_id));
280+
eval->val[6] = ECOMMUNITY_NODE_TARGET_RESERVED;
281+
eval->val[7] = ECOMMUNITY_NODE_TARGET_RESERVED;
282+
}
283+
260284
extern void ecommunity_init(void);
261285
extern void ecommunity_finish(void);
262286
extern void ecommunity_free(struct ecommunity **);
@@ -338,4 +362,9 @@ static inline void ecommunity_strip_rts(struct ecommunity *ecom)
338362
extern struct ecommunity *
339363
ecommunity_add_origin_validation_state(enum rpki_states rpki_state,
340364
struct ecommunity *ecom);
365+
extern struct ecommunity *ecommunity_add_node_target(struct in_addr *node_id,
366+
struct ecommunity *old,
367+
bool non_trans);
368+
extern bool ecommunity_node_target_match(struct ecommunity *ecomm,
369+
struct in_addr *local_id);
341370
#endif /* _QUAGGA_BGP_ECOMMUNITY_H */

bgpd/bgp_route.c

+15
Original file line numberDiff line numberDiff line change
@@ -4154,6 +4154,21 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
41544154
goto filtered;
41554155
}
41564156

4157+
/* If the route has Node Target Extended Communities, check
4158+
* if it's allowed to be installed locally.
4159+
*/
4160+
if ((attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) {
4161+
struct ecommunity *ecomm = bgp_attr_get_ecommunity(attr);
4162+
4163+
if (ecommunity_lookup(ecomm, ECOMMUNITY_ENCODE_IP,
4164+
ECOMMUNITY_NODE_TARGET) &&
4165+
!ecommunity_node_target_match(ecomm, &peer->local_id)) {
4166+
reason =
4167+
"Node-Target Extended Communities do not contain own BGP Identifier;";
4168+
goto filtered;
4169+
}
4170+
}
4171+
41574172
/* RFC 8212 to prevent route leaks.
41584173
* This specification intends to improve this situation by requiring the
41594174
* explicit configuration of both BGP Import and Export Policies for any

bgpd/bgp_routemap.c

+76
Original file line numberDiff line numberDiff line change
@@ -2867,6 +2867,29 @@ static const struct route_map_rule_cmd route_set_ecommunity_soo_cmd = {
28672867
route_set_ecommunity_free,
28682868
};
28692869

2870+
static void *route_set_ecommunity_nt_compile(const char *arg)
2871+
{
2872+
struct rmap_ecom_set *rcs;
2873+
struct ecommunity *ecom;
2874+
2875+
ecom = ecommunity_str2com(arg, ECOMMUNITY_NODE_TARGET, 0);
2876+
if (!ecom)
2877+
return NULL;
2878+
2879+
rcs = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_ecom_set));
2880+
rcs->ecom = ecommunity_intern(ecom);
2881+
rcs->none = false;
2882+
2883+
return rcs;
2884+
}
2885+
2886+
static const struct route_map_rule_cmd route_set_ecommunity_nt_cmd = {
2887+
"extcommunity nt",
2888+
route_set_ecommunity,
2889+
route_set_ecommunity_nt_compile,
2890+
route_set_ecommunity_free,
2891+
};
2892+
28702893
/* `set extcommunity bandwidth' */
28712894

28722895
struct rmap_ecomm_lb_set {
@@ -6418,6 +6441,55 @@ ALIAS_YANG (no_set_ecommunity_lb,
64186441
"BGP extended community attribute\n"
64196442
"Link bandwidth extended community\n")
64206443

6444+
DEFPY_YANG (set_ecommunity_nt,
6445+
set_ecommunity_nt_cmd,
6446+
"set extcommunity nt RTLIST...",
6447+
SET_STR
6448+
"BGP extended community attribute\n"
6449+
"Node Target extended community\n"
6450+
"Node Target ID\n")
6451+
{
6452+
int idx_nt = 3;
6453+
char *str;
6454+
int ret;
6455+
const char *xpath =
6456+
"./set-action[action='frr-bgp-route-map:set-extcommunity-nt']";
6457+
char xpath_value[XPATH_MAXLEN];
6458+
6459+
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
6460+
6461+
snprintf(xpath_value, sizeof(xpath_value),
6462+
"%s/rmap-set-action/frr-bgp-route-map:extcommunity-nt", xpath);
6463+
str = argv_concat(argv, argc, idx_nt);
6464+
nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, str);
6465+
ret = nb_cli_apply_changes(vty, NULL);
6466+
XFREE(MTYPE_TMP, str);
6467+
return ret;
6468+
}
6469+
6470+
DEFPY_YANG (no_set_ecommunity_nt,
6471+
no_set_ecommunity_nt_cmd,
6472+
"no set extcommunity nt RTLIST...",
6473+
NO_STR
6474+
SET_STR
6475+
"BGP extended community attribute\n"
6476+
"Node Target extended community\n"
6477+
"Node Target ID\n")
6478+
{
6479+
const char *xpath =
6480+
"./set-action[action='frr-bgp-route-map:set-extcommunity-nt']";
6481+
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
6482+
return nb_cli_apply_changes(vty, NULL);
6483+
}
6484+
6485+
ALIAS_YANG (no_set_ecommunity_nt,
6486+
no_set_ecommunity_nt_short_cmd,
6487+
"no set extcommunity nt",
6488+
NO_STR
6489+
SET_STR
6490+
"BGP extended community attribute\n"
6491+
"Node Target extended community\n")
6492+
64216493
DEFUN_YANG (set_origin,
64226494
set_origin_cmd,
64236495
"set origin <egp|igp|incomplete>",
@@ -7223,6 +7295,7 @@ void bgp_route_map_init(void)
72237295
route_map_install_set(&route_set_vpnv6_nexthop_cmd);
72247296
route_map_install_set(&route_set_originator_id_cmd);
72257297
route_map_install_set(&route_set_ecommunity_rt_cmd);
7298+
route_map_install_set(&route_set_ecommunity_nt_cmd);
72267299
route_map_install_set(&route_set_ecommunity_soo_cmd);
72277300
route_map_install_set(&route_set_ecommunity_lb_cmd);
72287301
route_map_install_set(&route_set_ecommunity_none_cmd);
@@ -7325,6 +7398,9 @@ void bgp_route_map_init(void)
73257398
install_element(RMAP_NODE, &no_set_ecommunity_lb_short_cmd);
73267399
install_element(RMAP_NODE, &set_ecommunity_none_cmd);
73277400
install_element(RMAP_NODE, &no_set_ecommunity_none_cmd);
7401+
install_element(RMAP_NODE, &set_ecommunity_nt_cmd);
7402+
install_element(RMAP_NODE, &no_set_ecommunity_nt_cmd);
7403+
install_element(RMAP_NODE, &no_set_ecommunity_nt_short_cmd);
73287404
#ifdef KEEP_OLD_VPN_COMMANDS
73297405
install_element(RMAP_NODE, &set_vpn_nexthop_cmd);
73307406
install_element(RMAP_NODE, &no_set_vpn_nexthop_cmd);

bgpd/bgp_routemap_nb.c

+7
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = {
185185
.destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy,
186186
}
187187
},
188+
{
189+
.xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-nt",
190+
.cbs = {
191+
.modify = lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_modify,
192+
.destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_destroy,
193+
}
194+
},
188195
{
189196
.xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-soo",
190197
.cbs = {

bgpd/bgp_routemap_nb.h

+4
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ int lib_route_map_entry_set_action_rmap_set_action_distance_modify(struct nb_cb_
6969
int lib_route_map_entry_set_action_rmap_set_action_distance_destroy(struct nb_cb_destroy_args *args);
7070
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_modify(struct nb_cb_modify_args *args);
7171
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy(struct nb_cb_destroy_args *args);
72+
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_modify(
73+
struct nb_cb_modify_args *args);
74+
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_destroy(
75+
struct nb_cb_destroy_args *args);
7276
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_modify(struct nb_cb_modify_args *args);
7377
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_destroy(struct nb_cb_destroy_args *args);
7478
int lib_route_map_entry_set_action_rmap_set_action_ipv4_address_modify(struct nb_cb_modify_args *args);

0 commit comments

Comments
 (0)