From 804684663b6aa7d8db8793d1b1295d045496f436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Tvrd=C3=ADk?= Date: Mon, 5 Oct 2015 16:25:48 +0200 Subject: [PATCH] RPKI: load RTRLib dynamically with dlopen() In configuration bird.conf you can specify a path to the RTRlib: protocol rpki { rtrlib "/usr/lib/librtr.so"; ... } --- proto/rpki/config.Y | 4 +- proto/rpki/rpki.c | 130 ++++++++++++++++++--- proto/rpki/rpki.h | 6 +- proto/rpki/rtrlib-mockup.h | 229 +++++++++++++++++++++++++++++++++++++ tools/Makefile-top.in | 1 - tools/Makefile.in | 2 +- tools/Rules.in | 2 +- 7 files changed, 353 insertions(+), 21 deletions(-) create mode 100644 proto/rpki/rtrlib-mockup.h diff --git a/proto/rpki/config.Y b/proto/rpki/config.Y index 384cc199..757a94f6 100644 --- a/proto/rpki/config.Y +++ b/proto/rpki/config.Y @@ -18,7 +18,7 @@ static struct rpki_cache *this_rpki_cache; CF_DECLS -CF_KEYWORDS(RPKI, CACHE, LIST, PREFERENCE) +CF_KEYWORDS(RPKI, CACHE, LIST, PREFERENCE, RTRLIB) CF_GRAMMAR @@ -37,6 +37,7 @@ rpki_proto_start: proto_start RPKI { this_proto = proto_config_new(&proto_rpki, $1); init_list(&RPKI_CFG->cache_list); + RPKI_CFG->rtrlib_path = RPKI_RTRLIB_PATH; } ; @@ -49,6 +50,7 @@ rpki_proto_item: proto_item | CACHE LIST '{' rpki_cache_list '}' | ROA TABLE roa_table_cf { RPKI_CFG->roa_table_cf = $3; } + | RTRLIB text { RPKI_CFG->rtrlib_path = $2; } ; rpki_cache_list: diff --git a/proto/rpki/rpki.c b/proto/rpki/rpki.c index 57445ef0..cef80d7b 100644 --- a/proto/rpki/rpki.c +++ b/proto/rpki/rpki.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "proto/rpki/rpki.h" #include "lib/socket.h" @@ -25,8 +26,8 @@ struct rpki_entry { node n; u32 asn; ip_addr ip; - u8 min_len; - u8 max_len; + u8 pxlen; + u8 maxlen; u8 added; struct rpki_proto *rpki; }; @@ -36,10 +37,93 @@ void pipe_kick(int fd); /* implementation in io.c */ static list rpki_proto_list; +static void *rtrlib; +static struct rtr_mgr_config * (*rtr_mgr_init_fp)( + 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_fp)(struct rtr_mgr_config *config); +static const char * (*rtr_state_to_str_fp)(enum rtr_socket_state state); +static const char * (*rtr_mgr_status_to_str_fp)(enum rtr_mgr_status status); +static int (*tr_tcp_init_fp)(const struct tr_tcp_config *config, struct tr_socket *socket); +static void (*rtr_mgr_stop_fp)(struct rtr_mgr_config *config); +static void (*rtr_mgr_free_fp)(struct rtr_mgr_config *config); + +static int +was_dlsym_ok(struct rpki_proto *p) +{ + char *err_buffer = dlerror(); + + if (err_buffer != NULL) + { + RPKI_ERROR(p, "%s. Try the latest version of RTRLib.", err_buffer); + return 0; /* FAIL */ + } + return 1; /* OK */ +} + +static int +load_rtrlib(struct rpki_proto *p) +{ + rtrlib = dlopen(p->cf->rtrlib_path, RTLD_LAZY); + if (!rtrlib) + { + RPKI_ERROR(p, "dlopen(): %s. Try specify path to the shared RTRLib (http://rpki.realmv6.org/) with 'rtrlib' option" + "inside of the rpki protocol configuration", dlerror()); + return 0; /* FAIL */ + } + else + { + RPKI_TRACE(p, "Loaded RTRLib from %s", p->cf->rtrlib_path); + } + + dlerror(); /* Clear any existing error */ + + rtr_mgr_init_fp = (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 (!was_dlsym_ok(p)) + return 0; /* FAIL */ + + rtr_mgr_start_fp = (int (*)(struct rtr_mgr_config *)) dlsym(rtrlib, "rtr_mgr_start"); + if (!was_dlsym_ok(p)) + return 0; /* FAIL */ + + rtr_state_to_str_fp = (const char * (*)(enum rtr_socket_state state)) dlsym(rtrlib, "rtr_state_to_str"); + if (!was_dlsym_ok(p)) + return 0; /* FAIL */ + + rtr_mgr_status_to_str_fp = (const char * (*)(enum rtr_mgr_status status)) dlsym(rtrlib, "rtr_mgr_status_to_str"); + if (!was_dlsym_ok(p)) + return 0; /* FAIL */ + + tr_tcp_init_fp = (int (*)(const struct tr_tcp_config *config, struct tr_socket *socket)) dlsym(rtrlib, "tr_tcp_init"); + if (!was_dlsym_ok(p)) + return 0; /* FAIL */ + + rtr_mgr_stop_fp = (void (*)(struct rtr_mgr_config *config)) dlsym(rtrlib, "rtr_mgr_stop"); + if (!was_dlsym_ok(p)) + return 0; /* FAIL */ + + rtr_mgr_free_fp = (void (*)(struct rtr_mgr_config *config)) dlsym(rtrlib, "rtr_mgr_free"); + if (!was_dlsym_ok(p)) + return 0; /* FAIL */ + + return 1; /* OK */ +} + void rpki_init_all(void) { init_list(&rpki_proto_list); + rtrlib = NULL; } static void @@ -53,7 +137,7 @@ status_cb(const struct rtr_mgr_group *group, enum rtr_mgr_status status, const s } else { - RPKI_TRACE(p, "status: %s\t%s", rtr_mgr_status_to_str(status), rtr_state_to_str(socket->state)); + RPKI_TRACE(p, "status: %s\t%s", (*rtr_mgr_status_to_str_fp)(status), (*rtr_state_to_str_fp)(socket->state)); } } @@ -85,15 +169,19 @@ log_skip_entry(struct rpki_proto *p, const struct pfx_record *rec, const bool ad ip6_ntop(ip6, ip_buf); } -#define LOG_SKIP_ENTRY_FMT(operation_name) "skip unsupported IP version: " operation_name " %25s/%u-%-3u \tASN: %10u" +#define RPKI_LOG_ADD "add" +#define RPKI_LOG_DEL "del" +#define RPKI_LOG_ENTRY_FMT(ip_fmt) " roa %-25" ip_fmt "/%u-%-3u ASN: %u" +#define RPKI_LOG_FMT(operation_name) operation_name RPKI_LOG_ENTRY_FMT("I") +#define RPKI_LOG_SKIP_FMT(operation_name) "skip " operation_name RPKI_LOG_ENTRY_FMT("s") " (unsupported IP version)" if (added) { - RPKI_TRACE(p, LOG_SKIP_ENTRY_FMT("add"), ip_buf, rec->min_len, rec->max_len, rec->asn); + RPKI_TRACE(p, RPKI_LOG_SKIP_FMT(RPKI_LOG_ADD), ip_buf, rec->min_len, rec->max_len, rec->asn); } else { - RPKI_TRACE(p, LOG_SKIP_ENTRY_FMT("del"), ip_buf, rec->min_len, rec->max_len, rec->asn); + RPKI_TRACE(p, RPKI_LOG_SKIP_FMT(RPKI_LOG_DEL), ip_buf, rec->min_len, rec->max_len, rec->asn); } } @@ -150,10 +238,19 @@ rtr_thread_update_hook(struct pfx_table *pfx_table, const struct pfx_record rec, e->added = added; e->asn = rec.asn; e->ip = ip; - e->max_len = rec.max_len; - e->min_len = rec.min_len; + e->pxlen = rec.min_len; + e->maxlen = rec.max_len; e->rpki = p; + if (e->added) + { + RPKI_TRACE(p, RPKI_LOG_FMT(RPKI_LOG_ADD), e->ip, e->pxlen, e->maxlen, e->asn); + } + else + { + RPKI_TRACE(p, RPKI_LOG_FMT(RPKI_LOG_DEL), e->ip, e->pxlen, e->maxlen, e->asn); + } + send_data_to_main_thread(p, e); } @@ -206,9 +303,9 @@ rpki_notify_hook(struct birdsock *sk, int size) { rem2_node(&entry->n); if (entry->added) - roa_add_item(p->cf->roa_table_cf->table, entry->ip, entry->min_len, entry->max_len, entry->asn, ROA_SRC_RPKI); + roa_add_item(p->cf->roa_table_cf->table, entry->ip, entry->pxlen, entry->maxlen, entry->asn, ROA_SRC_RPKI); else - roa_delete_item(p->cf->roa_table_cf->table, entry->ip, entry->min_len, entry->max_len, entry->asn, ROA_SRC_RPKI); + roa_delete_item(p->cf->roa_table_cf->table, entry->ip, entry->pxlen, entry->maxlen, entry->asn, ROA_SRC_RPKI); } rpki_unlock_sessions(p); } @@ -268,6 +365,9 @@ rpki_start(struct proto *P) RPKI_TRACE(p, "------------- rpki_start -------------"); + if (!rtrlib && !load_rtrlib(p)) + return PS_DOWN; + create_rw_sockets(p); init_list(&p->notify_list); pthread_spin_init(&p->notify_lock, PTHREAD_PROCESS_PRIVATE); @@ -296,7 +396,7 @@ rpki_start(struct proto *P) tcp_config->host = cache->full_domain_name; tcp_config->port = cache->port; - tr_tcp_init(tcp_config, tr_tcp); + (*tr_tcp_init_fp)(tcp_config, tr_tcp); // create an rtr_socket and associate it with the transport socket rtr_tcp->tr_socket = tr_tcp; @@ -306,8 +406,8 @@ rpki_start(struct proto *P) idx++; } - p->rtr_conf = rtr_mgr_init(groups, 1, 30, 520, &rtr_thread_update_hook, NULL, &status_cb, p); - rtr_mgr_start(p->rtr_conf); + p->rtr_conf = (*rtr_mgr_init_fp)(groups, 1, 30, 520, &rtr_thread_update_hook, NULL, &status_cb, p); + (*rtr_mgr_start_fp)(p->rtr_conf); return PS_UP; } @@ -317,8 +417,8 @@ rpki_shutdown(struct proto *P) { struct rpki_proto *p = (struct rpki_proto *) P; - rtr_mgr_stop(p->rtr_conf); - rtr_mgr_free(p->rtr_conf); + (*rtr_mgr_stop_fp)(p->rtr_conf); + (*rtr_mgr_free_fp)(p->rtr_conf); mb_free(p->rtr_groups); mb_free(p->rtr_sockets); diff --git a/proto/rpki/rpki.h b/proto/rpki/rpki.h index a0957180..e5da7acd 100644 --- a/proto/rpki/rpki.h +++ b/proto/rpki/rpki.h @@ -11,16 +11,17 @@ #include -#include "rtrlib/rtrlib.h" - #include "nest/bird.h" #include "nest/protocol.h" #include "lib/socket.h" +#include "proto/rpki/rtrlib-mockup.h" + #define RPKI_PORT "8282" #define RPKI_PORT_MAX_LENGTH_STR 6 #define RPKI_RX_BUFFER_EXT_SIZE 0xffff #define RPKI_TX_BUFFER_EXT_SIZE 0xffff +#define RPKI_RTRLIB_PATH "/usr/local/lib64/librtr.so" #define RPKI_LOG(log_level, p, msg, args...) \ do { \ @@ -60,6 +61,7 @@ struct rpki_config { struct proto_config c; list cache_list; /* (struct rpki_cache *) */ struct roa_table_config *roa_table_cf; + const char *rtrlib_path; }; struct rpki_proto { diff --git a/proto/rpki/rtrlib-mockup.h b/proto/rpki/rtrlib-mockup.h new file mode 100644 index 00000000..160d3e5b --- /dev/null +++ b/proto/rpki/rtrlib-mockup.h @@ -0,0 +1,229 @@ +/* + * BIRD -- RTRLib Headers mockup + * + * (c) 2015 CZ.NIC + * + * Can be freely distributed and used under the terms of the GNU GPL. + * + * + * RTRlib is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * RTRlib is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RTRlib; see the file COPYING.LESSER. + * + * INET group, Hamburg University of Applied Sciences, + * CST group, Freie Universitaet Berlin + * Website: http://rpki.realmv6.org/ + * + */ + +#ifndef _BIRD_RTRLIB_MOCKUP_H_ +#define _BIRD_RTRLIB_MOCKUP_H_ + +#include + +/** + * @brief A transport socket datastructure. + * + * @param socket A pointer to a technology specific socket. + * @param open_fp Pointer to a function that establishes the socket connection. + * @param close_fp Pointer to a function that closes the socket. + * @param free_fp Pointer to a function that frees all memory allocated with this socket. + * @param send_fp Pointer to a function that sends data through this socket. + * @param recv_fp Pointer to a function that receives data from this socket. + */ +struct tr_socket { + void *socket; + void *open_fp; /* voided for mockuping */ + void *close_fp; /* voided for mockuping */ + void *free_fp; /* voided for mockuping */ + void *send_fp; /* voided for mockuping */ + void *recv_fp; /* voided for mockuping */ + void *ident_fp; /* voided for mockuping */ +}; + +/** + * @brief States of the RTR socket. + */ +enum rtr_socket_state { + /** Socket is establishing the transport connection. */ + RTR_CONNECTING, + + /** Connection is established, socket is waiting for a Serial Notify or expiration of the refresh_interval timer */ + RTR_ESTABLISHED, + + /** Resetting RTR connection. */ + RTR_RESET, + + /** Receiving validation records from the RTR server. */ + RTR_SYNC, + + /** Reconnect without any waiting period */ + RTR_FAST_RECONNECT, + + /** No validation records are available on the RTR server. */ + RTR_ERROR_NO_DATA_AVAIL, + + /** Server was unable to answer the last serial or reset query. */ + RTR_ERROR_NO_INCR_UPDATE_AVAIL, + + /** Fatal protocol error occurred. */ + RTR_ERROR_FATAL, + + /** Error on the transport socket occurred. */ + RTR_ERROR_TRANSPORT, + + /** RTR Socket is stopped. */ + RTR_SHUTDOWN, +}; + +/** + * @brief A RTR socket. + * @param tr_socket Pointer to an initialized tr_socket that will be used to communicate with the RTR server. + * @param refresh_interval Time period in seconds. Tells the router how long to wait before next attempting to poll the cache, using a Serial Query or + * Reset Query PDU. + * @param last_update Timestamp of the last validation record update. Is 0 if the pfx_table doesn't stores any + * validation reords from this rtr_socket. + * @param expire_interval Time period in seconds. Received records are deleted if the client was unable to refresh data for this time period. + * If 0 is specified, the expire_interval is twice the refresh_interval. + * @param retry_interval Time period in seconds between a faild quary and the next attempt. + * @param state Current state of the socket. + * @param session_id session_id of the RTR session. + * @param request_session_id True, if the rtr_client have to request a new none from the server. + * @param serial_number Last serial number of the obtained validation records. + * @param pfx_table pfx_table that stores the validation records obtained from the connected rtr server. + * @param connection_state_fp A callback function that is executed when the state of the socket changes. + * @param connection_state_fp_param Parameter that is passed to the connection_state_fp callback. + */ +struct rtr_socket { + struct tr_socket *tr_socket; + unsigned int refresh_interval; + time_t last_update; + unsigned int expire_interval; + unsigned int retry_interval; + enum rtr_socket_state state; + uint32_t session_id; + bool request_session_id; + uint32_t serial_number; + void *pfx_table; /* voided for mockuping */ + pthread_t thread_id; + void *connection_state_fp; /* voided for mockuping */ + void *connection_state_fp_param; + unsigned int version; + void *spki_table; /* voided for mockuping */ +}; + +/** + * @brief A tr_tcp_config struct holds configuration for a TCP connection. + * @param host Hostname or IP address to connect to. + * @param port Port to connect to. + * @param bindaddr Hostname or IP address to connect from. NULL for + * determination by OS. + * to use the source address of the system's default route to the server + */ +struct tr_tcp_config { + char *host; + char *port; + char *bindaddr; +}; + +/** + * @brief Status of a rtr_mgr_group. + */ +enum rtr_mgr_status { + /** RTR sockets are disconnected */ + RTR_MGR_CLOSED, + + /** RTR sockets trying to establish a connection. */ + RTR_MGR_CONNECTING, + + /** All RTR sockets of the group are synchronized with the rtr servers. */ + RTR_MGR_ESTABLISHED, + + /** Error occured on at least one RTR socket. */ + RTR_MGR_ERROR, +}; + +/** + * @brief A set of RTR sockets. + * @param sockets Array of rtr_socket pointer. The tr_socket element of the rtr_socket must be associated with an initialized transport socket. + * @param sockets_len Number of elements in the sockets array. + * @param preference The preference value of this group. Groups with lower preference values are preferred. + * @param status Status of the group. + */ +struct rtr_mgr_group { + struct rtr_socket **sockets; + unsigned int sockets_len; + uint8_t preference; + enum rtr_mgr_status status; +}; + +struct rtr_mgr_config { + struct rtr_mgr_group *groups; + unsigned int len; + /* some items deleted */ +}; + + +/** + * @brief Version of the IP protocol. + */ +enum rtr_ip_version { + RTRLIB_IPV4, + RTRLIB_IPV6 +}; + +/** + * @brief Struct storing an IPv4 address in host byte order. + * @param addr The IPv4 address. + */ +struct ipv4_addr { + uint32_t addr; +}; + +/** + * @brief Struct holding an IPv6 address in host byte order. + * @param addr The IPv6 address. + */ +struct ipv6_addr { + uint32_t addr[4]; +}; + +/** + * @brief The rtr_ip_addr struct stores a IPv4 or IPv6 address in host byte order. + * @param ver Specifies the type of the stored address. + * @param u Union holding a ipv4_addr or ipv6_addr. + */ +struct rtr_ip_addr { + enum rtr_ip_version ver; + union { + struct ipv4_addr addr4; + struct ipv6_addr addr6; + } u; +}; + +/** + * @brief pfx_record. + * @param asn Origin AS number. + * @param prefix IP prefix. + * @param min_len Minimum prefix length. + * @param max_len Maximum prefix length. + * @param socket_id unique id of the rtr_socket that received this record. + */ +struct pfx_record { + uint32_t asn; + struct rtr_ip_addr prefix; + uint8_t min_len; + uint8_t max_len; + const struct rtr_socket *socket; +}; + +#endif /* _BIRD_RTRLIB_MOCKUP_H_ */ diff --git a/tools/Makefile-top.in b/tools/Makefile-top.in index 0244a563..cf59f7a1 100644 --- a/tools/Makefile-top.in +++ b/tools/Makefile-top.in @@ -4,7 +4,6 @@ objdir=@objdir@ all depend tags install install-docs: - $(MAKE) -C rtrlib $(MAKE) -C $(objdir) $@ docs userdocs progdocs: diff --git a/tools/Makefile.in b/tools/Makefile.in index f19aceb2..01bb7a7c 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -13,7 +13,7 @@ birdc: $(exedir)/birdc birdcl: $(exedir)/birdcl -bird-dep := $(addsuffix /all.o, $(static-dirs)) conf/all.o lib/birdlib.a ../rtrlib/librtr.a +bird-dep := $(addsuffix /all.o, $(static-dirs)) conf/all.o lib/birdlib.a $(bird-dep): sysdep/paths.h .dep-stamp subdir diff --git a/tools/Rules.in b/tools/Rules.in index ea05b287..f00c85d1 100644 --- a/tools/Rules.in +++ b/tools/Rules.in @@ -19,7 +19,7 @@ doc-dir-paths := $(doc-dirs) all-dirs:=$(static-dirs) $(dynamic-dirs) $(client-dirs) $(doc-dirs) clean-dirs:=$(all-dirs) proto sysdep -CPPFLAGS=-I$(root-rel) -I$(srcdir) -I$(srcdir)/rtrlib @CPPFLAGS@ +CPPFLAGS=-I$(root-rel) -I$(srcdir) @CPPFLAGS@ CFLAGS=$(CPPFLAGS) @CFLAGS@ LDFLAGS=@LDFLAGS@ LIBS=@LIBS@