summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_routemap.c79
-rw-r--r--bgpd/bgp_routemap_nb.c7
-rw-r--r--bgpd/bgp_routemap_nb.h4
-rw-r--r--bgpd/bgp_routemap_nb_config.c54
-rw-r--r--doc/user/routemap.rst2
-rw-r--r--lib/routemap.h2
-rw-r--r--lib/routemap_cli.c7
-rw-r--r--tests/topotests/bgp_route_map_match_source_protocol/__init__.py0
-rw-r--r--tests/topotests/bgp_route_map_match_source_protocol/r1/frr.conf32
-rw-r--r--tests/topotests/bgp_route_map_match_source_protocol/r2/frr.conf10
-rw-r--r--tests/topotests/bgp_route_map_match_source_protocol/r3/frr.conf10
-rw-r--r--tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py115
-rw-r--r--yang/frr-bgp-route-map.yang17
13 files changed, 336 insertions, 3 deletions
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index 10fc3ecda4..7db110be93 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -872,6 +872,46 @@ static const struct route_map_rule_cmd
route_match_ip_next_hop_type_free
};
+/* `match source-protocol` */
+static enum route_map_cmd_result_t
+route_match_source_protocol(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ struct bgp_path_info *path = object;
+ int *protocol = rule;
+
+ if (!path)
+ return RMAP_NOMATCH;
+
+ if (path->type == *protocol)
+ return RMAP_MATCH;
+
+ return RMAP_NOMATCH;
+}
+
+static void *route_match_source_protocol_compile(const char *arg)
+{
+ int *protocol;
+
+ protocol = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(*protocol));
+ *protocol = proto_name2num(arg);
+
+ return protocol;
+}
+
+static void route_match_source_protocol_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd route_match_source_protocol_cmd = {
+ "source-protocol",
+ route_match_source_protocol,
+ route_match_source_protocol_compile,
+ route_match_source_protocol_free
+};
+
+
/* `match ip route-source prefix-list PREFIX_LIST' */
static enum route_map_cmd_result_t
@@ -7177,6 +7217,42 @@ DEFPY_YANG (match_rpki_extcommunity,
return nb_cli_apply_changes(vty, NULL);
}
+DEFPY_YANG (match_source_protocol,
+ match_source_protocol_cmd,
+ "match source-protocol " FRR_REDIST_STR_ZEBRA "$proto",
+ MATCH_STR
+ "Match protocol via which the route was learnt\n"
+ FRR_REDIST_HELP_STR_ZEBRA)
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:source-protocol']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:source-protocol",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, proto);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG (no_match_source_protocol,
+ no_match_source_protocol_cmd,
+ "no match source-protocol [" FRR_REDIST_STR_ZEBRA "]",
+ NO_STR
+ MATCH_STR
+ "Match protocol via which the route was learnt\n"
+ FRR_REDIST_HELP_STR_ZEBRA)
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:source-protocol']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
/* Initialization of route map. */
void bgp_route_map_init(void)
{
@@ -7252,6 +7328,7 @@ void bgp_route_map_init(void)
route_map_install_match(&route_match_ip_address_prefix_list_cmd);
route_map_install_match(&route_match_ip_next_hop_prefix_list_cmd);
route_map_install_match(&route_match_ip_next_hop_type_cmd);
+ route_map_install_match(&route_match_source_protocol_cmd);
route_map_install_match(&route_match_ip_route_source_prefix_list_cmd);
route_map_install_match(&route_match_aspath_cmd);
route_map_install_match(&route_match_community_cmd);
@@ -7441,6 +7518,8 @@ void bgp_route_map_init(void)
install_element(RMAP_NODE, &set_ipv6_nexthop_peer_cmd);
install_element(RMAP_NODE, &no_set_ipv6_nexthop_peer_cmd);
install_element(RMAP_NODE, &match_rpki_extcommunity_cmd);
+ install_element(RMAP_NODE, &match_source_protocol_cmd);
+ install_element(RMAP_NODE, &no_match_source_protocol_cmd);
#ifdef HAVE_SCRIPTING
install_element(RMAP_NODE, &match_script_cmd);
#endif
diff --git a/bgpd/bgp_routemap_nb.c b/bgpd/bgp_routemap_nb.c
index 6e8439cc26..282ebe9116 100644
--- a/bgpd/bgp_routemap_nb.c
+++ b/bgpd/bgp_routemap_nb.c
@@ -74,6 +74,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = {
.destroy = lib_route_map_entry_match_condition_rmap_match_condition_source_vrf_destroy,
}
},
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:source-protocol",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_destroy,
+ }
+ },
{
.xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:peer-ipv4-address",
.cbs = {
diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h
index bcd1e837e8..7066fdb419 100644
--- a/bgpd/bgp_routemap_nb.h
+++ b/bgpd/bgp_routemap_nb.h
@@ -30,6 +30,10 @@ int lib_route_map_entry_match_condition_rmap_match_condition_rpki_extcommunity_m
struct nb_cb_modify_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_rpki_extcommunity_destroy(
struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_modify(
+ struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_destroy(
+ struct nb_cb_destroy_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_probability_modify(struct nb_cb_modify_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_probability_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_source_vrf_modify(struct nb_cb_modify_args *args);
diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c
index 938a5ec31b..02564b0004 100644
--- a/bgpd/bgp_routemap_nb_config.c
+++ b/bgpd/bgp_routemap_nb_config.c
@@ -369,6 +369,60 @@ lib_route_map_entry_match_condition_rmap_match_condition_rpki_destroy(
/*
* XPath:
+ * /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:source-protocol
+ */
+int lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ enum rmap_compile_rets ret;
+ const char *proto;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ proto = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "source-protocol";
+ rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, "source-protocol",
+ proto, RMAP_EVENT_MATCH_ADDED,
+ args->errmsg, args->errmsg_len);
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
* /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:rpki-extcommunity
*/
int lib_route_map_entry_match_condition_rmap_match_condition_rpki_extcommunity_modify(
diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst
index b7f5336564..bd19ae88e2 100644
--- a/doc/user/routemap.rst
+++ b/doc/user/routemap.rst
@@ -212,7 +212,7 @@ Route Map Match Command
.. clicmd:: match source-protocol PROTOCOL_NAME
- This is a ZEBRA specific match command. Matches the
+ This is a ZEBRA and BGP specific match command. Matches the
originating protocol specified.
.. clicmd:: match source-instance NUMBER
diff --git a/lib/routemap.h b/lib/routemap.h
index 9b2e18b4a7..7277744dc5 100644
--- a/lib/routemap.h
+++ b/lib/routemap.h
@@ -259,6 +259,8 @@ DECLARE_QOBJ_TYPE(route_map);
(strmatch(C, "frr-zebra-route-map:ipv4-next-hop-prefix-length"))
#define IS_MATCH_SRC_PROTO(C) \
(strmatch(C, "frr-zebra-route-map:source-protocol"))
+#define IS_MATCH_BGP_SRC_PROTO(C) \
+ (strmatch(C, "frr-bgp-route-map:source-protocol"))
#define IS_MATCH_SRC_INSTANCE(C) \
(strmatch(C, "frr-zebra-route-map:source-instance"))
/* BGP route-map match conditions */
diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c
index 419086c4c6..0ccc78e838 100644
--- a/lib/routemap_cli.c
+++ b/lib/routemap_cli.c
@@ -599,11 +599,14 @@ void route_map_condition_show(struct vty *vty, const struct lyd_node *dnode,
yang_dnode_get_string(
dnode,
"./rmap-match-condition/frr-zebra-route-map:ipv4-prefix-length"));
- } else if (IS_MATCH_SRC_PROTO(condition)) {
+ } else if (IS_MATCH_SRC_PROTO(condition) ||
+ IS_MATCH_BGP_SRC_PROTO(condition)) {
vty_out(vty, " match source-protocol %s\n",
yang_dnode_get_string(
dnode,
- "./rmap-match-condition/frr-zebra-route-map:source-protocol"));
+ IS_MATCH_SRC_PROTO(condition)
+ ? "./rmap-match-condition/frr-zebra-route-map:source-protocol"
+ : "./rmap-match-condition/frr-bgp-route-map:source-protocol"));
} else if (IS_MATCH_SRC_INSTANCE(condition)) {
vty_out(vty, " match source-instance %s\n",
yang_dnode_get_string(
diff --git a/tests/topotests/bgp_route_map_match_source_protocol/__init__.py b/tests/topotests/bgp_route_map_match_source_protocol/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_route_map_match_source_protocol/__init__.py
diff --git a/tests/topotests/bgp_route_map_match_source_protocol/r1/frr.conf b/tests/topotests/bgp_route_map_match_source_protocol/r1/frr.conf
new file mode 100644
index 0000000000..7c3efeea4b
--- /dev/null
+++ b/tests/topotests/bgp_route_map_match_source_protocol/r1/frr.conf
@@ -0,0 +1,32 @@
+!
+interface lo
+ ip address 172.16.255.1/32
+!
+interface r1-eth0
+ ip address 192.168.1.1/24
+!
+interface r1-eth1
+ ip address 192.168.2.1/24
+!
+ip route 10.10.10.10/32 192.168.2.2
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as external
+ neighbor 192.168.1.2 timers 1 3
+ neighbor 192.168.1.2 timers connect 1
+ neighbor 192.168.2.2 remote-as external
+ neighbor 192.168.2.2 timers 1 3
+ neighbor 192.168.2.2 timers connect 1
+ address-family ipv4
+ redistribute connected
+ redistribute static
+ neighbor 192.168.1.2 route-map r2 out
+ neighbor 192.168.2.2 route-map r3 out
+ exit-address-family
+!
+route-map r2 permit 10
+ match source-protocol static
+route-map r3 permit 10
+ match source-protocol connected
+!
diff --git a/tests/topotests/bgp_route_map_match_source_protocol/r2/frr.conf b/tests/topotests/bgp_route_map_match_source_protocol/r2/frr.conf
new file mode 100644
index 0000000000..7213975cc0
--- /dev/null
+++ b/tests/topotests/bgp_route_map_match_source_protocol/r2/frr.conf
@@ -0,0 +1,10 @@
+!
+interface r2-eth0
+ ip address 192.168.1.2/24
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ neighbor 192.168.1.1 timers 1 3
+ neighbor 192.168.1.1 timers connect 1
+!
diff --git a/tests/topotests/bgp_route_map_match_source_protocol/r3/frr.conf b/tests/topotests/bgp_route_map_match_source_protocol/r3/frr.conf
new file mode 100644
index 0000000000..4a1d830b0a
--- /dev/null
+++ b/tests/topotests/bgp_route_map_match_source_protocol/r3/frr.conf
@@ -0,0 +1,10 @@
+!
+interface r3-eth0
+ ip address 192.168.2.2/24
+!
+router bgp 65003
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.1 remote-as external
+ neighbor 192.168.2.1 timers 1 3
+ neighbor 192.168.2.1 timers connect 1
+!
diff --git a/tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py b/tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py
new file mode 100644
index 0000000000..2828796405
--- /dev/null
+++ b/tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if r1 can announce only static routes to r2, and only connected
+routes to r3 using `match source-protocol` with 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, 4):
+ 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["r1"])
+ 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_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_source_protocol():
+ 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 neighbors 192.168.1.2 advertised-routes json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {
+ "10.10.10.10/32": {
+ "valid": True,
+ }
+ },
+ "totalPrefixCounter": 1,
+ }
+ 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, "Failed to filter routes by source-protocol for r2"
+
+ def _bgp_check_advertised_routes_r3():
+ output = json.loads(
+ tgen.gears["r1"].vtysh_cmd(
+ "show bgp ipv4 unicast neighbors 192.168.2.2 advertised-routes json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {
+ "192.168.1.0/24": {
+ "valid": True,
+ },
+ "192.168.2.0/24": {
+ "valid": True,
+ },
+ "172.16.255.1/32": {
+ "valid": True,
+ },
+ },
+ "totalPrefixCounter": 3,
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_advertised_routes_r3)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Failed to filter routes by source-protocol for r3"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang
index 23e5b0227c..b557cabb22 100644
--- a/yang/frr-bgp-route-map.yang
+++ b/yang/frr-bgp-route-map.yang
@@ -23,6 +23,10 @@ module frr-bgp-route-map {
prefix rt-types;
}
+ import frr-route-types {
+ prefix frr-route-types;
+ }
+
organization
"Free Range Routing";
contact
@@ -168,6 +172,12 @@ module frr-bgp-route-map {
"Match IPv6 next hop address";
}
+ identity source-protocol {
+ base frr-route-map:rmap-match-type;
+ description
+ "Match protocol via which the route was learnt";
+ }
+
identity distance {
base frr-route-map:rmap-set-type;
description
@@ -759,6 +769,13 @@ module frr-bgp-route-map {
"IPv6 address";
}
}
+
+ case source-protocol {
+ when "derived-from-or-self(../frr-route-map:condition, 'frr-bgp-route-map:source-protocol')";
+ leaf source-protocol {
+ type frr-route-types:frr-route-types;
+ }
+ }
}
augment "/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:rmap-set-action/frr-route-map:set-action" {