bgpd: add support for rpki revalidation on rpki updates

Signed-off-by: Marcel Röthke <marcel.roethke@haw-hamburg.de>
This commit is contained in:
Marcel Röthke 2018-03-29 11:15:18 +02:00
parent 262c829d7e
commit 1dacdd8b25
4 changed files with 248 additions and 2 deletions

View File

@ -47,6 +47,7 @@
#include "bgpd/bgp_attr.h" #include "bgpd/bgp_attr.h"
#include "bgpd/bgp_aspath.h" #include "bgpd/bgp_aspath.h"
#include "bgpd/bgp_route.h" #include "bgpd/bgp_route.h"
#include "lib/thread.h"
#include "rtrlib/rtrlib.h" #include "rtrlib/rtrlib.h"
#include "rtrlib/rtr_mgr.h" #include "rtrlib/rtr_mgr.h"
#include "rtrlib/lib/ip.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, static route_map_result_t route_match(void *rule, struct prefix *prefix,
route_map_object_t type, void *object); route_map_object_t type, void *object);
static void *route_match_compile(const char *arg); 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 rtr_mgr_config *rtr_config;
static struct list *cache_list; static struct list *cache_list;
static int rtr_is_running; static int rtr_is_running;
static int rtr_is_stopping;
static int rtr_is_starting;
static int rpki_debug; static int rpki_debug;
static unsigned int polling_period; static unsigned int polling_period;
static unsigned int expire_interval; static unsigned int expire_interval;
static unsigned int retry_interval; static unsigned int retry_interval;
static unsigned int timeout; static unsigned int timeout;
static unsigned int initial_synchronisation_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 cmd_node rpki_node = {RPKI_NODE, "%s(config-rpki)# ", 1};
static struct route_map_rule_cmd route_match_rpki_cmd = { 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, static int rpki_validate_prefix(struct peer *peer, struct attr *attr,
struct prefix *prefix); 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) static void ipv6_addr_to_host_byte_order(const uint32_t *src, uint32_t *dest)
{ {
int i; int i;
@ -303,10 +318,159 @@ inline int is_running(void)
return rtr_is_running; 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) static int bgp_rpki_init(struct thread_master *master)
{ {
rpki_debug = 0; rpki_debug = 0;
rtr_is_running = 0; rtr_is_running = 0;
rtr_is_stopping = 0;
cache_list = list_new(); cache_list = list_new();
cache_list->del = (void (*)(void *)) & free_cache; 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 =
INITIAL_SYNCHRONISATION_TIMEOUT_DEFAULT; INITIAL_SYNCHRONISATION_TIMEOUT_DEFAULT;
install_cli_commands(); install_cli_commands();
rpki_init_sync_socket();
return 0; return 0;
} }
@ -326,6 +491,9 @@ static int bgp_rpki_fini(void)
stop(); stop();
list_delete_and_null(&cache_list); list_delete_and_null(&cache_list);
close(rpki_sync_socket_rtr);
close(rpki_sync_socket_bgpd);
return 0; return 0;
} }
@ -344,6 +512,9 @@ static int start(void)
unsigned int waiting_time = 0; unsigned int waiting_time = 0;
int ret; int ret;
rtr_is_stopping = 0;
rtr_is_starting = 1;
if (list_isempty(cache_list)) { if (list_isempty(cache_list)) {
RPKI_DEBUG( RPKI_DEBUG(
"No caches were found in config. Prefix validation is off."); "No caches were found in config. Prefix validation is off.");
@ -353,9 +524,10 @@ static int start(void)
int groups_len = listcount(cache_list); int groups_len = listcount(cache_list);
struct rtr_mgr_group *groups = get_groups(); 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, ret = rtr_mgr_init(&rtr_config, groups, groups_len, polling_period,
expire_interval, retry_interval, NULL, NULL, NULL, expire_interval, retry_interval,
NULL); rpki_update_cb_sync_rtr, NULL, NULL, NULL);
if (ret == RTR_ERROR) { if (ret == RTR_ERROR) {
RPKI_DEBUG("Init rtr_mgr failed."); RPKI_DEBUG("Init rtr_mgr failed.");
return ERROR; return ERROR;
@ -378,9 +550,13 @@ static int start(void)
} }
if (rtr_mgr_conf_in_sync(rtr_config)) { if (rtr_mgr_conf_in_sync(rtr_config)) {
RPKI_DEBUG("Got synchronisation with at least one RPKI cache!"); RPKI_DEBUG("Got synchronisation with at least one RPKI cache!");
RPKI_DEBUG("Forcing revalidation.");
rtr_is_starting = 0;
revalidate_all_routes();
} else { } else {
RPKI_DEBUG( RPKI_DEBUG(
"Timeout expired! Proceeding without RPKI validation data."); "Timeout expired! Proceeding without RPKI validation data.");
rtr_is_starting = 0;
} }
XFREE(MTYPE_BGP_RPKI_CACHE_GROUP, groups); XFREE(MTYPE_BGP_RPKI_CACHE_GROUP, groups);
@ -390,6 +566,7 @@ static int start(void)
static void stop(void) static void stop(void)
{ {
rtr_is_stopping = 1;
if (rtr_is_running) { if (rtr_is_running) {
rtr_mgr_stop(rtr_config); rtr_mgr_stop(rtr_config);
rtr_mgr_free(rtr_config); rtr_mgr_free(rtr_config);

View File

@ -114,3 +114,65 @@ struct bgp_table *bgp_table_init(struct bgp *bgp, afi_t afi, safi_t safi)
return rt; 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);
}
}
}
}

View File

@ -24,6 +24,7 @@
#include "mpls.h" #include "mpls.h"
#include "table.h" #include "table.h"
#include "queue.h" #include "queue.h"
#include "linklist.h"
struct bgp_table { struct bgp_table {
/* table belongs to this instance */ /* table belongs to this instance */
@ -309,4 +310,7 @@ static inline uint64_t bgp_table_version(struct bgp_table *table)
return table->version; 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 */ #endif /* _QUAGGA_BGP_TABLE_H */

View File

@ -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 - 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 allows the creation of local policies, which handle BGP routes based on the
outcome of the Prefix Origin Validation. 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: .. _enabling-rpki: