diff options
Diffstat (limited to 'rtrlib/rtr/rtr.c')
-rw-r--r-- | rtrlib/rtr/rtr.c | 272 |
1 files changed, 272 insertions, 0 deletions
diff --git a/rtrlib/rtr/rtr.c b/rtrlib/rtr/rtr.c new file mode 100644 index 0000000..086c953 --- /dev/null +++ b/rtrlib/rtr/rtr.c @@ -0,0 +1,272 @@ +/* + * This file is part of RTRlib. + * + * This file is subject to the terms and conditions of the MIT license. + * See the file LICENSE in the top level directory for more details. + * + * Website: http://rtrlib.realmv6.org/ + */ + +#include "rtr_private.h" + +#include "rtrlib/lib/log_private.h" +#include "rtrlib/lib/utils_private.h" +#include "rtrlib/pfx/pfx_private.h" +#include "rtrlib/rtr/packets_private.h" +#include "rtrlib/rtrlib_export_private.h" +#include "rtrlib/spki/hashtable/ht-spkitable_private.h" +#include "rtrlib/transport/transport_private.h" + +#include <assert.h> +#include <pthread.h> +#include <signal.h> +#include <unistd.h> + +static void rtr_purge_outdated_records(struct rtr_socket *rtr_socket); +static void *rtr_fsm_start(struct rtr_socket *rtr_socket); + +static const char *socket_str_states[] = {[RTR_CONNECTING] = "RTR_CONNECTING", + [RTR_ESTABLISHED] = "RTR_ESTABLISHED", + [RTR_RESET] = "RTR_RESET", + [RTR_SYNC] = "RTR_SYNC", + [RTR_FAST_RECONNECT] = "RTR_FAST_RECONNECT", + [RTR_ERROR_NO_DATA_AVAIL] = "RTR_ERROR_NO_DATA_AVAIL", + [RTR_ERROR_NO_INCR_UPDATE_AVAIL] = "RTR_ERROR_NO_INCR_UPDATE_AVAIL", + [RTR_ERROR_FATAL] = "RTR_ERROR_FATAL", + [RTR_ERROR_TRANSPORT] = "RTR_ERROR_TRANSPORT", + [RTR_SHUTDOWN] = "RTR_SHUTDOWN"}; + +int rtr_init(struct rtr_socket *rtr_socket, struct tr_socket *tr, struct pfx_table *pfx_table, + struct spki_table *spki_table, const unsigned int refresh_interval, const unsigned int expire_interval, + const unsigned int retry_interval, enum rtr_interval_mode iv_mode, rtr_connection_state_fp fp, + void *fp_param_config, void *fp_param_group) +{ + if (tr) + rtr_socket->tr_socket = tr; + + // Check if one of the intervals is not in range of the predefined values. + if (rtr_check_interval_range(refresh_interval, RTR_REFRESH_MIN, RTR_REFRESH_MAX) != RTR_INSIDE_INTERVAL_RANGE || + rtr_check_interval_range(expire_interval, RTR_EXPIRATION_MIN, RTR_EXPIRATION_MAX) != + RTR_INSIDE_INTERVAL_RANGE || + rtr_check_interval_range(retry_interval, RTR_RETRY_MIN, RTR_RETRY_MAX) != RTR_INSIDE_INTERVAL_RANGE) { + RTR_DBG("Interval value not in range."); + return RTR_INVALID_PARAM; + } + rtr_socket->refresh_interval = refresh_interval; + rtr_socket->expire_interval = expire_interval; + rtr_socket->retry_interval = retry_interval; + rtr_socket->iv_mode = iv_mode; + + rtr_socket->state = RTR_CLOSED; + rtr_socket->request_session_id = true; + rtr_socket->serial_number = 0; + rtr_socket->last_update = 0; + rtr_socket->pfx_table = pfx_table; + rtr_socket->spki_table = spki_table; + rtr_socket->connection_state_fp = fp; + rtr_socket->connection_state_fp_param_config = fp_param_config; + rtr_socket->connection_state_fp_param_group = fp_param_group; + rtr_socket->thread_id = 0; + rtr_socket->version = RTR_PROTOCOL_MAX_SUPPORTED_VERSION; + rtr_socket->has_received_pdus = false; + rtr_socket->is_resetting = false; + return RTR_SUCCESS; +} + +int rtr_start(struct rtr_socket *rtr_socket) +{ + if (rtr_socket->thread_id) + return RTR_ERROR; + + int rtval = pthread_create(&(rtr_socket->thread_id), NULL, (void *(*)(void *)) &rtr_fsm_start, rtr_socket); + + if (rtval == 0) + return RTR_SUCCESS; + return RTR_ERROR; +} + +void rtr_purge_outdated_records(struct rtr_socket *rtr_socket) +{ + if (rtr_socket->last_update == 0) + return; + time_t cur_time; + int rtval = lrtr_get_monotonic_time(&cur_time); + + if (rtval == -1 || (rtr_socket->last_update + rtr_socket->expire_interval) < cur_time) { + if (rtval == -1) + RTR_DBG1("get_monotic_time(..) failed"); + pfx_table_src_remove(rtr_socket->pfx_table, rtr_socket); + RTR_DBG1("Removed outdated records from pfx_table"); + spki_table_src_remove(rtr_socket->spki_table, rtr_socket); + RTR_DBG1("Removed outdated router keys from spki_table"); + rtr_socket->request_session_id = true; + rtr_socket->serial_number = 0; + rtr_socket->last_update = 0; + rtr_socket->is_resetting = true; + } +} + +/* WARNING: This Function has cancelable sections*/ +void *rtr_fsm_start(struct rtr_socket *rtr_socket) +{ + if (rtr_socket->state == RTR_SHUTDOWN) + return NULL; + + // We don't care about the old state, but POSIX demands a non null value for setcancelstate + int oldcancelstate; + + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldcancelstate); + + rtr_socket->state = RTR_CONNECTING; + while (1) { + if (rtr_socket->state == RTR_CONNECTING) { + RTR_DBG1("State: RTR_CONNECTING"); + rtr_socket->has_received_pdus = false; + + // old pfx_record could exists in the pfx_table, check if they are too old and must be removed + // old key_entry could exists in the spki_table, check if they are too old and must be removed + rtr_purge_outdated_records(rtr_socket); + + if (tr_open(rtr_socket->tr_socket) == TR_ERROR) { + rtr_change_socket_state(rtr_socket, RTR_ERROR_TRANSPORT); + } else if (rtr_socket->request_session_id) { + // change to state RESET, if socket doesn't have a session_id + rtr_change_socket_state(rtr_socket, RTR_RESET); + } else { + // if we already have a session_id, send a serial query and start to sync + if (rtr_send_serial_query(rtr_socket) == RTR_SUCCESS) + rtr_change_socket_state(rtr_socket, RTR_SYNC); + else + rtr_change_socket_state(rtr_socket, RTR_ERROR_FATAL); + } + } + + else if (rtr_socket->state == RTR_RESET) { + RTR_DBG1("State: RTR_RESET"); + if (rtr_send_reset_query(rtr_socket) == RTR_SUCCESS) { + RTR_DBG1("rtr_start: reset pdu sent"); + rtr_change_socket_state(rtr_socket, + RTR_SYNC); // start to sync after connection is established + } + } + + else if (rtr_socket->state == RTR_SYNC) { + RTR_DBG1("State: RTR_SYNC"); + if (rtr_sync(rtr_socket) == RTR_SUCCESS) + rtr_change_socket_state( + rtr_socket, + RTR_ESTABLISHED); // wait for next sync after first successful sync + } + + else if (rtr_socket->state == RTR_ESTABLISHED) { + RTR_DBG1("State: RTR_ESTABLISHED"); + + // Allow thread cancellation for recv code path only. + // This should be enough since we spend most of the time blocking on recv + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldcancelstate); + int ret = rtr_wait_for_sync( + rtr_socket); // blocks till expire_interval is expired or PDU was received + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldcancelstate); + + if (ret == RTR_SUCCESS) { // send serial query + if (rtr_send_serial_query(rtr_socket) == RTR_SUCCESS) + rtr_change_socket_state(rtr_socket, RTR_SYNC); + } + } + + else if (rtr_socket->state == RTR_FAST_RECONNECT) { + RTR_DBG1("State: RTR_FAST_RECONNECT"); + tr_close(rtr_socket->tr_socket); + rtr_change_socket_state(rtr_socket, RTR_CONNECTING); + } + + else if (rtr_socket->state == RTR_ERROR_NO_DATA_AVAIL) { + RTR_DBG1("State: RTR_ERROR_NO_DATA_AVAIL"); + rtr_socket->request_session_id = true; + rtr_socket->serial_number = 0; + rtr_change_socket_state(rtr_socket, RTR_RESET); + sleep(rtr_socket->retry_interval); + rtr_purge_outdated_records(rtr_socket); + } + + else if (rtr_socket->state == RTR_ERROR_NO_INCR_UPDATE_AVAIL) { + RTR_DBG1("State: RTR_ERROR_NO_INCR_UPDATE_AVAIL"); + rtr_socket->request_session_id = true; + rtr_socket->serial_number = 0; + rtr_change_socket_state(rtr_socket, RTR_RESET); + rtr_purge_outdated_records(rtr_socket); + } + + else if (rtr_socket->state == RTR_ERROR_TRANSPORT) { + RTR_DBG1("State: RTR_ERROR_TRANSPORT"); + tr_close(rtr_socket->tr_socket); + rtr_change_socket_state(rtr_socket, RTR_CONNECTING); + RTR_DBG("Waiting %u", rtr_socket->retry_interval); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldcancelstate); + sleep(rtr_socket->retry_interval); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldcancelstate); + } + + else if (rtr_socket->state == RTR_ERROR_FATAL) { + RTR_DBG1("State: RTR_ERROR_FATAL"); + tr_close(rtr_socket->tr_socket); + rtr_change_socket_state(rtr_socket, RTR_CONNECTING); + RTR_DBG("Waiting %u", rtr_socket->retry_interval); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldcancelstate); + sleep(rtr_socket->retry_interval); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldcancelstate); + } + + else if (rtr_socket->state == RTR_SHUTDOWN) { + RTR_DBG1("State: RTR_SHUTDOWN"); + pthread_exit(NULL); + } + } +} + +void rtr_stop(struct rtr_socket *rtr_socket) +{ + RTR_DBG("%s()", __func__); + rtr_change_socket_state(rtr_socket, RTR_SHUTDOWN); + if (rtr_socket->thread_id != 0) { + RTR_DBG1("pthread_cancel()"); + pthread_cancel(rtr_socket->thread_id); + RTR_DBG1("pthread_join()"); + pthread_join(rtr_socket->thread_id, NULL); + + tr_close(rtr_socket->tr_socket); + rtr_socket->request_session_id = true; + rtr_socket->serial_number = 0; + rtr_socket->last_update = 0; + pfx_table_src_remove(rtr_socket->pfx_table, rtr_socket); + spki_table_src_remove(rtr_socket->spki_table, rtr_socket); + rtr_socket->thread_id = 0; + } + RTR_DBG1("Socket shut down"); +} + +RTRLIB_EXPORT const char *rtr_state_to_str(enum rtr_socket_state state) +{ + return socket_str_states[state]; +} + +/* cppcheck-suppress unusedFunction */ +RTRLIB_EXPORT enum rtr_interval_mode rtr_get_interval_mode(struct rtr_socket *rtr_socket) +{ + return rtr_socket->iv_mode; +} + +/* cppcheck-suppress unusedFunction */ +RTRLIB_EXPORT void rtr_set_interval_mode(struct rtr_socket *rtr_socket, enum rtr_interval_mode option) +{ + switch (option) { + case RTR_INTERVAL_MODE_IGNORE_ANY: + case RTR_INTERVAL_MODE_ACCEPT_ANY: + case RTR_INTERVAL_MODE_DEFAULT_MIN_MAX: + case RTR_INTERVAL_MODE_IGNORE_ON_FAILURE: + rtr_socket->iv_mode = option; + break; + default: + RTR_DBG1("Invalid interval mode. Mode remains unchanged."); + } +} |