]> git.puffer.fish Git - mirror/frr.git/commitdiff
bgpd: add 'set as-path replace' with a configured ASN 13836/head
authorPhilippe Guibert <philippe.guibert@6wind.com>
Mon, 19 Jun 2023 14:22:55 +0000 (16:22 +0200)
committerPhilippe Guibert <philippe.guibert@6wind.com>
Wed, 28 Jun 2023 19:21:55 +0000 (21:21 +0200)
There is no route-map set action to replace any ASN,
or a part of an ASN, with a configured ASN.

The current commit adds a new command to use a configured
ASN as replacement, instead of using the local as number.

> set as-path replace any 65500

Update the 'bgp_set_aspath_replace' test.

Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
bgpd/bgp_routemap.c
doc/user/bgp.rst
tests/topotests/bgp_set_aspath_replace/r1/bgpd.conf
tests/topotests/bgp_set_aspath_replace/test_bgp_set_aspath_replace.py

index 8b633f8402f2ae83a5f128d11ea550bff5404df2..cf915c5d8a0aa7985a463b85c3b6ef5d9d5ede87 100644 (file)
@@ -2385,8 +2385,10 @@ route_set_aspath_replace(void *rule, const struct prefix *dummy, void *object)
        struct aspath *aspath_new;
        const char *replace = rule;
        struct bgp_path_info *path = object;
-       as_t own_asn = path->peer->change_local_as ? path->peer->change_local_as
-                                                  : path->peer->local_as;
+       as_t replace_asn = 0;
+       as_t configured_asn;
+       char *buf;
+       char src_asn[ASN_STRING_MAX_SIZE];
 
        if (path->peer->sort != BGP_PEER_EBGP) {
                zlog_warn(
@@ -2394,6 +2396,29 @@ route_set_aspath_replace(void *rule, const struct prefix *dummy, void *object)
                return RMAP_NOOP;
        }
 
+       buf = strchr(replace, ' ');
+       if (!buf) {
+               configured_asn = path->peer->change_local_as
+                                        ? path->peer->change_local_as
+                                        : path->peer->local_as;
+       } else {
+               memcpy(src_asn, replace, (size_t)(buf - replace));
+               src_asn[(size_t)(buf - replace)] = '\0';
+               replace = src_asn;
+               buf++;
+               if (!asn_str2asn(buf, &configured_asn)) {
+                       zlog_warn(
+                               "`set as-path replace`, invalid configured AS %s",
+                               buf);
+                       return RMAP_NOOP;
+               }
+       }
+
+       if (!strmatch(replace, "any") && !asn_str2asn(replace, &replace_asn)) {
+               zlog_warn("`set as-path replace`, invalid AS %s", replace);
+               return RMAP_NOOP;
+       }
+
        if (path->attr->aspath->refcnt)
                aspath_new = aspath_dup(path->attr->aspath);
        else
@@ -2401,13 +2426,10 @@ route_set_aspath_replace(void *rule, const struct prefix *dummy, void *object)
 
        if (strmatch(replace, "any")) {
                path->attr->aspath =
-                       aspath_replace_all_asn(aspath_new, own_asn);
-       } else {
-               as_t replace_asn = strtoul(replace, NULL, 10);
-
+                       aspath_replace_all_asn(aspath_new, configured_asn);
+       } else
                path->attr->aspath = aspath_replace_specific_asn(
-                       aspath_new, replace_asn, own_asn);
-       }
+                       aspath_new, replace_asn, configured_asn);
 
        aspath_free(aspath_new);
 
@@ -5875,41 +5897,49 @@ DEFUN_YANG (set_aspath_prepend_lastas,
        return nb_cli_apply_changes(vty, NULL);
 }
 
-DEFPY_YANG (set_aspath_replace_asn,
-           set_aspath_replace_asn_cmd,
-           "set as-path replace <any|ASNUM>$replace",
-           SET_STR
-           "Transform BGP AS_PATH attribute\n"
-           "Replace AS number to local AS number\n"
-           "Replace any AS number to local AS number\n"
-           "Replace a specific AS number in plain or dotted format to local AS number\n")
+DEFPY_YANG(set_aspath_replace_asn, set_aspath_replace_asn_cmd,
+          "set as-path replace <any|ASNUM>$replace [<ASNUM>$configured_asn]",
+          SET_STR
+          "Transform BGP AS_PATH attribute\n"
+          "Replace AS number to local or configured AS number\n"
+          "Replace any AS number to local or configured AS number\n"
+          "Replace a specific AS number to local or configured AS number\n"
+          "Define the configured AS number\n")
 {
        const char *xpath =
                "./set-action[action='frr-bgp-route-map:as-path-replace']";
        char xpath_value[XPATH_MAXLEN];
-       as_t as_value;
+       as_t as_value, as_configured_value;
+       char replace_value[ASN_STRING_MAX_SIZE * 2];
 
        if (!strmatch(replace, "any") && !asn_str2asn(replace, &as_value)) {
                vty_out(vty, "%% Invalid AS value %s\n", replace);
                return CMD_WARNING_CONFIG_FAILED;
        }
-
-       nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+       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;
+       }
        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, replace);
+       snprintf(replace_value, sizeof(replace_value), "%s%s%s", replace,
+                configured_asn_str ? " " : "",
+                configured_asn_str ? configured_asn_str : "");
+       nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, replace_value);
        return nb_cli_apply_changes(vty, NULL);
 }
 
-DEFPY_YANG (no_set_aspath_replace_asn,
-           no_set_aspath_replace_asn_cmd,
-           "no set as-path replace [<any|ASNUM>]",
-           NO_STR
-           SET_STR
-           "Transform BGP AS_PATH attribute\n"
-           "Replace AS number to local AS number\n"
-           "Replace any AS number to local AS number\n"
-           "Replace a specific AS number in plain or dotted format to local AS number\n")
+DEFPY_YANG(no_set_aspath_replace_asn, no_set_aspath_replace_asn_cmd,
+          "no set as-path replace [<any|ASNUM>] [<ASNUM>$configured_asn]",
+          NO_STR SET_STR
+          "Transform BGP AS_PATH attribute\n"
+          "Replace AS number to local or configured AS number\n"
+          "Replace any AS number to local or configured AS number\n"
+          "Replace a specific AS number to local or configured AS number\n"
+          "Replace AS number with a configured AS number\n"
+          "Define the configured AS number\n")
 {
        const char *xpath =
                "./set-action[action='frr-bgp-route-map:as-path-replace']";
index 46b22ff452858b5aa887a1042479ace3d1263b57..f09512de327815e2ef94962b2185114127100987 100644 (file)
@@ -2105,10 +2105,11 @@ Using AS Path in Route Map
    Prepend the existing last AS number (the leftmost ASN) to the AS_PATH.
    The no form of this command removes this set operation from the route-map.
 
-.. clicmd:: set as-path replace <any|ASN>
+.. clicmd:: set as-path replace <any|ASN> [<ASN>]
 
-   Replace a specific AS number to local AS number. ``any`` replaces each
-   AS number in the AS-PATH with the local AS number.
+   Replace a specific AS number to local AS number or a configured AS number.
+   ``any`` replaces each AS number in the AS-PATH with either the local AS
+   number or the configured AS number.
 
 .. clicmd:: set as-path exclude all
 
index 1e98f4e491df386a3219d272c91acef1cd887027..f586c1f99c0e95961ed5a67c56aa3ee9b217b8b5 100644 (file)
@@ -9,6 +9,7 @@ router bgp 65001
 !
 ip prefix-list p1 seq 5 permit 172.16.255.31/32
 !
+bgp route-map delay-timer 1
 route-map r2 permit 10
  match ip address prefix-list p1
  set as-path replace 65003
index 463df2f2a6881984c906b57e0edcb3c1ac0d5464..0433c15e0a1ebf589d34fef14c5726b1f6762618 100644 (file)
@@ -24,6 +24,7 @@ sys.path.append(os.path.join(CWD, "../"))
 # pylint: disable=C0413
 from lib import topotest
 from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
 
 pytestmark = [pytest.mark.bgpd]
 
@@ -63,7 +64,7 @@ def teardown_module(mod):
     tgen.stop_topology()
 
 
-def test_bgp_maximum_prefix_out():
+def test_bgp_set_aspath_replace_test1():
     tgen = get_topogen()
 
     if tgen.routers_have_failure():
@@ -85,6 +86,40 @@ def test_bgp_maximum_prefix_out():
     assert result is None, "Failed overriding incoming AS-PATH with route-map"
 
 
+def test_bgp_set_aspath_replace_test2():
+    tgen = get_topogen()
+
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+    logger.info("Configuring r1 to replace the matching AS with a configured ASN")
+    router = tgen.gears["r1"]
+    router.vtysh_cmd(
+        "configure terminal\nroute-map r2 permit 10\nset as-path replace 65003 65500\n",
+        isjson=False,
+    )
+    router.vtysh_cmd(
+        "configure terminal\nroute-map r2 permit 20\nset as-path replace any 65501\n",
+        isjson=False,
+    )
+
+    def _bgp_converge(router):
+        output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json"))
+        expected = {
+            "routes": {
+                "172.16.255.31/32": [{"path": "65002 65500"}],
+                "172.16.255.32/32": [{"path": "65501 65501"}],
+            }
+        }
+        return topotest.json_cmp(output, expected)
+
+    test_func = functools.partial(_bgp_converge, router)
+    _, 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 replace with configured ASN"
+
+
 if __name__ == "__main__":
     args = ["-s"] + sys.argv[1:]
     sys.exit(pytest.main(args))