diff options
-rw-r--r-- | bgpd/bgp_ecommunity.c | 70 | ||||
-rw-r--r-- | bgpd/bgp_ecommunity.h | 39 | ||||
-rw-r--r-- | bgpd/bgp_route.c | 15 | ||||
-rw-r--r-- | bgpd/bgp_routemap.c | 97 | ||||
-rw-r--r-- | bgpd/bgp_routemap_nb.c | 7 | ||||
-rw-r--r-- | bgpd/bgp_routemap_nb.h | 4 | ||||
-rw-r--r-- | bgpd/bgp_routemap_nb_config.c | 54 | ||||
-rw-r--r-- | doc/user/overview.rst | 4 | ||||
-rw-r--r-- | doc/user/rpki.rst | 6 | ||||
-rw-r--r-- | lib/routemap.h | 2 | ||||
-rw-r--r-- | lib/routemap_cli.c | 5 | ||||
-rw-r--r-- | yang/frr-bgp-route-map.yang | 29 |
12 files changed, 332 insertions, 0 deletions
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 3f627521e..f57e9ae88 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -394,6 +394,44 @@ enum ecommunity_token { ecommunity_token_val6, }; +static const char *ecommunity_origin_validation_state2str( + enum ecommunity_origin_validation_states state) +{ + switch (state) { + case ECOMMUNITY_ORIGIN_VALIDATION_STATE_VALID: + return "valid"; + case ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTFOUND: + return "not-found"; + case ECOMMUNITY_ORIGIN_VALIDATION_STATE_INVALID: + return "invalid"; + case ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTUSED: + return "not-used"; + } + + return "ERROR"; +} + +static void ecommunity_origin_validation_state_str(char *buf, size_t bufsz, + uint8_t *ptr) +{ + /* Origin Validation State is encoded in the last octet + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x43 | 0x00 | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reserved |validationstate| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + uint8_t state = *(ptr + ECOMMUNITY_SIZE - 3); + + snprintf(buf, bufsz, "OVS:%s", + ecommunity_origin_validation_state2str(state)); + + (void)ptr; /* consume value */ +} + static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type, int trans, as_t as, struct in_addr *ip, @@ -1172,6 +1210,13 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) ecom->disable_ieee_floating); else unk_ecom = 1; + } else if (type == ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS) { + sub_type = *pnt++; + if (sub_type == ECOMMUNITY_ORIGIN_VALIDATION_STATE) + ecommunity_origin_validation_state_str( + encbuf, sizeof(encbuf), pnt); + else + unk_ecom = 1; } else { sub_type = *pnt++; unk_ecom = 1; @@ -1541,6 +1586,31 @@ void bgp_remove_ecomm_from_aggregate_hash(struct bgp_aggregate *aggregate, } } +struct ecommunity * +ecommunity_add_origin_validation_state(enum rpki_states rpki_state, + struct ecommunity *old) +{ + struct ecommunity *new = NULL; + struct ecommunity ovs_ecomm = {0}; + struct ecommunity_val ovs_eval; + + encode_origin_validation_state(rpki_state, &ovs_eval); + + if (old) { + new = ecommunity_dup(old); + ecommunity_add_val(new, &ovs_eval, true, true); + if (!old->refcnt) + ecommunity_free(&old); + } else { + ovs_ecomm.size = 1; + ovs_ecomm.unit_size = ECOMMUNITY_SIZE; + ovs_ecomm.val = (uint8_t *)&ovs_eval.val; + new = ecommunity_dup(&ovs_ecomm); + } + + return new; +} + /* * return the BGP link bandwidth extended community, if present; * the actual bandwidth is returned via param diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 84e310c3f..4d7d4234a 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -22,6 +22,7 @@ #define _QUAGGA_BGP_ECOMMUNITY_H #include "bgpd/bgp_route.h" +#include "bgpd/bgp_rpki.h" #include "bgpd/bgpd.h" /* Refer to rfc7153 for the IANA registry definitions. These are @@ -51,6 +52,7 @@ /* Note: This really depends on the high-order octet. This means that * multiple definitions for the same value are possible. */ +#define ECOMMUNITY_ORIGIN_VALIDATION_STATE 0x00 #define ECOMMUNITY_ROUTE_TARGET 0x02 #define ECOMMUNITY_SITE_ORIGIN 0x03 #define ECOMMUNITY_LINK_BANDWIDTH 0x04 @@ -100,6 +102,14 @@ #define ECOMMUNITY_SIZE 8 #define IPV6_ECOMMUNITY_SIZE 20 +/* Extended Community Origin Validation State */ +enum ecommunity_origin_validation_states { + ECOMMUNITY_ORIGIN_VALIDATION_STATE_VALID, + ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTFOUND, + ECOMMUNITY_ORIGIN_VALIDATION_STATE_INVALID, + ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTUSED +}; + /* Extended Communities type flag. */ #define ECOMMUNITY_FLAG_NON_TRANSITIVE 0x40 @@ -236,6 +246,32 @@ static inline void encode_lb_extcomm(as_t as, uint32_t bw, bool non_trans, eval->val[7] = bandwidth & 0xff; } +static inline void encode_origin_validation_state(enum rpki_states state, + struct ecommunity_val *eval) +{ + enum ecommunity_origin_validation_states ovs_state = + ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTUSED; + + switch (state) { + case RPKI_VALID: + ovs_state = ECOMMUNITY_ORIGIN_VALIDATION_STATE_VALID; + break; + case RPKI_NOTFOUND: + ovs_state = ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTFOUND; + break; + case RPKI_INVALID: + ovs_state = ECOMMUNITY_ORIGIN_VALIDATION_STATE_INVALID; + break; + case RPKI_NOT_BEING_USED: + break; + } + + memset(eval, 0, sizeof(*eval)); + eval->val[0] = ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS; + eval->val[1] = ECOMMUNITY_ORIGIN_VALIDATION_STATE; + eval->val[7] = ovs_state; +} + extern void ecommunity_init(void); extern void ecommunity_finish(void); extern void ecommunity_free(struct ecommunity **); @@ -314,4 +350,7 @@ static inline void ecommunity_strip_rts(struct ecommunity *ecom) ecommunity_strip(ecom, ECOMMUNITY_ENCODE_IP, subtype); ecommunity_strip(ecom, ECOMMUNITY_ENCODE_AS4, subtype); } +extern struct ecommunity * +ecommunity_add_origin_validation_state(enum rpki_states rpki_state, + struct ecommunity *ecom); #endif /* _QUAGGA_BGP_ECOMMUNITY_H */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 9eb7407af..162a7ed88 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -2486,6 +2486,21 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, } } + /* If this is an iBGP, send Origin Validation State (OVS) + * extended community (rfc8097). + */ + if (peer->sort == BGP_PEER_IBGP) { + enum rpki_states rpki_state = RPKI_NOT_BEING_USED; + + rpki_state = hook_call(bgp_rpki_prefix_status, peer, attr, p); + + if (rpki_state != RPKI_NOT_BEING_USED) + bgp_attr_set_ecommunity( + attr, ecommunity_add_origin_validation_state( + rpki_state, + bgp_attr_get_ecommunity(attr))); + } + /* * When the next hop is set to ourselves, if all multipaths have * link-bandwidth announce the cumulative bandwidth as that makes diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 64c867f98..ded47028a 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -3791,6 +3791,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. @@ -6792,6 +6859,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) { @@ -7030,6 +7125,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); @@ -7047,6 +7143,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 2117334f7..c47c37dc6 100644 --- a/bgpd/bgp_routemap_nb.c +++ b/bgpd/bgp_routemap_nb.c @@ -67,6 +67,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = { } }, { + .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 = { .modify = lib_route_map_entry_match_condition_rmap_match_condition_probability_modify, diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h index cd7a70dbc..163e3b55c 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 585c2a3ff..b18cf9d4d 100644 --- a/bgpd/bgp_routemap_nb_config.c +++ b/bgpd/bgp_routemap_nb_config.c @@ -381,6 +381,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: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 */ int diff --git a/doc/user/overview.rst b/doc/user/overview.rst index 91ca1b89f..70bfee283 100644 --- a/doc/user/overview.rst +++ b/doc/user/overview.rst @@ -341,6 +341,8 @@ BGP :t:`The Generalized TTL Security Mechanism (GTSM). V. Gill, J. Heasley, D. Meyer, P. Savola, C. Pingnataro. October 2007.` - :rfc:`5575` :t:`Dissemination of Flow Specification Rules. P. Marques, N. Sheth, R. Raszuk, B. Greene, J. Mauch, D. McPherson. August 2009` +- :rfc:`5668` + :t:`4-Octet AS Specific BGP Extended Community. Y. Rekhter, S. Sangli, D. Tappan October 2009` - :rfc:`6286` :t:`Autonomous-System-Wide Unique BGP Identifier for BGP-4. E. Chen, J. Yuan, June 2011.` - :rfc:`6608` @@ -367,6 +369,8 @@ BGP :t:`BLACKHOLE Community. T. King, C. Dietzel, J. Snijders, G. Doering, G. Hankins. Oct 2016.` - :rfc:`8092` :t:`BGP Large Communities Attribute. J. Heitz, Ed., J. Snijders, Ed, K. Patel, I. Bagdonas, N. Hilliard. February 2017` +- :rfc:`8097` + :t:`BGP Prefix Origin Validation State Extended Community. P. Mohapatra, K. Patel, J. Scudder, D. Ward, R. Bush. March 2017` - :rfc:`8195` :t:`Use of BGP Large Communities. J. Snijders, J. Heasley, M. Schmidt, June 2017` - :rfc:`8203` diff --git a/doc/user/rpki.rst b/doc/user/rpki.rst index cc0e7f70c..405353624 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 0152e820d..a36592585 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 42c7a05d1..6be5d15ec 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 fcfd14e4f..3f3d82921 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 @@ -436,6 +442,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 { |