mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2025-01-09 02:21:54 +00:00
882 lines
24 KiB
C
882 lines
24 KiB
C
/*
|
|
* BIRD -- The Resource Public Key Infrastructure (RPKI) to Router Protocol
|
|
*
|
|
* Using RTRLib: http://rpki.realmv6.org/
|
|
*
|
|
* (c) 2015 CZ.NIC
|
|
*
|
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
|
*/
|
|
|
|
/**
|
|
* DOC: The Resource Public Key Infrastructure (RPKI) to Router Protocol
|
|
*/
|
|
|
|
/*
|
|
* TODO
|
|
* - Make correct log depending on protocol option 'debug all|off|{ states, routes, filters, interfaces, events, packets }'
|
|
*/
|
|
|
|
#define LOCAL_DEBUG
|
|
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
#include <dlfcn.h>
|
|
|
|
#include "proto/rpki/rpki.h"
|
|
#include "lib/socket.h"
|
|
#include "lib/ip.h"
|
|
#include "nest/route.h"
|
|
|
|
#define RPKI_LOG_ADD "add"
|
|
#define RPKI_LOG_DEL "del"
|
|
#define RPKI_LOG_ENTRY_FMT(ip_fmt) " roa " ip_fmt "/%u max %u as %u"
|
|
#define RPKI_LOG_FMT(operation_name) operation_name RPKI_LOG_ENTRY_FMT("%I")
|
|
#define RPKI_LOG_SKIP_FMT(operation_name) operation_name RPKI_LOG_ENTRY_FMT("%s") " skipped incompatible IP version"
|
|
|
|
static inline const char *
|
|
get_rtr_socket_ident(const struct rtr_socket *socket)
|
|
{
|
|
return socket->tr_socket->ident_fp(socket->tr_socket->socket);
|
|
}
|
|
#define RPKI_CACHE_TRACE(p, rtr_socket, msg, args...) RPKI_TRACE(p, "%s " msg, get_rtr_socket_ident(rtr_socket), ## args);
|
|
#define RPKI_CACHE_ERROR(p, rtr_socket, msg, args...) RPKI_ERROR(p, "%s " msg, get_rtr_socket_ident(rtr_socket), ## args);
|
|
|
|
struct rpki_entry {
|
|
node n;
|
|
u32 asn;
|
|
ip_addr ip;
|
|
u8 pxlen;
|
|
u8 maxlen;
|
|
u8 added;
|
|
};
|
|
|
|
void pipe_drain(int fd); /* implementation in io.c */
|
|
void pipe_kick(int fd); /* implementation in io.c */
|
|
|
|
ssize_t
|
|
pipe_drain_data(int fd, void *data, size_t size)
|
|
{
|
|
int ret_val;
|
|
|
|
try:
|
|
ret_val = read(fd, data, size);
|
|
if (ret_val < 0)
|
|
{
|
|
if (errno == EINTR)
|
|
goto try;
|
|
if (errno == EAGAIN)
|
|
return 0;
|
|
die("wakeup read: %m");
|
|
}
|
|
// if (ret_val == 64)
|
|
// goto try;
|
|
return ret_val;
|
|
}
|
|
|
|
void
|
|
pipe_kick_data(int fd, void *data, size_t size)
|
|
{
|
|
u64 v = 1;
|
|
int ret_val;
|
|
|
|
try:
|
|
ret_val = write(fd, data, size);
|
|
if (ret_val < 0)
|
|
{
|
|
if (errno == EINTR)
|
|
goto try;
|
|
if (errno == EAGAIN)
|
|
return;
|
|
die("wakeup write: %m");
|
|
}
|
|
}
|
|
|
|
static list rpki_proto_list;
|
|
static pthread_mutex_t rpki_proto_list_lock;
|
|
|
|
/* RTRLib and function pointers */
|
|
static void *rtrlib;
|
|
static struct rtr_mgr_config * (*rtr_mgr_init_x)(
|
|
struct rtr_mgr_group groups[], const unsigned int groups_len,
|
|
const unsigned int refresh_interval, const unsigned int expire_interval,
|
|
const void *update_fp,
|
|
const void *spki_update_fp,
|
|
const void *status_fp,
|
|
void *status_fp_data);
|
|
static int (*rtr_mgr_start_x)(struct rtr_mgr_config *config);
|
|
static const char * (*rtr_state_to_str_x)(enum rtr_socket_state state);
|
|
static const char * (*rtr_mgr_status_to_str_x)(enum rtr_mgr_status status);
|
|
static int (*tr_tcp_init_x)(const struct tr_tcp_config *config, struct tr_socket *socket);
|
|
static int (*tr_ssh_init_x)(const struct tr_ssh_config *config, struct tr_socket *socket);
|
|
static void (*tr_free_x)(struct tr_socket *tr_sock);
|
|
static void (*rtr_mgr_stop_x)(struct rtr_mgr_config *config);
|
|
static void (*rtr_mgr_free_x)(struct rtr_mgr_config *config);
|
|
|
|
static inline void
|
|
lock_rpki_proto_list(void)
|
|
{
|
|
pthread_mutex_lock(&rpki_proto_list_lock);
|
|
}
|
|
|
|
static inline void
|
|
unlock_rpki_proto_list(void)
|
|
{
|
|
pthread_mutex_unlock(&rpki_proto_list_lock);
|
|
}
|
|
|
|
/*
|
|
* Try load system shared library RTRLib
|
|
* Return NULL pointer if successful
|
|
* Otherwise return a pointer to a description of the error
|
|
*/
|
|
char *
|
|
rpki_load_rtrlib(void)
|
|
{
|
|
char *err_buf = NULL;
|
|
|
|
if (rtrlib != NULL)
|
|
return NULL; /* RTRLib is loaded already */
|
|
|
|
const char *rtrlib_name = RPKI_LIBRTR_DEFAULT;
|
|
|
|
#ifdef LIBRTR
|
|
rtrlib_name = LIBRTR; /* Use a compile variable */
|
|
#endif
|
|
|
|
rtrlib = dlopen(rtrlib_name, RTLD_LAZY);
|
|
if (!rtrlib)
|
|
{
|
|
/* This would be probably often repeated problem */
|
|
char *help_msg = "Try recompile BIRD with CFLAGS='-DLIBRTR=\\\"/path/to/librtr.so\\\"' "
|
|
"or see BIRD User's Guide for more information.";
|
|
err_buf = mb_alloc(&root_pool, 512);
|
|
bsnprintf(err_buf, 512, "%s. %s", dlerror(), help_msg);
|
|
return err_buf;
|
|
}
|
|
|
|
dlerror(); /* Clear any existing error */
|
|
|
|
rtr_mgr_init_x = (struct rtr_mgr_config * (*)(
|
|
struct rtr_mgr_group groups[], const unsigned int groups_len,
|
|
const unsigned int refresh_interval, const unsigned int expire_interval,
|
|
const void *update_fp,
|
|
const void *spki_update_fp,
|
|
const void *status_fp,
|
|
void *status_fp_data)) dlsym(rtrlib, "rtr_mgr_init");
|
|
if ((err_buf = dlerror()) != NULL)
|
|
return err_buf;
|
|
|
|
rtr_mgr_start_x = (int (*)(struct rtr_mgr_config *)) dlsym(rtrlib, "rtr_mgr_start");
|
|
if ((err_buf = dlerror()) != NULL)
|
|
return err_buf;
|
|
|
|
rtr_state_to_str_x = (const char * (*)(enum rtr_socket_state state)) dlsym(rtrlib, "rtr_state_to_str");
|
|
if ((err_buf = dlerror()) != NULL)
|
|
return err_buf;
|
|
|
|
rtr_mgr_status_to_str_x = (const char * (*)(enum rtr_mgr_status status)) dlsym(rtrlib, "rtr_mgr_status_to_str");
|
|
if ((err_buf = dlerror()) != NULL)
|
|
return err_buf;
|
|
|
|
tr_tcp_init_x = (int (*)(const struct tr_tcp_config *config, struct tr_socket *socket)) dlsym(rtrlib, "tr_tcp_init");
|
|
if ((err_buf = dlerror()) != NULL)
|
|
return err_buf;
|
|
|
|
tr_ssh_init_x = (int (*)(const struct tr_ssh_config *config, struct tr_socket *socket)) dlsym(rtrlib, "tr_ssh_init");
|
|
if ((err_buf = dlerror()) != NULL)
|
|
return err_buf;
|
|
|
|
tr_free_x = (void (*)(struct tr_socket *)) dlsym(rtrlib, "tr_free");
|
|
if ((err_buf = dlerror()) != NULL)
|
|
return err_buf;
|
|
|
|
rtr_mgr_stop_x = (void (*)(struct rtr_mgr_config *config)) dlsym(rtrlib, "rtr_mgr_stop");
|
|
if ((err_buf = dlerror()) != NULL)
|
|
return err_buf;
|
|
|
|
rtr_mgr_free_x = (void (*)(struct rtr_mgr_config *config)) dlsym(rtrlib, "rtr_mgr_free");
|
|
if ((err_buf = dlerror()) != NULL)
|
|
return err_buf;
|
|
|
|
return NULL; /* OK */
|
|
}
|
|
|
|
void
|
|
rpki_init_all(void)
|
|
{
|
|
init_list(&rpki_proto_list);
|
|
pthread_mutex_init(&rpki_proto_list_lock, NULL);
|
|
rtrlib = NULL;
|
|
}
|
|
|
|
static const char *mgr_str_status_descript[] = {
|
|
[RTR_MGR_CLOSED] = "RTR sockets are disconnected",
|
|
[RTR_MGR_CONNECTING] = "RTR sockets trying to establish a connection.",
|
|
[RTR_MGR_ESTABLISHED] = "All RTR sockets of the group are synchronized with the rtr servers",
|
|
[RTR_MGR_ERROR] = "Error occured on at least one RTR socket",
|
|
};
|
|
|
|
static const char *rtr_socket_states_descript[] = {
|
|
[RTR_CONNECTING] = "Socket is establishing the transport connection",
|
|
[RTR_ESTABLISHED] = "Connection is established, socket is waiting for a Serial Notify or expiration of the refresh_interval timer",
|
|
[RTR_RESET] = "Resetting RTR connection",
|
|
[RTR_SYNC] = "Receiving validation records from the RTR server",
|
|
[RTR_FAST_RECONNECT] = "Reconnect without any waiting period",
|
|
[RTR_ERROR_NO_DATA_AVAIL] = "No validation records are available on the RTR server",
|
|
[RTR_ERROR_NO_INCR_UPDATE_AVAIL] = "Server was unable to answer the last serial or reset query",
|
|
[RTR_ERROR_FATAL] = "Fatal protocol error occurred",
|
|
[RTR_ERROR_TRANSPORT] = "Error on the transport socket occurred",
|
|
[RTR_SHUTDOWN] = "RTR Socket is stopped",
|
|
};
|
|
|
|
static void
|
|
status_update_rtrlib_thread_hook(const struct rtr_mgr_group *group, enum rtr_mgr_status mgr_status, const struct rtr_socket *socket, void *data)
|
|
{
|
|
struct rpki_proto *p = data;
|
|
|
|
switch (socket->state)
|
|
{
|
|
case RTR_SHUTDOWN:
|
|
if (mgr_status == RTR_MGR_CLOSED)
|
|
{
|
|
RPKI_CACHE_TRACE(p, socket, "%s", mgr_str_status_descript[mgr_status]);
|
|
}
|
|
break;
|
|
case RTR_ERROR_FATAL:
|
|
case RTR_ERROR_TRANSPORT:
|
|
case RTR_ERROR_NO_DATA_AVAIL: /** No validation records are available on the RTR server. */
|
|
case RTR_ERROR_NO_INCR_UPDATE_AVAIL: /** Server was unable to answer the last serial or reset query. */
|
|
RPKI_CACHE_ERROR(p, socket, "%s", rtr_socket_states_descript[socket->state]);
|
|
break;
|
|
case RTR_FAST_RECONNECT:
|
|
case RTR_SYNC:
|
|
case RTR_RESET:
|
|
case RTR_CONNECTING:
|
|
RPKI_CACHE_TRACE(p, socket, "[%s] %s", rtr_state_to_str_x(socket->state), rtr_socket_states_descript[socket->state]);
|
|
if (mgr_status == RTR_MGR_CONNECTING || mgr_status == RTR_MGR_ERROR)
|
|
{
|
|
pipe_kick_data(p->status_update.write->fd, &((int){PS_START}), sizeof(int));
|
|
}
|
|
break;
|
|
case RTR_ESTABLISHED:
|
|
RPKI_CACHE_TRACE(p, socket, "%s", mgr_str_status_descript[mgr_status]);
|
|
if (mgr_status == RTR_MGR_ESTABLISHED)
|
|
{
|
|
pipe_kick_data(p->status_update.write->fd, &((int){PS_UP}), sizeof(int));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
log_skipped_entry(struct rpki_proto *p, const struct pfx_record *rec, const bool added)
|
|
{
|
|
char ip_buf[INET6_ADDRSTRLEN];
|
|
ip4_addr ip4;
|
|
ip6_addr ip6;
|
|
|
|
if (rec->prefix.ver == RTRLIB_IPV4)
|
|
{
|
|
ip4 = ip4_from_u32(rec->prefix.u.addr4.addr);
|
|
ip4_ntop(ip4, ip_buf);
|
|
}
|
|
else
|
|
{
|
|
ip6 = ip6_build(rec->prefix.u.addr6.addr[0], rec->prefix.u.addr6.addr[1], rec->prefix.u.addr6.addr[2], rec->prefix.u.addr6.addr[3]);
|
|
ip6_ntop(ip6, ip_buf);
|
|
}
|
|
|
|
if (added)
|
|
{
|
|
RPKI_CACHE_TRACE(p, rec->socket, RPKI_LOG_SKIP_FMT(RPKI_LOG_ADD), ip_buf, rec->min_len, rec->max_len, rec->asn);
|
|
}
|
|
else
|
|
{
|
|
RPKI_CACHE_TRACE(p, rec->socket, RPKI_LOG_SKIP_FMT(RPKI_LOG_DEL), ip_buf, rec->min_len, rec->max_len, rec->asn);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Return (struct rpki_proto *) or NULL
|
|
*/
|
|
static struct rpki_proto *
|
|
get_rpki_proto_by_rtr_socket(const struct rtr_socket *socket)
|
|
{
|
|
struct rpki_proto *p_not_skipped_back;
|
|
unsigned int i, j;
|
|
|
|
lock_rpki_proto_list();
|
|
WALK_LIST(p_not_skipped_back, rpki_proto_list)
|
|
{
|
|
struct rpki_proto *p = SKIP_BACK(struct rpki_proto, rpki_node, p_not_skipped_back);
|
|
|
|
for (i = 0; i < p->rtr_conf->len; i++)
|
|
{
|
|
for (j = 0; j < p->rtr_conf->groups[i].sockets_len; j++)
|
|
{
|
|
if (socket == p->rtr_conf->groups[i].sockets[j])
|
|
{
|
|
unlock_rpki_proto_list();
|
|
return p;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
unlock_rpki_proto_list();
|
|
return NULL; /* FAIL */
|
|
}
|
|
|
|
static void
|
|
send_data_to_main_thread(struct rpki_proto *p, struct rpki_entry *e)
|
|
{
|
|
rpki_lock_notify(p);
|
|
add_tail(&p->roa_update_list, &e->n);
|
|
rpki_unlock_notify(p);
|
|
pipe_kick(p->roa_update.write->fd);
|
|
}
|
|
|
|
static void
|
|
roa_update_rtrlib_thread_hook(void *pfx_table, const struct pfx_record rec, const bool added)
|
|
{
|
|
struct rpki_proto *p = get_rpki_proto_by_rtr_socket(rec.socket);
|
|
if (!p)
|
|
{
|
|
bug("rtr_thread_update_hook: Cannot find matching protocol for %s\n", get_rtr_socket_ident(rec.socket));
|
|
return;
|
|
}
|
|
/* process only records that are the same with BIRD IP version */
|
|
#ifdef IPV6
|
|
if (rec.prefix.ver != RTRLIB_IPV6)
|
|
{
|
|
log_skipped_entry(p, &rec, added);
|
|
return;
|
|
}
|
|
|
|
ip_addr ip = ip6_build(rec.prefix.u.addr6.addr[0], rec.prefix.u.addr6.addr[1], rec.prefix.u.addr6.addr[2], rec.prefix.u.addr6.addr[3]);
|
|
#else
|
|
if (rec.prefix.ver != RTRLIB_IPV4)
|
|
{
|
|
log_skipped_entry(p, &rec, added);
|
|
return;
|
|
}
|
|
|
|
ip_addr ip = ip4_from_u32(rec.prefix.u.addr4.addr);
|
|
#endif
|
|
|
|
/* TODO: Make more effective solution with thread-safe recycle-able pool/queue of rpki_entry structures
|
|
* without endless allocations and frees */
|
|
struct rpki_entry *e = mb_allocz(p->p.pool, sizeof(struct rpki_entry));
|
|
e->added = added;
|
|
e->asn = rec.asn;
|
|
e->ip = ip;
|
|
e->pxlen = rec.min_len;
|
|
e->maxlen = rec.max_len;
|
|
|
|
if (e->added)
|
|
{
|
|
RPKI_CACHE_TRACE(p, rec.socket, RPKI_LOG_FMT(RPKI_LOG_ADD), e->ip, e->pxlen, e->maxlen, e->asn);
|
|
}
|
|
else
|
|
{
|
|
RPKI_CACHE_TRACE(p, rec.socket, RPKI_LOG_FMT(RPKI_LOG_DEL), e->ip, e->pxlen, e->maxlen, e->asn);
|
|
}
|
|
|
|
send_data_to_main_thread(p, e);
|
|
}
|
|
|
|
static struct proto *
|
|
rpki_init(struct proto_config *C)
|
|
{
|
|
struct proto *P = proto_new(C, sizeof(struct rpki_proto));
|
|
struct rpki_proto *p = (struct rpki_proto *) P;
|
|
p->cf = (struct rpki_config *) C;
|
|
|
|
RPKI_TRACE(p, "------------- rpki_init -------------");
|
|
|
|
return P;
|
|
}
|
|
|
|
struct rpki_cache *
|
|
rpki_new_cache(void)
|
|
{
|
|
struct rpki_cache *cache = (struct rpki_cache *)cfg_allocz(sizeof(struct rpki_cache));
|
|
cache->preference = RPKI_DEFAULT_CACHE_PREFERENCE;
|
|
/* cache port will be set afterwards */
|
|
return cache;
|
|
}
|
|
|
|
static int
|
|
status_update_bird_thread_hook(struct birdsock *sk, int size)
|
|
{
|
|
struct rpki_proto *p = sk->data;
|
|
char *p_states[] = { "DOWN", "START", "UP", "STOP" };
|
|
|
|
int proto_state = -1;
|
|
if (pipe_drain_data(sk->fd, &proto_state, sizeof(int)) > 0)
|
|
{
|
|
switch (proto_state)
|
|
{
|
|
case PS_DOWN:
|
|
case PS_START:
|
|
case PS_STOP:
|
|
case PS_UP:
|
|
if (proto_state != p->p.proto_state)
|
|
{
|
|
RPKI_TRACE(p, "Change protokol state %s -> %s", p_states[p->p.proto_state], p_states[proto_state]);
|
|
proto_notify_state(&p->p, proto_state);
|
|
}
|
|
break;
|
|
default:
|
|
RPKI_ERROR(p, "%s: we received some nonsense %d", __func__, proto_state);
|
|
abort();
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
roa_update_bird_thread_hook(struct birdsock *sk, int size)
|
|
{
|
|
struct rpki_proto *p = sk->data;
|
|
struct rpki_entry *e;
|
|
list tmp_list;
|
|
|
|
pipe_drain(sk->fd);
|
|
|
|
rpki_lock_notify(p);
|
|
init_list(&tmp_list);
|
|
add_tail_list(&tmp_list, &p->roa_update_list);
|
|
init_list(&p->roa_update_list);
|
|
rpki_unlock_notify(p);
|
|
|
|
WALK_LIST_FIRST(e, tmp_list)
|
|
{
|
|
rpki_lock_notify(p);
|
|
rem2_node(&e->n);
|
|
rpki_unlock_notify(p);
|
|
|
|
if (e->added)
|
|
roa_add_item(p->cf->roa_table_cf->table, e->ip, e->pxlen, e->maxlen, e->asn, ROA_SRC_RPKI);
|
|
else
|
|
roa_delete_item(p->cf->roa_table_cf->table, e->ip, e->pxlen, e->maxlen, e->asn, ROA_SRC_RPKI);
|
|
mb_free(e);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
pipe_error_hook(struct birdsock *sk, int err)
|
|
{
|
|
struct rpki_proto *p = sk->data;
|
|
RPKI_ERROR(p, "Notify socket error[%d] %m", err);
|
|
}
|
|
|
|
static sock *
|
|
create_pipe(struct rpki_proto *p, int fd)
|
|
{
|
|
sock *sk = sk_new(p->p.pool);
|
|
sk->type = SK_MAGIC;
|
|
sk->fd = fd;
|
|
sk->data = p;
|
|
return sk;
|
|
}
|
|
|
|
static sock *
|
|
create_read_pipe(struct rpki_proto *p, int fd)
|
|
{
|
|
sock *sk = create_pipe(p, fd);
|
|
sk->err_hook = pipe_error_hook;
|
|
if (sk_open(sk) < 0)
|
|
return NULL;
|
|
return sk;
|
|
}
|
|
|
|
static sock *
|
|
create_write_pipe(struct rpki_proto *p, int fd)
|
|
{
|
|
sock *sk = create_pipe(p, fd);
|
|
sk->flags = SKF_THREAD;
|
|
if (sk_open(sk) < 0)
|
|
return NULL;
|
|
return sk;
|
|
}
|
|
|
|
static void
|
|
create_pipe_pair(struct rpki_proto *p, struct rpki_rw_sk_pair *sk_pair, int (*recv_callback)(struct birdsock *, int))
|
|
{
|
|
int pipe_fildes[2];
|
|
|
|
if ((pipe(pipe_fildes) < 0)
|
|
|| ((sk_pair->read = create_read_pipe(p, pipe_fildes[0])) == NULL)
|
|
|| ((sk_pair->write = create_write_pipe(p, pipe_fildes[1])) == NULL)
|
|
)
|
|
RPKI_DIE(p, "pipe: %m");
|
|
|
|
sk_pair->read->rx_hook = recv_callback;
|
|
}
|
|
|
|
static uint
|
|
count_number_of_various_preferences(list *cache_list)
|
|
{
|
|
uint i;
|
|
u8 preference[256];
|
|
bzero(preference, sizeof(preference));
|
|
|
|
struct rpki_cache *cache;
|
|
WALK_LIST(cache, *cache_list)
|
|
{
|
|
preference[cache->preference]++;
|
|
}
|
|
|
|
uint count = 0;
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
if (preference[i])
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static uint
|
|
count_number_of_caches_with_specific_preference(list *cache_list, uint preference)
|
|
{
|
|
uint count = 0;
|
|
|
|
struct rpki_cache *cache;
|
|
WALK_LIST(cache, *cache_list)
|
|
{
|
|
if (cache->preference == preference)
|
|
count++;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static struct rtr_socket *
|
|
create_rtrlib_tcp_socket(struct rpki_cache *cache, pool *pool)
|
|
{
|
|
struct rtr_socket *rtrlib_tcp = mb_allocz(pool, sizeof(struct rtr_socket));
|
|
rtrlib_tcp->tr_socket = mb_allocz(pool, sizeof(struct tr_socket));
|
|
|
|
struct tr_tcp_config tcp_config = {
|
|
.host = cache->host,
|
|
.port = cache->port
|
|
};
|
|
|
|
tr_tcp_init_x(&tcp_config, rtrlib_tcp->tr_socket);
|
|
|
|
return rtrlib_tcp;
|
|
}
|
|
|
|
static struct rtr_socket *
|
|
create_rtrlib_ssh_socket(struct rpki_cache *cache, pool *pool)
|
|
{
|
|
struct rtr_socket *rtrlib_ssh = mb_allocz(pool, sizeof(struct rtr_socket));
|
|
rtrlib_ssh->tr_socket = mb_allocz(pool, sizeof(struct tr_socket));
|
|
struct tr_ssh_config ssh_config = {
|
|
.host = cache->host,
|
|
.port = atoi(cache->port), /* TCP transport needs port in (char *) */
|
|
.username = cache->ssh->user,
|
|
.client_privkey_path = cache->ssh->bird_private_key,
|
|
.server_hostkey_path = cache->ssh->cache_public_key,
|
|
};
|
|
|
|
tr_ssh_init_x(&ssh_config, rtrlib_ssh->tr_socket);
|
|
|
|
return rtrlib_ssh;
|
|
}
|
|
|
|
static struct rtr_socket *
|
|
create_rtrlib_socket(struct rpki_proto *p, struct rpki_cache *cache, pool *pool)
|
|
{
|
|
struct rtr_socket *s;
|
|
if (cache->ssh)
|
|
s = create_rtrlib_ssh_socket(cache, pool);
|
|
else
|
|
s = create_rtrlib_tcp_socket(cache, pool);
|
|
|
|
s->connection_state_fp_param = p;
|
|
return s;
|
|
}
|
|
|
|
struct rtr_mgr_group_crate {
|
|
struct rtr_mgr_group *groups;
|
|
uint groups_len;
|
|
};
|
|
|
|
static struct rtr_mgr_group_crate
|
|
group_cache_list_by_preferences(struct rpki_proto *p, list *cache_list, pool *pool)
|
|
{
|
|
/* TODO: Improve algorithm for grouping cache servers by preferences.
|
|
* At the beginning sort a list of caches by preferences... */
|
|
|
|
u8 completed_preference[256];
|
|
bzero(completed_preference, sizeof(completed_preference));
|
|
|
|
uint groups_len = count_number_of_various_preferences(cache_list);
|
|
struct rtr_mgr_group *groups = mb_allocz(pool, groups_len * sizeof(struct rtr_mgr_group));
|
|
|
|
DBG("group_cache_list_by_preferences(): groups_len %u \n", groups_len);
|
|
|
|
uint group_idx = 0;
|
|
struct rpki_cache *first_cache_in_group;
|
|
WALK_LIST(first_cache_in_group, *cache_list)
|
|
{
|
|
if (completed_preference[first_cache_in_group->preference])
|
|
continue;
|
|
completed_preference[first_cache_in_group->preference] = 1;
|
|
|
|
struct rtr_mgr_group *group = &groups[group_idx];
|
|
|
|
group->preference = first_cache_in_group->preference;
|
|
group->sockets_len = count_number_of_caches_with_specific_preference(cache_list, first_cache_in_group->preference);
|
|
group->sockets = mb_allocz(pool, group->sockets_len * sizeof(struct rtr_socket *));
|
|
|
|
uint socket_idx = 0;
|
|
struct rpki_cache *cache;
|
|
WALK_LIST(cache, *cache_list)
|
|
{
|
|
if (cache->preference == groups[group_idx].preference)
|
|
{
|
|
group->sockets[socket_idx] = cache->rtrlib_sock = create_rtrlib_socket(p, cache, pool);
|
|
DBG("group_cache_list_by_preferences(): add cache %s:%s to group %u, socket %u \n", cache->host, cache->port, group_idx, socket_idx);
|
|
socket_idx++;
|
|
}
|
|
}
|
|
group_idx++;
|
|
}
|
|
|
|
return (struct rtr_mgr_group_crate) {groups, groups_len};
|
|
}
|
|
|
|
/*
|
|
* Return RTR_SUCCESS or RTR_ERROR
|
|
*/
|
|
static int
|
|
rpki_start_rtrlib_mgr(struct rpki_proto *p, struct rpki_config *cf)
|
|
{
|
|
struct rtr_mgr_group_crate grouped_list = group_cache_list_by_preferences(p, &cf->cache_list, p->p.pool);
|
|
|
|
p->rtr_conf = rtr_mgr_init_x(
|
|
grouped_list.groups,
|
|
grouped_list.groups_len,
|
|
10, 20,
|
|
&roa_update_rtrlib_thread_hook,
|
|
NULL,
|
|
&status_update_rtrlib_thread_hook, p);
|
|
|
|
return rtr_mgr_start_x(p->rtr_conf);
|
|
}
|
|
|
|
static int
|
|
rpki_start(struct proto *P)
|
|
{
|
|
struct rpki_proto *p = (struct rpki_proto *) P;
|
|
struct rpki_config *cf = (struct rpki_config *) (P->cf);
|
|
|
|
create_pipe_pair(p, &p->status_update, status_update_bird_thread_hook);
|
|
|
|
create_pipe_pair(p, &p->roa_update, roa_update_bird_thread_hook);
|
|
init_list(&p->roa_update_list);
|
|
pthread_mutex_init(&p->roa_update_lock, NULL);
|
|
|
|
lock_rpki_proto_list();
|
|
add_tail(&rpki_proto_list, &p->rpki_node);
|
|
unlock_rpki_proto_list();
|
|
|
|
if (rpki_start_rtrlib_mgr(p, cf) != RTR_SUCCESS)
|
|
{
|
|
RPKI_ERROR(p, "Cannot start RTRLib Manager");
|
|
return PS_DOWN;
|
|
}
|
|
|
|
return PS_START;
|
|
}
|
|
|
|
static void
|
|
rpki_stop_and_free_rtrlib_mgr(struct rpki_proto *p)
|
|
{
|
|
/* TODO: fire stop&free() asynchronously */
|
|
|
|
RPKI_TRACE(p, "Stopping RTRLib Manager");
|
|
|
|
rtr_mgr_stop_x(p->rtr_conf); /* this takes a long time */
|
|
rtr_mgr_free_x(p->rtr_conf);
|
|
|
|
struct rpki_cache *cache;
|
|
WALK_LIST(cache, p->cf->cache_list)
|
|
{
|
|
if (cache->rtrlib_sock)
|
|
{
|
|
tr_free_x(cache->rtrlib_sock->tr_socket);
|
|
|
|
mb_free(cache->rtrlib_sock->tr_socket);
|
|
mb_free(cache->rtrlib_sock);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
rpki_shutdown(struct proto *P)
|
|
{
|
|
struct rpki_proto *p = (struct rpki_proto *) P;
|
|
|
|
log(L_DEBUG "------------- rpki_shutdown -------------");
|
|
|
|
rpki_stop_and_free_rtrlib_mgr(p);
|
|
|
|
lock_rpki_proto_list();
|
|
rem2_node(&p->rpki_node);
|
|
unlock_rpki_proto_list();
|
|
|
|
pthread_mutex_destroy(&p->roa_update_lock);
|
|
|
|
return PS_DOWN;
|
|
}
|
|
|
|
static struct rpki_cache *
|
|
get_cache_by_host_and_port(list *cache_list, struct rpki_cache *needle)
|
|
{
|
|
struct rpki_cache *cache;
|
|
WALK_LIST(cache, *cache_list)
|
|
{
|
|
if ((strcmp(needle->host, cache->host) == 0) && (strcmp(needle->port, cache->port) == 0))
|
|
{
|
|
return cache;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Return 1 if need to restart rtrlib manager
|
|
* Return 0 if not need to restart rtrlib manager
|
|
*/
|
|
static int
|
|
is_required_restart_rtrlib_mgr(struct rpki_proto *p, struct rpki_config *new_cf)
|
|
{
|
|
struct rpki_config *old_cf = p->cf;
|
|
|
|
struct rpki_cache *cache;
|
|
WALK_LIST(cache, old_cf->cache_list)
|
|
{
|
|
struct rpki_cache *match = get_cache_by_host_and_port(&new_cf->cache_list, cache);
|
|
if (!match)
|
|
{
|
|
/* some cache was deleted from old configuration */
|
|
RPKI_WARN(p, "reconfiguration has no match for cache %s:%s", cache->host, cache->port);
|
|
return 1; /* TODO: maybe can be called only rtr_stop(); without manager restart */
|
|
}
|
|
|
|
if (cache->preference != match->preference || (!!cache->ssh != !!match->ssh))
|
|
return 1;
|
|
|
|
if (cache->ssh && match->ssh)
|
|
{
|
|
if (strcmp(cache->ssh->bird_private_key, match->ssh->bird_private_key) != 0)
|
|
return 1;
|
|
|
|
if (strcmp(cache->ssh->cache_public_key, match->ssh->cache_public_key) != 0)
|
|
return 1;
|
|
|
|
if (strcmp(cache->ssh->user, match->ssh->user) != 0)
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
WALK_LIST(cache, new_cf->cache_list)
|
|
{
|
|
struct rpki_cache *match = get_cache_by_host_and_port(&new_cf->cache_list, cache);
|
|
if (!match)
|
|
{
|
|
/* some cache was added to new configuration */
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0; /* no restart required */
|
|
}
|
|
|
|
static int
|
|
rpki_reconfigure(struct proto *P, struct proto_config *c)
|
|
{
|
|
struct rpki_proto *p = (struct rpki_proto *) P;
|
|
struct rpki_config *new_cf = (struct rpki_config *) c;
|
|
|
|
if (is_required_restart_rtrlib_mgr(p, new_cf))
|
|
{
|
|
RPKI_TRACE(p, "Reconfiguration: Something changed, RTRLib Manager must be restarted");
|
|
if (P->proto_state != PS_DOWN)
|
|
rpki_stop_and_free_rtrlib_mgr(p);
|
|
|
|
if (rpki_start_rtrlib_mgr(p, new_cf) != RTR_SUCCESS)
|
|
{
|
|
RPKI_ERROR(p, "Reconfiguration failed: Cannot start RTRLib Manager");
|
|
p->cf = new_cf;
|
|
return 0; /* FAIL */
|
|
}
|
|
}
|
|
p->cf = new_cf;
|
|
return 1; /* OK */
|
|
}
|
|
|
|
static void
|
|
rpki_get_status(struct proto *P, byte *buf)
|
|
{
|
|
struct rpki_proto *p = (struct rpki_proto *) P;
|
|
unsigned int i, j;
|
|
|
|
uint established_connections = 0;
|
|
uint cache_servers = 0;
|
|
uint connecting = 0;
|
|
|
|
pthread_mutex_lock(&p->rtr_conf->mutex);
|
|
for (i = 0; i < p->rtr_conf->len; i++)
|
|
{
|
|
for (j = 0; j < p->rtr_conf->groups[i].sockets_len; j++)
|
|
{
|
|
cache_servers++;
|
|
switch (p->rtr_conf->groups[i].sockets[j]->state)
|
|
{
|
|
case RTR_ESTABLISHED:
|
|
established_connections++;
|
|
break;
|
|
case RTR_RESET:
|
|
case RTR_SYNC:
|
|
case RTR_FAST_RECONNECT:
|
|
case RTR_CONNECTING:
|
|
connecting++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
pthread_mutex_unlock(&p->rtr_conf->mutex);
|
|
|
|
if (established_connections > 0)
|
|
bsprintf(buf, "Keep synchronized with %u cache server%s", established_connections, (established_connections > 1) ? "s" : "");
|
|
else if (connecting > 0)
|
|
bsprintf(buf, "Connecting to %u cache server%s", connecting, (connecting > 1) ? "s" : "");
|
|
else if (cache_servers == 0)
|
|
bsprintf(buf, "No cache server is configured");
|
|
else if (cache_servers == 1)
|
|
bsprintf(buf, "Cannot connect to a cache server");
|
|
else
|
|
bsprintf(buf, "Cannot connect to any cache servers");
|
|
}
|
|
|
|
struct protocol proto_rpki = {
|
|
.name = "RPKI",
|
|
.template = "rpki%d",
|
|
.config_size = sizeof(struct rpki_config),
|
|
.init = rpki_init,
|
|
.start = rpki_start,
|
|
// .show_proto_info = rpki_show_proto_info, // TODO: be nice to be implemented
|
|
.shutdown = rpki_shutdown,
|
|
.reconfigure = rpki_reconfigure,
|
|
.get_status = rpki_get_status,
|
|
};
|