From 324e8b1f79ccd00f75cdb8ba95ba0c0ee5e82b4d Mon Sep 17 00:00:00 2001 From: Donatas Abraitis Date: Wed, 31 Aug 2022 23:00:26 +0300 Subject: [PATCH] bgpd: Handle Origin Validation State extended community via route-map match Add an ability to match via route-maps. An additional route-map command `match rpki-extcommunity ` added. Signed-off-by: Donatas Abraitis --- bgpd/bgp_ecommunity.h | 4 +- bgpd/bgp_routemap.c | 97 +++++++++++++++++++++++++++++++++++ bgpd/bgp_routemap_nb.c | 7 +++ bgpd/bgp_routemap_nb.h | 4 ++ bgpd/bgp_routemap_nb_config.c | 54 +++++++++++++++++++ doc/user/rpki.rst | 6 +++ lib/routemap.h | 2 + lib/routemap_cli.c | 5 ++ yang/frr-bgp-route-map.yang | 29 +++++++++++ 9 files changed, 206 insertions(+), 2 deletions(-) diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 3d8ef947d5..4d7d4234a2 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -104,10 +104,10 @@ /* Extended Community Origin Validation State */ enum ecommunity_origin_validation_states { - ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTUSED = -1, ECOMMUNITY_ORIGIN_VALIDATION_STATE_VALID, ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTFOUND, - ECOMMUNITY_ORIGIN_VALIDATION_STATE_INVALID + ECOMMUNITY_ORIGIN_VALIDATION_STATE_INVALID, + ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTUSED }; /* Extended Communities type flag. */ diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 33f68c9e88..45ceb6bfde 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -3738,6 +3738,73 @@ static const struct route_map_rule_cmd route_set_originator_id_cmd = { route_set_originator_id_free, }; +static enum route_map_cmd_result_t +route_match_rpki_extcommunity(void *rule, const struct prefix *prefix, + void *object) +{ + struct bgp_path_info *path; + struct ecommunity *ecomm; + struct ecommunity_val *ecomm_val; + enum rpki_states *rpki_status = rule; + enum rpki_states ecomm_rpki_status = RPKI_NOT_BEING_USED; + + path = object; + + ecomm = bgp_attr_get_ecommunity(path->attr); + if (!ecomm) + return RMAP_NOMATCH; + + ecomm_val = ecommunity_lookup(ecomm, ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS, + ECOMMUNITY_ORIGIN_VALIDATION_STATE); + if (!ecomm_val) + return RMAP_NOMATCH; + + /* The Origin Validation State is encoded in the last octet of + * the extended community. + */ + switch (ecomm_val->val[7]) { + case ECOMMUNITY_ORIGIN_VALIDATION_STATE_VALID: + ecomm_rpki_status = RPKI_VALID; + break; + case ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTFOUND: + ecomm_rpki_status = RPKI_NOTFOUND; + break; + case ECOMMUNITY_ORIGIN_VALIDATION_STATE_INVALID: + ecomm_rpki_status = RPKI_INVALID; + break; + case ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTUSED: + break; + } + + if (ecomm_rpki_status == *rpki_status) + return RMAP_MATCH; + + return RMAP_NOMATCH; +} + +static void *route_match_extcommunity_compile(const char *arg) +{ + int *rpki_status; + + rpki_status = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(int)); + + if (strcmp(arg, "valid") == 0) + *rpki_status = RPKI_VALID; + else if (strcmp(arg, "invalid") == 0) + *rpki_status = RPKI_INVALID; + else + *rpki_status = RPKI_NOTFOUND; + + return rpki_status; +} + +static const struct route_map_rule_cmd route_match_rpki_extcommunity_cmd = { + "rpki-extcommunity", + route_match_rpki_extcommunity, + route_match_extcommunity_compile, + route_value_free +}; + /* * This is the workhorse routine for processing in/out routemap * modifications. @@ -6713,6 +6780,34 @@ DEFUN_YANG (no_set_originator_id, return nb_cli_apply_changes(vty, NULL); } +DEFPY_YANG (match_rpki_extcommunity, + match_rpki_extcommunity_cmd, + "[no$no] match rpki-extcommunity ", + NO_STR + MATCH_STR + "BGP RPKI (Origin Validation State) extended community attribute\n" + "Valid prefix\n" + "Invalid prefix\n" + "Prefix not found\n") +{ + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:rpki-extcommunity']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + if (!no) { + snprintf( + xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:rpki-extcommunity", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + argv[2]->arg); + } + + return nb_cli_apply_changes(vty, NULL); +} + /* Initialization of route map. */ void bgp_route_map_init(void) { @@ -6949,6 +7044,7 @@ void bgp_route_map_init(void) route_map_install_set(&route_set_ipv6_nexthop_prefer_global_cmd); route_map_install_set(&route_set_ipv6_nexthop_local_cmd); route_map_install_set(&route_set_ipv6_nexthop_peer_cmd); + route_map_install_match(&route_match_rpki_extcommunity_cmd); install_element(RMAP_NODE, &match_ipv6_next_hop_cmd); install_element(RMAP_NODE, &match_ipv6_next_hop_address_cmd); @@ -6966,6 +7062,7 @@ void bgp_route_map_init(void) install_element(RMAP_NODE, &no_set_ipv6_nexthop_prefer_global_cmd); 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); #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 585596e1aa..21517d66d7 100644 --- a/bgpd/bgp_routemap_nb.c +++ b/bgpd/bgp_routemap_nb.c @@ -66,6 +66,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = { .destroy = 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:rpki-extcommunity", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_rpki_extcommunity_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_rpki_extcommunity_destroy, + } + }, { .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:probability", .cbs = { diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h index a01adf7d5d..6064d3ad1a 100644 --- a/bgpd/bgp_routemap_nb.h +++ b/bgpd/bgp_routemap_nb.h @@ -39,6 +39,10 @@ int lib_route_map_entry_match_condition_rmap_match_condition_origin_modify(struc int lib_route_map_entry_match_condition_rmap_match_condition_origin_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_rpki_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_rpki_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_rpki_extcommunity_modify( + 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_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 b87877b1e0..0b956b5943 100644 --- a/bgpd/bgp_routemap_nb_config.c +++ b/bgpd/bgp_routemap_nb_config.c @@ -380,6 +380,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:rpki-extcommunity + */ +int lib_route_map_entry_match_condition_rmap_match_condition_rpki_extcommunity_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *rpki; + enum rmap_compile_rets ret; + + 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); + rpki = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "rpki-extcommunity"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, "rpki-extcommunity", + rpki, 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_rpki_extcommunity_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:probability */ diff --git a/doc/user/rpki.rst b/doc/user/rpki.rst index cc0e7f70c6..4053536247 100644 --- a/doc/user/rpki.rst +++ b/doc/user/rpki.rst @@ -178,6 +178,12 @@ Validating BGP Updates match rpki valid set local-preference 500 +.. clicmd:: match rpki-extcommunity notfound|invalid|valid + + Create a clause for a route map to match prefixes with the specified RPKI + state, that is derived from the Origin Validation State extended community + attribute (OVS). OVS extended community is non-transitive and is exchanged + only between iBGP peers. .. _debugging: diff --git a/lib/routemap.h b/lib/routemap.h index ad391981e0..1a8a94e375 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -280,6 +280,8 @@ DECLARE_QOBJ_TYPE(route_map); #define IS_MATCH_ORIGIN(C) \ (strmatch(C, "frr-bgp-route-map:match-origin")) #define IS_MATCH_RPKI(C) (strmatch(C, "frr-bgp-route-map:rpki")) +#define IS_MATCH_RPKI_EXTCOMMUNITY(C) \ + (strmatch(C, "frr-bgp-route-map:rpki-extcommunity")) #define IS_MATCH_PROBABILITY(C) \ (strmatch(C, "frr-bgp-route-map:probability")) #define IS_MATCH_SRC_VRF(C) \ diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index ff98a14c41..59253942ad 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -650,6 +650,11 @@ void route_map_condition_show(struct vty *vty, const struct lyd_node *dnode, yang_dnode_get_string( dnode, "./rmap-match-condition/frr-bgp-route-map:rpki")); + } else if (IS_MATCH_RPKI_EXTCOMMUNITY(condition)) { + vty_out(vty, " match rpki-extcommunity %s\n", + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-bgp-route-map:rpki-extcommunity")); } else if (IS_MATCH_PROBABILITY(condition)) { vty_out(vty, " match probability %s\n", yang_dnode_get_string( diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang index eaa7891f0c..84d5b74ddc 100644 --- a/yang/frr-bgp-route-map.yang +++ b/yang/frr-bgp-route-map.yang @@ -66,6 +66,12 @@ module frr-bgp-route-map { "Control rpki specific settings"; } + identity rpki-extcommunity { + base frr-route-map:rmap-match-type; + description + "Control rpki specific settings derived from extended community"; + } + identity probability { base frr-route-map:rmap-match-type; description @@ -430,6 +436,29 @@ module frr-bgp-route-map { } } + case rpki-extcommunity { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'frr-bgp-route-map:rpki-extcommunity')"; + leaf rpki-extcommunity { + type enumeration { + enum "valid" { + value 0; + description + "Valid prefix"; + } + enum "notfound" { + value 1; + description + "Prefix not found"; + } + enum "invalid" { + value 2; + description + "Invalid prefix"; + } + } + } + } + case probability { when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'frr-bgp-route-map:probability')"; leaf probability {