From 1dacdd8b2596da01df4ff0eda44bdcf94bb4ca31 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Marcel=20R=C3=B6thke?= Date: Thu, 29 Mar 2018 11:15:18 +0200 Subject: [PATCH] bgpd: add support for rpki revalidation on rpki updates MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Röthke --- bgpd/bgp_rpki.c | 181 +++++++++++++++++++++++++++++++++++++++++++++- bgpd/bgp_table.c | 62 ++++++++++++++++ bgpd/bgp_table.h | 4 + doc/user/rpki.rst | 3 + 4 files changed, 248 insertions(+), 2 deletions(-) diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index db182eef98..d73744950f 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -47,6 +47,7 @@ #include "bgpd/bgp_attr.h" #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_route.h" +#include "lib/thread.h" #include "rtrlib/rtrlib.h" #include "rtrlib/rtr_mgr.h" #include "rtrlib/lib/ip.h" @@ -128,16 +129,22 @@ static void route_match_free(void *rule); static route_map_result_t route_match(void *rule, struct prefix *prefix, route_map_object_t type, void *object); static void *route_match_compile(const char *arg); +static void revalidate_bgp_node(struct bgp_node *bgp_node, afi_t afi, + safi_t safi); static struct rtr_mgr_config *rtr_config; static struct list *cache_list; static int rtr_is_running; +static int rtr_is_stopping; +static int rtr_is_starting; static int rpki_debug; static unsigned int polling_period; static unsigned int expire_interval; static unsigned int retry_interval; static unsigned int timeout; static unsigned int initial_synchronisation_timeout; +static int rpki_sync_socket_rtr; +static int rpki_sync_socket_bgpd; static struct cmd_node rpki_node = {RPKI_NODE, "%s(config-rpki)# ", 1}; static struct route_map_rule_cmd route_match_rpki_cmd = { @@ -185,6 +192,14 @@ static void free_tr_socket(struct cache *cache) static int rpki_validate_prefix(struct peer *peer, struct attr *attr, struct prefix *prefix); +static void ipv6_addr_to_network_byte_order(const uint32_t *src, uint32_t *dest) +{ + int i; + + for (i = 0; i < 4; i++) + dest[i] = htonl(src[i]); +} + static void ipv6_addr_to_host_byte_order(const uint32_t *src, uint32_t *dest) { int i; @@ -303,10 +318,159 @@ inline int is_running(void) return rtr_is_running; } +static struct prefix *pfx_record_to_prefix(struct pfx_record *record) +{ + struct prefix *prefix = prefix_new(); + + prefix->prefixlen = record->min_len; + + if (record->prefix.ver == LRTR_IPV4) { + prefix->family = AF_INET; + prefix->u.prefix4.s_addr = htonl(record->prefix.u.addr4.addr); + } else { + prefix->family = AF_INET6; + ipv6_addr_to_network_byte_order(record->prefix.u.addr6.addr, + prefix->u.prefix6.s6_addr32); + } + + return prefix; +} + +static int bgpd_sync_callback(struct thread *thread) +{ + struct bgp *bgp; + struct listnode *node; + struct prefix *prefix; + struct pfx_record rec; + + thread_add_read(bm->master, bgpd_sync_callback, NULL, + rpki_sync_socket_bgpd, NULL); + int retval = + read(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 retval; + } + prefix = pfx_record_to_prefix(&rec); + + afi_t afi = (rec.prefix.ver == LRTR_IPV4) ? AFI_IP : AFI_IP6; + + for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) { + safi_t safi; + + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { + if (!bgp->rib[afi][safi]) + continue; + + struct list *matches = list_new(); + + matches->del = (void (*)(void *))bgp_unlock_node; + + bgp_table_range_lookup(bgp->rib[afi][safi], prefix, + rec.max_len, matches); + + + struct bgp_node *bgp_node; + + for (ALL_LIST_ELEMENTS_RO(matches, node, bgp_node)) + revalidate_bgp_node(bgp_node, afi, safi); + + list_delete_and_null(&matches); + } + } + + prefix_free(prefix); + return 0; +} + +static void revalidate_bgp_node(struct bgp_node *bgp_node, afi_t afi, + safi_t safi) +{ + struct bgp_adj_in *ain; + + for (ain = bgp_node->adj_in; ain; ain = ain->next) { + int ret; + struct bgp_info *bgp_info = bgp_node->info; + mpls_label_t *label = NULL; + uint32_t num_labels = 0; + + if (bgp_info && bgp_info->extra) { + label = bgp_info->extra->label; + num_labels = bgp_info->extra->num_labels; + } + ret = bgp_update(ain->peer, &bgp_node->p, 0, ain->attr, afi, + safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, + label, num_labels, 1, NULL); + + if (ret < 0) { + bgp_unlock_node(bgp_node); + return; + } + } +} + +static void revalidate_all_routes(void) +{ + struct bgp *bgp; + struct listnode *node; + struct bgp_node *bgp_node; + + for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) { + for (size_t i = 0; i < 2; i++) { + safi_t safi; + afi_t afi = (i == 0) ? AFI_IP : AFI_IP6; + + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { + if (!bgp->rib[afi][safi]) + continue; + + for (bgp_node = + bgp_table_top(bgp->rib[afi][safi]); + bgp_node; + bgp_node = bgp_route_next(bgp_node)) { + if (bgp_node->info != NULL) { + revalidate_bgp_node(bgp_node, + afi, safi); + } + } + } + } + } +} + +static void rpki_update_cb_sync_rtr(struct pfx_table *p __attribute__((unused)), + const struct pfx_record rec, + const bool added __attribute__((unused))) +{ + if (rtr_is_stopping || rtr_is_starting) + return; + + int retval = + write(rpki_sync_socket_rtr, &rec, sizeof(struct pfx_record)); + if (retval != sizeof(struct pfx_record)) + RPKI_DEBUG("Could not write to rpki_sync_socket_rtr"); +} + +static void rpki_init_sync_socket(void) +{ + int fds[2]; + + RPKI_DEBUG("initializing sync socket"); + if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, fds) != 0) { + RPKI_DEBUG("Could not open rpki sync socket"); + return; + } + rpki_sync_socket_rtr = fds[0]; + rpki_sync_socket_bgpd = fds[1]; + thread_add_read(bm->master, bgpd_sync_callback, NULL, + rpki_sync_socket_bgpd, NULL); +} + static int bgp_rpki_init(struct thread_master *master) { rpki_debug = 0; rtr_is_running = 0; + rtr_is_stopping = 0; cache_list = list_new(); cache_list->del = (void (*)(void *)) & free_cache; @@ -318,6 +482,7 @@ static int bgp_rpki_init(struct thread_master *master) initial_synchronisation_timeout = INITIAL_SYNCHRONISATION_TIMEOUT_DEFAULT; install_cli_commands(); + rpki_init_sync_socket(); return 0; } @@ -326,6 +491,9 @@ static int bgp_rpki_fini(void) stop(); list_delete_and_null(&cache_list); + close(rpki_sync_socket_rtr); + close(rpki_sync_socket_bgpd); + return 0; } @@ -344,6 +512,9 @@ static int start(void) unsigned int waiting_time = 0; int ret; + rtr_is_stopping = 0; + rtr_is_starting = 1; + if (list_isempty(cache_list)) { RPKI_DEBUG( "No caches were found in config. Prefix validation is off."); @@ -353,9 +524,10 @@ static int start(void) int groups_len = listcount(cache_list); struct rtr_mgr_group *groups = get_groups(); + RPKI_DEBUG("Polling period: %d", polling_period); ret = rtr_mgr_init(&rtr_config, groups, groups_len, polling_period, - expire_interval, retry_interval, NULL, NULL, NULL, - NULL); + expire_interval, retry_interval, + rpki_update_cb_sync_rtr, NULL, NULL, NULL); if (ret == RTR_ERROR) { RPKI_DEBUG("Init rtr_mgr failed."); return ERROR; @@ -378,9 +550,13 @@ static int start(void) } if (rtr_mgr_conf_in_sync(rtr_config)) { RPKI_DEBUG("Got synchronisation with at least one RPKI cache!"); + RPKI_DEBUG("Forcing revalidation."); + rtr_is_starting = 0; + revalidate_all_routes(); } else { RPKI_DEBUG( "Timeout expired! Proceeding without RPKI validation data."); + rtr_is_starting = 0; } XFREE(MTYPE_BGP_RPKI_CACHE_GROUP, groups); @@ -390,6 +566,7 @@ static int start(void) static void stop(void) { + rtr_is_stopping = 1; if (rtr_is_running) { rtr_mgr_stop(rtr_config); rtr_mgr_free(rtr_config); diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c index 613b924d0d..94e2d83cfe 100644 --- a/bgpd/bgp_table.c +++ b/bgpd/bgp_table.c @@ -114,3 +114,65 @@ struct bgp_table *bgp_table_init(struct bgp *bgp, afi_t afi, safi_t safi) return rt; } + +static struct bgp_node * +bgp_route_next_until_maxlen(struct bgp_node *node, const struct bgp_node *limit, + const uint8_t maxlen) +{ + if (node->l_left && node->p.prefixlen < maxlen + && node->l_left->p.prefixlen <= maxlen) { + return bgp_node_from_rnode(node->l_left); + } + if (node->l_right && node->p.prefixlen < maxlen + && node->l_right->p.prefixlen <= maxlen) { + return bgp_node_from_rnode(node->l_right); + } + + while (node->parent && node != limit) { + if (bgp_node_from_rnode(node->parent->l_left) == node + && node->parent->l_right) { + return bgp_node_from_rnode(node->parent->l_right); + } + node = bgp_node_from_rnode(node->parent); + } + return NULL; +} + +void bgp_table_range_lookup(const struct bgp_table *table, struct prefix *p, + uint8_t maxlen, struct list *matches) +{ + struct bgp_node *node = bgp_node_from_rnode(table->route_table->top); + struct bgp_node *matched = NULL; + + if (node == NULL) + return; + + while (node && node->p.prefixlen <= p->prefixlen + && prefix_match(&node->p, p)) { + if (node->info && node->p.prefixlen == p->prefixlen) { + matched = node; + break; + } + node = bgp_node_from_rnode(node->link[prefix_bit( + &p->u.prefix, node->p.prefixlen)]); + } + + if ((matched == NULL && node->p.prefixlen > maxlen) || !node->parent) + return; + else if (matched == NULL) + matched = node = bgp_node_from_rnode(node->parent); + + if (matched->info) { + bgp_lock_node(matched); + listnode_add(matches, matched); + } + + while ((node = bgp_route_next_until_maxlen(node, matched, maxlen))) { + if (prefix_match(p, &node->p)) { + if (node->info) { + bgp_lock_node(node); + listnode_add(matches, node); + } + } + } +} diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h index 388c247227..60c2cbd4a4 100644 --- a/bgpd/bgp_table.h +++ b/bgpd/bgp_table.h @@ -24,6 +24,7 @@ #include "mpls.h" #include "table.h" #include "queue.h" +#include "linklist.h" struct bgp_table { /* table belongs to this instance */ @@ -309,4 +310,7 @@ static inline uint64_t bgp_table_version(struct bgp_table *table) return table->version; } +void bgp_table_range_lookup(const struct bgp_table *table, struct prefix *p, + uint8_t maxlen, struct list *matches); + #endif /* _QUAGGA_BGP_TABLE_H */ diff --git a/doc/user/rpki.rst b/doc/user/rpki.rst index 93a8e4396a..c35928db07 100644 --- a/doc/user/rpki.rst +++ b/doc/user/rpki.rst @@ -44,6 +44,9 @@ In a nutshell, the current implementation provides the following features - Route maps can be configured to match a specific RPKI validation state. This allows the creation of local policies, which handle BGP routes based on the outcome of the Prefix Origin Validation. +- Updates from the RPKI cache servers are directly applied and path selection + is updated accordingly. (Soft reconfiguration **must** be enabled for this + to work). .. _enabling-rpki: -- 2.39.5