summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilippe Guibert <philippe.guibert@6wind.com>2023-06-13 14:53:03 +0200
committerPhilippe Guibert <philippe.guibert@6wind.com>2023-06-19 18:04:44 +0200
commit92550adfc77d9d01a2d7a96d67d8a5d27f7b6877 (patch)
tree03aba5159dd9ba8a06f27793648f166596715c53
parent0fb16305200113a92e3862e05d6833217f935211 (diff)
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>
-rw-r--r--bgpd/bgp_aspath.c18
-rw-r--r--bgpd/bgp_aspath.h1
-rw-r--r--bgpd/bgp_routemap.c74
-rw-r--r--doc/user/bgp.rst5
-rw-r--r--tests/topotests/bgp_set_aspath_exclude/__init__.py0
-rw-r--r--tests/topotests/bgp_set_aspath_exclude/r1/bgpd.conf17
-rw-r--r--tests/topotests/bgp_set_aspath_exclude/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_set_aspath_exclude/r2/bgpd.conf8
-rw-r--r--tests/topotests/bgp_set_aspath_exclude/r2/zebra.conf9
-rw-r--r--tests/topotests/bgp_set_aspath_exclude/r3/bgpd.conf9
-rw-r--r--tests/topotests/bgp_set_aspath_exclude/r3/zebra.conf10
-rw-r--r--tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py89
12 files changed, 241 insertions, 5 deletions
diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c
index 2c0de43c9b..22c9fe0e66 100644
--- a/bgpd/bgp_aspath.c
+++ b/bgpd/bgp_aspath.c
@@ -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)
diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h
index 18af375c13..a3aae14f8f 100644
--- a/bgpd/bgp_aspath.h
+++ b/bgpd/bgp_aspath.h
@@ -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);
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index 7db110be93..d29b91b48f 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -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);
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index a2585f3a57..215f473c36 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -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
diff --git a/tests/topotests/bgp_set_aspath_exclude/__init__.py b/tests/topotests/bgp_set_aspath_exclude/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_exclude/__init__.py
diff --git a/tests/topotests/bgp_set_aspath_exclude/r1/bgpd.conf b/tests/topotests/bgp_set_aspath_exclude/r1/bgpd.conf
new file mode 100644
index 0000000000..9bef24f931
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_exclude/r1/bgpd.conf
@@ -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
+!
diff --git a/tests/topotests/bgp_set_aspath_exclude/r1/zebra.conf b/tests/topotests/bgp_set_aspath_exclude/r1/zebra.conf
new file mode 100644
index 0000000000..acf120b200
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_exclude/r1/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r1-eth0
+ ip address 192.168.1.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_set_aspath_exclude/r2/bgpd.conf b/tests/topotests/bgp_set_aspath_exclude/r2/bgpd.conf
new file mode 100644
index 0000000000..23367f94ff
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_exclude/r2/bgpd.conf
@@ -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
+!
diff --git a/tests/topotests/bgp_set_aspath_exclude/r2/zebra.conf b/tests/topotests/bgp_set_aspath_exclude/r2/zebra.conf
new file mode 100644
index 0000000000..f229954341
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_exclude/r2/zebra.conf
@@ -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
+!
diff --git a/tests/topotests/bgp_set_aspath_exclude/r3/bgpd.conf b/tests/topotests/bgp_set_aspath_exclude/r3/bgpd.conf
new file mode 100644
index 0000000000..b7a7ceda13
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_exclude/r3/bgpd.conf
@@ -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
+!
diff --git a/tests/topotests/bgp_set_aspath_exclude/r3/zebra.conf b/tests/topotests/bgp_set_aspath_exclude/r3/zebra.conf
new file mode 100644
index 0000000000..3fa6c64484
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_exclude/r3/zebra.conf
@@ -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
+!
diff --git a/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py b/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py
new file mode 100644
index 0000000000..8af7e7d60d
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py
@@ -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))