Skip to content

Commit

Permalink
Merge pull request FRRouting#14073 from fdumontet6WIND/as_path_replac…
Browse files Browse the repository at this point in the history
…e_reg

  bgpd: add set as-path replace acl-list command
  • Loading branch information
ton31337 authored Aug 9, 2023
2 parents 456b63d + 7ac7cd8 commit df04c23
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 7 deletions.
40 changes: 40 additions & 0 deletions bgpd/bgp_aspath.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions bgpd/bgp_aspath.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
130 changes: 123 additions & 7 deletions bgpd/bgp_routemap.c
Original file line number Diff line number Diff line change
Expand Up @@ -2411,18 +2411,63 @@ 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, ' ');
if (!buf) {
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';
Expand All @@ -2432,30 +2477,44 @@ 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)
aspath_new = aspath_dup(path->attr->aspath);
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 = {
Expand Down Expand Up @@ -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]",
Expand Down Expand Up @@ -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);
Expand Down
8 changes: 8 additions & 0 deletions doc/user/bgp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,82 @@ def _bgp_converge(router):
), "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))

0 comments on commit df04c23

Please sign in to comment.