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",
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")
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 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
"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")
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 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))