From 1c035c8c2c1bc57699697e7d35a631c9e6a1df55 Mon Sep 17 00:00:00 2001 From: Donatas Abraitis Date: Wed, 10 May 2023 23:37:47 +0300 Subject: [PATCH] bgpd: Implement `match source-protocol` for route-maps The main idea is to filter routes by matching source (originating) protocol for outgoing direction. For instance, filter outgoing routes to an arbitrary router that are static only. Or filter out only routes learned from RIP. Signed-off-by: Donatas Abraitis --- bgpd/bgp_routemap.c | 79 +++++++++++++++++++++++++++++++++++ bgpd/bgp_routemap_nb.c | 7 ++++ bgpd/bgp_routemap_nb.h | 4 ++ bgpd/bgp_routemap_nb_config.c | 54 ++++++++++++++++++++++++ lib/routemap.h | 2 + lib/routemap_cli.c | 7 +++- yang/frr-bgp-route-map.yang | 17 ++++++++ 7 files changed, 168 insertions(+), 2 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 @@ -367,6 +367,60 @@ lib_route_map_entry_match_condition_rmap_match_condition_rpki_destroy( return NB_OK; } +/* + * 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 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/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" {