diff options
author | Donatas Abraitis <donatas@opensourcerouting.org> | 2024-01-22 15:16:34 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-22 15:16:34 +0100 |
commit | 20ec72d7ca84c38894ac8799c4f86bc0fe914a3d (patch) | |
tree | 6c334245401005892300049742ce31930ba2f3ec | |
parent | Merge pull request #15176 from LabNConsulting/chopps/mgmtd-northbound-fixes (diff) | |
parent | topotests: add vrf test to bgp_rpki_topo1 (diff) | |
download | frr-20ec72d7ca84c38894ac8799c4f86bc0fe914a3d.tar.xz frr-20ec72d7ca84c38894ac8799c4f86bc0fe914a3d.zip |
Merge pull request #15052 from louis-6wind/rpki-vrf-92
bgpd: add VRF support to RPKI
-rw-r--r-- | bgpd/bgp_main.c | 28 | ||||
-rw-r--r-- | bgpd/bgp_rpki.c | 1161 | ||||
-rw-r--r-- | bgpd/bgp_rpki.h | 2 | ||||
-rw-r--r-- | bgpd/bgpd.h | 5 | ||||
-rw-r--r-- | doc/user/rpki.rst | 80 | ||||
-rw-r--r-- | lib/command.h | 1 | ||||
-rw-r--r-- | tests/topotests/bgp_rpki_topo1/r2/staticd.conf | 4 | ||||
-rw-r--r-- | tests/topotests/bgp_rpki_topo1/r2/zebra.conf | 3 | ||||
-rw-r--r-- | tests/topotests/bgp_rpki_topo1/r3/bgpd.conf | 14 | ||||
l--------- | tests/topotests/bgp_rpki_topo1/r3/rtrd.py | 1 | ||||
-rw-r--r-- | tests/topotests/bgp_rpki_topo1/r3/staticd.conf | 1 | ||||
l--------- | tests/topotests/bgp_rpki_topo1/r3/vrps.csv | 1 | ||||
-rw-r--r-- | tests/topotests/bgp_rpki_topo1/r3/zebra.conf | 5 | ||||
-rw-r--r-- | tests/topotests/bgp_rpki_topo1/test_bgp_rpki_topo1.py | 147 | ||||
-rw-r--r-- | vtysh/vtysh.c | 19 | ||||
-rw-r--r-- | vtysh/vtysh.h | 2 | ||||
-rw-r--r-- | vtysh/vtysh_config.c | 11 |
17 files changed, 1221 insertions, 264 deletions
diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index e8d5a0307..1666d654c 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -51,10 +51,16 @@ #include "bgpd/bgp_routemap_nb.h" #include "bgpd/bgp_community_alias.h" +DEFINE_HOOK(bgp_hook_config_write_vrf, (struct vty *vty, struct vrf *vrf), + (vty, vrf)); + #ifdef ENABLE_BGP_VNC #include "bgpd/rfapi/rfapi_backend.h" #endif +DEFINE_HOOK(bgp_hook_vrf_update, (struct vrf *vrf, bool enabled), + (vrf, enabled)); + /* bgpd options, we use GNU getopt library. */ static const struct option longopts[] = { { "bgp_port", required_argument, NULL, 'p' }, @@ -287,6 +293,7 @@ static int bgp_vrf_enable(struct vrf *vrf) bgp_handle_socket(bgp, vrf, old_vrf_id, true); bgp_instance_up(bgp); + hook_call(bgp_hook_vrf_update, vrf, true); vpn_leak_zebra_vrf_label_update(bgp, AFI_IP); vpn_leak_zebra_vrf_label_update(bgp, AFI_IP6); vpn_leak_zebra_vrf_sid_update(bgp, AFI_IP); @@ -333,15 +340,36 @@ static int bgp_vrf_disable(struct vrf *vrf) * "down". */ bgp_instance_down(bgp); bgp_vrf_unlink(bgp, vrf); + hook_call(bgp_hook_vrf_update, vrf, false); } /* Note: This is a callback, the VRF will be deleted by the caller. */ return 0; } +static int bgp_vrf_config_write(struct vty *vty) +{ + struct vrf *vrf; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + if (vrf->vrf_id == VRF_DEFAULT) { + vty_out(vty, "!\n"); + continue; + } + vty_out(vty, "vrf %s\n", vrf->name); + + hook_call(bgp_hook_config_write_vrf, vty, vrf); + + vty_out(vty, "exit-vrf\n!\n"); + } + + return 0; +} + static void bgp_vrf_init(void) { vrf_init(bgp_vrf_new, bgp_vrf_enable, bgp_vrf_disable, bgp_vrf_delete); + vrf_cmd_init(bgp_vrf_config_write); } static void bgp_vrf_terminate(void) diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index 78083a83a..219cb2998 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -44,18 +44,19 @@ #include "bgpd/bgp_rpki_clippy.c" +DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_TEMP, "BGP RPKI Intermediate Buffer"); DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE, "BGP RPKI Cache server"); DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE_GROUP, "BGP RPKI Cache server group"); DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_RTRLIB, "BGP RPKI RTRLib"); DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_REVALIDATE, "BGP RPKI Revalidation"); +#define STR_SEPARATOR 10 + #define POLLING_PERIOD_DEFAULT 3600 #define EXPIRE_INTERVAL_DEFAULT 7200 #define RETRY_INTERVAL_DEFAULT 600 #define BGP_RPKI_CACHE_SERVER_SYNC_RETRY_TIMEOUT 3 -static struct event *t_rpki_sync; - #define RPKI_DEBUG(...) \ if (rpki_debug_conf || rpki_debug_term) { \ zlog_debug("RPKI: " __VA_ARGS__); \ @@ -77,6 +78,7 @@ struct cache { } tr_config; struct rtr_socket *rtr_socket; uint8_t preference; + struct rpki_vrf *rpki_vrf; }; enum return_values { SUCCESS = 0, ERROR = -1 }; @@ -89,33 +91,59 @@ struct rpki_for_each_record_arg { enum asnotation_mode asnotation; }; +struct rpki_vrf { + struct rtr_mgr_config *rtr_config; + struct list *cache_list; + bool rtr_is_running; + bool rtr_is_stopping; + bool rtr_is_synced; + _Atomic int rtr_update_overflow; + unsigned int polling_period; + unsigned int expire_interval; + unsigned int retry_interval; + int rpki_sync_socket_rtr; + int rpki_sync_socket_bgpd; + char *vrfname; + struct event *t_rpki_sync; + + QOBJ_FIELDS; +}; + +static struct rpki_vrf *find_rpki_vrf(const char *vrfname); +static int bgp_rpki_vrf_update(struct vrf *vrf, bool enabled); +static int bgp_rpki_write_vrf(struct vty *vty, struct vrf *vrf); +static int bgp_rpki_hook_write_vrf(struct vty *vty, struct vrf *vrf); static int bgp_rpki_write_debug(struct vty *vty, bool running); -static int start(void); -static void stop(void); -static int reset(bool force); -static struct rtr_mgr_group *get_connected_group(void); -static void print_prefix_table(struct vty *vty, json_object *json); +static int start(struct rpki_vrf *rpki_vrf); +static void stop(struct rpki_vrf *rpki_vrf); +static int reset(bool force, struct rpki_vrf *rpki_vrf); +static struct rtr_mgr_group *get_connected_group(struct rpki_vrf *rpki_vrf); +static void print_prefix_table(struct vty *vty, struct rpki_vrf *rpki_vrf, + json_object *json, bool count_only); static void install_cli_commands(void); static int config_write(struct vty *vty); static int config_on_exit(struct vty *vty); static void free_cache(struct cache *cache); -static struct rtr_mgr_group *get_groups(void); +static struct rtr_mgr_group *get_groups(struct list *cache_list); #if defined(FOUND_SSH) -static int add_ssh_cache(const char *host, const unsigned int port, - const char *username, const char *client_privkey_path, +static int add_ssh_cache(struct rpki_vrf *rpki_vrf, const char *host, + const unsigned int port, const char *username, + const char *client_privkey_path, const char *server_pubkey_path, const uint8_t preference, const char *bindaddr); #endif static struct rtr_socket *create_rtr_socket(struct tr_socket *tr_socket); -static struct cache *find_cache(const uint8_t preference); -static void rpki_delete_all_cache_nodes(void); -static int add_tcp_cache(const char *host, const char *port, - const uint8_t preference, const char *bindaddr); +static struct cache *find_cache(const uint8_t preference, + struct list *cache_list); +static void rpki_delete_all_cache_nodes(struct rpki_vrf *rpki_vrf); +static int add_tcp_cache(struct rpki_vrf *rpki_vrf, const char *host, + const char *port, const uint8_t preference, + const char *bindaddr); static void print_record(const struct pfx_record *record, struct vty *vty, json_object *json, enum asnotation_mode asnotation); -static bool is_synchronized(void); -static bool is_running(void); -static bool is_stopping(void); +static bool is_synchronized(struct rpki_vrf *rpki_vrf); +static bool is_running(struct rpki_vrf *rpki_vrf); +static bool is_stopping(struct rpki_vrf *rpki_vrf); static void route_match_free(void *rule); static enum route_map_cmd_result_t route_match(void *rule, const struct prefix *prefix, @@ -123,20 +151,14 @@ static enum route_map_cmd_result_t route_match(void *rule, void *object); static void *route_match_compile(const char *arg); static void revalidate_bgp_node(struct bgp_dest *dest, afi_t afi, safi_t safi); -static void revalidate_all_routes(void); - -static struct rtr_mgr_config *rtr_config; -static struct list *cache_list; -static bool rtr_is_running; -static bool rtr_is_stopping; -static bool rtr_is_synced; -static _Atomic int rtr_update_overflow; +static void revalidate_all_routes(struct rpki_vrf *rpki_vrf); + static bool rpki_debug_conf, rpki_debug_term; -static unsigned int polling_period; -static unsigned int expire_interval; -static unsigned int retry_interval; -static int rpki_sync_socket_rtr; -static int rpki_sync_socket_bgpd; + +DECLARE_QOBJ_TYPE(rpki_vrf); +DEFINE_QOBJ_TYPE(rpki_vrf); + +struct list *rpki_vrf_list; static struct cmd_node rpki_node = { .name = "rpki", @@ -146,6 +168,16 @@ static struct cmd_node rpki_node = { .config_write = config_write, .node_exit = config_on_exit, }; + +static struct cmd_node rpki_vrf_node = { + .name = "rpki", + .node = RPKI_VRF_NODE, + .parent_node = VRF_NODE, + .prompt = "%s(config-vrf-rpki)# ", + .config_write = NULL, + .node_exit = config_on_exit, +}; + static const struct route_map_rule_cmd route_match_rpki_cmd = { "rpki", route_match, route_match_compile, route_match_free}; @@ -253,11 +285,142 @@ static struct rtr_socket *create_rtr_socket(struct tr_socket *tr_socket) return rtr_socket; } -static struct cache *find_cache(const uint8_t preference) +static int bgp_rpki_vrf_update(struct vrf *vrf, bool enabled) +{ + struct rpki_vrf *rpki; + + if (vrf->vrf_id == VRF_DEFAULT) + rpki = find_rpki_vrf(NULL); + else + rpki = find_rpki_vrf(vrf->name); + if (!rpki) + return 0; + + if (enabled) + start(rpki); + else + stop(rpki); + return 1; +} + +/* tcp identifier : <HOST>:<PORT> + * ssh identifier : <user>@<HOST>:<PORT> + */ +static struct rpki_vrf *find_rpki_vrf_from_ident(const char *ident) +{ +#if defined(FOUND_SSH) + struct tr_ssh_config *ssh_config; +#endif + struct tr_tcp_config *tcp_config; + struct listnode *rpki_vrf_nnode; + unsigned int cache_port, port; + struct listnode *cache_node; + struct rpki_vrf *rpki_vrf; + struct cache *cache; + bool is_tcp = true; + size_t host_len; + char *endptr; + char *host; + char *ptr; + char *buf; + + /* extract the <SOCKET> */ + ptr = strrchr(ident, ':'); + if (!ptr) + return NULL; + + ptr++; + /* extract port */ + port = atoi(ptr); + if (port == 0) + /* not ours */ + return NULL; + + /* extract host */ + ptr--; + host_len = (size_t)(ptr - ident); + buf = XCALLOC(MTYPE_BGP_RPKI_TEMP, host_len + 1); + memcpy(buf, ident, host_len); + buf[host_len] = '\0'; + endptr = strrchr(buf, '@'); + + /* ssh session */ + if (endptr) { + host = XCALLOC(MTYPE_BGP_RPKI_TEMP, + (size_t)(buf + host_len - endptr) + 1); + memcpy(host, endptr + 1, (size_t)(buf + host_len - endptr) + 1); + is_tcp = false; + } else { + host = buf; + buf = NULL; + } + + for (ALL_LIST_ELEMENTS_RO(rpki_vrf_list, rpki_vrf_nnode, rpki_vrf)) { + for (ALL_LIST_ELEMENTS_RO(rpki_vrf->cache_list, cache_node, + cache)) { + if ((cache->type == TCP && !is_tcp) +#if defined(FOUND_SSH) + || (cache->type == SSH && is_tcp) +#endif + ) + continue; + + if (is_tcp) { + tcp_config = cache->tr_config.tcp_config; + cache_port = atoi(tcp_config->port); + if (cache_port != port) + continue; + if (strlen(tcp_config->host) != strlen(host)) + continue; + if (memcmp(tcp_config->host, host, host_len) == + 0) + break; + } +#if defined(FOUND_SSH) + else { + ssh_config = cache->tr_config.ssh_config; + if (port != ssh_config->port) + continue; + if (strmatch(ssh_config->host, host)) + break; + } +#endif + } + if (cache) + break; + } + if (host) + XFREE(MTYPE_BGP_RPKI_TEMP, host); + if (buf) + XFREE(MTYPE_BGP_RPKI_TEMP, buf); + return rpki_vrf; +} + +static struct rpki_vrf *find_rpki_vrf(const char *vrfname) +{ + struct listnode *rpki_vrf_nnode; + struct rpki_vrf *rpki_vrf; + + for (ALL_LIST_ELEMENTS_RO(rpki_vrf_list, rpki_vrf_nnode, rpki_vrf)) { + if (!vrfname && !rpki_vrf->vrfname) + /* rpki_vrf struct of the default VRF */ + return rpki_vrf; + if (vrfname && rpki_vrf->vrfname && + strmatch(vrfname, rpki_vrf->vrfname)) + return rpki_vrf; + } + return NULL; +} + +static struct cache *find_cache(const uint8_t preference, + struct list *cache_list) { struct listnode *cache_node; struct cache *cache; + if (!cache_list) + return NULL; + for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) { if (cache->preference == preference) return cache; @@ -265,14 +428,15 @@ static struct cache *find_cache(const uint8_t preference) return NULL; } -static void rpki_delete_all_cache_nodes(void) +static void rpki_delete_all_cache_nodes(struct rpki_vrf *rpki_vrf) { struct listnode *cache_node, *cache_next; struct cache *cache; - for (ALL_LIST_ELEMENTS(cache_list, cache_node, cache_next, cache)) { - rtr_mgr_remove_group(rtr_config, cache->preference); - listnode_delete(cache_list, cache); + for (ALL_LIST_ELEMENTS(rpki_vrf->cache_list, cache_node, cache_next, + cache)) { + rtr_mgr_remove_group(rpki_vrf->rtr_config, cache->preference); + listnode_delete(rpki_vrf->cache_list, cache); } } @@ -322,7 +486,14 @@ static void print_record_cb(const struct pfx_record *record, void *data) print_record(record, vty, arg->json, arg->asnotation); } -static struct rtr_mgr_group *get_groups(void) +static void count_record_cb(const struct pfx_record *record, void *data) +{ + struct rpki_for_each_record_arg *arg = data; + + (*arg->prefix_amount)++; +} + +static struct rtr_mgr_group *get_groups(struct list *cache_list) { struct listnode *cache_node; struct rtr_mgr_group *rtr_mgr_groups; @@ -351,19 +522,19 @@ static struct rtr_mgr_group *get_groups(void) return rtr_mgr_groups; } -inline bool is_synchronized(void) +inline bool is_synchronized(struct rpki_vrf *rpki_vrf) { - return rtr_is_synced; + return rpki_vrf->rtr_is_synced; } -inline bool is_running(void) +inline bool is_running(struct rpki_vrf *rpki_vrf) { - return rtr_is_running; + return rpki_vrf->rtr_is_running; } -inline bool is_stopping(void) +inline bool is_stopping(struct rpki_vrf *rpki_vrf) { - return rtr_is_stopping; + return rpki_vrf->rtr_is_stopping; } static void pfx_record_to_prefix(struct pfx_record *record, @@ -415,23 +586,26 @@ static void bgpd_sync_callback(struct event *thread) struct listnode *node; struct prefix prefix; struct pfx_record rec; + struct rpki_vrf *rpki_vrf = EVENT_ARG(thread); + struct vrf *vrf = NULL; - event_add_read(bm->master, bgpd_sync_callback, NULL, - rpki_sync_socket_bgpd, NULL); + event_add_read(bm->master, bgpd_sync_callback, rpki_vrf, + rpki_vrf->rpki_sync_socket_bgpd, NULL); - if (atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst)) { - while (read(rpki_sync_socket_bgpd, &rec, + if (atomic_load_explicit(&rpki_vrf->rtr_update_overflow, + memory_order_seq_cst)) { + while (read(rpki_vrf->rpki_sync_socket_bgpd, &rec, sizeof(struct pfx_record)) != -1) ; - atomic_store_explicit(&rtr_update_overflow, 0, + atomic_store_explicit(&rpki_vrf->rtr_update_overflow, 0, memory_order_seq_cst); - revalidate_all_routes(); + revalidate_all_routes(rpki_vrf); return; } - int retval = - read(rpki_sync_socket_bgpd, &rec, sizeof(struct pfx_record)); + int retval = read(rpki_vrf->rpki_sync_socket_bgpd, &rec, + sizeof(struct pfx_record)); if (retval != sizeof(struct pfx_record)) { RPKI_DEBUG("Could not read from rpki_sync_socket_bgpd"); return; @@ -440,9 +614,23 @@ static void bgpd_sync_callback(struct event *thread) afi_t afi = (rec.prefix.ver == LRTR_IPV4) ? AFI_IP : AFI_IP6; + if (rpki_vrf->vrfname) { + vrf = vrf_lookup_by_name(rpki_vrf->vrfname); + if (!vrf) { + zlog_err("%s(): vrf for rpki %s not found", __func__, + rpki_vrf->vrfname); + return; + } + } + for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) { safi_t safi; + if (!vrf && bgp->vrf_id != VRF_DEFAULT) + continue; + if (vrf && bgp->vrf_id != vrf->vrf_id) + continue; + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { struct bgp_table *table = bgp->rib[afi][safi]; struct rpki_revalidate_prefix *rrp; @@ -508,15 +696,30 @@ static void bgp_rpki_revalidate_peer(struct event *thread) XFREE(MTYPE_BGP_RPKI_REVALIDATE, rvp); } -static void revalidate_all_routes(void) +static void revalidate_all_routes(struct rpki_vrf *rpki_vrf) { struct bgp *bgp; struct listnode *node; + struct vrf *vrf = NULL; + + if (rpki_vrf->vrfname) { + vrf = vrf_lookup_by_name(rpki_vrf->vrfname); + if (!vrf) { + zlog_err("%s(): vrf for rpki %s not found", __func__, + rpki_vrf->vrfname); + return; + } + } for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) { struct peer *peer; struct listnode *peer_listnode; + if (!vrf && bgp->vrf_id != VRF_DEFAULT) + continue; + if (vrf && bgp->vrf_id != vrf->vrf_id) + continue; + for (ALL_LIST_ELEMENTS_RO(bgp->peer, peer_listnode, peer)) { afi_t afi; safi_t safi; @@ -549,21 +752,49 @@ static void rpki_update_cb_sync_rtr(struct pfx_table *p __attribute__((unused)), const struct pfx_record rec, const bool added __attribute__((unused))) { - if (is_stopping() || - atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst)) + struct rpki_vrf *rpki_vrf; + const char *msg; + const struct rtr_socket *rtr = rec.socket; + const char *ident; + + if (!rtr) { + msg = "could not find rtr_socket from cb_sync_rtr"; + goto err; + } + if (!rtr->tr_socket) { + msg = "could not find tr_socket from cb_sync_rtr"; + goto err; + } + ident = rtr->tr_socket->ident_fp(rtr->tr_socket->socket); + if (!ident) { + msg = "could not find ident from cb_sync_rtr"; + goto err; + } + rpki_vrf = find_rpki_vrf_from_ident(ident); + if (!rpki_vrf) { + msg = "could not find rpki_vrf"; + goto err; + } + + if (is_stopping(rpki_vrf) || + atomic_load_explicit(&rpki_vrf->rtr_update_overflow, + memory_order_seq_cst)) return; - int retval = - write(rpki_sync_socket_rtr, &rec, sizeof(struct pfx_record)); + int retval = write(rpki_vrf->rpki_sync_socket_rtr, &rec, + sizeof(struct pfx_record)); if (retval == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) - atomic_store_explicit(&rtr_update_overflow, 1, + atomic_store_explicit(&rpki_vrf->rtr_update_overflow, 1, memory_order_seq_cst); else if (retval != sizeof(struct pfx_record)) RPKI_DEBUG("Could not write to rpki_sync_socket_rtr"); + return; +err: + zlog_err("RPKI: %s", msg); } -static void rpki_init_sync_socket(void) +static void rpki_init_sync_socket(struct rpki_vrf *rpki_vrf) { int fds[2]; const char *msg; @@ -573,22 +804,22 @@ static void rpki_init_sync_socket(void) msg = "could not open rpki sync socketpair"; goto err; } - rpki_sync_socket_rtr = fds[0]; - rpki_sync_socket_bgpd = fds[1]; + rpki_vrf->rpki_sync_socket_rtr = fds[0]; + rpki_vrf->rpki_sync_socket_bgpd = fds[1]; - if (set_nonblocking(rpki_sync_socket_rtr) != 0) { + if (set_nonblocking(rpki_vrf->rpki_sync_socket_rtr) != 0) { msg = "could not set rpki_sync_socket_rtr to non blocking"; goto err; } - if (set_nonblocking(rpki_sync_socket_bgpd) != 0) { + if (set_nonblocking(rpki_vrf->rpki_sync_socket_bgpd) != 0) { msg = "could not set rpki_sync_socket_bgpd to non blocking"; goto err; } - event_add_read(bm->master, bgpd_sync_callback, NULL, - rpki_sync_socket_bgpd, NULL); + event_add_read(bm->master, bgpd_sync_callback, rpki_vrf, + rpki_vrf->rpki_sync_socket_bgpd, NULL); return; @@ -598,32 +829,57 @@ err: } +static struct rpki_vrf *bgp_rpki_allocate(const char *vrfname) +{ + struct rpki_vrf *rpki_vrf; + + /* initialise default vrf cache list */ + rpki_vrf = XCALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct rpki_vrf)); + + rpki_vrf->cache_list = list_new(); + rpki_vrf->cache_list->del = (void (*)(void *)) & free_cache; + rpki_vrf->polling_period = POLLING_PERIOD_DEFAULT; + rpki_vrf->expire_interval = EXPIRE_INTERVAL_DEFAULT; + rpki_vrf->retry_interval = RETRY_INTERVAL_DEFAULT; + + if (vrfname && !strmatch(vrfname, VRF_DEFAULT_NAME)) + rpki_vrf->vrfname = XSTRDUP(MTYPE_BGP_RPKI_CACHE, vrfname); + QOBJ_REG(rpki_vrf, rpki_vrf); + listnode_add(rpki_vrf_list, rpki_vrf); + + return rpki_vrf; +} + static int bgp_rpki_init(struct event_loop *master) { rpki_debug_conf = false; rpki_debug_term = false; - rtr_is_running = false; - rtr_is_stopping = false; - rtr_is_synced = false; - cache_list = list_new(); - cache_list->del = (void (*)(void *)) & free_cache; + rpki_vrf_list = list_new(); - polling_period = POLLING_PERIOD_DEFAULT; - expire_interval = EXPIRE_INTERVAL_DEFAULT; - retry_interval = RETRY_INTERVAL_DEFAULT; install_cli_commands(); - rpki_init_sync_socket(); + return 0; } static int bgp_rpki_fini(void) { - stop(); - list_delete(&cache_list); + struct listnode *node, *nnode; + struct rpki_vrf *rpki_vrf; + + for (ALL_LIST_ELEMENTS(rpki_vrf_list, node, nnode, rpki_vrf)) { + stop(rpki_vrf); + list_delete(&rpki_vrf->cache_list); - close(rpki_sync_socket_rtr); - close(rpki_sync_socket_bgpd); + close(rpki_vrf->rpki_sync_socket_rtr); + close(rpki_vrf->rpki_sync_socket_bgpd); + + listnode_delete(rpki_vrf_list, rpki_vrf); + QOBJ_UNREG(rpki_vrf); + if (rpki_vrf->vrfname) + XFREE(MTYPE_BGP_RPKI_CACHE, rpki_vrf->vrfname); + XFREE(MTYPE_BGP_RPKI_CACHE, rpki_vrf); + } return 0; } @@ -636,103 +892,130 @@ static int bgp_rpki_module_init(void) hook_register(frr_late_init, bgp_rpki_init); hook_register(frr_early_fini, bgp_rpki_fini); hook_register(bgp_hook_config_write_debug, &bgp_rpki_write_debug); + hook_register(bgp_hook_vrf_update, &bgp_rpki_vrf_update); + hook_register(bgp_hook_config_write_vrf, &bgp_rpki_hook_write_vrf); return 0; } static void sync_expired(struct event *thread) { - if (!rtr_mgr_conf_in_sync(rtr_config)) { + struct rpki_vrf *rpki_vrf = EVENT_ARG(thread); + + if (!rtr_mgr_conf_in_sync(rpki_vrf->rtr_config)) { RPKI_DEBUG("rtr_mgr is not synced, retrying."); - event_add_timer(bm->master, sync_expired, NULL, + event_add_timer(bm->master, sync_expired, rpki_vrf, BGP_RPKI_CACHE_SERVER_SYNC_RETRY_TIMEOUT, - &t_rpki_sync); + &rpki_vrf->t_rpki_sync); return; } RPKI_DEBUG("rtr_mgr sync is done."); - rtr_is_synced = true; + rpki_vrf->rtr_is_synced = true; } -static int start(void) +static int start(struct rpki_vrf *rpki_vrf) { + struct list *cache_list = NULL; + struct vrf *vrf; int ret; - rtr_is_stopping = false; - rtr_is_synced = false; - rtr_update_overflow = 0; + rpki_vrf->rtr_is_stopping = false; + rpki_vrf->rtr_is_synced = false; + rpki_vrf->rtr_update_overflow = 0; + cache_list = rpki_vrf->cache_list; + rpki_vrf->rtr_update_overflow = 0; - if (list_isempty(cache_list)) { + if (!cache_list || list_isempty(cache_list)) { RPKI_DEBUG( "No caches were found in config. Prefix validation is off."); return ERROR; } - RPKI_DEBUG("Init rtr_mgr."); + + if (rpki_vrf->vrfname) + vrf = vrf_lookup_by_name(rpki_vrf->vrfname); + else + vrf = vrf_lookup_by_id(VRF_DEFAULT); + if (!vrf || !CHECK_FLAG(vrf->status, VRF_ACTIVE)) { + RPKI_DEBUG("VRF %s not present or disabled", rpki_vrf->vrfname); + return ERROR; + } + + RPKI_DEBUG("Init rtr_mgr (%s).", vrf->name); int groups_len = listcount(cache_list); - struct rtr_mgr_group *groups = get_groups(); + struct rtr_mgr_group *groups = get_groups(rpki_vrf->cache_list); - RPKI_DEBUG("Polling period: %d", polling_period); - ret = rtr_mgr_init(&rtr_config, groups, groups_len, polling_period, - expire_interval, retry_interval, - rpki_update_cb_sync_rtr, NULL, NULL, NULL); + RPKI_DEBUG("Polling period: %d", rpki_vrf->polling_period); + ret = rtr_mgr_init(&rpki_vrf->rtr_config, groups, groups_len, + rpki_vrf->polling_period, rpki_vrf->expire_interval, + rpki_vrf->retry_interval, rpki_update_cb_sync_rtr, + NULL, NULL, NULL); if (ret == RTR_ERROR) { - RPKI_DEBUG("Init rtr_mgr failed."); + RPKI_DEBUG("Init rtr_mgr failed (%s).", vrf->name); return ERROR; } - RPKI_DEBUG("Starting rtr_mgr."); - ret = rtr_mgr_start(rtr_config); + RPKI_DEBUG("Starting rtr_mgr (%s).", vrf->name); + ret = rtr_mgr_start(rpki_vrf->rtr_config); if (ret == RTR_ERROR) { - RPKI_DEBUG("Starting rtr_mgr failed."); - rtr_mgr_free(rtr_config); + RPKI_DEBUG("Starting rtr_mgr failed (%s).", vrf->name); + rtr_mgr_free(rpki_vrf->rtr_config); return ERROR; } - event_add_timer(bm->master, sync_expired, NULL, 0, &t_rpki_sync); + event_add_timer(bm->master, sync_expired, rpki_vrf, 0, + &rpki_vrf->t_rpki_sync); XFREE(MTYPE_BGP_RPKI_CACHE_GROUP, groups); - rtr_is_running = true; + rpki_vrf->rtr_is_running = true; return SUCCESS; } -static void stop(void) +static void stop(struct rpki_vrf *rpki_vrf) { - rtr_is_stopping = true; - if (is_running()) { - EVENT_OFF(t_rpki_sync); - rtr_mgr_stop(rtr_config); - rtr_mgr_free(rtr_config); - rtr_is_running = false; + rpki_vrf->rtr_is_stopping = true; + if (is_running(rpki_vrf)) { + EVENT_OFF(rpki_vrf->t_rpki_sync); + rtr_mgr_stop(rpki_vrf->rtr_config); + rtr_mgr_free(rpki_vrf->rtr_config); + rpki_vrf->rtr_is_running = false; } } -static int reset(bool force) +static int reset(bool force, struct rpki_vrf *rpki_vrf) { - if (is_running() && !force) + if (is_running(rpki_vrf) && !force) return SUCCESS; RPKI_DEBUG("Resetting RPKI Session"); - stop(); - return start(); + stop(rpki_vrf); + return start(rpki_vrf); } -static struct rtr_mgr_group *get_connected_group(void) +static struct rtr_mgr_group *get_connected_group(struct rpki_vrf *rpki_vrf) { + struct list *cache_list; + + if (!rpki_vrf) + return NULL; + + cache_list = rpki_vrf->cache_list; if (!cache_list || list_isempty(cache_list)) return NULL; - return rtr_mgr_get_first_group(rtr_config); + return rtr_mgr_get_first_group(rpki_vrf->rtr_config); } static void print_prefix_table_by_asn(struct vty *vty, as_t as, + struct rpki_vrf *rpki_vrf, json_object *json) { unsigned int number_of_ipv4_prefixes = 0; unsigned int number_of_ipv6_prefixes = 0; - struct rtr_mgr_group *group = get_connected_group(); + struct rtr_mgr_group *group = get_connected_group(rpki_vrf); struct rpki_for_each_record_arg arg; json_object *json_records = NULL; @@ -741,6 +1024,9 @@ static void print_prefix_table_by_asn(struct vty *vty, as_t as, arg.json = NULL; arg.asnotation = bgp_get_asnotation(bgp_lookup_by_vrf_id(VRF_DEFAULT)); + if (!rpki_vrf) + return; + if (!group) { if (json) { json_object_string_add(json, "error", "Cannot find a connected group."); @@ -784,15 +1070,20 @@ static void print_prefix_table_by_asn(struct vty *vty, as_t as, vty_json(vty, json); } -static void print_prefix_table(struct vty *vty, json_object *json) +static void print_prefix_table(struct vty *vty, struct rpki_vrf *rpki_vrf, + json_object *json, bool count_only) { struct rpki_for_each_record_arg arg; unsigned int number_of_ipv4_prefixes = 0; unsigned int number_of_ipv6_prefixes = 0; - struct rtr_mgr_group *group = get_connected_group(); + struct rtr_mgr_group *group = get_connected_group(rpki_vrf); json_object *json_records = NULL; + if (!rpki_vrf) + return; + + group = get_connected_group(rpki_vrf); arg.vty = vty; arg.json = NULL; arg.asnotation = bgp_get_asnotation(bgp_lookup_by_vrf_id(VRF_DEFAULT)); @@ -808,21 +1099,29 @@ static void print_prefix_table(struct vty *vty, json_object *json) struct pfx_table *pfx_table = group->sockets[0]->pfx_table; - if (!json) { - vty_out(vty, "RPKI/RTR prefix table\n"); - vty_out(vty, "%-40s %s %s\n", "Prefix", "Prefix Length", - "Origin-AS"); - } else { - json_records = json_object_new_array(); - json_object_object_add(json, "prefixes", json_records); - arg.json = json_records; + if (!count_only) { + if (!json) { + vty_out(vty, "RPKI/RTR prefix table\n"); + vty_out(vty, "%-40s %s %s\n", "Prefix", + "Prefix Length", "Origin-AS"); + } else { + json_records = json_object_new_array(); + json_object_object_add(json, "prefixes", json_records); + arg.json = json_records; + } } arg.prefix_amount = &number_of_ipv4_prefixes; - pfx_table_for_each_ipv4_record(pfx_table, print_record_cb, &arg); + if (count_only) + pfx_table_for_each_ipv4_record(pfx_table, count_record_cb, &arg); + else + pfx_table_for_each_ipv4_record(pfx_table, print_record_cb, &arg); arg.prefix_amount = &number_of_ipv6_prefixes; - pfx_table_for_each_ipv6_record(pfx_table, print_record_cb, &arg); + if (count_only) + pfx_table_for_each_ipv6_record(pfx_table, count_record_cb, &arg); + else + pfx_table_for_each_ipv6_record(pfx_table, print_record_cb, &arg); if (!json) { vty_out(vty, "Number of IPv4 Prefixes: %u\n", @@ -847,8 +1146,25 @@ static int rpki_validate_prefix(struct peer *peer, struct attr *attr, as_t as_number = 0; struct lrtr_ip_addr ip_addr_prefix; enum pfxv_state result; + struct bgp *bgp = peer->bgp; + struct vrf *vrf; + struct rpki_vrf *rpki_vrf; - if (!is_synchronized()) + if (!bgp) + return 0; + + vrf = vrf_lookup_by_id(bgp->vrf_id); + if (!vrf) + return 0; + + if (vrf->vrf_id == VRF_DEFAULT) + rpki_vrf = find_rpki_vrf(NULL); + else + rpki_vrf = find_rpki_vrf(vrf->name); + if (!rpki_vrf || !is_synchronized(rpki_vrf)) + return 0; + + if (!is_synchronized(rpki_vrf)) return RPKI_NOT_BEING_USED; // No aspath means route comes from iBGP @@ -893,7 +1209,7 @@ static int rpki_validate_prefix(struct peer *peer, struct attr *attr, } // Do the actual validation - rtr_mgr_validate(rtr_config, as_number, &ip_addr_prefix, + rtr_mgr_validate(rpki_vrf->rtr_config, as_number, &ip_addr_prefix, prefix->prefixlen, &result); // Print Debug output @@ -926,15 +1242,26 @@ static int add_cache(struct cache *cache) { uint8_t preference = cache->preference; struct rtr_mgr_group group; + struct list *cache_list; + struct rpki_vrf *rpki_vrf; + + rpki_vrf = cache->rpki_vrf; + if (!rpki_vrf) + return ERROR; group.preference = preference; group.sockets_len = 1; group.sockets = &cache->rtr_socket; - if (is_running()) { + cache_list = rpki_vrf->cache_list; + if (!cache_list) + return ERROR; + + if (is_running(rpki_vrf)) { init_tr_socket(cache); - if (rtr_mgr_add_group(rtr_config, &group) != RTR_SUCCESS) { + if (rtr_mgr_add_group(rpki_vrf->rtr_config, &group) != + RTR_SUCCESS) { free_tr_socket(cache); return ERROR; } @@ -945,8 +1272,108 @@ static int add_cache(struct cache *cache) return SUCCESS; } -static int add_tcp_cache(const char *host, const char *port, - const uint8_t preference, const char *bindaddr) +static int rpki_create_socket(void *_cache) +{ + struct timeval prev_snd_tmout, prev_rcv_tmout, timeout; + struct cache *cache = (struct cache *)_cache; + struct rpki_vrf *rpki_vrf = cache->rpki_vrf; + struct tr_tcp_config *tcp_config; + struct addrinfo *res = NULL; + struct addrinfo hints = {}; + socklen_t optlen; + char *host, *port; + struct vrf *vrf; + int cancel_state; + int socket; + int ret; +#if defined(FOUND_SSH) + struct tr_ssh_config *ssh_config; + char s_port[10]; +#endif + + if (!cache) + return -1; + + if (rpki_vrf->vrfname == NULL) + vrf = vrf_lookup_by_id(VRF_DEFAULT); + else + vrf = vrf_lookup_by_name(rpki_vrf->vrfname); + if (!vrf) + return -1; + + if (!CHECK_FLAG(vrf->status, VRF_ACTIVE) || vrf->vrf_id == VRF_UNKNOWN) + return -1; + + if (cache->type == TCP) { + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG; + + tcp_config = cache->tr_config.tcp_config; + host = tcp_config->host; + port = tcp_config->port; + } +#if defined(FOUND_SSH) + else { + ssh_config = cache->tr_config.ssh_config; + host = ssh_config->host; + snprintf(s_port, sizeof(s_port), "%hu", ssh_config->port); + port = s_port; + + hints.ai_flags |= AI_NUMERICHOST; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + } +#endif + + frr_with_privs (&bgpd_privs) { + ret = vrf_getaddrinfo(host, port, &hints, &res, vrf->vrf_id); + } + if (ret != 0) + return -1; + + frr_with_privs (&bgpd_privs) { + socket = vrf_socket(res->ai_family, res->ai_socktype, + res->ai_protocol, vrf->vrf_id, NULL); + } + if (socket < 0) + return -1; + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &cancel_state); + timeout.tv_sec = 30; + timeout.tv_usec = 0; + + optlen = sizeof(prev_rcv_tmout); + getsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &prev_rcv_tmout, &optlen); + getsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &prev_snd_tmout, &optlen); + + setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + + if (connect(socket, res->ai_addr, res->ai_addrlen) == -1) { + if (res) + freeaddrinfo(res); + close(socket); + pthread_setcancelstate(cancel_state, NULL); + return -1; + } + + if (res) + freeaddrinfo(res); + pthread_setcancelstate(cancel_state, NULL); + + setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &prev_rcv_tmout, + sizeof(prev_rcv_tmout)); + setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &prev_snd_tmout, + sizeof(prev_snd_tmout)); + + return socket; +} + +static int add_tcp_cache(struct rpki_vrf *rpki_vrf, const char *host, + const char *port, const uint8_t preference, + const char *bindaddr) { struct rtr_socket *rtr_socket; struct tr_tcp_config *tcp_config = @@ -963,8 +1390,11 @@ static int add_tcp_cache(const char *host, const char *port, else tcp_config->bindaddr = NULL; + tcp_config->data = cache; + tcp_config->new_socket = rpki_create_socket; rtr_socket = create_rtr_socket(tr_socket); + cache->rpki_vrf = rpki_vrf; cache->type = TCP; cache->tr_socket = tr_socket; cache->tr_config.tcp_config = tcp_config; @@ -973,15 +1403,16 @@ static int add_tcp_cache(const char *host, const char *port, int ret = add_cache(cache); if (ret != SUCCESS) { + tcp_config->data = NULL; free_cache(cache); } - return ret; } #if defined(FOUND_SSH) -static int add_ssh_cache(const char *host, const unsigned int port, - const char *username, const char *client_privkey_path, +static int add_ssh_cache(struct rpki_vrf *rpki_vrf, const char *host, + const unsigned int port, const char *username, + const char *client_privkey_path, const char *server_pubkey_path, const uint8_t preference, const char *bindaddr) { @@ -999,6 +1430,8 @@ static int add_ssh_cache(const char *host, const unsigned int port, ssh_config->bindaddr = XSTRDUP(MTYPE_BGP_RPKI_CACHE, bindaddr); else ssh_config->bindaddr = NULL; + ssh_config->data = cache; + ssh_config->new_socket = rpki_create_socket; ssh_config->username = XSTRDUP(MTYPE_BGP_RPKI_CACHE, username); ssh_config->client_privkey_path = @@ -1008,6 +1441,7 @@ static int add_ssh_cache(const char *host, const unsigned int port, rtr_socket = create_rtr_socket(tr_socket); + cache->rpki_vrf = rpki_vrf; cache->type = SSH; cache->tr_socket = tr_socket; cache->tr_config.ssh_config = ssh_config; @@ -1016,6 +1450,7 @@ static int add_ssh_cache(const char *host, const unsigned int port, int ret = add_cache(cache); if (ret != SUCCESS) { + ssh_config->data = NULL; free_cache(cache); } @@ -1064,29 +1499,59 @@ static int bgp_rpki_write_debug(struct vty *vty, bool running) return 0; } -static int config_write(struct vty *vty) +static int bgp_rpki_hook_write_vrf(struct vty *vty, struct vrf *vrf) +{ + int ret; + + ret = bgp_rpki_write_vrf(vty, vrf); + if (ret == ERROR) + return 0; + return ret; +} + +static int bgp_rpki_write_vrf(struct vty *vty, struct vrf *vrf) { struct listnode *cache_node; struct cache *cache; + struct rpki_vrf *rpki_vrf = NULL; + char sep[STR_SEPARATOR]; + vrf_id_t vrf_id = VRF_DEFAULT; + + if (!vrf) { + rpki_vrf = find_rpki_vrf(NULL); + snprintf(sep, sizeof(sep), "%s", ""); + } else if (vrf->vrf_id != VRF_DEFAULT) { + rpki_vrf = find_rpki_vrf(vrf->name); + snprintf(sep, sizeof(sep), "%s", " "); + vrf_id = vrf->vrf_id; + } else + return ERROR; + + if (!rpki_vrf) + return ERROR; - if (list_isempty(cache_list) && - polling_period == POLLING_PERIOD_DEFAULT && - retry_interval == RETRY_INTERVAL_DEFAULT && - expire_interval == EXPIRE_INTERVAL_DEFAULT) + if (rpki_vrf->cache_list && list_isempty(rpki_vrf->cache_list) && + rpki_vrf->polling_period == POLLING_PERIOD_DEFAULT && + rpki_vrf->retry_interval == RETRY_INTERVAL_DEFAULT && + rpki_vrf->expire_interval == EXPIRE_INTERVAL_DEFAULT) /* do not display the default config values */ return 0; - vty_out(vty, "!\n"); - vty_out(vty, "rpki\n"); - - if (polling_period != POLLING_PERIOD_DEFAULT) - vty_out(vty, " rpki polling_period %d\n", polling_period); - if (retry_interval != RETRY_INTERVAL_DEFAULT) - vty_out(vty, " rpki retry_interval %d\n", retry_interval); - if (expire_interval != EXPIRE_INTERVAL_DEFAULT) - vty_out(vty, " rpki expire_interval %d\n", expire_interval); - - for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) { + if (vrf_id == VRF_DEFAULT) + vty_out(vty, "%s!\n", sep); + vty_out(vty, "%srpki\n", sep); + + if (rpki_vrf->polling_period != POLLING_PERIOD_DEFAULT) + vty_out(vty, "%s rpki polling_period %d\n", sep, + rpki_vrf->polling_period); + if (rpki_vrf->retry_interval != RETRY_INTERVAL_DEFAULT) + vty_out(vty, "%s rpki retry_interval %d\n", sep, + rpki_vrf->retry_interval); + if (rpki_vrf->expire_interval != EXPIRE_INTERVAL_DEFAULT) + vty_out(vty, "%s rpki expire_interval %d\n", sep, + rpki_vrf->expire_interval); + + for (ALL_LIST_ELEMENTS_RO(rpki_vrf->cache_list, cache_node, cache)) { switch (cache->type) { struct tr_tcp_config *tcp_config; #if defined(FOUND_SSH) @@ -1094,8 +1559,8 @@ static int config_write(struct vty *vty) #endif case TCP: tcp_config = cache->tr_config.tcp_config; - vty_out(vty, " rpki cache %s %s ", tcp_config->host, - tcp_config->port); + vty_out(vty, "%s rpki cache %s %s ", sep, + tcp_config->host, tcp_config->port); if (tcp_config->bindaddr) vty_out(vty, "source %s ", tcp_config->bindaddr); @@ -1103,7 +1568,7 @@ static int config_write(struct vty *vty) #if defined(FOUND_SSH) case SSH: ssh_config = cache->tr_config.ssh_config; - vty_out(vty, " rpki cache %s %u %s %s %s ", + vty_out(vty, "%s rpki cache %s %u %s %s %s ", sep, ssh_config->host, ssh_config->port, ssh_config->username, ssh_config->client_privkey_path, @@ -1121,17 +1586,62 @@ static int config_write(struct vty *vty) vty_out(vty, "preference %hhu\n", cache->preference); } - vty_out(vty, "exit\n"); + + vty_out(vty, "%sexit\n%s", sep, vrf_id == VRF_DEFAULT ? "!\n" : ""); return 1; } +static int config_write(struct vty *vty) +{ + return bgp_rpki_write_vrf(vty, NULL); +} + +static struct rpki_vrf *get_rpki_vrf(const char *vrfname) +{ + struct rpki_vrf *rpki_vrf = NULL; + struct vrf *vrf = NULL; + + if (vrfname && !strmatch(vrfname, VRF_DEFAULT_NAME)) { + vrf = vrf_lookup_by_name(vrfname); + if (!vrf) + return NULL; + rpki_vrf = find_rpki_vrf(vrf->name); + } else + /* default VRF */ + rpki_vrf = find_rpki_vrf(NULL); + + return rpki_vrf; +} + DEFUN_NOSH (rpki, rpki_cmd, "rpki", "Enable rpki and enter rpki configuration mode\n") { - vty->node = RPKI_NODE; + struct rpki_vrf *rpki_vrf; + char *vrfname = NULL; + + if (vty->node == CONFIG_NODE) + vty->node = RPKI_NODE; + else { + struct vrf *vrf = VTY_GET_CONTEXT(vrf); + + vty->node = RPKI_VRF_NODE; + if (vrf->vrf_id != VRF_DEFAULT) + vrfname = vrf->name; + } + + rpki_vrf = find_rpki_vrf(vrfname); + if (!rpki_vrf) { + rpki_vrf = bgp_rpki_allocate(vrfname); + + rpki_init_sync_socket(rpki_vrf); + } + if (vty->node == RPKI_VRF_NODE) + VTY_PUSH_CONTEXT_SUB(vty->node, rpki_vrf); + else + VTY_PUSH_CONTEXT(vty->node, rpki_vrf); return CMD_SUCCESS; } @@ -1141,27 +1651,49 @@ DEFPY (no_rpki, NO_STR "Enable rpki and enter rpki configuration mode\n") { - rpki_delete_all_cache_nodes(); - stop(); - polling_period = POLLING_PERIOD_DEFAULT; - expire_interval = EXPIRE_INTERVAL_DEFAULT; - retry_interval = RETRY_INTERVAL_DEFAULT; + struct rpki_vrf *rpki_vrf; + char *vrfname = NULL; + + if (vty->node == VRF_NODE) { + VTY_DECLVAR_CONTEXT(vrf, vrf); + + if (vrf->vrf_id != VRF_DEFAULT) + vrfname = vrf->name; + } + + rpki_vrf = find_rpki_vrf(vrfname); + + rpki_delete_all_cache_nodes(rpki_vrf); + stop(rpki_vrf); + rpki_vrf->polling_period = POLLING_PERIOD_DEFAULT; + rpki_vrf->expire_interval = EXPIRE_INTERVAL_DEFAULT; + rpki_vrf->retry_interval = RETRY_INTERVAL_DEFAULT; return CMD_SUCCESS; } -DEFUN (bgp_rpki_start, +DEFPY (bgp_rpki_start, bgp_rpki_start_cmd, - "rpki start", + "rpki start [vrf NAME$vrfname]", RPKI_OUTPUT_STRING - "start rpki support\n") + "start rpki support\n" + VRF_CMD_HELP_STR) { - if (listcount(cache_list) == 0) + struct list *cache_list = NULL; + struct rpki_vrf *rpki_vrf; + + rpki_vrf = get_rpki_vrf(vrfname); + + if (!rpki_vrf) + return CMD_WARNING; + + cache_list = rpki_vrf->cache_list; + if (!cache_list || listcount(cache_list) == 0) vty_out(vty, "Could not start rpki because no caches are configured\n"); - if (!is_running()) { - if (start() == ERROR) { + if (!is_running(rpki_vrf)) { + if (start(rpki_vrf) == ERROR) { RPKI_DEBUG("RPKI failed to start"); return CMD_WARNING; } @@ -1169,14 +1701,19 @@ DEFUN (bgp_rpki_start, return CMD_SUCCESS; } -DEFUN (bgp_rpki_stop, +DEFPY (bgp_rpki_stop, bgp_rpki_stop_cmd, - "rpki stop", + "rpki stop [vrf NAME$vrfname]", RPKI_OUTPUT_STRING - "start rpki support\n") + "start rpki support\n" + VRF_CMD_HELP_STR) { - if (is_running()) - stop(); + struct rpki_vrf *rpki_vrf; + + rpki_vrf = get_rpki_vrf(vrfname); + + if (rpki_vrf && is_running(rpki_vrf)) + stop(rpki_vrf); return CMD_SUCCESS; } @@ -1188,7 +1725,14 @@ DEFPY (rpki_polling_period, "Set polling period\n" "Polling period value\n") { - polling_period = pp; + struct rpki_vrf *rpki_vrf; + + if (vty->node == RPKI_VRF_NODE) + rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf); + else + rpki_vrf = VTY_GET_CONTEXT(rpki_vrf); + + rpki_vrf->polling_period = pp; return CMD_SUCCESS; } @@ -1200,7 +1744,14 @@ DEFUN (no_rpki_polling_period, "Set polling period back to default\n" "Polling period value\n") { - polling_period = POLLING_PERIOD_DEFAULT; + struct rpki_vrf *rpki_vrf; + + if (vty->node == RPKI_VRF_NODE) + rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf); + else + rpki_vrf = VTY_GET_CONTEXT(rpki_vrf); + + rpki_vrf->polling_period = POLLING_PERIOD_DEFAULT; return CMD_SUCCESS; } @@ -1211,8 +1762,15 @@ DEFPY (rpki_expire_interval, "Set expire interval\n" "Expire interval value\n") { - if ((unsigned int)tmp >= polling_period) { - expire_interval = tmp; + struct rpki_vrf *rpki_vrf; + + if (vty->node == RPKI_VRF_NODE) + rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf); + else + rpki_vrf = VTY_GET_CONTEXT(rpki_vrf); + + if ((unsigned int)tmp >= rpki_vrf->polling_period) { + rpki_vrf->expire_interval = tmp; return CMD_SUCCESS; } @@ -1228,7 +1786,14 @@ DEFUN (no_rpki_expire_interval, "Set expire interval back to default\n" "Expire interval value\n") { - expire_interval = polling_period * 2; + struct rpki_vrf *rpki_vrf; + + if (vty->node == RPKI_VRF_NODE) + rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf); + else + rpki_vrf = VTY_GET_CONTEXT(rpki_vrf); + + rpki_vrf->expire_interval = rpki_vrf->polling_period * 2; return CMD_SUCCESS; } @@ -1239,7 +1804,14 @@ DEFPY (rpki_retry_interval, "Set retry interval\n" "retry interval value\n") { - retry_interval = tmp; + struct rpki_vrf *rpki_vrf; + + if (vty->node == RPKI_VRF_NODE) + rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf); + else + rpki_vrf = VTY_GET_CONTEXT(rpki_vrf); + + rpki_vrf->retry_interval = tmp; return CMD_SUCCESS; } @@ -1251,7 +1823,14 @@ DEFUN (no_rpki_retry_interval, "Set retry interval back to default\n" "retry interval value\n") { - retry_interval = RETRY_INTERVAL_DEFAULT; + struct rpki_vrf *rpki_vrf; + + if (vty->node == RPKI_VRF_NODE) + rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf); + else + rpki_vrf = VTY_GET_CONTEXT(rpki_vrf); + + rpki_vrf->retry_interval = RETRY_INTERVAL_DEFAULT; return CMD_SUCCESS; } @@ -1274,9 +1853,21 @@ DEFPY(rpki_cache, rpki_cache_cmd, int return_value; struct listnode *cache_node; struct cache *current_cache; - bool init = !!list_isempty(cache_list); + struct rpki_vrf *rpki_vrf; + bool init; + + if (vty->node == RPKI_VRF_NODE) + rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf); + else + rpki_vrf = VTY_GET_CONTEXT(rpki_vrf); + + if (!rpki_vrf || !rpki_vrf->cache_list) + return CMD_WARNING; + + init = !!list_isempty(rpki_vrf->cache_list); - for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, current_cache)) { + for (ALL_LIST_ELEMENTS_RO(rpki_vrf->cache_list, cache_node, + current_cache)) { if (current_cache->preference == preference) { vty_out(vty, "Cache with preference %ld is already configured\n", @@ -1285,21 +1876,20 @@ DEFPY(rpki_cache, rpki_cache_cmd, } } - // use ssh connection if (ssh_uname) { #if defined(FOUND_SSH) - return_value = - add_ssh_cache(cache, sshport, ssh_uname, ssh_privkey, - server_pubkey, preference, bindaddr_str); + return_value = add_ssh_cache(rpki_vrf, cache, sshport, ssh_uname, + ssh_privkey, server_pubkey, + preference, bindaddr_str); #else return_value = SUCCESS; vty_out(vty, "ssh sockets are not supported. Please recompile rtrlib and frr with ssh support. If you want to use it\n"); #endif } else { // use tcp connection - return_value = - add_tcp_cache(cache, tcpport, preference, bindaddr_str); + return_value = add_tcp_cache(rpki_vrf, cache, tcpport, + preference, bindaddr_str); } if (return_value == ERROR) { @@ -1308,7 +1898,7 @@ DEFPY(rpki_cache, rpki_cache_cmd, } if (init) - start(); + start(rpki_vrf); return CMD_SUCCESS; } @@ -1331,18 +1921,28 @@ DEFPY (no_rpki_cache, "Preference of the cache server\n" "Preference value\n") { - struct cache *cache_p = find_cache(preference); + struct cache *cache_p; + struct list *cache_list = NULL; + struct rpki_vrf *rpki_vrf; + + if (vty->node == RPKI_VRF_NODE) + rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf); + else + rpki_vrf = VTY_GET_CONTEXT(rpki_vrf); - if (!cache_p) { + cache_list = rpki_vrf->cache_list; + cache_p = find_cache(preference, cache_list); + if (!rpki_vrf || !cache_p) { vty_out(vty, "Could not find cache with preference %ld\n", preference); return CMD_WARNING; } - if (is_running() && listcount(cache_list) == 1) { - stop(); - } else if (is_running()) { - if (rtr_mgr_remove_group(rtr_config, preference) == RTR_ERROR) { + if (is_running(rpki_vrf) && listcount(cache_list) == 1) { + stop(rpki_vrf); + } else if (is_running(rpki_vrf)) { + if (rtr_mgr_remove_group(rpki_vrf->rtr_config, preference) == + RTR_ERROR) { vty_out(vty, "Could not remove cache with preference %ld\n", preference); @@ -1358,18 +1958,33 @@ DEFPY (no_rpki_cache, DEFPY (show_rpki_prefix_table, show_rpki_prefix_table_cmd, - "show rpki prefix-table [json$uj]", + "show rpki <prefix-table|prefix-count>$prefixkind [vrf NAME$vrfname] [json$uj]", SHOW_STR RPKI_OUTPUT_STRING "Show validated prefixes which were received from RPKI Cache\n" + "Show prefixes count which were received from RPKI Cache\n" + VRF_CMD_HELP_STR JSON_STR) { struct json_object *json = NULL; + struct rpki_vrf *rpki_vrf; if (uj) json = json_object_new_object(); - if (!is_synchronized()) { + rpki_vrf = get_rpki_vrf(vrfname); + if (!rpki_vrf) { + if (uj) + vty_json(vty, json); + return CMD_SUCCESS; + } + + if (is_synchronized(rpki_vrf)) { + if (strmatch(prefixkind, "prefix-count")) + print_prefix_table(vty, rpki_vrf, json, true); + else + print_prefix_table(vty, rpki_vrf, json, false); + } else { if (json) { json_object_string_add(json, "error", "No Connection to RPKI cache server."); vty_json(vty, json); @@ -1378,25 +1993,33 @@ DEFPY (show_rpki_prefix_table, return CMD_WARNING; } - print_prefix_table(vty, json); return CMD_SUCCESS; } DEFPY (show_rpki_as_number, show_rpki_as_number_cmd, - "show rpki as-number ASNUM$by_asn [json$uj]", + "show rpki as-number ASNUM$by_asn [vrf NAME$vrfname] [json$uj]", SHOW_STR RPKI_OUTPUT_STRING "Lookup by ASN in prefix table\n" "AS Number\n" + VRF_CMD_HELP_STR JSON_STR) { struct json_object *json = NULL; + struct rpki_vrf *rpki_vrf; if (uj) json = json_object_new_object(); - if (!is_synchronized()) { + rpki_vrf = get_rpki_vrf(vrfname); + if (!rpki_vrf) { + if (uj) + vty_json(vty, json); + return CMD_SUCCESS; + } + + if (!is_synchronized(rpki_vrf)) { if (json) { json_object_string_add(json, "error", "No Connection to RPKI cache server."); vty_json(vty, json); @@ -1405,29 +2028,33 @@ DEFPY (show_rpki_as_number, return CMD_WARNING; } - print_prefix_table_by_asn(vty, by_asn, json); + print_prefix_table_by_asn(vty, by_asn, rpki_vrf, json); return CMD_SUCCESS; } DEFPY (show_rpki_prefix, show_rpki_prefix_cmd, - "show rpki prefix <A.B.C.D/M|X:X::X:X/M> [ASNUM$asn] [json$uj]", + "show rpki prefix <A.B.C.D/M|X:X::X:X/M> [ASNUM$asn] [vrf NAME$vrfname] [json$uj]", SHOW_STR RPKI_OUTPUT_STRING "Lookup IP prefix and optionally ASN in prefix table\n" "IPv4 prefix\n" "IPv6 prefix\n" "AS Number\n" + VRF_CMD_HELP_STR JSON_STR) { json_object *json = NULL; json_object *json_records = NULL; enum asnotation_mode asnotation; + struct rpki_vrf *rpki_vrf; if (uj) json = json_object_new_object(); - if (!is_synchronized()) { + rpki_vrf = get_rpki_vrf(vrfname); + + if (!rpki_vrf || !is_synchronized(rpki_vrf)) { if (json) { json_object_string_add(json, "error", "No Connection to RPKI cache server."); vty_json(vty, json); @@ -1456,8 +2083,8 @@ DEFPY (show_rpki_prefix, unsigned int match_count = 0; enum pfxv_state result; - if (pfx_table_validate_r(rtr_config->pfx_table, &matches, &match_count, - asn, &addr, prefix->prefixlen, + if (pfx_table_validate_r(rpki_vrf->rtr_config->pfx_table, &matches, + &match_count, asn, &addr, prefix->prefixlen, &result) != PFX_SUCCESS) { if (json) { json_object_string_add(json, "error", "Prefix lookup failed."); @@ -1495,10 +2122,11 @@ DEFPY (show_rpki_prefix, DEFPY (show_rpki_cache_server, show_rpki_cache_server_cmd, - "show rpki cache-server [json$uj]", + "show rpki cache-server [vrf NAME$vrfname] [json$uj]", SHOW_STR RPKI_OUTPUT_STRING "Show configured cache server\n" + VRF_CMD_HELP_STR JSON_STR) { struct json_object *json = NULL; @@ -1506,6 +2134,7 @@ DEFPY (show_rpki_cache_server, struct json_object *json_servers = NULL; struct listnode *cache_node; struct cache *cache; + struct rpki_vrf *rpki_vrf; if (uj) { json = json_object_new_object(); @@ -1513,7 +2142,14 @@ DEFPY (show_rpki_cache_server, json_object_object_add(json, "servers", json_servers); } - for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) { + rpki_vrf = get_rpki_vrf(vrfname); + if (!rpki_vrf) { + if (json) + vty_json(vty, json); + return CMD_SUCCESS; + } + + for (ALL_LIST_ELEMENTS_RO(rpki_vrf->cache_list, cache_node, cache)) { if (cache->type == TCP) { if (!json) { vty_out(vty, @@ -1588,10 +2224,11 @@ DEFPY (show_rpki_cache_server, DEFPY (show_rpki_cache_connection, show_rpki_cache_connection_cmd, - "show rpki cache-connection [json$uj]", + "show rpki cache-connection [vrf NAME$vrfname] [json$uj]", SHOW_STR RPKI_OUTPUT_STRING "Show to which RPKI Cache Servers we have a connection\n" + VRF_CMD_HELP_STR JSON_STR) { struct json_object *json = NULL; @@ -1600,11 +2237,19 @@ DEFPY (show_rpki_cache_connection, struct listnode *cache_node; struct cache *cache; struct rtr_mgr_group *group; + struct rpki_vrf *rpki_vrf; if (uj) json = json_object_new_object(); - if (!is_synchronized()) { + rpki_vrf = get_rpki_vrf(vrfname); + if (!rpki_vrf) { + if (json) + vty_json(vty, json); + return CMD_SUCCESS; + } + + if (!is_synchronized(rpki_vrf)) { if (json) { json_object_string_add(json, "error", "No connection to RPKI cache server."); vty_json(vty, json); @@ -1614,7 +2259,7 @@ DEFPY (show_rpki_cache_connection, return CMD_SUCCESS; } - group = get_connected_group(); + group = get_connected_group(rpki_vrf); if (!group) { if (json) { json_object_string_add(json, "error", "Cannot find a connected group."); @@ -1633,7 +2278,7 @@ DEFPY (show_rpki_cache_connection, json_object_object_add(json, "connections", json_conns); } - for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) { + for (ALL_LIST_ELEMENTS_RO(rpki_vrf->cache_list, cache_node, cache)) { struct tr_tcp_config *tcp_config; #if defined(FOUND_SSH) struct tr_ssh_config *ssh_config; @@ -1715,24 +2360,36 @@ DEFPY (show_rpki_cache_connection, } DEFPY(show_rpki_configuration, show_rpki_configuration_cmd, - "show rpki configuration [json$uj]", + "show rpki configuration [vrf NAME$vrfname] [json$uj]", SHOW_STR RPKI_OUTPUT_STRING "Show RPKI configuration\n" + VRF_CMD_HELP_STR JSON_STR) { struct json_object *json = NULL; + struct rpki_vrf *rpki_vrf; - if (uj) { + if (uj) json = json_object_new_object(); + + rpki_vrf = find_rpki_vrf(vrfname); + if (!rpki_vrf) { + if (uj) + vty_json(vty, json); + return CMD_SUCCESS; + } + + if (uj) { json_object_boolean_add(json, "enabled", - !!listcount(cache_list)); - json_object_int_add(json, "serversCount", listcount(cache_list)); + !!listcount(rpki_vrf->cache_list)); + json_object_int_add(json, "serversCount", + listcount(rpki_vrf->cache_list)); json_object_int_add(json, "pollingPeriodSeconds", - polling_period); + rpki_vrf->polling_period); json_object_int_add(json, "retryIntervalSeconds", - retry_interval); + rpki_vrf->retry_interval); json_object_int_add(json, "expireIntervalSeconds", - expire_interval); + rpki_vrf->expire_interval); vty_json(vty, json); @@ -1740,35 +2397,64 @@ DEFPY(show_rpki_configuration, show_rpki_configuration_cmd, } vty_out(vty, "rpki is %s", - listcount(cache_list) ? "Enabled" : "Disabled"); + listcount(rpki_vrf->cache_list) ? "Enabled" : "Disabled"); - if (list_isempty(cache_list)) { + if (list_isempty(rpki_vrf->cache_list)) { vty_out(vty, "\n"); return CMD_SUCCESS; } - vty_out(vty, " (%d cache servers configured)", listcount(cache_list)); + vty_out(vty, " (%d cache servers configured)", + listcount(rpki_vrf->cache_list)); vty_out(vty, "\n"); - vty_out(vty, "\tpolling period %d\n", polling_period); - vty_out(vty, "\tretry interval %d\n", retry_interval); - vty_out(vty, "\texpire interval %d\n", expire_interval); + vty_out(vty, "\tpolling period %d\n", rpki_vrf->polling_period); + vty_out(vty, "\tretry interval %d\n", rpki_vrf->retry_interval); + vty_out(vty, "\texpire interval %d\n", rpki_vrf->expire_interval); return CMD_SUCCESS; } static int config_on_exit(struct vty *vty) { - reset(false); + struct rpki_vrf *rpki_vrf; + + if (vty->node == RPKI_VRF_NODE) + rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf); + else + rpki_vrf = VTY_GET_CONTEXT(rpki_vrf); + reset(false, rpki_vrf); return 1; } -DEFUN (rpki_reset, +DEFPY(rpki_reset, rpki_reset_cmd, + "rpki reset [vrf NAME$vrfname]", + RPKI_OUTPUT_STRING + "reset rpki\n" + VRF_CMD_HELP_STR) +{ + struct rpki_vrf *rpki_vrf; + + rpki_vrf = find_rpki_vrf(vrfname); + if (!rpki_vrf) + return CMD_WARNING; + + return reset(true, rpki_vrf) == SUCCESS ? CMD_SUCCESS : CMD_WARNING; +} + +DEFPY (rpki_reset_config_mode, + rpki_reset_config_mode_cmd, "rpki reset", RPKI_OUTPUT_STRING "reset rpki\n") { - return reset(true) == SUCCESS ? CMD_SUCCESS : CMD_WARNING; + struct rpki_vrf *rpki_vrf; + + if (vty->node == RPKI_VRF_NODE) + rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf); + else + rpki_vrf = VTY_GET_CONTEXT(rpki_vrf); + return reset(true, rpki_vrf) == SUCCESS ? CMD_SUCCESS : CMD_WARNING; } DEFUN (debug_rpki, @@ -1841,8 +2527,9 @@ static void install_cli_commands(void) // TODO: make config write work install_node(&rpki_node); install_default(RPKI_NODE); + install_node(&rpki_vrf_node); + install_default(RPKI_VRF_NODE); install_element(CONFIG_NODE, &rpki_cmd); - install_element(ENABLE_NODE, &rpki_cmd); install_element(CONFIG_NODE, &no_rpki_cmd); @@ -1851,7 +2538,7 @@ static void install_cli_commands(void) /* Install rpki reset command */ install_element(ENABLE_NODE, &rpki_reset_cmd); - install_element(RPKI_NODE, &rpki_reset_cmd); + install_element(RPKI_NODE, &rpki_reset_config_mode_cmd); /* Install rpki polling period commands */ install_element(RPKI_NODE, &rpki_polling_period_cmd); @@ -1869,6 +2556,28 @@ static void install_cli_commands(void) install_element(RPKI_NODE, &rpki_cache_cmd); install_element(RPKI_NODE, &no_rpki_cache_cmd); + /* RPKI_VRF_NODE commands */ + install_element(VRF_NODE, &rpki_cmd); + install_element(VRF_NODE, &no_rpki_cmd); + /* Install rpki reset command */ + install_element(RPKI_VRF_NODE, &rpki_reset_config_mode_cmd); + + /* Install rpki polling period commands */ + install_element(RPKI_VRF_NODE, &rpki_polling_period_cmd); + install_element(RPKI_VRF_NODE, &no_rpki_polling_period_cmd); + + /* Install rpki expire interval commands */ + install_element(RPKI_VRF_NODE, &rpki_expire_interval_cmd); + install_element(RPKI_VRF_NODE, &no_rpki_expire_interval_cmd); + + /* Install rpki retry interval commands */ + install_element(RPKI_VRF_NODE, &rpki_retry_interval_cmd); + install_element(RPKI_VRF_NODE, &no_rpki_retry_interval_cmd); + + /* Install rpki cache commands */ + install_element(RPKI_VRF_NODE, &rpki_cache_cmd); + install_element(RPKI_VRF_NODE, &no_rpki_cache_cmd); + /* Install show commands */ install_element(VIEW_NODE, &show_rpki_prefix_table_cmd); install_element(VIEW_NODE, &show_rpki_cache_connection_cmd); diff --git a/bgpd/bgp_rpki.h b/bgpd/bgp_rpki.h index de28715d1..4f2f87d2e 100644 --- a/bgpd/bgp_rpki.h +++ b/bgpd/bgp_rpki.h @@ -8,6 +8,8 @@ #ifndef __BGP_RPKI_H__ #define __BGP_RPKI_H__ +extern struct zebra_privs_t bgpd_privs; + enum rpki_states { RPKI_NOT_BEING_USED, RPKI_VALID, diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 4c12872ee..1cc82748b 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -34,6 +34,9 @@ #include "lib/bfd.h" +DECLARE_HOOK(bgp_hook_config_write_vrf, (struct vty *vty, struct vrf *vrf), + (vty, vrf)); + #define BGP_MAX_HOSTNAME 64 /* Linux max, is larger than most other sys */ #define BGP_PEER_MAX_HASH_SIZE 16384 @@ -839,6 +842,8 @@ DECLARE_HOOK(bgp_inst_config_write, (bgp, vty)); DECLARE_HOOK(bgp_snmp_traps_config_write, (struct vty *vty), (vty)); DECLARE_HOOK(bgp_config_end, (struct bgp *bgp), (bgp)); +DECLARE_HOOK(bgp_hook_vrf_update, (struct vrf *vrf, bool enabled), + (vrf, enabled)); /* Thread callback information */ struct afi_safi_info { diff --git a/doc/user/rpki.rst b/doc/user/rpki.rst index 06fb3c2f2..76910ee7b 100644 --- a/doc/user/rpki.rst +++ b/doc/user/rpki.rst @@ -62,8 +62,9 @@ otherwise ``bgpd`` daemon won't startup. This command enables the RPKI configuration mode. Most commands that start with *rpki* can only be used in this mode. - When it is used in a telnet session, leaving of this mode cause rpki to be - initialized. + This command is available either in *configure node* for default *vrf* or + in *vrf node* for specific *vrf*. When it is used in a telnet session, + leaving of this mode cause rpki to be initialized. Executing this command alone does not activate prefix validation. You need to configure at least one reachable cache server. See section @@ -90,6 +91,9 @@ Examples of the error:: router(config)# rpki % [BGP] Unknown command: rpki + router(config-vrf)# rpki + % [BGP] Unknown command: rpki + Note that the RPKI commands will be available in vtysh when running ``find rpki`` regardless of whether the module is loaded. @@ -98,7 +102,14 @@ Note that the RPKI commands will be available in vtysh when running Configuring RPKI/RTR Cache Servers ---------------------------------- -The following commands are independent of a specific cache server. +RPKI/RTR can be configured independently, either in configure node, or in *vrf* +sub context. If configured in configure node, the core *bgp* instance of default +*vrf* is impacted by the configuration. + +Each RPKI/RTR context is mapped to a *vrf* and can be made up of a specific list +of cache-servers, and specific settings. + +The following commands are available for independent of a specific cache server. .. clicmd:: rpki polling_period (1-3600) @@ -166,7 +177,7 @@ Validating BGP Updates .. code-block:: frr ! Allow for invalid routes in route selection process - route bgp 60001 + route bgp 65001 ! ! Set local preference of invalid prefixes to 10 route-map rpki permit 10 @@ -200,39 +211,39 @@ Debugging Displaying RPKI --------------- -.. clicmd:: show rpki configuration [json] +.. clicmd:: show rpki configuration [vrf NAME] [json] Display RPKI configuration state including timers values. -.. clicmd:: show rpki prefix <A.B.C.D/M|X:X::X:X/M> [(1-4294967295)] [json] +.. clicmd:: show rpki prefix <A.B.C.D/M|X:X::X:X/M> [(1-4294967295)] [vrf NAME] [json] Display validated prefixes received from the cache servers filtered by the specified prefix. -.. clicmd:: show rpki as-number ASN [json] +.. clicmd:: show rpki as-number ASN [vrf NAME] [json] Display validated prefixes received from the cache servers filtered by ASN. -.. clicmd:: show rpki prefix-table [json] +.. clicmd:: show rpki prefix-table [vrf NAME] [json] Display all validated prefix to origin AS mappings/records which have been received from the cache servers and stored in the router. Based on this data, the router validates BGP Updates. -.. clicmd:: show rpki cache-server [json] +.. clicmd:: show rpki cache-server [vrf NAME] [json] Display all configured cache servers, whether active or not. -.. clicmd:: show rpki cache-connection [json] +.. clicmd:: show rpki cache-connection [vrf NAME] [json] Display all cache connections, and show which is connected or not. -.. clicmd:: show bgp [afi] [safi] <A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M> rpki <valid|invalid|notfound> +.. clicmd:: show bgp [vrf NAME] [afi] [safi] <A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M> rpki <valid|invalid|notfound> Display for the specified prefix or address the bgp paths that match the given rpki state. -.. clicmd:: show bgp [afi] [safi] rpki <valid|invalid|notfound> +.. clicmd:: show bgp [vrf NAME] [afi] [safi] rpki <valid|invalid|notfound> Display all prefixes that match the given rpki state. @@ -248,25 +259,52 @@ RPKI Configuration Example debug bgp keepalives debug rpki ! + vrf VRF1 + rpki + rpki polling_period 1000 + rpki timeout 10 + ! SSH Example: + rpki cache example.com 22 rtr-ssh ./ssh_key/id_rsa preference 1 + ! TCP Example: + rpki cache rpki-validator.realmv6.org 8282 preference 2 + exit + ! + exit-vrf + ! rpki rpki polling_period 1000 rpki timeout 10 ! SSH Example: - rpki cache example.com source 141.22.28.223 22 rtr-ssh ./ssh_key/id_rsa ./ssh_key/id_rsa.pub preference 1 + rpki cache example.com source 198.51.100.223 22 rtr-ssh ./ssh_key/id_rsa preference 1 ! TCP Example: rpki cache rpki-validator.realmv6.org 8282 preference 2 exit ! - router bgp 60001 - bgp router-id 141.22.28.223 - network 192.168.0.0/16 - neighbor 123.123.123.0 remote-as 60002 - neighbor 123.123.123.0 route-map rpki in - neighbor 123.123.123.0 update-source 141.22.28.223 + router bgp 65001 + bgp router-id 198.51.100.223 + neighbor 203.0.113.1 remote-as 65002 + neighbor 203.0.113.1 update-source 198.51.100.223 + address-family ipv4 + network 192.0.2.0/24 + neighbor 203.0.113.1 route-map rpki in + exit-address-family + ! + address-family ipv6 + neighbor 203.0.113.1 activate + neighbor 203.0.113.1 route-map rpki in + exit-address-family + ! + router bgp 65001 vrf VRF1 + bgp router-id 198.51.100.223 + neighbor 203.0.113.1 remote-as 65002 + address-family ipv4 + network 192.0.2.0/24 + neighbor 203.0.113.1 route-map rpki in + exit-address-family ! address-family ipv6 - neighbor 123.123.123.0 activate - neighbor 123.123.123.0 route-map rpki in + neighbor 203.0.113.1 activate + neighbor 203.0.113.1 route-map rpki in exit-address-family ! route-map rpki permit 10 diff --git a/lib/command.h b/lib/command.h index b6419e6fe..4886b4bc4 100644 --- a/lib/command.h +++ b/lib/command.h @@ -178,6 +178,7 @@ enum node_type { ISIS_SRV6_NODE, /* ISIS SRv6 node */ ISIS_SRV6_NODE_MSD_NODE, /* ISIS SRv6 Node MSDs node */ MGMTD_NODE, /* MGMTD node. */ + RPKI_VRF_NODE, /* RPKI node for VRF */ NODE_TYPE_MAX, /* maximum */ }; /* clang-format on */ diff --git a/tests/topotests/bgp_rpki_topo1/r2/staticd.conf b/tests/topotests/bgp_rpki_topo1/r2/staticd.conf index e3f5d7dba..de58ddef6 100644 --- a/tests/topotests/bgp_rpki_topo1/r2/staticd.conf +++ b/tests/topotests/bgp_rpki_topo1/r2/staticd.conf @@ -1 +1,5 @@ ip route 192.0.2.1/32 192.168.1.1 +! +vrf vrf10 + ip route 192.0.2.3/32 192.168.2.3 +! diff --git a/tests/topotests/bgp_rpki_topo1/r2/zebra.conf b/tests/topotests/bgp_rpki_topo1/r2/zebra.conf index 96865f0b6..d44a8a908 100644 --- a/tests/topotests/bgp_rpki_topo1/r2/zebra.conf +++ b/tests/topotests/bgp_rpki_topo1/r2/zebra.conf @@ -7,3 +7,6 @@ interface vrf10 vrf vrf10 interface r2-eth0 ip address 192.168.1.2/24 ! +interface r2-eth1 vrf vrf10 + ip address 192.168.2.2/24 +! diff --git a/tests/topotests/bgp_rpki_topo1/r3/bgpd.conf b/tests/topotests/bgp_rpki_topo1/r3/bgpd.conf new file mode 100644 index 000000000..596dc20d3 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r3/bgpd.conf @@ -0,0 +1,14 @@ +router bgp 65530 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.0.2.2 remote-as 65002 + neighbor 192.0.2.2 timers 1 3 + neighbor 192.0.2.2 timers connect 1 + neighbor 192.0.2.2 ebgp-multihop 3 + neighbor 192.0.2.2 update-source 192.0.2.3 + address-family ipv4 unicast + network 198.51.100.0/24 + network 203.0.113.0/24 + network 10.0.0.0/24 + exit-address-family +! diff --git a/tests/topotests/bgp_rpki_topo1/r3/rtrd.py b/tests/topotests/bgp_rpki_topo1/r3/rtrd.py new file mode 120000 index 000000000..1c5871a2a --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r3/rtrd.py @@ -0,0 +1 @@ +../r1/rtrd.py
\ No newline at end of file diff --git a/tests/topotests/bgp_rpki_topo1/r3/staticd.conf b/tests/topotests/bgp_rpki_topo1/r3/staticd.conf new file mode 100644 index 000000000..6822f7ec0 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r3/staticd.conf @@ -0,0 +1 @@ +ip route 192.0.2.2/32 192.168.2.2 diff --git a/tests/topotests/bgp_rpki_topo1/r3/vrps.csv b/tests/topotests/bgp_rpki_topo1/r3/vrps.csv new file mode 120000 index 000000000..8daa27f13 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r3/vrps.csv @@ -0,0 +1 @@ +../r1/vrps.csv
\ No newline at end of file diff --git a/tests/topotests/bgp_rpki_topo1/r3/zebra.conf b/tests/topotests/bgp_rpki_topo1/r3/zebra.conf new file mode 100644 index 000000000..097511476 --- /dev/null +++ b/tests/topotests/bgp_rpki_topo1/r3/zebra.conf @@ -0,0 +1,5 @@ +interface lo + ip address 192.0.2.3/32 +! +interface r3-eth0 + ip address 192.168.2.3/24
\ No newline at end of file diff --git a/tests/topotests/bgp_rpki_topo1/test_bgp_rpki_topo1.py b/tests/topotests/bgp_rpki_topo1/test_bgp_rpki_topo1.py index 36bc0b720..0416148b2 100644 --- a/tests/topotests/bgp_rpki_topo1/test_bgp_rpki_topo1.py +++ b/tests/topotests/bgp_rpki_topo1/test_bgp_rpki_topo1.py @@ -22,13 +22,17 @@ pytestmark = [pytest.mark.bgpd] def build_topo(tgen): - for routern in range(1, 3): + 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["r2"]) + switch.add_link(tgen.gears["r3"]) + def setup_module(mod): tgen = Topogen(build_topo, mod.__name__) @@ -49,25 +53,34 @@ def setup_module(mod): " -M bgpd_rpki" if rname == "r2" else "", ) + tgen.gears["r2"].run("ip link add vrf10 type vrf table 10") + tgen.gears["r2"].run("ip link set vrf10 up") + + tgen.gears["r2"].run("ip link set r2-eth1 master vrf10") + tgen.start_router() global rtrd_process + rtrd_process = {} - rname = "r1" - - rtr_path = os.path.join(CWD, rname) - log_dir = os.path.join(tgen.logdir, rname) - log_file = os.path.join(log_dir, "rtrd.log") + for rname in ["r1", "r3"]: + rtr_path = os.path.join(CWD, rname) + log_dir = os.path.join(tgen.logdir, rname) + log_file = os.path.join(log_dir, "rtrd.log") - tgen.gears[rname].cmd("chmod u+x {}/rtrd.py".format(rtr_path)) - rtrd_process = tgen.gears[rname].popen("{}/rtrd.py {}".format(rtr_path, log_file)) + tgen.gears[rname].cmd("chmod u+x {}/rtrd.py".format(rtr_path)) + rtrd_process[rname] = tgen.gears[rname].popen( + "{}/rtrd.py {}".format(rtr_path, log_file) + ) def teardown_module(mod): tgen = get_topogen() - logger.info("r1: sending SIGTERM to rtrd RPKI server") - rtrd_process.kill() + for rname in ["r1", "r3"]: + logger.info("{}: sending SIGTERM to rtrd RPKI server".format(rname)) + rtrd_process[rname].kill() + tgen.stop_topology() @@ -114,7 +127,7 @@ def test_show_bgp_rpki_prefixes(): for rname in ["r1", "r3"]: logger.info("{}: checking if rtrd is running".format(rname)) - if rtrd_process.poll() is not None: + if rtrd_process[rname].poll() is not None: pytest.skip(tgen.errors) rname = "r2" @@ -156,7 +169,7 @@ def test_show_bgp_rpki_prefixes_no_rpki_cache(): for rname in ["r1", "r3"]: logger.info("{}: checking if rtrd is running".format(rname)) - if rtrd_process.poll() is not None: + if rtrd_process[rname].poll() is not None: pytest.skip(tgen.errors) def _show_rpki_no_connection(rname): @@ -192,7 +205,7 @@ def test_show_bgp_rpki_prefixes_reconnect(): for rname in ["r1", "r3"]: logger.info("{}: checking if rtrd is running".format(rname)) - if rtrd_process.poll() is not None: + if rtrd_process[rname].poll() is not None: pytest.skip(tgen.errors) step("Restore RPKI server configuration") @@ -241,7 +254,7 @@ def test_show_bgp_rpki_route_map(): for rname in ["r1", "r3"]: logger.info("{}: checking if rtrd is running".format(rname)) - if rtrd_process.poll() is not None: + if rtrd_process[rname].poll() is not None: pytest.skip(tgen.errors) step("Apply RPKI valid route-map on neighbor") @@ -283,6 +296,112 @@ router bgp 65002 assert result is None, "Unexpected prefixes RPKI state on {}".format(rname) +def test_show_bgp_rpki_prefixes_vrf(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r3"]: + logger.info("{}: checking if rtrd is running".format(rname)) + if rtrd_process[rname].poll() is not None: + pytest.skip(tgen.errors) + + step("Configure RPKI cache server on vrf10") + + rname = "r2" + tgen.gears[rname].vtysh_cmd( + """ +configure +vrf vrf10 + rpki + rpki cache 192.0.2.3 15432 preference 1 + exit +exit +""" + ) + + step("Check vrf10 RPKI prefix table") + + expected = open(os.path.join(CWD, "{}/rpki_prefix_table.json".format(rname))).read() + expected_json = json.loads(expected) + test_func = functools.partial(show_rpki_prefixes, rname, expected_json, vrf="vrf10") + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see RPKI prefixes on {}".format(rname) + + for rpki_state in ["valid", "notfound", None]: + if rpki_state: + step( + "Check RPKI state of prefixes in vrf10 BGP table: {}".format(rpki_state) + ) + else: + step("Check prefixes in vrf10 BGP table") + expected = open( + os.path.join( + CWD, + "{}/bgp_table_rpki_{}.json".format( + rname, rpki_state if rpki_state else "any" + ), + ) + ).read() + expected_json = json.loads(expected) + test_func = functools.partial( + show_bgp_ipv4_table_rpki, rname, rpki_state, expected_json, vrf="vrf10" + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected prefixes RPKI state on {}".format(rname) + + +def test_show_bgp_rpki_route_map_vrf(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r3"]: + logger.info("{}: checking if rtrd is running".format(rname)) + if rtrd_process[rname].poll() is not None: + pytest.skip(tgen.errors) + + step("Apply RPKI valid route-map on vrf10 neighbor") + + rname = "r2" + tgen.gears[rname].vtysh_cmd( + """ +configure +router bgp 65002 vrf vrf10 + address-family ipv4 unicast + neighbor 192.0.2.3 route-map RPKI in +""" + ) + + for rpki_state in ["valid", "notfound", None]: + if rpki_state: + step( + "Check RPKI state of prefixes in vrf10 BGP table: {}".format(rpki_state) + ) + else: + step("Check prefixes in vrf10 BGP table") + expected = open( + os.path.join( + CWD, + "{}/bgp_table_rmap_rpki_{}.json".format( + rname, rpki_state if rpki_state else "any" + ), + ) + ).read() + expected_json = json.loads(expected) + test_func = functools.partial( + show_bgp_ipv4_table_rpki, + rname, + rpki_state, + expected_json, + vrf="vrf10", + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Unexpected prefixes RPKI state on {}".format(rname) + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index d5ac87eb3..3109f1510 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -1625,6 +1625,14 @@ static struct cmd_node rpki_node = { .parent_node = CONFIG_NODE, .prompt = "%s(config-rpki)# ", }; + +static struct cmd_node rpki_vrf_node = { + .name = "rpki", + .node = RPKI_VRF_NODE, + .parent_node = VRF_NODE, + .prompt = "%s(config-vrf-rpki)# ", +}; + #endif /* HAVE_BGPD */ #if HAVE_BFDD > 0 @@ -1855,7 +1863,10 @@ DEFUNSH(VTYSH_BGPD, "rpki", "Enable rpki and enter rpki configuration mode\n") { - vty->node = RPKI_NODE; + if (vty->node == CONFIG_NODE) + vty->node = RPKI_NODE; + else + vty->node = RPKI_VRF_NODE; return CMD_SUCCESS; } @@ -5071,6 +5082,12 @@ void vtysh_init_vty(void) install_element(VRF_NODE, &vtysh_exit_vrf_cmd); install_element(VRF_NODE, &vtysh_quit_vrf_cmd); + install_node(&rpki_vrf_node); + install_element(VRF_NODE, &rpki_cmd); + install_element(RPKI_VRF_NODE, &rpki_exit_cmd); + install_element(RPKI_VRF_NODE, &rpki_quit_cmd); + install_element(RPKI_VRF_NODE, &vtysh_end_all_cmd); + install_element(CONFIG_NODE, &vtysh_affinity_map_cmd); install_element(CONFIG_NODE, &vtysh_no_affinity_map_cmd); diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index 11751d027..f63d4be6e 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -58,7 +58,7 @@ extern struct event_loop *master; VTYSH_EIGRPD | VTYSH_BABELD | VTYSH_PBRD | VTYSH_FABRICD | \ VTYSH_VRRPD | VTYSH_MGMTD #define VTYSH_INTERFACE VTYSH_INTERFACE_SUBSET | VTYSH_BGPD -#define VTYSH_VRF VTYSH_INTERFACE_SUBSET +#define VTYSH_VRF VTYSH_INTERFACE_SUBSET | RPKI_VRF_NODE #define VTYSH_KEYS VTYSH_RIPD | VTYSH_EIGRPD | VTYSH_OSPF6D | VTYSH_OSPFD /* Daemons who can process nexthop-group configs */ #define VTYSH_NH_GROUP VTYSH_PBRD|VTYSH_SHARPD diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index c02f27c87..888f6a8c2 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -315,11 +315,20 @@ void vtysh_config_parse_line(void *arg, const char *line) } else if (!strncmp(line, " ip mroute", strlen(" ip mroute"))) { config_add_line_uniq_end(config->line, line); + } else if ((strncmp(line, " rpki", strlen(" rpki")) == + 0) && + config->index == VRF_NODE) { + config_add_line(config->line, line); + config->index = RPKI_VRF_NODE; } else if (config->index == RMAP_NODE || config->index == INTERFACE_NODE || config->index == VTY_NODE) config_add_line_uniq(config->line, line); - else if (config->index == NH_GROUP_NODE) { + else if (config->index == RPKI_VRF_NODE && + strncmp(line, " exit", strlen(" exit")) == 0) { + config_add_line(config->line, line); + config->index = VRF_NODE; + } else if (config->index == NH_GROUP_NODE) { if (strncmp(line, " resilient", strlen(" resilient")) == 0) config_add_line_head(config->line, |