Merge pull request #14073 from fdumontet6WIND/as_path_replace_reg

bgpd: add set as-path replace acl-list command
This commit is contained in:
Donatas Abraitis 2023-08-09 15:28:58 +03:00 committed by GitHub
commit df04c23c8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 250 additions and 7 deletions

View File

@ -1231,6 +1231,46 @@ bool aspath_private_as_check(struct aspath *aspath)
return true;
}
/* Replace all ASN instances of the regex rule with our own ASN */
struct aspath *aspath_replace_regex_asn(struct aspath *aspath,
struct as_list *acl_list, as_t our_asn)
{
struct aspath *new;
struct assegment *cur_seg;
struct as_list *cur_as_list;
struct as_filter *cur_as_filter;
char str_buf[ASPATH_STR_DEFAULT_LEN];
uint32_t i;
new = aspath_dup(aspath);
cur_seg = new->segments;
while (cur_seg) {
cur_as_list = acl_list;
while (cur_as_list) {
cur_as_filter = cur_as_list->head;
while (cur_as_filter) {
for (i = 0; i < cur_seg->length; i++) {
snprintfrr(str_buf,
ASPATH_STR_DEFAULT_LEN,
ASN_FORMAT(new->asnotation),
&cur_seg->as[i]);
if (!regexec(cur_as_filter->reg,
str_buf, 0, NULL, 0))
cur_seg->as[i] = our_asn;
}
cur_as_filter = cur_as_filter->next;
}
cur_as_list = cur_as_list->next;
}
cur_seg = cur_seg->next;
}
aspath_str_update(new, false);
return new;
}
/* Replace all instances of the target ASN with our own ASN */
struct aspath *aspath_replace_specific_asn(struct aspath *aspath,
as_t target_asn, as_t our_asn)

View File

@ -107,6 +107,9 @@ extern unsigned int aspath_get_last_as(struct aspath *aspath);
extern int aspath_loop_check(struct aspath *aspath, as_t asno);
extern int aspath_loop_check_confed(struct aspath *aspath, as_t asno);
extern bool aspath_private_as_check(struct aspath *aspath);
extern struct aspath *aspath_replace_regex_asn(struct aspath *aspath,
struct as_list *acl_list,
as_t our_asn);
extern struct aspath *aspath_replace_specific_asn(struct aspath *aspath,
as_t target_asn,
as_t our_asn);

View File

@ -2411,11 +2411,16 @@ route_set_aspath_replace(void *rule, const struct prefix *dummy, void *object)
as_t configured_asn;
char *buf;
char src_asn[ASN_STRING_MAX_SIZE];
char *acl_list_name = NULL;
uint32_t acl_list_name_len = 0;
char *buf_acl_name = NULL;
static const char asp_acl[] = "as-path-access-list";
struct as_list *aspath_acl = NULL;
if (path->peer->sort != BGP_PEER_EBGP) {
zlog_warn(
"`set as-path replace` is supported only for EBGP peers");
return RMAP_NOOP;
goto end_ko;
}
buf = strchr(replace, ' ');
@ -2423,6 +2428,46 @@ route_set_aspath_replace(void *rule, const struct prefix *dummy, void *object)
configured_asn = path->peer->change_local_as
? path->peer->change_local_as
: path->peer->local_as;
} else if (!strncmp(replace, asp_acl, strlen(asp_acl))) {
/* its as-path-acl-list command get the access list name */
while (*buf == ' ')
buf++;
buf_acl_name = buf;
buf = strchr(buf_acl_name, ' ');
if (buf)
acl_list_name_len = buf - buf_acl_name;
else
acl_list_name_len = strlen(buf_acl_name);
buf_acl_name[acl_list_name_len] = 0;
/* get the acl-list */
aspath_acl = as_list_lookup(buf_acl_name);
if (!aspath_acl) {
zlog_warn("`set as-path replace`, invalid as-path-access-list name: %s",
buf_acl_name);
goto end_ko;
}
acl_list_name = XSTRDUP(MTYPE_TMP, buf_acl_name);
buf_acl_name[acl_list_name_len] = ' ';
if (!buf) {
configured_asn = path->peer->change_local_as
? path->peer->change_local_as
: path->peer->local_as;
} else {
while (*buf == ' ')
buf++;
/* get the configured asn */
if (!asn_str2asn(buf, &configured_asn)) {
zlog_warn(
"`set as-path replace`, invalid configured AS %s",
buf);
goto end_ko;
}
}
replace = buf;
} else {
memcpy(src_asn, replace, (size_t)(buf - replace));
src_asn[(size_t)(buf - replace)] = '\0';
@ -2432,13 +2477,14 @@ route_set_aspath_replace(void *rule, const struct prefix *dummy, void *object)
zlog_warn(
"`set as-path replace`, invalid configured AS %s",
buf);
return RMAP_NOOP;
goto end_ko;
}
}
if (!strmatch(replace, "any") && !asn_str2asn(replace, &replace_asn)) {
if (replace && !strmatch(replace, "any") &&
!asn_str2asn(replace, &replace_asn)) {
zlog_warn("`set as-path replace`, invalid AS %s", replace);
return RMAP_NOOP;
goto end_ko;
}
if (path->attr->aspath->refcnt)
@ -2446,16 +2492,29 @@ route_set_aspath_replace(void *rule, const struct prefix *dummy, void *object)
else
aspath_new = path->attr->aspath;
if (strmatch(replace, "any")) {
if (aspath_acl) {
path->attr->aspath = aspath_replace_regex_asn(aspath_new,
aspath_acl,
configured_asn);
} else if (strmatch(replace, "any")) {
path->attr->aspath =
aspath_replace_all_asn(aspath_new, configured_asn);
} else
} else {
path->attr->aspath = aspath_replace_specific_asn(
aspath_new, replace_asn, configured_asn);
}
aspath_free(aspath_new);
if (acl_list_name)
XFREE(MTYPE_TMP, acl_list_name);
return RMAP_OKAY;
end_ko:
if (acl_list_name)
XFREE(MTYPE_TMP, acl_list_name);
return RMAP_NOOP;
}
static const struct route_map_rule_cmd route_set_aspath_replace_cmd = {
@ -6088,6 +6147,61 @@ DEFPY_YANG(no_set_aspath_replace_asn, no_set_aspath_replace_asn_cmd,
return nb_cli_apply_changes(vty, NULL);
}
DEFPY_YANG(
set_aspath_replace_access_list, set_aspath_replace_access_list_cmd,
"set as-path replace as-path-access-list AS_PATH_FILTER_NAME$aspath_filter_name [<ASNUM>$configured_asn]",
SET_STR
"Transform BGP AS-path attribute\n"
"Replace AS number to local or configured AS number\n"
"Specify an as path access list name\n"
"AS path access list name\n"
"Define the configured AS number\n")
{
char *str;
const char *xpath =
"./set-action[action='frr-bgp-route-map:as-path-replace']";
char xpath_value[XPATH_MAXLEN];
as_t as_configured_value;
char replace_value[ASN_STRING_MAX_SIZE * 2];
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;
}
str = argv_concat(argv, argc, 3);
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
snprintf(replace_value, sizeof(replace_value), "%s %s", aspath_filter_name, str);
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, str);
return nb_cli_apply_changes(vty, NULL);
}
DEFPY_YANG(
no_set_aspath_replace_access_list, no_set_aspath_replace_access_list_cmd,
"no set as-path replace as-path-access-list [AS_PATH_FILTER_NAME] [<ASNUM>$configured_asn]",
NO_STR
SET_STR
"Transform BGP AS_PATH attribute\n"
"Replace AS number to local or configured AS number\n"
"Specify an as path access list name\n"
"AS path access list name\n"
"Define the configured AS number\n")
{
const char *xpath =
"./set-action[action='frr-bgp-route-map:as-path-replace']";
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
return nb_cli_apply_changes(vty, NULL);
}
DEFUN_YANG (no_set_aspath_prepend,
no_set_aspath_prepend_cmd,
"no set as-path prepend [ASNUM]",
@ -7793,12 +7907,14 @@ void bgp_route_map_init(void)
install_element(RMAP_NODE, &set_aspath_exclude_all_cmd);
install_element(RMAP_NODE, &set_aspath_exclude_access_list_cmd);
install_element(RMAP_NODE, &set_aspath_replace_asn_cmd);
install_element(RMAP_NODE, &set_aspath_replace_access_list_cmd);
install_element(RMAP_NODE, &no_set_aspath_prepend_cmd);
install_element(RMAP_NODE, &no_set_aspath_prepend_lastas_cmd);
install_element(RMAP_NODE, &no_set_aspath_exclude_cmd);
install_element(RMAP_NODE, &no_set_aspath_exclude_all_cmd);
install_element(RMAP_NODE, &no_set_aspath_exclude_access_list_cmd);
install_element(RMAP_NODE, &no_set_aspath_replace_asn_cmd);
install_element(RMAP_NODE, &no_set_aspath_replace_access_list_cmd);
install_element(RMAP_NODE, &set_origin_cmd);
install_element(RMAP_NODE, &no_set_origin_cmd);
install_element(RMAP_NODE, &set_atomic_aggregate_cmd);

View File

@ -2142,6 +2142,14 @@ Using AS Path in Route Map
``any`` replaces each AS number in the AS-PATH with either the local AS
number or the configured AS number.
.. clicmd:: set as-path replace as-path-access-list WORD [<ASN>]
Replace some AS numbers from the AS_PATH of the BGP path's NLRI. Substituted
AS numbers are conformant with the regex defined in as-path access-list
WORD. Changed AS numbers are replaced either by the local AS number or the
configured AS number.
The no form of this command removes this set operation from the route-map.
.. clicmd:: set as-path exclude all
Remove all AS numbers from the AS_PATH of the BGP path's NLRI. The no form of

View File

@ -120,6 +120,82 @@ def test_bgp_set_aspath_replace_test2():
), "Failed overriding incoming AS-PATH with route-map replace with configured ASN"
def test_bgp_set_aspath_replace_access_list():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
rname = "r1"
r1 = tgen.gears[rname]
r1.vtysh_cmd(
"""
conf
bgp as-path access-list FIRST permit ^65
route-map r2 permit 20
set as-path replace as-path-access-list FIRST 65002
"""
)
expected = {
"routes": {
"172.16.255.31/32": [{"path": "65002 65500"}],
"172.16.255.32/32": [{"path": "65002 65002"}],
}
}
def _bgp_regexp_1(router):
output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json"))
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_regexp_1, tgen.gears["r1"])
_, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
assert result is None, "Failed overriding incoming AS-PATH with regex 1 route-map"
r1.vtysh_cmd(
"""
conf
bgp as-path access-list SECOND permit 2
route-map r2 permit 10
set as-path replace as-path-access-list SECOND 65001
"""
)
expected = {
"routes": {
"172.16.255.31/32": [{"path": "65001 65003"}],
"172.16.255.32/32": [{"path": "65002 65002"}],
}
}
test_func = functools.partial(_bgp_regexp_1, tgen.gears["r1"])
_, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
assert result is None, "Failed overriding incoming AS-PATH with regex 2 route-map"
r1.vtysh_cmd(
"""
conf
bgp as-path access-list TER permit 3
route-map r2 permit 10
set as-path replace as-path-access-list TER
"""
)
expected = {
"routes": {
"172.16.255.31/32": [{"path": "65002 65001"}],
"172.16.255.32/32": [{"path": "65002 65002"}],
}
}
test_func = functools.partial(_bgp_regexp_1, tgen.gears["r1"])
_, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
assert result is None, "Failed overriding incoming AS-PATH with regex 3 route-map"
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))