mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-05 20:51:17 +00:00
Merge pull request #14073 from fdumontet6WIND/as_path_replace_reg
bgpd: add set as-path replace acl-list command
This commit is contained in:
commit
df04c23c8f
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
Loading…
Reference in New Issue
Block a user