From 8ef1590927d209f42f7a4a6f150af06d75bf0b2d Mon Sep 17 00:00:00 2001 From: Donatas Abraitis Date: Fri, 7 Apr 2023 10:37:47 +0300 Subject: [PATCH 1/4] doc: Add `set extcommunity nt` command for BGP route-maps Signed-off-by: Donatas Abraitis --- doc/user/bgp.rst | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 946f0699f2..436a7e800c 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2584,11 +2584,19 @@ BGP Extended Communities in Route Map .. 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 - 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] From 068c4dfe0b8196e6f67d1211e492f5d265801c9e Mon Sep 17 00:00:00 2001 From: Donatas Abraitis Date: Sat, 8 Apr 2023 18:40:01 +0300 Subject: [PATCH 2/4] tests: Check if Node Target Extended Communities work Signed-off-by: Donatas Abraitis --- .../__init__.py | 0 .../r1/frr.conf | 21 +++ .../r2/frr.conf | 8 ++ .../r3/frr.conf | 8 ++ .../r4/frr.conf | 8 ++ .../test_bgp_node_target_extcommunities.py | 132 ++++++++++++++++++ 6 files changed, 177 insertions(+) create mode 100644 tests/topotests/bgp_node_target_extcommunities/__init__.py create mode 100644 tests/topotests/bgp_node_target_extcommunities/r1/frr.conf create mode 100644 tests/topotests/bgp_node_target_extcommunities/r2/frr.conf create mode 100644 tests/topotests/bgp_node_target_extcommunities/r3/frr.conf create mode 100644 tests/topotests/bgp_node_target_extcommunities/r4/frr.conf create mode 100644 tests/topotests/bgp_node_target_extcommunities/test_bgp_node_target_extcommunities.py diff --git a/tests/topotests/bgp_node_target_extcommunities/__init__.py b/tests/topotests/bgp_node_target_extcommunities/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_node_target_extcommunities/r1/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r1/frr.conf new file mode 100644 index 0000000000..86983387d5 --- /dev/null +++ b/tests/topotests/bgp_node_target_extcommunities/r1/frr.conf @@ -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 diff --git a/tests/topotests/bgp_node_target_extcommunities/r2/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r2/frr.conf new file mode 100644 index 0000000000..09fda78a3d --- /dev/null +++ b/tests/topotests/bgp_node_target_extcommunities/r2/frr.conf @@ -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 diff --git a/tests/topotests/bgp_node_target_extcommunities/r3/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r3/frr.conf new file mode 100644 index 0000000000..4883f1f5c2 --- /dev/null +++ b/tests/topotests/bgp_node_target_extcommunities/r3/frr.conf @@ -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 diff --git a/tests/topotests/bgp_node_target_extcommunities/r4/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r4/frr.conf new file mode 100644 index 0000000000..f518bd1fa0 --- /dev/null +++ b/tests/topotests/bgp_node_target_extcommunities/r4/frr.conf @@ -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 diff --git a/tests/topotests/bgp_node_target_extcommunities/test_bgp_node_target_extcommunities.py b/tests/topotests/bgp_node_target_extcommunities/test_bgp_node_target_extcommunities.py new file mode 100644 index 0000000000..23e820b4fc --- /dev/null +++ b/tests/topotests/bgp_node_target_extcommunities/test_bgp_node_target_extcommunities.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis +# + +""" +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)) From c9a256144479bb0836491b6bdad6a79df85e6960 Mon Sep 17 00:00:00 2001 From: Donatas Abraitis Date: Wed, 5 Apr 2023 15:51:45 +0300 Subject: [PATCH 3/4] 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 --- bgpd/bgp_ecommunity.c | 98 +++++++++++++++++++++++++++++++---- bgpd/bgp_ecommunity.h | 29 +++++++++++ bgpd/bgp_route.c | 15 ++++++ bgpd/bgp_routemap.c | 76 +++++++++++++++++++++++++++ bgpd/bgp_routemap_nb.c | 7 +++ bgpd/bgp_routemap_nb.h | 4 ++ bgpd/bgp_routemap_nb_config.c | 52 +++++++++++++++++++ lib/routemap.h | 2 + lib/routemap_cli.c | 5 ++ yang/frr-bgp-route-map.yang | 17 ++++++ 10 files changed, 296 insertions(+), 9 deletions(-) diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index dc1905868b..596c5dbe37 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -371,6 +371,7 @@ void ecommunity_finish(void) enum ecommunity_token { ecommunity_token_unknown = 0, ecommunity_token_rt, + ecommunity_token_nt, ecommunity_token_soo, ecommunity_token_val, ecommunity_token_rt6, @@ -415,6 +416,53 @@ static void ecommunity_origin_validation_state_str(char *buf, size_t bufsz, (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, int trans, as_t as, struct in_addr *ip, @@ -453,9 +501,13 @@ static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type, eval->val[6] = (val >> 8) & 0xff; eval->val[7] = val & 0xff; } else if (type == ECOMMUNITY_ENCODE_IP) { - memcpy(&eval->val[2], ip, sizeof(struct in_addr)); - eval->val[6] = (val >> 8) & 0xff; - eval->val[7] = val & 0xff; + if (sub_type == ECOMMUNITY_NODE_TARGET) { + encode_node_target(ip, eval, trans); + } else { + memcpy(&eval->val[2], ip, sizeof(struct in_addr)); + eval->val[6] = (val >> 8) & 0xff; + eval->val[7] = val & 0xff; + } } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP && sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) { 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, } /* Get next Extended Communities token from the string. */ -static const char *ecommunity_gettoken(const char *str, - void *eval_ptr, - enum ecommunity_token *token) +static const char *ecommunity_gettoken(const char *str, void *eval_ptr, + enum ecommunity_token *token, int type) { int ret; int dot = 0; @@ -515,7 +566,7 @@ static const char *ecommunity_gettoken(const char *str, if (*p == '\0') return NULL; - /* "rt" and "soo" keyword parse. */ + /* "rt", "nt", and "soo" keyword parse. */ if (!isdigit((unsigned char)*p)) { /* "rt" match check. */ if (tolower((unsigned char)*p) == 'r') { @@ -534,6 +585,20 @@ static const char *ecommunity_gettoken(const char *str, } 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. */ else if (tolower((unsigned char)*p) == 's') { p++; @@ -679,7 +744,7 @@ static const char *ecommunity_gettoken(const char *str, ecomm_type = ECOMMUNITY_ENCODE_AS4; else 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; *token = ecommunity_token_val; return p; @@ -700,9 +765,10 @@ static struct ecommunity *ecommunity_str2com_internal(const char *str, int type, if (is_ipv6_extcomm) token = ecommunity_token_rt6; - while ((str = ecommunity_gettoken(str, (void *)&eval, &token))) { + while ((str = ecommunity_gettoken(str, (void *)&eval, &token, type))) { switch (token) { case ecommunity_token_rt: + case ecommunity_token_nt: case ecommunity_token_rt6: case ecommunity_token_soo: if (!keyword_included || keyword) { @@ -719,6 +785,9 @@ static struct ecommunity *ecommunity_str2com_internal(const char *str, int type, if (token == ecommunity_token_soo) { type = ECOMMUNITY_SITE_ORIGIN; } + if (token == ecommunity_token_nt) { + type = ECOMMUNITY_NODE_TARGET; + } break; case ecommunity_token_val: if (keyword_included) { @@ -1000,6 +1069,10 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) ecommunity_lb_str( encbuf, sizeof(encbuf), pnt, ecom->disable_ieee_floating); + } else if (sub_type == ECOMMUNITY_NODE_TARGET && + type == ECOMMUNITY_ENCODE_IP) { + ecommunity_node_target_str( + encbuf, sizeof(encbuf), pnt); } else unk_ecom = 1; } else { @@ -1209,6 +1282,13 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) ecom->disable_ieee_floating); else 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) { sub_type = *pnt++; if (sub_type == ECOMMUNITY_ORIGIN_VALIDATION_STATE) diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 83a1584489..1efb0276d0 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -101,6 +101,10 @@ enum ecommunity_origin_validation_states { /* Extended Community readable string length */ #define ECOMMUNITY_STRLEN 64 +/* Node Target Extended Communities */ +#define ECOMMUNITY_NODE_TARGET 0x09 +#define ECOMMUNITY_NODE_TARGET_RESERVED 0 + /* Extended Communities attribute. */ struct ecommunity { /* Reference counter. */ @@ -257,6 +261,26 @@ static inline void encode_origin_validation_state(enum rpki_states 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_finish(void); extern void ecommunity_free(struct ecommunity **); @@ -338,4 +362,9 @@ static inline void ecommunity_strip_rts(struct ecommunity *ecom) extern struct ecommunity * ecommunity_add_origin_validation_state(enum rpki_states rpki_state, 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 */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index a23bffd4ae..957b30794c 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -4154,6 +4154,21 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, 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. * This specification intends to improve this situation by requiring the * explicit configuration of both BGP Import and Export Policies for any diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 32ecb083be..10fc3ecda4 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -2867,6 +2867,29 @@ static const struct route_map_rule_cmd route_set_ecommunity_soo_cmd = { 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' */ struct rmap_ecomm_lb_set { @@ -6418,6 +6441,55 @@ ALIAS_YANG (no_set_ecommunity_lb, "BGP extended community attribute\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, set_origin_cmd, "set origin ", @@ -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_originator_id_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_lb_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, &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 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 9188a40cc9..6e8439cc26 100644 --- a/bgpd/bgp_routemap_nb.c +++ b/bgpd/bgp_routemap_nb.c @@ -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, } }, + { + .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", .cbs = { diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h index 07a1a6eb57..bcd1e837e8 100644 --- a/bgpd/bgp_routemap_nb.h +++ b/bgpd/bgp_routemap_nb.h @@ -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_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_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_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); diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c index 4db8dba2cc..938a5ec31b 100644 --- a/bgpd/bgp_routemap_nb_config.c +++ b/bgpd/bgp_routemap_nb_config.c @@ -1413,6 +1413,58 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy( 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: * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-soo diff --git a/lib/routemap.h b/lib/routemap.h index 2197a49e76..fda42b069d 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -352,6 +352,8 @@ DECLARE_QOBJ_TYPE(route_map); (strmatch(A, "frr-bgp-route-map:set-extcommunity-none")) #define IS_SET_EXTCOMMUNITY_RT(A) \ (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) \ (strmatch(A, "frr-bgp-route-map:set-extcommunity-soo")) #define IS_SET_EXTCOMMUNITY_LB(A) \ diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index 4345b74bc0..52a604bb22 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -1140,6 +1140,11 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *dnode, yang_dnode_get_string( dnode, "./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)) { vty_out(vty, " set extcommunity soo %s\n", yang_dnode_get_string( diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang index 666f2bb235..23e5b0227c 100644 --- a/yang/frr-bgp-route-map.yang +++ b/yang/frr-bgp-route-map.yang @@ -186,6 +186,12 @@ module frr-bgp-route-map { "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 { base frr-route-map:rmap-set-type; 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 { 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 From 8f2a51b7b76eccac7511acb9116cab4c492831bc Mon Sep 17 00:00:00 2001 From: Donatas Abraitis Date: Mon, 10 Apr 2023 22:40:30 +0300 Subject: [PATCH 4/4] bgpd: Reuse encode_route_target_ip() function Before this patch, this function wasn't used in the code. Let's reuse this since it's uses the same pattern for encoding route-target extcommunity. Also reuse encode_route_target_as[4]() as well. Signed-off-by: Donatas Abraitis --- bgpd/bgp_ecommunity.c | 23 +++++------------------ bgpd/bgp_ecommunity.h | 19 ++++++++++++++----- bgpd/bgp_evpn.c | 4 ++-- 3 files changed, 21 insertions(+), 25 deletions(-) diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 596c5dbe37..7bf2609019 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -494,32 +494,19 @@ static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type, eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE; eval->val[1] = sub_type; if (type == ECOMMUNITY_ENCODE_AS) { - eval->val[2] = (as >> 8) & 0xff; - 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; + encode_route_target_as(as, val, eval, trans); } else if (type == ECOMMUNITY_ENCODE_IP) { - if (sub_type == ECOMMUNITY_NODE_TARGET) { + if (sub_type == ECOMMUNITY_NODE_TARGET) encode_node_target(ip, eval, trans); - } else { - memcpy(&eval->val[2], ip, sizeof(struct in_addr)); - eval->val[6] = (val >> 8) & 0xff; - eval->val[7] = val & 0xff; - } + else + encode_route_target_ip(ip, val, eval, trans); } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP && sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) { memcpy(&eval6->val[2], ip6, sizeof(struct in6_addr)); eval6->val[18] = (val >> 8) & 0xff; eval6->val[19] = val & 0xff; } else { - eval->val[2] = (as >> 24) & 0xff; - 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; + encode_route_target_as4(as, val, eval, trans); } return 0; diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 1efb0276d0..94a178bbb6 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -159,9 +159,12 @@ struct ecommunity_val_ipv6 { * Encode BGP Route Target AS:nn. */ 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; + if (!trans) + eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE; eval->val[1] = ECOMMUNITY_ROUTE_TARGET; eval->val[2] = (as >> 8) & 0xff; eval->val[3] = as & 0xff; @@ -174,12 +177,15 @@ static inline void encode_route_target_as(as_t as, uint32_t val, /* * Encode BGP Route Target IP:nn. */ -static inline void encode_route_target_ip(struct in_addr ip, uint16_t val, - struct ecommunity_val *eval) +static inline void encode_route_target_ip(struct in_addr *ip, uint16_t val, + struct ecommunity_val *eval, + bool trans) { eval->val[0] = ECOMMUNITY_ENCODE_IP; + if (!trans) + eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE; 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[7] = val & 0xff; } @@ -188,9 +194,12 @@ static inline void encode_route_target_ip(struct in_addr ip, uint16_t val, * Encode BGP Route Target AS4:nn. */ 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; + if (!trans) + eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE; eval->val[1] = ECOMMUNITY_ROUTE_TARGET; eval->val[2] = (as >> 24) & 0xff; eval->val[3] = (as >> 16) & 0xff; diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 81117e94ef..87139e9e53 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -577,7 +577,7 @@ static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl, if (bgp->advertise_autort_rfc8365) 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(); ecommunity_add_val(ecomadd, &eval, false, false); @@ -5168,7 +5168,7 @@ void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl, if (bgp->advertise_autort_rfc8365) 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(); ecommunity_add_val(ecom_auto, &eval, false, false);