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 <invalid|notfound|valid>` added.

Signed-off-by: Donatas Abraitis <donatas@opensourcerouting.org>
This commit is contained in:
Donatas Abraitis 2022-08-31 23:00:26 +03:00
parent 7b27cf7bbd
commit 324e8b1f79
9 changed files with 206 additions and 2 deletions

View File

@ -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. */

View File

@ -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 <valid|invalid|notfound>",
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

View File

@ -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 = {

View File

@ -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);

View File

@ -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
*/

View File

@ -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:

View File

@ -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) \

View File

@ -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(

View File

@ -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 {