bgpd: add 'set as-path replace' with a configured ASN

There is no route-map set action to replace any ASN,
or a part of an ASN, with a configured ASN.

The current commit adds a new command to use a configured
ASN as replacement, instead of using the local as number.

> set as-path replace any 65500

Update the 'bgp_set_aspath_replace' test.

Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
This commit is contained in:
Philippe Guibert 2023-06-19 16:22:55 +02:00
parent 07a2b9d41e
commit a3f0a1f5ed
4 changed files with 100 additions and 33 deletions

View File

@ -2385,8 +2385,10 @@ route_set_aspath_replace(void *rule, const struct prefix *dummy, void *object)
struct aspath *aspath_new;
const char *replace = rule;
struct bgp_path_info *path = object;
as_t own_asn = path->peer->change_local_as ? path->peer->change_local_as
: path->peer->local_as;
as_t replace_asn = 0;
as_t configured_asn;
char *buf;
char src_asn[ASN_STRING_MAX_SIZE];
if (path->peer->sort != BGP_PEER_EBGP) {
zlog_warn(
@ -2394,6 +2396,29 @@ route_set_aspath_replace(void *rule, const struct prefix *dummy, void *object)
return RMAP_NOOP;
}
buf = strchr(replace, ' ');
if (!buf) {
configured_asn = path->peer->change_local_as
? path->peer->change_local_as
: path->peer->local_as;
} else {
memcpy(src_asn, replace, (size_t)(buf - replace));
src_asn[(size_t)(buf - replace)] = '\0';
replace = src_asn;
buf++;
if (!asn_str2asn(buf, &configured_asn)) {
zlog_warn(
"`set as-path replace`, invalid configured AS %s",
buf);
return RMAP_NOOP;
}
}
if (!strmatch(replace, "any") && !asn_str2asn(replace, &replace_asn)) {
zlog_warn("`set as-path replace`, invalid AS %s", replace);
return RMAP_NOOP;
}
if (path->attr->aspath->refcnt)
aspath_new = aspath_dup(path->attr->aspath);
else
@ -2401,13 +2426,10 @@ route_set_aspath_replace(void *rule, const struct prefix *dummy, void *object)
if (strmatch(replace, "any")) {
path->attr->aspath =
aspath_replace_all_asn(aspath_new, own_asn);
} else {
as_t replace_asn = strtoul(replace, NULL, 10);
aspath_replace_all_asn(aspath_new, configured_asn);
} else
path->attr->aspath = aspath_replace_specific_asn(
aspath_new, replace_asn, own_asn);
}
aspath_new, replace_asn, configured_asn);
aspath_free(aspath_new);
@ -5875,41 +5897,49 @@ DEFUN_YANG (set_aspath_prepend_lastas,
return nb_cli_apply_changes(vty, NULL);
}
DEFPY_YANG (set_aspath_replace_asn,
set_aspath_replace_asn_cmd,
"set as-path replace <any|ASNUM>$replace",
DEFPY_YANG(set_aspath_replace_asn, set_aspath_replace_asn_cmd,
"set as-path replace <any|ASNUM>$replace [<ASNUM>$configured_asn]",
SET_STR
"Transform BGP AS_PATH attribute\n"
"Replace AS number to local AS number\n"
"Replace any AS number to local AS number\n"
"Replace a specific AS number in plain or dotted format to local AS number\n")
"Replace AS number to local or configured AS number\n"
"Replace any AS number to local or configured AS number\n"
"Replace a specific AS number to local or configured AS number\n"
"Define the configured AS number\n")
{
const char *xpath =
"./set-action[action='frr-bgp-route-map:as-path-replace']";
char xpath_value[XPATH_MAXLEN];
as_t as_value;
as_t as_value, as_configured_value;
char replace_value[ASN_STRING_MAX_SIZE * 2];
if (!strmatch(replace, "any") && !asn_str2asn(replace, &as_value)) {
vty_out(vty, "%% Invalid AS value %s\n", replace);
return CMD_WARNING_CONFIG_FAILED;
}
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
if (configured_asn_str &&
!asn_str2asn(configured_asn_str, &as_configured_value)) {
vty_out(vty, "%% Invalid AS configured value %s\n",
configured_asn_str);
return CMD_WARNING_CONFIG_FAILED;
}
snprintf(xpath_value, sizeof(xpath_value),
"%s/rmap-set-action/frr-bgp-route-map:replace-as-path", xpath);
nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, replace);
snprintf(replace_value, sizeof(replace_value), "%s%s%s", replace,
configured_asn_str ? " " : "",
configured_asn_str ? configured_asn_str : "");
nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, replace_value);
return nb_cli_apply_changes(vty, NULL);
}
DEFPY_YANG (no_set_aspath_replace_asn,
no_set_aspath_replace_asn_cmd,
"no set as-path replace [<any|ASNUM>]",
NO_STR
SET_STR
DEFPY_YANG(no_set_aspath_replace_asn, no_set_aspath_replace_asn_cmd,
"no set as-path replace [<any|ASNUM>] [<ASNUM>$configured_asn]",
NO_STR SET_STR
"Transform BGP AS_PATH attribute\n"
"Replace AS number to local AS number\n"
"Replace any AS number to local AS number\n"
"Replace a specific AS number in plain or dotted format to local AS number\n")
"Replace AS number to local or configured AS number\n"
"Replace any AS number to local or configured AS number\n"
"Replace a specific AS number to local or configured AS number\n"
"Replace AS number with a configured AS number\n"
"Define the configured AS number\n")
{
const char *xpath =
"./set-action[action='frr-bgp-route-map:as-path-replace']";

View File

@ -2105,10 +2105,11 @@ Using AS Path in Route Map
Prepend the existing last AS number (the leftmost ASN) to the AS_PATH.
The no form of this command removes this set operation from the route-map.
.. clicmd:: set as-path replace <any|ASN>
.. clicmd:: set as-path replace <any|ASN> [<ASN>]
Replace a specific AS number to local AS number. ``any`` replaces each
AS number in the AS-PATH with the local AS number.
Replace a specific AS number to local AS number or a configured AS number.
``any`` replaces each AS number in the AS-PATH with either the local AS
number or the configured AS number.
.. clicmd:: set as-path exclude all

View File

@ -9,6 +9,7 @@ router bgp 65001
!
ip prefix-list p1 seq 5 permit 172.16.255.31/32
!
bgp route-map delay-timer 1
route-map r2 permit 10
match ip address prefix-list p1
set as-path replace 65003

View File

@ -24,6 +24,7 @@ sys.path.append(os.path.join(CWD, "../"))
# pylint: disable=C0413
from lib import topotest
from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
pytestmark = [pytest.mark.bgpd]
@ -63,7 +64,7 @@ def teardown_module(mod):
tgen.stop_topology()
def test_bgp_maximum_prefix_out():
def test_bgp_set_aspath_replace_test1():
tgen = get_topogen()
if tgen.routers_have_failure():
@ -85,6 +86,40 @@ def test_bgp_maximum_prefix_out():
assert result is None, "Failed overriding incoming AS-PATH with route-map"
def test_bgp_set_aspath_replace_test2():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
logger.info("Configuring r1 to replace the matching AS with a configured ASN")
router = tgen.gears["r1"]
router.vtysh_cmd(
"configure terminal\nroute-map r2 permit 10\nset as-path replace 65003 65500\n",
isjson=False,
)
router.vtysh_cmd(
"configure terminal\nroute-map r2 permit 20\nset as-path replace any 65501\n",
isjson=False,
)
def _bgp_converge(router):
output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json"))
expected = {
"routes": {
"172.16.255.31/32": [{"path": "65002 65500"}],
"172.16.255.32/32": [{"path": "65501 65501"}],
}
}
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_converge, router)
_, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
assert (
result is None
), "Failed overriding incoming AS-PATH with route-map replace with configured ASN"
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))