* Berlin
* Copyright (C) 2016-2017 Colin Sames (colin.sames@haw-hamburg.de), for HAW
* Hamburg
- * Copyright (C) 2017 Marcel Röthke (marcel.roethke@haw-hamburg.de), for HAW
- * Hamburg
+ * Copyright (C) 2017-2018 Marcel Röthke (marcel.roethke@haw-hamburg.de),
+ * for HAW Hamburg
*
* This file is part of FRRouting.
*
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_aspath.h"
#include "bgpd/bgp_route.h"
+#include "lib/network.h"
+#include "lib/thread.h"
+#ifndef VTYSH_EXTRACT_PL
#include "rtrlib/rtrlib.h"
#include "rtrlib/rtr_mgr.h"
#include "rtrlib/lib/ip.h"
#if defined(FOUND_SSH)
#include "rtrlib/transport/ssh/ssh_transport.h"
#endif
+#endif
#include "hook.h"
#include "libfrr.h"
#include "version.h"
static int is_synchronized(void);
static int is_running(void);
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, const 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 void revalidate_all_routes(void);
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 _Atomic int rtr_update_overflow;
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 = {
XFREE(MTYPE_BGP_RPKI_CACHE, ptr);
}
+static void init_tr_socket(struct cache *cache)
+{
+ if (cache->type == TCP)
+ tr_tcp_init(cache->tr_config.tcp_config,
+ cache->tr_socket);
+#if defined(FOUND_SSH)
+ else
+ tr_ssh_init(cache->tr_config.ssh_config,
+ cache->tr_socket);
+#endif
+}
+
+static void free_tr_socket(struct cache *cache)
+{
+ if (cache->type == TCP)
+ tr_tcp_init(cache->tr_config.tcp_config,
+ cache->tr_socket);
+#if defined(FOUND_SSH)
+ else
+ tr_ssh_init(cache->tr_config.ssh_config,
+ cache->tr_socket);
+#endif
+}
+
static int rpki_validate_prefix(struct peer *peer, struct attr *attr,
- struct prefix *prefix);
+ const 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;
-static route_map_result_t route_match(void *rule, struct prefix *prefix,
+ for (i = 0; i < 4; i++)
+ dest[i] = ntohl(src[i]);
+}
+
+static route_map_result_t route_match(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object)
{
int *rpki_status = rule;
- struct bgp_info *bgp_info;
+ struct bgp_path_info *path;
if (type == RMAP_BGP) {
- bgp_info = object;
+ path = object;
- if (rpki_validate_prefix(bgp_info->peer, bgp_info->attr, prefix)
+ if (rpki_validate_prefix(path->peer, path->attr, prefix)
== *rpki_status) {
return RMAP_MATCH;
}
{
int *rpki_status;
- rpki_status = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint8_t));
+ rpki_status = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(int));
if (strcmp(arg, "valid") == 0)
*rpki_status = RPKI_VALID;
rtr_mgr_groups[i].sockets_len = 1;
rtr_mgr_groups[i].preference = cache->preference;
- if (cache->type == TCP)
- tr_tcp_init(cache->tr_config.tcp_config,
- cache->tr_socket);
-#if defined(FOUND_SSH)
- else
- tr_ssh_init(cache->tr_config.ssh_config,
- cache->tr_socket);
-#endif
+ init_tr_socket(cache);
i++;
}
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);
+
+ if (atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst)) {
+ while (read(rpki_sync_socket_bgpd, &rec,
+ sizeof(struct pfx_record))
+ != -1)
+ ;
+
+ atomic_store_explicit(&rtr_update_overflow, 0,
+ memory_order_seq_cst);
+ revalidate_all_routes();
+ return 0;
+ }
+
+ 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)) {
+ struct peer *peer;
+ struct listnode *peer_listnode;
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, peer_listnode, peer)) {
+ safi_t safi;
+
+ for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) {
+ if (!peer->bgp->rib[afi][safi])
+ continue;
+
+ struct list *matches = list_new();
+
+ matches->del =
+ (void (*)(void *))bgp_unlock_node;
+
+ bgp_table_range_lookup(
+ peer->bgp->rib[afi][safi], prefix,
+ rec.max_len, matches);
+
+
+ struct bgp_node *bgp_node;
+ struct listnode *bgp_listnode;
+
+ for (ALL_LIST_ELEMENTS_RO(matches, bgp_listnode,
+ bgp_node))
+ revalidate_bgp_node(bgp_node, afi,
+ safi);
+
+ list_delete(&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_path_info *path = bgp_node->info;
+ mpls_label_t *label = NULL;
+ uint32_t num_labels = 0;
+
+ if (path && path->extra) {
+ label = path->extra->label;
+ num_labels = path->extra->num_labels;
+ }
+ ret = bgp_update(ain->peer, &bgp_node->p, ain->addpath_rx_id,
+ ain->attr, afi, safi, ZEBRA_ROUTE_BGP,
+ BGP_ROUTE_NORMAL, NULL, label, num_labels, 1,
+ NULL);
+
+ if (ret < 0)
+ return;
+ }
+}
+
+static void revalidate_all_routes(void)
+{
+ struct bgp *bgp;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
+ struct peer *peer;
+ struct listnode *peer_listnode;
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, peer_listnode, peer)) {
+
+ 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 (!peer->bgp->rib[afi][safi])
+ continue;
+
+ bgp_soft_reconfig_in(peer, 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
+ || atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst))
+ return;
+
+ int retval =
+ write(rpki_sync_socket_rtr, &rec, sizeof(struct pfx_record));
+ if (retval == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
+ atomic_store_explicit(&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");
+}
+
+static void rpki_init_sync_socket(void)
+{
+ int fds[2];
+ const char *msg;
+
+ RPKI_DEBUG("initializing sync socket");
+ if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, fds) != 0) {
+ msg = "could not open rpki sync socketpair";
+ goto err;
+ }
+ rpki_sync_socket_rtr = fds[0];
+ rpki_sync_socket_bgpd = fds[1];
+
+ if (set_nonblocking(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) {
+ msg = "could not set rpki_sync_socket_bgpd to non blocking";
+ goto err;
+ }
+
+
+ thread_add_read(bm->master, bgpd_sync_callback, NULL,
+ rpki_sync_socket_bgpd, NULL);
+
+ return;
+
+err:
+ zlog_err("RPKI: %s", msg);
+ abort();
+
+}
+
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;
initial_synchronisation_timeout =
INITIAL_SYNCHRONISATION_TIMEOUT_DEFAULT;
install_cli_commands();
+ rpki_init_sync_socket();
return 0;
}
static int bgp_rpki_fini(void)
{
stop();
- list_delete_and_null(&cache_list);
+ list_delete(&cache_list);
+
+ close(rpki_sync_socket_rtr);
+ close(rpki_sync_socket_bgpd);
return 0;
}
unsigned int waiting_time = 0;
int ret;
+ rtr_is_stopping = 0;
+ rtr_is_starting = 1;
+ rtr_update_overflow = 0;
+
if (list_isempty(cache_list)) {
RPKI_DEBUG(
"No caches were found in config. Prefix validation is off.");
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;
}
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);
static void stop(void)
{
+ rtr_is_stopping = 1;
if (rtr_is_running) {
rtr_mgr_stop(rtr_config);
rtr_mgr_free(rtr_config);
}
static int rpki_validate_prefix(struct peer *peer, struct attr *attr,
- struct prefix *prefix)
+ const struct prefix *prefix)
{
struct assegment *as_segment;
as_t as_number = 0;
ip_addr_prefix.u.addr4.addr = ntohl(prefix->u.prefix4.s_addr);
break;
-#ifdef HAVE_IPV6
case AF_INET6:
ip_addr_prefix.ver = LRTR_IPV6;
ipv6_addr_to_host_byte_order(prefix->u.prefix6.s6_addr32,
ip_addr_prefix.u.addr6.addr);
break;
-#endif /* HAVE_IPV6 */
default:
return 0;
listnode_add(cache_list, cache);
- if (rtr_is_running
- && rtr_mgr_add_group(rtr_config, &group) != RTR_SUCCESS) {
- return ERROR;
+ if (rtr_is_running) {
+ init_tr_socket(cache);
+
+ if (rtr_mgr_add_group(rtr_config, &group) != RTR_SUCCESS) {
+ free_tr_socket(cache);
+ return ERROR;
+ }
}
return SUCCESS;
"Preference of the cache server\n"
"Preference value\n")
{
- int return_value = SUCCESS;
+ int return_value;
// use ssh connection
if (ssh_uname) {
add_ssh_cache(cache, sshport, ssh_uname, ssh_privkey,
ssh_pubkey, server_pubkey, preference);
#else
+ return_value = SUCCESS;
vty_out(vty,
"ssh sockets are not supported. "
"Please recompile rtrlib and frr with ssh support. "
"exit",
"Exit rpki configuration and restart rpki session\n")
{
- int ret = reset(false);
+ reset(false);
vty->node = CONFIG_NODE;
- return ret == SUCCESS ? CMD_SUCCESS : CMD_WARNING;
+ return CMD_SUCCESS;
}
DEFUN_NOSH (rpki_quit,
{
int ret = reset(false);
- vty_config_unlock(vty);
+ vty_config_exit(vty);
vty->node = ENABLE_NODE;
return ret == SUCCESS ? CMD_SUCCESS : CMD_WARNING;
}