mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-05 20:07:46 +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;
|
||||
}
|
||||
|
||||
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. */
|
||||
static struct aspath *aspath_add_asns(struct aspath *aspath, as_t asno,
|
||||
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_filter_exclude(struct aspath *source,
|
||||
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,
|
||||
unsigned num);
|
||||
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' */
|
||||
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.
|
||||
* 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
|
||||
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 aspath_exclude *ase = rule;
|
||||
|
||||
exclude_path = rule;
|
||||
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)
|
||||
new_path = aspath_dup(path->attr->aspath);
|
||||
else
|
||||
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;
|
||||
}
|
||||
@ -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 = {
|
||||
"as-path exclude",
|
||||
route_set_aspath_exclude,
|
||||
route_aspath_compile,
|
||||
route_aspath_free,
|
||||
route_aspath_exclude_compile,
|
||||
route_aspath_exclude_free,
|
||||
};
|
||||
|
||||
/* `set as-path replace AS-PATH` */
|
||||
@ -5910,6 +5947,32 @@ DEFUN_YANG (set_aspath_exclude,
|
||||
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,
|
||||
no_set_aspath_exclude_cmd,
|
||||
"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_lastas_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, &no_set_aspath_prepend_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
|
||||
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:
|
||||
|
||||
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