mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-07 11:25:41 +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;
|
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 */
|
/* Replace all instances of the target ASN with our own ASN */
|
||||||
struct aspath *aspath_replace_specific_asn(struct aspath *aspath,
|
struct aspath *aspath_replace_specific_asn(struct aspath *aspath,
|
||||||
as_t target_asn, as_t our_asn)
|
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(struct aspath *aspath, as_t asno);
|
||||||
extern int aspath_loop_check_confed(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 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,
|
extern struct aspath *aspath_replace_specific_asn(struct aspath *aspath,
|
||||||
as_t target_asn,
|
as_t target_asn,
|
||||||
as_t our_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;
|
as_t configured_asn;
|
||||||
char *buf;
|
char *buf;
|
||||||
char src_asn[ASN_STRING_MAX_SIZE];
|
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) {
|
if (path->peer->sort != BGP_PEER_EBGP) {
|
||||||
zlog_warn(
|
zlog_warn(
|
||||||
"`set as-path replace` is supported only for EBGP peers");
|
"`set as-path replace` is supported only for EBGP peers");
|
||||||
return RMAP_NOOP;
|
goto end_ko;
|
||||||
}
|
}
|
||||||
|
|
||||||
buf = strchr(replace, ' ');
|
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
|
configured_asn = path->peer->change_local_as
|
||||||
? path->peer->change_local_as
|
? path->peer->change_local_as
|
||||||
: path->peer->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 {
|
} else {
|
||||||
memcpy(src_asn, replace, (size_t)(buf - replace));
|
memcpy(src_asn, replace, (size_t)(buf - replace));
|
||||||
src_asn[(size_t)(buf - replace)] = '\0';
|
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(
|
zlog_warn(
|
||||||
"`set as-path replace`, invalid configured AS %s",
|
"`set as-path replace`, invalid configured AS %s",
|
||||||
buf);
|
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);
|
zlog_warn("`set as-path replace`, invalid AS %s", replace);
|
||||||
return RMAP_NOOP;
|
goto end_ko;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path->attr->aspath->refcnt)
|
if (path->attr->aspath->refcnt)
|
||||||
@ -2446,16 +2492,29 @@ route_set_aspath_replace(void *rule, const struct prefix *dummy, void *object)
|
|||||||
else
|
else
|
||||||
aspath_new = path->attr->aspath;
|
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 =
|
path->attr->aspath =
|
||||||
aspath_replace_all_asn(aspath_new, configured_asn);
|
aspath_replace_all_asn(aspath_new, configured_asn);
|
||||||
} else
|
} else {
|
||||||
path->attr->aspath = aspath_replace_specific_asn(
|
path->attr->aspath = aspath_replace_specific_asn(
|
||||||
aspath_new, replace_asn, configured_asn);
|
aspath_new, replace_asn, configured_asn);
|
||||||
|
}
|
||||||
aspath_free(aspath_new);
|
aspath_free(aspath_new);
|
||||||
|
|
||||||
|
|
||||||
|
if (acl_list_name)
|
||||||
|
XFREE(MTYPE_TMP, acl_list_name);
|
||||||
return RMAP_OKAY;
|
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 = {
|
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);
|
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,
|
DEFUN_YANG (no_set_aspath_prepend,
|
||||||
no_set_aspath_prepend_cmd,
|
no_set_aspath_prepend_cmd,
|
||||||
"no set as-path prepend [ASNUM]",
|
"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_all_cmd);
|
||||||
install_element(RMAP_NODE, &set_aspath_exclude_access_list_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_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_cmd);
|
||||||
install_element(RMAP_NODE, &no_set_aspath_prepend_lastas_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_cmd);
|
||||||
install_element(RMAP_NODE, &no_set_aspath_exclude_all_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_exclude_access_list_cmd);
|
||||||
install_element(RMAP_NODE, &no_set_aspath_replace_asn_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, &set_origin_cmd);
|
||||||
install_element(RMAP_NODE, &no_set_origin_cmd);
|
install_element(RMAP_NODE, &no_set_origin_cmd);
|
||||||
install_element(RMAP_NODE, &set_atomic_aggregate_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
|
``any`` replaces each AS number in the AS-PATH with either the local AS
|
||||||
number or the configured AS number.
|
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
|
.. clicmd:: set as-path exclude all
|
||||||
|
|
||||||
Remove all AS numbers from the AS_PATH of the BGP path's NLRI. The no form of
|
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"
|
), "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__":
|
if __name__ == "__main__":
|
||||||
args = ["-s"] + sys.argv[1:]
|
args = ["-s"] + sys.argv[1:]
|
||||||
sys.exit(pytest.main(args))
|
sys.exit(pytest.main(args))
|
||||||
|
Loading…
Reference in New Issue
Block a user