diff --git a/doc/user/ripngd.rst b/doc/user/ripngd.rst index f898bed57a..f55ee39445 100644 --- a/doc/user/ripngd.rst +++ b/doc/user/ripngd.rst @@ -136,7 +136,7 @@ functionality. range is very large for compatibility with other protocols. For RIPng, valid metric values are from 1 to 16. -.. clicmd:: set tag (1-4294967295) +.. clicmd:: set tag Set a tag on the matched route. diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst index 791762aa7b..1d2f4e352f 100644 --- a/doc/user/routemap.rst +++ b/doc/user/routemap.rst @@ -176,10 +176,9 @@ Route Map Match Command Matches the specified `metric`. -.. clicmd:: match tag TAG +.. clicmd:: match tag - Matches the specified tag value associated with the route. This tag value - can be in the range of (1-4294967295). + Matches the specified tag (or untagged) value associated with the route. .. clicmd:: match local-preference METRIC @@ -241,9 +240,10 @@ Route Map Set Command .. program:: configure -.. clicmd:: set tag TAG +.. clicmd:: set tag + + Set a tag on the matched route. - Set a tag on the matched route. This tag value can be from (1-4294967295). Additionally if you have compiled with the :option:`--enable-realms` configure option. Tag values from (1-255) are sent to the Linux kernel as a realm value. Then route policy can be applied. See the tc man page. As diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index a12a07c14f..8e2e497e09 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -490,29 +490,33 @@ DEFPY_YANG( DEFPY_YANG( match_tag, match_tag_cmd, - "match tag (1-4294967295)$tag", + "match tag ", MATCH_STR "Match tag of route\n" + "Untagged route\n" "Tag value\n") { const char *xpath = "./match-condition[condition='frr-route-map:match-tag']"; char xpath_value[XPATH_MAXLEN]; + char value[64]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); snprintf(xpath_value, sizeof(xpath_value), "%s/rmap-match-condition/tag", xpath); - nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, tag_str); + snprintf(value, sizeof(value), "%lu", tagged ? tagged : 0); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, value); return nb_cli_apply_changes(vty, NULL); } DEFPY_YANG( no_match_tag, no_match_tag_cmd, - "no match tag [(1-4294967295)]", + "no match tag []", NO_STR MATCH_STR "Match tag of route\n" + "Untagged route\n" "Tag value\n") { const char *xpath = @@ -581,9 +585,15 @@ void route_map_condition_show(struct vty *vty, const struct lyd_node *dnode, yang_dnode_get_string(dnode, "./rmap-match-condition/metric")); } else if (IS_MATCH_TAG(condition)) { - vty_out(vty, " match tag %s\n", - yang_dnode_get_string(dnode, - "./rmap-match-condition/tag")); + uint32_t tag = + strtoul(yang_dnode_get_string(dnode, + "./rmap-match-condition/tag"), + NULL, 10); + + if (!tag) + vty_out(vty, " match tag untagged\n"); + else + vty_out(vty, " match tag %u\n", tag); } else if (IS_MATCH_IPv4_PREFIX_LEN(condition)) { vty_out(vty, " match ip address prefix-len %s\n", yang_dnode_get_string( @@ -973,28 +983,32 @@ DEFPY_YANG(no_set_max_metric, no_set_max_metric_cmd, DEFPY_YANG( set_tag, set_tag_cmd, - "set tag (1-4294967295)$tag", + "set tag ", SET_STR "Tag value for routing protocol\n" + "Untagged route\n" "Tag value\n") { const char *xpath = "./set-action[action='frr-route-map:set-tag']"; char xpath_value[XPATH_MAXLEN]; + char value[64]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); snprintf(xpath_value, sizeof(xpath_value), "%s/rmap-set-action/tag", xpath); - nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, tag_str); + snprintf(value, sizeof(value), "%lu", tagged ? tagged : 0); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, value); return nb_cli_apply_changes(vty, NULL); } DEFPY_YANG( no_set_tag, no_set_tag_cmd, - "no set tag [(1-4294967295)]", + "no set tag []", NO_STR SET_STR "Tag value for routing protocol\n" + "Untagged route\n" "Tag value\n") { const char *xpath = "./set-action[action='frr-route-map:set-tag']"; @@ -1101,8 +1115,15 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *dnode, yang_dnode_get_string(dnode, "./rmap-set-action/max-metric")); } else if (IS_SET_TAG(action)) { - vty_out(vty, " set tag %s\n", - yang_dnode_get_string(dnode, "rmap-set-action/tag")); + uint32_t tag = + strtoul(yang_dnode_get_string(dnode, + "rmap-set-action/tag"), + NULL, 10); + + if (!tag) + vty_out(vty, " set tag untagged\n"); + else + vty_out(vty, " set tag %u\n", tag); } else if (IS_SET_SR_TE_COLOR(action)) { vty_out(vty, " set sr-te color %s\n", yang_dnode_get_string(dnode, diff --git a/tests/topotests/bgp_route_map_match_tag_untagged/__init__.py b/tests/topotests/bgp_route_map_match_tag_untagged/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_route_map_match_tag_untagged/r1/frr.conf b/tests/topotests/bgp_route_map_match_tag_untagged/r1/frr.conf new file mode 100644 index 0000000000..13eced2042 --- /dev/null +++ b/tests/topotests/bgp_route_map_match_tag_untagged/r1/frr.conf @@ -0,0 +1,19 @@ +! +interface r1-eth0 + ip address 192.168.1.1/24 +! +router bgp 65001 + address-family ipv4 + redistribute static route-map untagged + exit-address-family +! +ip route 10.10.10.10/32 Null0 +ip route 10.10.10.20/32 Null0 tag 20 +! +route-map untagged permit 10 + match tag untagged + set tag 10 +route-map untagged permit 20 + match tag 20 + set tag untagged +exit diff --git a/tests/topotests/bgp_route_map_match_tag_untagged/test_bgp_route_map_match_tag_untagged.py b/tests/topotests/bgp_route_map_match_tag_untagged/test_bgp_route_map_match_tag_untagged.py new file mode 100644 index 0000000000..7dd63fdac0 --- /dev/null +++ b/tests/topotests/bgp_route_map_match_tag_untagged/test_bgp_route_map_match_tag_untagged.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2024 by +# Donatas Abraitis +# + +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, 2): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_route_map_match_tag_untagged(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_check_advertised_routes_r2(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show bgp ipv4 unicast detail json") + ) + expected = { + "routes": { + "10.10.10.10/32": [ + { + "tag": 10, + } + ], + "10.10.10.20/32": [ + { + "tag": None, + } + ], + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_advertised_routes_r2) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Tags for static routes are not as expected" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/yang/frr-route-map.yang b/yang/frr-route-map.yang index 7cb13b60f2..26d56acc03 100644 --- a/yang/frr-route-map.yang +++ b/yang/frr-route-map.yang @@ -268,7 +268,7 @@ module frr-route-map { when "derived-from-or-self(../condition, 'match-tag')"; leaf tag { type uint32 { - range "1..4294967295"; + range "0..4294967295"; } } }