mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-07 13:33:15 +00:00
bgpd: add 'set as-path exclude all' command
It is not possible to flush all the incoming as-path list from a given BGP update. Add a route-map set command to remove all as-paths from a given AS path. Add the necessary tests. Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
This commit is contained in:
parent
0fb1630520
commit
92550adfc7
@ -1598,6 +1598,24 @@ struct aspath *aspath_filter_exclude(struct aspath *source,
|
|||||||
return newpath;
|
return newpath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct aspath *aspath_filter_exclude_all(struct aspath *source)
|
||||||
|
{
|
||||||
|
struct aspath *newpath;
|
||||||
|
|
||||||
|
newpath = aspath_new(source->asnotation);
|
||||||
|
|
||||||
|
aspath_str_update(newpath, false);
|
||||||
|
/* We are happy returning even an empty AS_PATH, because the
|
||||||
|
* administrator
|
||||||
|
* might expect this very behaviour. There's a mean to avoid this, if
|
||||||
|
* necessary,
|
||||||
|
* by having a match rule against certain AS_PATH regexps in the
|
||||||
|
* route-map index.
|
||||||
|
*/
|
||||||
|
aspath_free(source);
|
||||||
|
return newpath;
|
||||||
|
}
|
||||||
|
|
||||||
/* Add specified AS to the leftmost of aspath. */
|
/* Add specified AS to the leftmost of aspath. */
|
||||||
static struct aspath *aspath_add_asns(struct aspath *aspath, as_t asno,
|
static struct aspath *aspath_add_asns(struct aspath *aspath, as_t asno,
|
||||||
uint8_t type, unsigned num)
|
uint8_t type, unsigned num)
|
||||||
|
@ -76,6 +76,7 @@ extern struct aspath *aspath_aggregate(struct aspath *as1, struct aspath *as2);
|
|||||||
extern struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2);
|
extern struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2);
|
||||||
extern struct aspath *aspath_filter_exclude(struct aspath *source,
|
extern struct aspath *aspath_filter_exclude(struct aspath *source,
|
||||||
struct aspath *exclude_list);
|
struct aspath *exclude_list);
|
||||||
|
extern struct aspath *aspath_filter_exclude_all(struct aspath *source);
|
||||||
extern struct aspath *aspath_add_seq_n(struct aspath *aspath, as_t asno,
|
extern struct aspath *aspath_add_seq_n(struct aspath *aspath, as_t asno,
|
||||||
unsigned num);
|
unsigned num);
|
||||||
extern struct aspath *aspath_add_seq(struct aspath *aspath, as_t asno);
|
extern struct aspath *aspath_add_seq(struct aspath *aspath, as_t asno);
|
||||||
|
@ -2300,6 +2300,31 @@ static const struct route_map_rule_cmd route_set_aspath_prepend_cmd = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* `set as-path exclude ASn' */
|
/* `set as-path exclude ASn' */
|
||||||
|
struct aspath_exclude {
|
||||||
|
struct aspath *aspath;
|
||||||
|
bool exclude_all;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void *route_aspath_exclude_compile(const char *arg)
|
||||||
|
{
|
||||||
|
struct aspath_exclude *ase;
|
||||||
|
const char *str = arg;
|
||||||
|
|
||||||
|
ase = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct aspath_exclude));
|
||||||
|
if (!strmatch(str, "all"))
|
||||||
|
ase->aspath = aspath_str2aspath(str, bgp_get_asnotation(NULL));
|
||||||
|
else
|
||||||
|
ase->exclude_all = true;
|
||||||
|
return ase;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void route_aspath_exclude_free(void *rule)
|
||||||
|
{
|
||||||
|
struct aspath_exclude *ase = rule;
|
||||||
|
|
||||||
|
aspath_free(ase->aspath);
|
||||||
|
XFREE(MTYPE_ROUTE_MAP_COMPILED, ase);
|
||||||
|
}
|
||||||
|
|
||||||
/* For ASN exclude mechanism.
|
/* For ASN exclude mechanism.
|
||||||
* Iterate over ASns requested and filter them from the given AS_PATH one by
|
* Iterate over ASns requested and filter them from the given AS_PATH one by
|
||||||
@ -2309,16 +2334,28 @@ static const struct route_map_rule_cmd route_set_aspath_prepend_cmd = {
|
|||||||
static enum route_map_cmd_result_t
|
static enum route_map_cmd_result_t
|
||||||
route_set_aspath_exclude(void *rule, const struct prefix *dummy, void *object)
|
route_set_aspath_exclude(void *rule, const struct prefix *dummy, void *object)
|
||||||
{
|
{
|
||||||
struct aspath *new_path, *exclude_path;
|
struct aspath *new_path;
|
||||||
struct bgp_path_info *path;
|
struct bgp_path_info *path;
|
||||||
|
struct aspath_exclude *ase = rule;
|
||||||
|
|
||||||
exclude_path = rule;
|
|
||||||
path = object;
|
path = object;
|
||||||
|
|
||||||
|
if (path->peer->sort != BGP_PEER_EBGP) {
|
||||||
|
zlog_warn(
|
||||||
|
"`set as-path exclude` is supported only for EBGP peers");
|
||||||
|
return RMAP_NOOP;
|
||||||
|
}
|
||||||
|
|
||||||
if (path->attr->aspath->refcnt)
|
if (path->attr->aspath->refcnt)
|
||||||
new_path = aspath_dup(path->attr->aspath);
|
new_path = aspath_dup(path->attr->aspath);
|
||||||
else
|
else
|
||||||
new_path = path->attr->aspath;
|
new_path = path->attr->aspath;
|
||||||
path->attr->aspath = aspath_filter_exclude(new_path, exclude_path);
|
|
||||||
|
if (ase->aspath)
|
||||||
|
path->attr->aspath =
|
||||||
|
aspath_filter_exclude(new_path, ase->aspath);
|
||||||
|
else if (ase->exclude_all)
|
||||||
|
path->attr->aspath = aspath_filter_exclude_all(new_path);
|
||||||
|
|
||||||
return RMAP_OKAY;
|
return RMAP_OKAY;
|
||||||
}
|
}
|
||||||
@ -2327,8 +2364,8 @@ route_set_aspath_exclude(void *rule, const struct prefix *dummy, void *object)
|
|||||||
static const struct route_map_rule_cmd route_set_aspath_exclude_cmd = {
|
static const struct route_map_rule_cmd route_set_aspath_exclude_cmd = {
|
||||||
"as-path exclude",
|
"as-path exclude",
|
||||||
route_set_aspath_exclude,
|
route_set_aspath_exclude,
|
||||||
route_aspath_compile,
|
route_aspath_exclude_compile,
|
||||||
route_aspath_free,
|
route_aspath_exclude_free,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* `set as-path replace AS-PATH` */
|
/* `set as-path replace AS-PATH` */
|
||||||
@ -5910,6 +5947,32 @@ DEFUN_YANG (set_aspath_exclude,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFPY_YANG(set_aspath_exclude_all, set_aspath_exclude_all_cmd,
|
||||||
|
"[no$no] set as-path exclude all$all",
|
||||||
|
NO_STR SET_STR
|
||||||
|
"Transform BGP AS-path attribute\n"
|
||||||
|
"Exclude from the as-path\n"
|
||||||
|
"Exclude all AS numbers from the as-path\n")
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
const char *xpath =
|
||||||
|
"./set-action[action='frr-bgp-route-map:as-path-exclude']";
|
||||||
|
char xpath_value[XPATH_MAXLEN];
|
||||||
|
|
||||||
|
if (no)
|
||||||
|
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
|
||||||
|
else {
|
||||||
|
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
|
||||||
|
snprintf(xpath_value, sizeof(xpath_value),
|
||||||
|
"%s/rmap-set-action/frr-bgp-route-map:exclude-as-path",
|
||||||
|
xpath);
|
||||||
|
nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, all);
|
||||||
|
}
|
||||||
|
ret = nb_cli_apply_changes(vty, NULL);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
DEFUN_YANG (no_set_aspath_exclude,
|
DEFUN_YANG (no_set_aspath_exclude,
|
||||||
no_set_aspath_exclude_cmd,
|
no_set_aspath_exclude_cmd,
|
||||||
"no set as-path exclude ASNUM...",
|
"no set as-path exclude ASNUM...",
|
||||||
@ -7436,6 +7499,7 @@ void bgp_route_map_init(void)
|
|||||||
install_element(RMAP_NODE, &set_aspath_prepend_asn_cmd);
|
install_element(RMAP_NODE, &set_aspath_prepend_asn_cmd);
|
||||||
install_element(RMAP_NODE, &set_aspath_prepend_lastas_cmd);
|
install_element(RMAP_NODE, &set_aspath_prepend_lastas_cmd);
|
||||||
install_element(RMAP_NODE, &set_aspath_exclude_cmd);
|
install_element(RMAP_NODE, &set_aspath_exclude_cmd);
|
||||||
|
install_element(RMAP_NODE, &set_aspath_exclude_all_cmd);
|
||||||
install_element(RMAP_NODE, &set_aspath_replace_asn_cmd);
|
install_element(RMAP_NODE, &set_aspath_replace_asn_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);
|
||||||
|
@ -2106,6 +2106,11 @@ Using AS Path in Route Map
|
|||||||
Replace a specific AS number to local AS number. ``any`` replaces each
|
Replace a specific AS number to local AS number. ``any`` replaces each
|
||||||
AS number in the AS-PATH with the local AS number.
|
AS number in the AS-PATH with the local AS number.
|
||||||
|
|
||||||
|
.. clicmd:: set as-path exclude all
|
||||||
|
|
||||||
|
Remove all AS numbers from the AS_PATH of the BGP path's NLRI. The no form of
|
||||||
|
this command removes this set operation from the route-map.
|
||||||
|
|
||||||
.. _bgp-communities-attribute:
|
.. _bgp-communities-attribute:
|
||||||
|
|
||||||
Communities Attribute
|
Communities Attribute
|
||||||
|
0
tests/topotests/bgp_set_aspath_exclude/__init__.py
Normal file
0
tests/topotests/bgp_set_aspath_exclude/__init__.py
Normal file
17
tests/topotests/bgp_set_aspath_exclude/r1/bgpd.conf
Normal file
17
tests/topotests/bgp_set_aspath_exclude/r1/bgpd.conf
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
!
|
||||||
|
router bgp 65001
|
||||||
|
no bgp ebgp-requires-policy
|
||||||
|
neighbor 192.168.1.2 remote-as external
|
||||||
|
neighbor 192.168.1.2 timers 3 10
|
||||||
|
address-family ipv4 unicast
|
||||||
|
neighbor 192.168.1.2 route-map r2 in
|
||||||
|
exit-address-family
|
||||||
|
!
|
||||||
|
ip prefix-list p1 seq 5 permit 172.16.255.31/32
|
||||||
|
!
|
||||||
|
route-map r2 permit 10
|
||||||
|
match ip address prefix-list p1
|
||||||
|
set as-path exclude 65003
|
||||||
|
route-map r2 permit 20
|
||||||
|
set as-path exclude all
|
||||||
|
!
|
6
tests/topotests/bgp_set_aspath_exclude/r1/zebra.conf
Normal file
6
tests/topotests/bgp_set_aspath_exclude/r1/zebra.conf
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
!
|
||||||
|
interface r1-eth0
|
||||||
|
ip address 192.168.1.1/24
|
||||||
|
!
|
||||||
|
ip forwarding
|
||||||
|
!
|
8
tests/topotests/bgp_set_aspath_exclude/r2/bgpd.conf
Normal file
8
tests/topotests/bgp_set_aspath_exclude/r2/bgpd.conf
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
!
|
||||||
|
router bgp 65002
|
||||||
|
no bgp ebgp-requires-policy
|
||||||
|
neighbor 192.168.1.1 remote-as external
|
||||||
|
neighbor 192.168.1.1 timers 3 10
|
||||||
|
neighbor 192.168.2.1 remote-as external
|
||||||
|
neighbor 192.168.2.1 timers 3 10
|
||||||
|
!
|
9
tests/topotests/bgp_set_aspath_exclude/r2/zebra.conf
Normal file
9
tests/topotests/bgp_set_aspath_exclude/r2/zebra.conf
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
!
|
||||||
|
interface r2-eth0
|
||||||
|
ip address 192.168.1.2/24
|
||||||
|
!
|
||||||
|
interface r2-eth1
|
||||||
|
ip address 192.168.2.2/24
|
||||||
|
!
|
||||||
|
ip forwarding
|
||||||
|
!
|
9
tests/topotests/bgp_set_aspath_exclude/r3/bgpd.conf
Normal file
9
tests/topotests/bgp_set_aspath_exclude/r3/bgpd.conf
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
!
|
||||||
|
router bgp 65003
|
||||||
|
no bgp ebgp-requires-policy
|
||||||
|
neighbor 192.168.2.2 remote-as external
|
||||||
|
neighbor 192.168.2.2 timers 3 10
|
||||||
|
address-family ipv4 unicast
|
||||||
|
redistribute connected
|
||||||
|
exit-address-family
|
||||||
|
!
|
10
tests/topotests/bgp_set_aspath_exclude/r3/zebra.conf
Normal file
10
tests/topotests/bgp_set_aspath_exclude/r3/zebra.conf
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
!
|
||||||
|
int lo
|
||||||
|
ip address 172.16.255.31/32
|
||||||
|
ip address 172.16.255.32/32
|
||||||
|
!
|
||||||
|
interface r3-eth0
|
||||||
|
ip address 192.168.2.1/24
|
||||||
|
!
|
||||||
|
ip forwarding
|
||||||
|
!
|
@ -0,0 +1,89 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# SPDX-License-Identifier: ISC
|
||||||
|
#
|
||||||
|
# test_bgp_set_aspath_exclude.py
|
||||||
|
#
|
||||||
|
# Copyright 2023 by 6WIND S.A.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Test if `set as-path exclude` is working correctly for route-maps.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import pytest
|
||||||
|
import functools
|
||||||
|
|
||||||
|
CWD = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
sys.path.append(os.path.join(CWD, "../"))
|
||||||
|
|
||||||
|
# pylint: disable=C0413
|
||||||
|
from lib import topotest
|
||||||
|
from lib.topogen import Topogen, TopoRouter, get_topogen
|
||||||
|
|
||||||
|
|
||||||
|
pytestmark = [pytest.mark.bgpd]
|
||||||
|
|
||||||
|
|
||||||
|
def build_topo(tgen):
|
||||||
|
for routern in range(1, 5):
|
||||||
|
tgen.add_router("r{}".format(routern))
|
||||||
|
|
||||||
|
switch = tgen.add_switch("s1")
|
||||||
|
switch.add_link(tgen.gears["r1"])
|
||||||
|
switch.add_link(tgen.gears["r2"])
|
||||||
|
|
||||||
|
switch = tgen.add_switch("s2")
|
||||||
|
switch.add_link(tgen.gears["r2"])
|
||||||
|
switch.add_link(tgen.gears["r3"])
|
||||||
|
|
||||||
|
|
||||||
|
def setup_module(mod):
|
||||||
|
tgen = Topogen(build_topo, mod.__name__)
|
||||||
|
tgen.start_topology()
|
||||||
|
|
||||||
|
router_list = tgen.routers()
|
||||||
|
|
||||||
|
for i, (rname, router) in enumerate(router_list.items(), 1):
|
||||||
|
router.load_config(
|
||||||
|
TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
|
||||||
|
)
|
||||||
|
router.load_config(
|
||||||
|
TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
|
||||||
|
)
|
||||||
|
|
||||||
|
tgen.start_router()
|
||||||
|
|
||||||
|
|
||||||
|
def teardown_module(mod):
|
||||||
|
tgen = get_topogen()
|
||||||
|
tgen.stop_topology()
|
||||||
|
|
||||||
|
|
||||||
|
def test_bgp_set_aspath_exclude():
|
||||||
|
tgen = get_topogen()
|
||||||
|
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
def _bgp_converge(router):
|
||||||
|
output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json"))
|
||||||
|
expected = {
|
||||||
|
"routes": {
|
||||||
|
"172.16.255.31/32": [{"path": "65002"}],
|
||||||
|
"172.16.255.32/32": [{"path": ""}],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return topotest.json_cmp(output, expected)
|
||||||
|
|
||||||
|
test_func = functools.partial(_bgp_converge, 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 route-map"
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
args = ["-s"] + sys.argv[1:]
|
||||||
|
sys.exit(pytest.main(args))
|
Loading…
Reference in New Issue
Block a user