diff options
Diffstat (limited to 'src')
208 files changed, 14326 insertions, 7601 deletions
diff --git a/src/ipcpd/CMakeLists.txt b/src/ipcpd/CMakeLists.txt index 1ce1bc0d..54294f11 100644 --- a/src/ipcpd/CMakeLists.txt +++ b/src/ipcpd/CMakeLists.txt @@ -1,3 +1,7 @@ +set(CONNMGR_RCV_TIMEOUT 1000 CACHE STRING + "Timeout for the connection manager to wait for OCEP info (ms).") +set(IPCP_DEBUG_LOCAL FALSE CACHE BOOL + "Use PID as address for local debugging") set(IPCP_QOS_CUBE_BE_PRIO 50 CACHE STRING "Priority for best effort QoS cube (0-99)") set(IPCP_QOS_CUBE_VIDEO_PRIO 90 CACHE STRING @@ -44,6 +48,10 @@ set(IPCP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/shim-data.c ) +set (COMMON_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/common/enroll.c + ) + add_subdirectory(local) add_subdirectory(eth) add_subdirectory(udp) diff --git a/src/ipcpd/broadcast/CMakeLists.txt b/src/ipcpd/broadcast/CMakeLists.txt index afcc8696..d85f335e 100644 --- a/src/ipcpd/broadcast/CMakeLists.txt +++ b/src/ipcpd/broadcast/CMakeLists.txt @@ -13,16 +13,17 @@ include_directories(${CMAKE_SOURCE_DIR}/include) include_directories(${CMAKE_BINARY_DIR}/include) set(IPCP_BROADCAST_TARGET ipcpd-broadcast CACHE INTERNAL "") +set(IPCP_BROADCAST_MPL 60 CACHE STRING + "Default maximum packet lifetime for the broadcast IPCP, in seconds") set(SOURCE_FILES # Add source files here connmgr.c dt.c - enroll.c main.c ) -add_executable(ipcpd-broadcast ${SOURCE_FILES} ${IPCP_SOURCES} +add_executable(ipcpd-broadcast ${SOURCE_FILES} ${IPCP_SOURCES} ${COMMON_SOURCES} ${LAYER_CONFIG_PROTO_SRCS}) target_link_libraries(ipcpd-broadcast LINK_PUBLIC ouroboros-dev) diff --git a/src/ipcpd/broadcast/connmgr.c b/src/ipcpd/broadcast/connmgr.c index b65f48b0..f297175d 100644 --- a/src/ipcpd/broadcast/connmgr.c +++ b/src/ipcpd/broadcast/connmgr.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Handles connections between components * diff --git a/src/ipcpd/broadcast/dt.c b/src/ipcpd/broadcast/dt.c index 00476027..938c9085 100644 --- a/src/ipcpd/broadcast/dt.c +++ b/src/ipcpd/broadcast/dt.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Forward loop for broadcast * @@ -78,15 +78,16 @@ static int dt_add_nb(int fd) list_for_each(p, &fwd.nbs) { struct nb * el = list_entry(p, struct nb, next); if (el->fd == fd) { - log_dbg("Already know neighbor."); pthread_rwlock_unlock(&fwd.nbs_lock); - return -EPERM; + log_warn("Already know neighbor on fd %d.", fd); + return 0; } } nb = malloc(sizeof(*nb)); if (nb == NULL) { pthread_rwlock_unlock(&fwd.nbs_lock); + log_err("Failed to malloc neighbor struct."); return -ENOMEM; } @@ -96,10 +97,10 @@ static int dt_add_nb(int fd) ++fwd.nbs_len; - log_dbg("Neighbor %d added.", fd); - pthread_rwlock_unlock(&fwd.nbs_lock); + log_dbg("Neighbor %d added.", fd); + return 0; } @@ -124,6 +125,8 @@ static int dt_del_nb(int fd) pthread_rwlock_unlock(&fwd.nbs_lock); + log_err("Neighbor not found on fd %d.", fd); + return -EPERM; } @@ -191,7 +194,7 @@ static void * dt_reader(void * o) while (true) { ret = fevent(fwd.set, fq, NULL); if (ret < 0) { - log_warn("Event error: %d.", ret); + log_warn("Event warning: %d.", ret); continue; } @@ -226,13 +229,13 @@ static void handle_event(void * self, switch (event) { case NOTIFY_DT_CONN_ADD: - if (dt_add_nb(c->flow_info.fd)) - log_dbg("Failed to add neighbor."); + if (dt_add_nb(c->flow_info.fd) < 0) + log_err("Failed to add neighbor."); fset_add(fwd.set, c->flow_info.fd); break; case NOTIFY_DT_CONN_DEL: - if (dt_del_nb(c->flow_info.fd)) - log_dbg("Failed to delete neighbor."); + if (dt_del_nb(c->flow_info.fd) < 0) + log_err("Failed to delete neighbor."); fset_del(fwd.set, c->flow_info.fd); break; default: diff --git a/src/ipcpd/broadcast/dt.h b/src/ipcpd/broadcast/dt.h index 4331bd3e..8d3b83f8 100644 --- a/src/ipcpd/broadcast/dt.h +++ b/src/ipcpd/broadcast/dt.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Forward loop for broadcast * diff --git a/src/ipcpd/broadcast/main.c b/src/ipcpd/broadcast/main.c index 522d1391..f51fc629 100644 --- a/src/ipcpd/broadcast/main.c +++ b/src/ipcpd/broadcast/main.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Broadcast IPC Process * @@ -31,14 +31,14 @@ #define OUROBOROS_PREFIX "broadcast-ipcp" #define THIS_TYPE IPCP_BROADCAST -#include <ouroboros/errno.h> -#include <ouroboros/hash.h> #include <ouroboros/dev.h> +#include <ouroboros/errno.h> #include <ouroboros/ipcp-dev.h> #include <ouroboros/logs.h> #include <ouroboros/notifier.h> +#include <ouroboros/random.h> #include <ouroboros/rib.h> -#include <ouroboros/time_utils.h> +#include <ouroboros/time.h> #include "common/connmgr.h" #include "common/enroll.h" @@ -56,50 +56,34 @@ struct ipcp ipcpi; static int initialize_components(const struct ipcp_config * conf) { - ipcpi.layer_name = strdup(conf->layer_info.layer_name); - if (ipcpi.layer_name == NULL) { - log_err("Failed to set layer name."); - goto fail_layer_name; - } - - ipcpi.dir_hash_algo = conf->layer_info.dir_hash_algo; + strcpy(ipcpi.layer_name, conf->layer_info.name); + ipcpi.dir_hash_algo = (enum hash_algo) conf->layer_info.dir_hash_algo; assert(ipcp_dir_hash_len() != 0); - if (dt_init()) { + if (dt_init() < 0) { log_err("Failed to initialize forwarding component."); - goto fail_dt; + return -1; } ipcp_set_state(IPCP_INIT); return 0; - - fail_dt: - free(ipcpi.layer_name); - fail_layer_name: - return -1; } static void finalize_components(void) { dt_fini(); - - free(ipcpi.layer_name); } static int start_components(void) { - assert(ipcp_get_state() == IPCP_INIT); - - ipcp_set_state(IPCP_OPERATIONAL); - - if (enroll_start()) { + if (enroll_start() < 0) { log_err("Failed to start enrollment."); goto fail_enroll_start; } - if (connmgr_start()) { + if (connmgr_start() < 0) { log_err("Failed to start AP connection manager."); goto fail_connmgr_start; } @@ -115,52 +99,55 @@ static int start_components(void) static void stop_components(void) { - assert(ipcp_get_state() == IPCP_OPERATIONAL || - ipcp_get_state() == IPCP_SHUTDOWN); - connmgr_stop(); enroll_stop(); - - ipcp_set_state(IPCP_INIT); } static int broadcast_ipcp_enroll(const char * dst, struct layer_info * info) { struct conn conn; + uint8_t id[ENROLL_ID_LEN]; - if (connmgr_alloc(COMPID_ENROLL, dst, NULL, &conn)) { - log_err("Failed to get connection."); - goto fail_er_flow; + if (random_buffer(id, ENROLL_ID_LEN) < 0) { + log_err("Failed to generate enrollment ID."); + goto fail_id; + } + + log_info_id(id, "Requesting enrollment."); + + if (connmgr_alloc(COMPID_ENROLL, dst, NULL, &conn) < 0) { + log_err_id(id, "Failed to get connection."); + goto fail_id; } /* Get boot state from peer. */ - if (enroll_boot(&conn)) { - log_err("Failed to get boot information."); + if (enroll_boot(&conn, id) < 0) { + log_err_id(id, "Failed to get boot information."); goto fail_enroll_boot; } - if (initialize_components(enroll_get_conf())) { - log_err("Failed to initialize IPCP components."); + if (initialize_components(enroll_get_conf()) < 0) { + log_err_id(id, "Failed to initialize components."); goto fail_enroll_boot; } - if (start_components()) { - log_err("Failed to start components."); + if (start_components() < 0) { + log_err_id(id, "Failed to start components."); goto fail_start_comp; } - if (enroll_done(&conn, 0)) - log_warn("Failed to confirm enrollment with peer."); + if (enroll_ack(&conn, id, 0) < 0) + log_err_id(id, "Failed to confirm enrollment."); - if (connmgr_dealloc(COMPID_ENROLL, &conn)) - log_warn("Failed to deallocate enrollment flow."); + if (connmgr_dealloc(COMPID_ENROLL, &conn) < 0) + log_warn_id(id, "Failed to dealloc enrollment flow."); - log_info("Enrolled with %s.", dst); + log_info_id(id, "Enrolled with %s.", dst); - info->dir_hash_algo = ipcpi.dir_hash_algo; - strcpy(info->layer_name, ipcpi.layer_name); + info->dir_hash_algo = (enum pol_dir_hash) ipcpi.dir_hash_algo; + strcpy(info->name, ipcpi.layer_name); return 0; @@ -168,7 +155,7 @@ static int broadcast_ipcp_enroll(const char * dst, finalize_components(); fail_enroll_boot: connmgr_dealloc(COMPID_ENROLL, &conn); - fail_er_flow: + fail_id: return -1; } @@ -176,6 +163,8 @@ static int broadcast_ipcp_bootstrap(const struct ipcp_config * conf) { assert(conf); assert(conf->type == THIS_TYPE); + ((struct ipcp_config *) conf)->layer_info.dir_hash_algo = + DIR_HASH_SHA3_256; enroll_bootstrap(conf); @@ -189,8 +178,6 @@ static int broadcast_ipcp_bootstrap(const struct ipcp_config * conf) goto fail_start; } - log_dbg("Bootstrapped in layer %s.", conf->layer_info.layer_name); - return 0; fail_start: @@ -224,6 +211,8 @@ static int broadcast_ipcp_join(int fd, qosspec_t qs) { struct conn conn; + time_t mpl = IPCP_BROADCAST_MPL; + buffer_t data = {NULL, 0}; (void) qs; @@ -231,12 +220,14 @@ static int broadcast_ipcp_join(int fd, conn.flow_info.fd = fd; - if (name_check(dst) != 0) + if (name_check(dst) != 0) { + log_err("Failed to check name."); return -1; + } notifier_event(NOTIFY_DT_CONN_ADD, &conn); - ipcp_flow_alloc_reply(fd, 0, NULL, 0); + ipcp_flow_alloc_reply(fd, 0, mpl, &data); return 0; } @@ -251,12 +242,11 @@ int broadcast_ipcp_dealloc(int fd) notifier_event(NOTIFY_DT_CONN_DEL, &conn); - flow_dealloc(fd); + ipcp_flow_dealloc(fd); return 0; } - static struct ipcp_ops broadcast_ops = { .ipcp_bootstrap = broadcast_ipcp_bootstrap, .ipcp_enroll = broadcast_ipcp_enroll, @@ -275,7 +265,7 @@ int main(int argc, char * argv[]) { if (ipcp_init(argc, argv, &broadcast_ops, THIS_TYPE) < 0) { - log_err("Failed to init IPCP."); + log_err("Failed to initialize IPCP."); goto fail_init; } @@ -294,24 +284,20 @@ int main(int argc, goto fail_enroll_init; } - if (ipcp_boot() < 0) { + if (ipcp_start() < 0) { log_err("Failed to boot IPCP."); - goto fail_boot; - } - - if (ipcp_create_r(0)) { - log_err("Failed to notify IRMd we are initialized."); - ipcp_set_state(IPCP_NULL); - goto fail_create_r; + goto fail_start; } - ipcp_shutdown(); + ipcp_sigwait(); if (ipcp_get_state() == IPCP_SHUTDOWN) { stop_components(); finalize_components(); } + ipcp_stop(); + enroll_fini(); connmgr_fini(); @@ -322,9 +308,7 @@ int main(int argc, exit(EXIT_SUCCESS); - fail_create_r: - ipcp_shutdown(); - fail_boot: + fail_start: enroll_fini(); fail_enroll_init: connmgr_fini(); @@ -333,6 +317,5 @@ int main(int argc, fail_notifier_init: ipcp_fini(); fail_init: - ipcp_create_r(-1); exit(EXIT_FAILURE); } diff --git a/src/ipcpd/common/comp.h b/src/ipcpd/common/comp.h index 95e59b24..f3790d9c 100644 --- a/src/ipcpd/common/comp.h +++ b/src/ipcpd/common/comp.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Components for the unicast/broadcast IPC process * @@ -23,7 +23,7 @@ #ifndef OUROBOROS_IPCPD_COMMON_COMP_H #define OUROBOROS_IPCPD_COMMON_COMP_H -#include <ouroboros/cacep.h> +#include <ouroboros/cep.h> #define DST_MAX_STRLEN 64 diff --git a/src/ipcpd/common/connmgr.c b/src/ipcpd/common/connmgr.c index 53a66992..4b5fd420 100644 --- a/src/ipcpd/common/connmgr.c +++ b/src/ipcpd/common/connmgr.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Handles connections between components * @@ -22,9 +22,10 @@ #define OUROBOROS_PREFIX "connection-manager" +#include <ouroboros/cep.h> #include <ouroboros/dev.h> -#include <ouroboros/cacep.h> #include <ouroboros/errno.h> +#include <ouroboros/fccntl.h> #include <ouroboros/list.h> #include <ouroboros/logs.h> #include <ouroboros/notifier.h> @@ -33,9 +34,9 @@ #include "connmgr.h" #include "ipcp.h" -#include <string.h> -#include <stdlib.h> #include <assert.h> +#include <stdlib.h> +#include <string.h> enum connmgr_state { CONNMGR_NULL = 0, @@ -128,10 +129,12 @@ static int add_comp_conn(enum comp_id id, static void * flow_acceptor(void * o) { - int fd; - qosspec_t qs; - struct conn_info rcv_info; - struct conn_info fail_info; + int fd; + qosspec_t qs; + struct conn_info rcv_info; + struct conn_info fail_info; + struct timespec timeo = TIMESPEC_INIT_MS(CONNMGR_RCV_TIMEOUT); + int err; (void) o; @@ -143,38 +146,48 @@ static void * flow_acceptor(void * o) fd = flow_accept(&qs, NULL); if (fd < 0) { if (fd != -EIRMD) - log_warn("Flow accept failed: %d", fd); + log_err("Flow accept failed: %d", fd); continue; } - if (cacep_rcv(fd, &rcv_info)) { - log_dbg("Error establishing application connection."); + log_info("Handling incoming flow %d.",fd); + + fccntl(fd, FLOWSRCVTIMEO, &timeo); + + err = cep_rcv(fd, &rcv_info); + if (err < 0) { + log_err("Error receiving OCEP info: %d.", err); flow_dealloc(fd); continue; } + log_info("Request to connect to %s.", rcv_info.comp_name); + id = get_id_by_name(rcv_info.comp_name); if (id < 0) { - log_dbg("Connection request for unknown component %s.", + log_err("Connection request for unknown component %s.", rcv_info.comp_name); - cacep_snd(fd, &fail_info); + cep_snd(fd, &fail_info); flow_dealloc(fd); continue; } - assert(id < COMPID_MAX); - - if (cacep_snd(fd, &connmgr.comps[id].info)) { - log_dbg("Failed to respond to request."); + err = cep_snd(fd, &connmgr.comps[id].info); + if (err < 0) { + log_err("Failed responding to OCEP request: %d.", err); flow_dealloc(fd); continue; } - if (add_comp_conn(id, fd, qs, &rcv_info)) { - log_dbg("Failed to add new connection."); + err = add_comp_conn(id, fd, qs, &rcv_info); + if (err < 0) { + log_err("Failed to add new connection: %d.", err); flow_dealloc(fd); continue; } + + log_info("Finished handling incoming flow %d for %s.", + fd, rcv_info.comp_name); } return (void *) 0; @@ -215,8 +228,10 @@ int connmgr_init(void) { connmgr.state = CONNMGR_INIT; - if (notifier_reg(handle_event, NULL)) + if (notifier_reg(handle_event, NULL)) { + log_err("Failed to register notifier."); return -1; + } return 0; } @@ -236,8 +251,10 @@ void connmgr_fini(void) int connmgr_start(void) { - if (pthread_create(&connmgr.acceptor, NULL, flow_acceptor, NULL)) + if (pthread_create(&connmgr.acceptor, NULL, flow_acceptor, NULL)) { + log_err("Failed to create pthread: %s.", strerror(errno)); return -1; + } connmgr.state = CONNMGR_RUNNING; @@ -259,12 +276,14 @@ int connmgr_comp_init(enum comp_id id, comp = connmgr.comps + id; - if (pthread_mutex_init(&comp->lock, NULL)) - return -1; + if (pthread_mutex_init(&comp->lock, NULL)) { + log_err("Failed to initialize mutex: %s.", strerror(errno)); + goto fail_mutex; + } if (pthread_cond_init(&comp->cond, NULL)) { - pthread_mutex_destroy(&comp->lock); - return -1; + log_err("Failed to initialize condvar: %s.", strerror(errno)); + goto fail_cond; } list_head_init(&comp->conns); @@ -273,6 +292,11 @@ int connmgr_comp_init(enum comp_id id, memcpy(&connmgr.comps[id].info, info, sizeof(connmgr.comps[id].info)); return 0; + + fail_cond: + pthread_mutex_destroy(&comp->lock); + fail_mutex: + return -1; } void connmgr_comp_fini(enum comp_id id) @@ -316,26 +340,32 @@ int connmgr_ipcp_connect(const char * dst, { struct conn_el * ce; int id; + int ret; assert(dst); assert(component); ce = malloc(sizeof(*ce)); if (ce == NULL) { - log_dbg("Out of memory."); - return -1; + log_err("Out of memory."); + goto fail_malloc; } id = get_id_by_name(component); if (id < 0) { - log_dbg("No such component: %s", component); - free(ce); - return -1; + log_err("No such component: %s", component); + goto fail_id; } - if (connmgr_alloc(id, dst, &qs, &ce->conn)) { - free(ce); - return -1; + pthread_cleanup_push(free, ce); + + ret = connmgr_alloc(id, dst, &qs, &ce->conn); + + pthread_cleanup_pop(false); + + if (ret < 0) { + log_err("Failed to allocate flow."); + goto fail_id; } if (strlen(dst) > DST_MAX_STRLEN) { @@ -353,6 +383,11 @@ int connmgr_ipcp_connect(const char * dst, pthread_mutex_unlock(&connmgr.comps[id].lock); return 0; + + fail_id: + free(ce); + fail_malloc: + return -1; } int connmgr_ipcp_disconnect(const char * dst, @@ -366,8 +401,10 @@ int connmgr_ipcp_disconnect(const char * dst, assert(component); id = get_id_by_name(component); - if (id < 0) + if (id < 0) { + log_err("No such component: %s.", component); return -1; + } pthread_mutex_lock(&connmgr.comps[id].lock); @@ -393,54 +430,58 @@ int connmgr_alloc(enum comp_id id, qosspec_t * qs, struct conn * conn) { + struct comp * comp; + int fd; + struct timespec timeo = TIMESPEC_INIT_MS(CONNMGR_RCV_TIMEOUT); + assert(id >= 0 && id < COMPID_MAX); assert(dst); - conn->flow_info.fd = flow_alloc(dst, qs, NULL); - if (conn->flow_info.fd < 0) { - log_dbg("Failed to allocate flow to %s.", dst); - return -1; + comp = connmgr.comps + id; + + fd = flow_alloc(dst, qs, NULL); + if (fd < 0) { + log_err("Failed to allocate flow to %s.", dst); + goto fail_alloc; } + conn->flow_info.fd = fd; + if (qs != NULL) conn->flow_info.qs = *qs; else memset(&conn->flow_info.qs, 0, sizeof(conn->flow_info.qs)); - log_dbg("Sending cacep info for protocol %s to fd %d.", - connmgr.comps[id].info.protocol, conn->flow_info.fd); + log_dbg("Sending OCEP info for protocol %s to fd %d.", + comp->info.protocol, conn->flow_info.fd); - if (cacep_snd(conn->flow_info.fd, &connmgr.comps[id].info)) { - log_dbg("Failed to create application connection."); - flow_dealloc(conn->flow_info.fd); - return -1; + fccntl(fd, FLOWSRCVTIMEO, &timeo); + + if (cep_snd(fd, &comp->info)) { + log_err("Failed to send OCEP info."); + goto fail_cep; } - if (cacep_rcv(conn->flow_info.fd, &conn->conn_info)) { - log_dbg("Failed to connect to application."); - flow_dealloc(conn->flow_info.fd); - return -1; + if (cep_rcv(fd, &conn->conn_info)) { + log_err("Failed to receive OCEP info."); + goto fail_cep; } - if (strcmp(connmgr.comps[id].info.protocol, conn->conn_info.protocol)) { - log_dbg("Unknown protocol (requested %s, got %s).", - connmgr.comps[id].info.protocol, - conn->conn_info.protocol); - flow_dealloc(conn->flow_info.fd); - return -1; + if (strcmp(comp->info.protocol, conn->conn_info.protocol)) { + log_err("Unknown protocol (requested %s, got %s).", + comp->info.protocol, conn->conn_info.protocol); + goto fail_cep; } - if (connmgr.comps[id].info.pref_version != - conn->conn_info.pref_version) { - log_dbg("Unknown protocol version."); - flow_dealloc(conn->flow_info.fd); - return -1; + if (comp->info.pref_version != conn->conn_info.pref_version) { + log_err("Unknown protocol version %d.", + conn->conn_info.pref_version); + goto fail_cep; } - if (connmgr.comps[id].info.pref_syntax != conn->conn_info.pref_syntax) { - log_dbg("Unknown protocol syntax."); - flow_dealloc(conn->flow_info.fd); - return -1; + if (comp->info.pref_syntax != conn->conn_info.pref_syntax) { + log_err("Unknown protocol syntax."); + goto fail_cep; } switch (id) { @@ -458,6 +499,11 @@ int connmgr_alloc(enum comp_id id, } return 0; + + fail_cep: + flow_dealloc(conn->flow_info.fd); + fail_alloc: + return -1; } int connmgr_dealloc(enum comp_id id, @@ -503,6 +549,7 @@ int connmgr_wait(enum comp_id id, el = list_first_entry((&comp->pending), struct conn_el, next); if (el == NULL) { pthread_mutex_unlock(&comp->lock); + log_err("Failed to get connection element."); return -1; } diff --git a/src/ipcpd/common/connmgr.h b/src/ipcpd/common/connmgr.h index 5f7b557f..0710dbbf 100644 --- a/src/ipcpd/common/connmgr.h +++ b/src/ipcpd/common/connmgr.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Handles the different AP connections * @@ -23,7 +23,7 @@ #ifndef OUROBOROS_IPCPD_COMMON_CONNMGR_H #define OUROBOROS_IPCPD_COMMON_CONNMGR_H -#include <ouroboros/cacep.h> +#include <ouroboros/cep.h> #include <ouroboros/qos.h> #include "comp.h" diff --git a/src/ipcpd/common/enroll.c b/src/ipcpd/common/enroll.c index 090067d8..5e35ce37 100644 --- a/src/ipcpd/common/enroll.c +++ b/src/ipcpd/common/enroll.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Enrollment Task * @@ -28,13 +28,11 @@ #define OUROBOROS_PREFIX "enrollment" -#include <ouroboros/endian.h> -#include <ouroboros/errno.h> -#include <ouroboros/time_utils.h> #include <ouroboros/dev.h> -#include <ouroboros/logs.h> #include <ouroboros/errno.h> -#include <ouroboros/sockets.h> +#include <ouroboros/logs.h> +#include <ouroboros/serdes-oep.h> +#include <ouroboros/time.h> #include "common/connmgr.h" #include "common/enroll.h" @@ -45,9 +43,6 @@ #include <string.h> #include <pthread.h> -#include "ipcp_config.pb-c.h" -typedef EnrollMsg enroll_msg_t; - #define ENROLL_COMP "Enrollment" #define ENROLL_PROTO "OEP" /* Ouroboros enrollment protocol */ #define ENROLL_WARN_TIME_OFFSET 20 @@ -65,261 +60,218 @@ struct { pthread_t listener; } enroll; -static int send_rcv_enroll_msg(int fd) +static void * enroll_handle(void * o) { - enroll_msg_t req = ENROLL_MSG__INIT; - enroll_msg_t * reply; - uint8_t buf[ENROLL_BUF_LEN]; - ssize_t len; - ssize_t delta_t; - struct timespec t0; - struct timespec rtt; - - req.code = ENROLL_CODE__ENROLL_REQ; - - len = enroll_msg__get_packed_size(&req); - if (len < 0) { - log_dbg("Failed pack request message."); - return -1; - } - - enroll_msg__pack(&req, buf); + struct enroll_req req; + struct enroll_resp resp; + struct enroll_ack ack; + struct conn conn; + uint8_t __buf[ENROLL_BUF_LEN]; + buffer_t buf; + ssize_t len; - clock_gettime(CLOCK_REALTIME, &t0); - - if (flow_write(fd, buf, len) < 0) { - log_dbg("Failed to send request message."); - return -1; - } - - len = flow_read(fd, buf, ENROLL_BUF_LEN); - if (len < 0) { - log_dbg("No enrollment reply received."); - return -1; - } - - log_dbg("Received enrollment info (%zd bytes).", len); - - reply = enroll_msg__unpack(NULL, len, buf); - if (reply == NULL) { - log_dbg("No enrollment response."); - return -1; - } + (void) o; - if (reply->code != ENROLL_CODE__ENROLL_BOOT) { - log_dbg("Failed to unpack enrollment response."); - enroll_msg__free_unpacked(reply, NULL); - return -1; - } + buf.data = __buf; + buf.len = sizeof(__buf); - if (!(reply->has_t_sec && reply->has_t_nsec)) { - log_dbg("No time in response message."); - enroll_msg__free_unpacked(reply, NULL); - return -1; - } + resp.response = 0; + resp.conf = enroll.conf; - clock_gettime(CLOCK_REALTIME, &rtt); + while (true) { + buffer_t msg; + int fd; - delta_t = ts_diff_ms(&t0, &rtt); + if (connmgr_wait(COMPID_ENROLL, &conn)) { + log_err("Failed to get next connection."); + continue; + } - rtt.tv_sec = reply->t_sec; - rtt.tv_nsec = reply->t_nsec; + fd = conn.flow_info.fd; - if (labs(ts_diff_ms(&t0, &rtt)) - delta_t > ENROLL_WARN_TIME_OFFSET) - log_warn("Clock offset above threshold."); - - strcpy(enroll.conf.layer_info.layer_name, - reply->conf->layer_info->layer_name); - enroll.conf.type = reply->conf->ipcp_type; -#ifdef BUILD_IPCP_UNICAST - enroll.conf.addr_size = reply->conf->addr_size; - enroll.conf.eid_size = reply->conf->eid_size; - enroll.conf.max_ttl = reply->conf->max_ttl; - enroll.conf.addr_auth_type = reply->conf->addr_auth_type; - enroll.conf.routing_type = reply->conf->routing_type; - enroll.conf.cong_avoid = reply->conf->cong_avoid; -#endif - enroll.conf.layer_info.dir_hash_algo - = reply->conf->layer_info->dir_hash_algo; - enroll_msg__free_unpacked(reply, NULL); + log_info("Incoming enrollment connection on flow %d.", fd); - return 0; -} + len = flow_read(fd, buf.data, buf.len); + if (len < 0) { + log_err("Failed to read from flow %d.", fd); + goto finish_flow; + } -static ssize_t enroll_pack(uint8_t ** buf) -{ - enroll_msg_t msg = ENROLL_MSG__INIT; - ipcp_config_msg_t config = IPCP_CONFIG_MSG__INIT; - layer_info_msg_t layer_info = LAYER_INFO_MSG__INIT; - struct timespec now; - ssize_t len; + msg.data = buf.data; + msg.len = (size_t) len; - clock_gettime(CLOCK_REALTIME, &now); - - msg.code = ENROLL_CODE__ENROLL_BOOT; - msg.has_t_sec = true; - msg.t_sec = now.tv_sec; - msg.has_t_nsec = true; - msg.t_nsec = now.tv_nsec; - msg.conf = &config; - - config.ipcp_type = enroll.conf.type; -#ifdef BUILD_IPCP_UNICAST - config.has_addr_size = true; - config.addr_size = enroll.conf.addr_size; - config.has_eid_size = true; - config.eid_size = enroll.conf.eid_size; - config.has_max_ttl = true; - config.max_ttl = enroll.conf.max_ttl; - config.has_addr_auth_type = true; - config.addr_auth_type = enroll.conf.addr_auth_type; - config.has_routing_type = true; - config.routing_type = enroll.conf.routing_type; - config.has_cong_avoid = true; - config.cong_avoid = enroll.conf.cong_avoid; -#endif - config.layer_info = &layer_info; + if (enroll_req_des(&req, msg) < 0) { + log_err("Failed to unpack request message."); + goto finish_flow; + } - layer_info.layer_name = (char *) enroll.conf.layer_info.layer_name; - layer_info.dir_hash_algo = enroll.conf.layer_info.dir_hash_algo; + log_info_id(req.id, "Handling incoming enrollment."); - len = enroll_msg__get_packed_size(&msg); + /* TODO: authentication, timezone handling (UTC). */ - *buf = malloc(len); - if (*buf == NULL) - return -1; + ack.result = -100; - enroll_msg__pack(&msg, *buf); + clock_gettime(CLOCK_REALTIME, &resp.t); - return len; -} + memcpy(resp.id, req.id, ENROLL_ID_LEN); -static void * enroll_handle(void * o) -{ - struct conn conn; - uint8_t buf[ENROLL_BUF_LEN]; - uint8_t * reply; - ssize_t len; - enroll_msg_t * msg; + len = enroll_resp_ser(&resp, buf); + if (len < 0) { + log_err_id(req.id, "Failed to pack reply."); + goto finish_enroll; + } - (void) o; + log_dbg_id(req.id, "Sending enrollment info (%zd bytes).", len); - while (true) { - if (connmgr_wait(COMPID_ENROLL, &conn)) { - log_err("Failed to get next connection."); - continue; + if (flow_write(conn.flow_info.fd, buf.data, len) < 0) { + log_err_id(req.id, "Failed te send response."); + goto finish_enroll; } - len = flow_read(conn.flow_info.fd, buf, ENROLL_BUF_LEN); + len = flow_read(conn.flow_info.fd, buf.data, buf.len); if (len < 0) { - log_err("Failed to read from flow."); - connmgr_dealloc(COMPID_ENROLL, &conn); - continue; + log_err_id(req.id, "Failed to read from flow."); + goto finish_enroll; } - msg = enroll_msg__unpack(NULL, len, buf); - if (msg == NULL) { - log_err("Failed to unpack message."); - connmgr_dealloc(COMPID_ENROLL, &conn); - continue; - } + msg.data = buf.data; + msg.len = (size_t) len; - if (msg->code != ENROLL_CODE__ENROLL_REQ) { - log_err("Wrong message type."); - connmgr_dealloc(COMPID_ENROLL, &conn); - enroll_msg__free_unpacked(msg, NULL); - continue; + if (enroll_ack_des(&ack, msg) < 0) { + log_err_id(req.id, "Failed to unpack ack."); + goto finish_enroll; } - log_dbg("Enrolling a new neighbor."); - - enroll_msg__free_unpacked(msg, NULL); - - len = enroll_pack(&reply); - if (reply == NULL) { - log_err("Failed to pack enrollment message."); - connmgr_dealloc(COMPID_ENROLL, &conn); - continue; + if (memcmp(req.id, ack.id, ENROLL_ID_LEN) != 0) + log_warn_id(req.id, "Enrollment ID mismatch."); + + finish_enroll: + switch(ack.result) { + case 0: + log_info_id(req.id, "Enrollment completed."); + break; + case -100: + log_warn_id(req.id, "Enrollment failed."); + break; + default: + log_warn_id(req.id, "Enrollment failed at remote."); } + finish_flow: + connmgr_dealloc(COMPID_ENROLL, &conn); - log_dbg("Sending enrollment info (%zd bytes).", len); + log_info("Enrollment flow %d closed.", fd); + } - if (flow_write(conn.flow_info.fd, reply, len) < 0) { - log_err("Failed respond to enrollment request."); - connmgr_dealloc(COMPID_ENROLL, &conn); - free(reply); - continue; - } + return 0; +} - free(reply); +int enroll_boot(struct conn * conn, + const uint8_t * id) +{ + uint8_t __buf[ENROLL_BUF_LEN]; + buffer_t buf; + buffer_t msg; + ssize_t len; + ssize_t delta_t; + struct timespec t0; + struct timespec rtt; + int fd; + int ret; + struct enroll_req req; + struct enroll_resp resp; + + fd = conn->flow_info.fd; + + buf.data = __buf; + buf.len = sizeof(__buf); + + memcpy(req.id, id, ENROLL_ID_LEN); + + len = enroll_req_ser(&req, buf); + if (len < 0) { + log_err_id(id, "Failed to pack request message."); + return -1; + } - len = flow_read(conn.flow_info.fd, buf, ENROLL_BUF_LEN); - if (len < 0) { - log_err("Failed to read from flow."); - connmgr_dealloc(COMPID_ENROLL, &conn); - continue; - } + clock_gettime(CLOCK_REALTIME, &t0); - msg = enroll_msg__unpack(NULL, len, buf); - if (msg == NULL) { - log_err("Failed to unpack message."); - connmgr_dealloc(COMPID_ENROLL, &conn); - continue; - } + if (flow_write(fd, buf.data, len) < 0) { + log_err_id(id, "Failed to send request message."); + return -1; + } - if (msg->code != ENROLL_CODE__ENROLL_DONE || !msg->has_result) { - log_err("Wrong message type."); - enroll_msg__free_unpacked(msg, NULL); - connmgr_dealloc(COMPID_ENROLL, &conn); - continue; - } + len = flow_read(fd, buf.data, buf.len); + if (len < 0) { + log_err_id(id, "No reply received."); + return -1; + } - if (msg->result == 0) - log_dbg("Neighbor enrollment successful."); - else - log_dbg("Neigbor reported failed enrollment."); + log_dbg_id(id, "Received configuration info (%zd bytes).", len); - enroll_msg__free_unpacked(msg, NULL); + msg.data = buf.data; + msg.len = len; - connmgr_dealloc(COMPID_ENROLL, &conn); + ret = enroll_resp_des(&resp, msg); + if (ret < 0) { + log_err_id(id, "Failed to unpack response message."); + return -1; } - return 0; -} + if (memcmp(resp.id, id, ENROLL_ID_LEN) != 0) { + log_err_id(id, "Enrollment ID mismatch."); + return -1; + } -int enroll_boot(struct conn * conn) -{ - log_dbg("Getting boot information."); + if (resp.response < 0) { + log_warn_id(id, "Remote denied request: %d.", resp.response); + return -1; + } - if (send_rcv_enroll_msg(conn->flow_info.fd)) { - log_err("Failed to enroll."); + if (resp.conf.type != ipcpi.type) { + log_err_id(id, "Wrong type in enrollment response %d (%d).", + resp.conf.type, ipcpi.type); return -1; } + clock_gettime(CLOCK_REALTIME, &rtt); + + delta_t = ts_diff_ms(&t0, &rtt); + + rtt.tv_sec = resp.t.tv_sec; + rtt.tv_nsec = resp.t.tv_nsec; + + if (labs(ts_diff_ms(&t0, &rtt)) - delta_t > ENROLL_WARN_TIME_OFFSET) + log_warn_id(id, "Clock offset above threshold."); + + enroll.conf = resp.conf; + return 0; } -int enroll_done(struct conn * conn, - int result) +int enroll_ack(struct conn * conn, + const uint8_t * id, + const int result) { - enroll_msg_t msg = ENROLL_MSG__INIT; - uint8_t buf[ENROLL_BUF_LEN]; - ssize_t len; + struct enroll_ack ack; + uint8_t __buf[ENROLL_BUF_LEN]; + buffer_t buf; + ssize_t len; - msg.code = ENROLL_CODE__ENROLL_DONE; - msg.has_result = true; - msg.result = result; + buf.data = __buf; + buf.len = sizeof(__buf); - len = enroll_msg__get_packed_size(&msg); + ack.result = result; + + memcpy(ack.id, id, ENROLL_ID_LEN); + + len = enroll_ack_ser(&ack, buf); if (len < 0) { - log_dbg("Failed pack request message."); + log_err_id(id, "Failed to pack acknowledgement."); return -1; } - enroll_msg__pack(&msg, buf); - - if (flow_write(conn->flow_info.fd, buf, len) < 0) { - log_dbg("Failed to send acknowledgment."); + if (flow_write(conn->flow_info.fd, buf.data, len) < 0) { + log_err_id(id, "Failed to send acknowledgment."); return -1; } diff --git a/src/ipcpd/common/enroll.h b/src/ipcpd/common/enroll.h index fb866416..f26c31a3 100644 --- a/src/ipcpd/common/enroll.h +++ b/src/ipcpd/common/enroll.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Enrollment Task * @@ -37,10 +37,12 @@ void enroll_stop(void); void enroll_bootstrap(const struct ipcp_config * conf); -int enroll_boot(struct conn * conn); +int enroll_boot(struct conn * conn, + const uint8_t * id); -int enroll_done(struct conn * conn, - int result); +int enroll_ack(struct conn * conn, + const uint8_t * id, + const int result); struct ipcp_config * enroll_get_conf(void); diff --git a/src/ipcpd/config.h.in b/src/ipcpd/config.h.in index 0bf3ad69..fe4f5fd2 100644 --- a/src/ipcpd/config.h.in +++ b/src/ipcpd/config.h.in @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * IPC process configuration * @@ -41,6 +41,8 @@ #define IPCP_LINUX_SLACK_NS @IPCP_LINUX_TIMERSLACK_NS@ +#cmakedefine IPCP_DEBUG_LOCAL + /* unicast IPCP */ #define QOS_PRIO_BE @IPCP_QOS_CUBE_BE_PRIO@ #define QOS_PRIO_VIDEO @IPCP_QOS_CUBE_VIDEO_PRIO@ @@ -48,6 +50,8 @@ #define IPCP_SCHED_THR_MUL @IPCP_SCHED_THR_MUL@ #define PFT_SIZE @PFT_SIZE@ #define DHT_ENROLL_SLACK @DHT_ENROLL_SLACK@ +#define IPCP_UNICAST_MPL @IPCP_UNICAST_MPL@ +#define CONNMGR_RCV_TIMEOUT @CONNMGR_RCV_TIMEOUT@ #cmakedefine IPCP_CONN_WAIT_DIR #cmakedefine DISABLE_CORE_LOCK @@ -59,8 +63,9 @@ #define NSLOOKUP_EXEC "@NSLOOKUP_EXECUTABLE@" #define IPCP_UDP_RD_THR @IPCP_UDP_RD_THR@ #define IPCP_UDP_WR_THR @IPCP_UDP_WR_THR@ +#define IPCP_UDP_MPL @IPCP_UDP_MPL@ -/* eth-llc */ +/* eth */ #cmakedefine HAVE_NETMAP #cmakedefine HAVE_BPF #cmakedefine HAVE_RAW_SOCKETS @@ -68,3 +73,11 @@ #define IPCP_ETH_RD_THR @IPCP_ETH_RD_THR@ #define IPCP_ETH_WR_THR @IPCP_ETH_WR_THR@ #define IPCP_ETH_LO_MTU @IPCP_ETH_LO_MTU@ +#define IPCP_ETH_MPL @IPCP_ETH_MPL@ + +/* local */ +#define IPCP_LOCAL_MPL @IPCP_LOCAL_MPL@ + +/* broadcast */ +/* local */ +#define IPCP_BROADCAST_MPL @IPCP_BROADCAST_MPL@ diff --git a/src/ipcpd/eth/CMakeLists.txt b/src/ipcpd/eth/CMakeLists.txt index d7105b4f..d57e1848 100644 --- a/src/ipcpd/eth/CMakeLists.txt +++ b/src/ipcpd/eth/CMakeLists.txt @@ -85,6 +85,8 @@ if (HAVE_ETH) "Bypass the Qdisc in the kernel when using raw sockets") set(IPCP_ETH_LO_MTU 1500 CACHE STRING "Restrict Ethernet MTU over loopback interfaces") + set(IPCP_ETH_MPL 5 CACHE STRING + "Default maximum packet lifetime for the Ethernet IPCPs, in seconds") set(ETH_LLC_SOURCES # Add source files here diff --git a/src/ipcpd/eth/dix.c b/src/ipcpd/eth/dix.c index ac117e37..37b9896d 100644 --- a/src/ipcpd/eth/dix.c +++ b/src/ipcpd/eth/dix.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * IPC processes over Ethernet - DIX * diff --git a/src/ipcpd/eth/eth.c b/src/ipcpd/eth/eth.c index 932034d5..ea6e0f1c 100644 --- a/src/ipcpd/eth/eth.c +++ b/src/ipcpd/eth/eth.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * IPC processes over Ethernet * @@ -37,6 +37,7 @@ #include "config.h" +#include <ouroboros/endian.h> #include <ouroboros/hash.h> #include <ouroboros/errno.h> #include <ouroboros/list.h> @@ -46,7 +47,7 @@ #include <ouroboros/ipcp-dev.h> #include <ouroboros/fqueue.h> #include <ouroboros/logs.h> -#include <ouroboros/time_utils.h> +#include <ouroboros/time.h> #include <ouroboros/fccntl.h> #include <ouroboros/pthread.h> @@ -135,7 +136,6 @@ #define ETH_FRAME_SIZE (ETH_HEADER_SIZE + ETH_MTU_MAX) #endif -#define ALLOC_TIMEO 10 /* ms */ #define NAME_QUERY_TIMEO 2000 /* ms */ #define MGMT_TIMEO 100 /* ms */ #define MGMT_FRAME_SIZE 2048 @@ -164,6 +164,7 @@ struct mgmt_msg { uint32_t ber; uint32_t max_gap; uint32_t delay; + uint32_t timeout; uint16_t cypher_s; uint8_t in_order; #if defined (BUILD_ETH_DIX) @@ -454,16 +455,15 @@ static int eth_ipcp_send_frame(const uint8_t * dst_addr, return 0; } -static int eth_ipcp_alloc(const uint8_t * dst_addr, +static int eth_ipcp_alloc(const uint8_t * dst_addr, #if defined(BUILD_ETH_DIX) - uint16_t eid, + uint16_t eid, #elif defined(BUILD_ETH_LLC) - uint8_t ssap, + uint8_t ssap, #endif - const uint8_t * hash, - qosspec_t qs, - const void * data, - size_t dlen) + const uint8_t * hash, + qosspec_t qs, + const buffer_t * data) { uint8_t * buf; struct mgmt_msg * msg; @@ -472,7 +472,7 @@ static int eth_ipcp_alloc(const uint8_t * dst_addr, len = sizeof(*msg) + ipcp_dir_hash_len(); - buf = malloc(len + ETH_HEADER_TOT_SIZE + dlen); + buf = malloc(len + ETH_HEADER_TOT_SIZE + data->len); if (buf == NULL) return -1; @@ -492,9 +492,11 @@ static int eth_ipcp_alloc(const uint8_t * dst_addr, msg->in_order = qs.in_order; msg->max_gap = hton32(qs.max_gap); msg->cypher_s = hton16(qs.cypher_s); + msg->timeout = hton32(qs.timeout); memcpy(msg + 1, hash, ipcp_dir_hash_len()); - memcpy(buf + len + ETH_HEADER_TOT_SIZE, data, dlen); + if (data->len > 0) + memcpy(buf + len + ETH_HEADER_TOT_SIZE, data->data, data->len); ret = eth_ipcp_send_frame(dst_addr, #if defined(BUILD_ETH_DIX) @@ -503,28 +505,27 @@ static int eth_ipcp_alloc(const uint8_t * dst_addr, reverse_bits(MGMT_SAP), reverse_bits(MGMT_SAP), #endif - buf, len + dlen); + buf, len + data->len); free(buf); return ret; } -static int eth_ipcp_alloc_resp(uint8_t * dst_addr, +static int eth_ipcp_alloc_resp(uint8_t * dst_addr, #if defined(BUILD_ETH_DIX) - uint16_t seid, - uint16_t deid, + uint16_t seid, + uint16_t deid, #elif defined(BUILD_ETH_LLC) - uint8_t ssap, - uint8_t dsap, + uint8_t ssap, + uint8_t dsap, #endif - int response, - const void * data, - size_t len) + int response, + const buffer_t * data) { struct mgmt_msg * msg; uint8_t * buf; - buf = malloc(sizeof(*msg) + ETH_HEADER_TOT_SIZE + len); + buf = malloc(sizeof(*msg) + ETH_HEADER_TOT_SIZE + data->len); if (buf == NULL) return -1; @@ -540,7 +541,8 @@ static int eth_ipcp_alloc_resp(uint8_t * dst_addr, #endif msg->response = response; - memcpy(msg + 1, data, len); + if (data->len > 0) + memcpy(msg + 1, data->data, data->len); if (eth_ipcp_send_frame(dst_addr, #if defined(BUILD_ETH_DIX) @@ -549,7 +551,7 @@ static int eth_ipcp_alloc_resp(uint8_t * dst_addr, reverse_bits(MGMT_SAP), reverse_bits(MGMT_SAP), #endif - buf, sizeof(*msg) + len)) { + buf, sizeof(*msg) + data->len)) { free(buf); return -1; } @@ -559,42 +561,20 @@ static int eth_ipcp_alloc_resp(uint8_t * dst_addr, return 0; } -static int eth_ipcp_req(uint8_t * r_addr, +static int eth_ipcp_req(uint8_t * r_addr, #if defined(BUILD_ETH_DIX) - uint16_t r_eid, + uint16_t r_eid, #elif defined(BUILD_ETH_LLC) - uint8_t r_sap, + uint8_t r_sap, #endif - const uint8_t * dst, - qosspec_t qs, - const void * data, - size_t len) + const uint8_t * dst, + qosspec_t qs, + const buffer_t * data) { - struct timespec ts = {0, ALLOC_TIMEO * MILLION}; - struct timespec abstime; - int fd; + int fd; - clock_gettime(PTHREAD_COND_CLOCK, &abstime); - - pthread_mutex_lock(&ipcpi.alloc_lock); - - while (ipcpi.alloc_id != -1 && ipcp_get_state() == IPCP_OPERATIONAL) { - ts_add(&abstime, &ts, &abstime); - pthread_cond_timedwait(&ipcpi.alloc_cond, - &ipcpi.alloc_lock, - &abstime); - } - - if (ipcp_get_state() != IPCP_OPERATIONAL) { - log_dbg("Won't allocate over non-operational IPCP."); - pthread_mutex_unlock(&ipcpi.alloc_lock); - return -1; - } - - /* reply to IRM, called under lock to prevent race */ - fd = ipcp_flow_req_arr(dst, ipcp_dir_hash_len(), qs, data, len); + fd = ipcp_wait_flow_req_arr(dst, qs, IPCP_ETH_MPL, data); if (fd < 0) { - pthread_mutex_unlock(&ipcpi.alloc_lock); log_err("Could not get new flow from IRMd."); return -1; } @@ -609,11 +589,6 @@ static int eth_ipcp_req(uint8_t * r_addr, pthread_rwlock_unlock(ð_data.flows_lock); - ipcpi.alloc_id = fd; - pthread_cond_broadcast(&ipcpi.alloc_cond); - - pthread_mutex_unlock(&ipcpi.alloc_lock); - #if defined(BUILD_ETH_DIX) log_dbg("New flow request, fd %d, remote endpoint %d.", fd, r_eid); #elif defined(BUILD_ETH_LLC) @@ -622,20 +597,20 @@ static int eth_ipcp_req(uint8_t * r_addr, return 0; } -static int eth_ipcp_alloc_reply(uint8_t * r_addr, +static int eth_ipcp_alloc_reply(uint8_t * r_addr, #if defined(BUILD_ETH_DIX) - uint16_t seid, - uint16_t deid, + uint16_t seid, + uint16_t deid, #elif defined(BUILD_ETH_LLC) - uint8_t ssap, - int dsap, + uint8_t ssap, + int dsap, #endif - int response, - const void * data, - size_t len) + int response, + const buffer_t * data) { - int ret = 0; - int fd = -1; + int ret = 0; + int fd = -1; + time_t mpl = IPCP_ETH_MPL; pthread_rwlock_wrlock(ð_data.flows_lock); @@ -670,11 +645,12 @@ static int eth_ipcp_alloc_reply(uint8_t * r_addr, #elif defined(BUILD_ETH_LLC) log_dbg("Flow reply, fd %d, SSAP %d, DSAP %d.", fd, ssap, dsap); #endif - if ((ret = ipcp_flow_alloc_reply(fd, response, data, len)) < 0) + if ((ret = ipcp_flow_alloc_reply(fd, response, mpl, data)) < 0) { + log_err("Failed to reply to flow allocation."); return -1; + } return ret; - } static int eth_ipcp_name_query_req(const uint8_t * hash, @@ -736,6 +712,7 @@ static int eth_ipcp_mgmt_frame(const uint8_t * buf, struct mgmt_msg * msg; size_t msg_len; qosspec_t qs; + buffer_t data; msg = (struct mgmt_msg *) buf; @@ -753,6 +730,10 @@ static int eth_ipcp_mgmt_frame(const uint8_t * buf, qs.in_order = msg->in_order; qs.max_gap = ntoh32(msg->max_gap); qs.cypher_s = ntoh16(msg->cypher_s); + qs.timeout = ntoh32(msg->timeout); + + data.data = (uint8_t *) buf + msg_len; + data.len = len - msg_len; if (shim_data_reg_has(eth_data.shim_data, buf + sizeof(*msg))) { @@ -764,13 +745,15 @@ static int eth_ipcp_mgmt_frame(const uint8_t * buf, #endif buf + sizeof(*msg), qs, - buf + msg_len, - len - msg_len); + &data); } break; case FLOW_REPLY: assert(len >= sizeof(*msg)); + data.data = (uint8_t *) buf + sizeof(*msg); + data.len = len - sizeof(*msg); + eth_ipcp_alloc_reply(r_addr, #if defined(BUILD_ETH_DIX) ntohs(msg->seid), @@ -780,8 +763,7 @@ static int eth_ipcp_mgmt_frame(const uint8_t * buf, msg->dsap, #endif msg->response, - buf + sizeof(*msg), - len - sizeof(*msg)); + &data); break; case NAME_QUERY_REQ: eth_ipcp_name_query_req(buf + sizeof(*msg), r_addr); @@ -799,18 +781,15 @@ static int eth_ipcp_mgmt_frame(const uint8_t * buf, static void * eth_ipcp_mgmt_handler(void * o) { - int ret; - struct timespec timeout = {(MGMT_TIMEO / 1000), - (MGMT_TIMEO % 1000) * MILLION}; - struct timespec abstime; - struct mgmt_frame * frame; - (void) o; pthread_cleanup_push(__cleanup_mutex_unlock, ð_data.mgmt_lock); while (true) { - ret = 0; + int ret = 0; + struct timespec timeout = TIMESPEC_INIT_MS(MGMT_TIMEO); + struct timespec abstime; + struct mgmt_frame * frame = NULL; clock_gettime(PTHREAD_COND_CLOCK, &abstime); ts_add(&abstime, &timeout, &abstime); @@ -822,23 +801,19 @@ static void * eth_ipcp_mgmt_handler(void * o) ret = -pthread_cond_timedwait(ð_data.mgmt_cond, ð_data.mgmt_lock, &abstime); + if (ret != -ETIMEDOUT) + frame = list_first_entry((ð_data.mgmt_frames), + struct mgmt_frame, next); + if (frame != NULL) + list_del(&frame->next); - if (ret == -ETIMEDOUT) { - pthread_mutex_unlock(ð_data.mgmt_lock); - continue; - } + pthread_mutex_unlock(ð_data.mgmt_lock); - frame = list_first_entry((ð_data.mgmt_frames), - struct mgmt_frame, next); - if (frame == NULL) { - pthread_mutex_unlock(ð_data.mgmt_lock); + if (frame == NULL) continue; - } - - list_del(&frame->next); - pthread_mutex_unlock(ð_data.mgmt_lock); eth_ipcp_mgmt_frame(frame->buf, frame->len, frame->r_addr); + free(frame); } @@ -884,7 +859,7 @@ static void * eth_ipcp_packet_reader(void * o) buf = nm_nextpkt(eth_data.nmd, &hdr); if (buf == NULL) { - log_err("Bad read from netmap device."); + log_dbg("Bad read from netmap device."); continue; } #else @@ -915,6 +890,7 @@ static void * eth_ipcp_packet_reader(void * o) ETH_MTU + ETH_HEADER_TOT_SIZE, 0); #endif if (frame_len <= 0) { + log_dbg("Failed to receive frame."); ipcp_sdb_release(sdb); continue; } @@ -941,22 +917,14 @@ static void * eth_ipcp_packet_reader(void * o) #endif length = ntohs(e_frame->length); #if defined(BUILD_ETH_DIX) - if (e_frame->ethertype != eth_data.ethertype) { -#ifndef HAVE_NETMAP - ipcp_sdb_release(sdb); -#endif - continue; - } + if (e_frame->ethertype != eth_data.ethertype) + goto fail_frame; deid = ntohs(e_frame->eid); if (deid == MGMT_EID) { #elif defined (BUILD_ETH_LLC) - if (length > 0x05FF) {/* DIX */ -#ifndef HAVE_NETMAP - ipcp_sdb_release(sdb); -#endif - continue; - } + if (length > 0x05FF) /* DIX */ + goto fail_frame; length -= LLC_HEADER_SIZE; @@ -965,12 +933,12 @@ static void * eth_ipcp_packet_reader(void * o) if (ssap == MGMT_SAP && dsap == MGMT_SAP) { #endif + ipcp_sdb_release(sdb); /* No need for the N+1 buffer. */ + frame = malloc(sizeof(*frame)); if (frame == NULL) { -#ifndef HAVE_NETMAP - ipcp_sdb_release(sdb); -#endif - continue; + log_err("Failed to allocate frame."); + goto fail_frame; } memcpy(frame->buf, &e_frame->payload, length); @@ -981,10 +949,6 @@ static void * eth_ipcp_packet_reader(void * o) list_add(&frame->next, ð_data.mgmt_frames); pthread_cond_signal(ð_data.mgmt_cond); pthread_mutex_unlock(ð_data.mgmt_lock); - -#ifndef HAVE_NETMAP - ipcp_sdb_release(sdb); -#endif } else { pthread_rwlock_rdlock(ð_data.flows_lock); @@ -995,10 +959,7 @@ static void * eth_ipcp_packet_reader(void * o) #endif if (fd < 0) { pthread_rwlock_unlock(ð_data.flows_lock); -#ifndef HAVE_NETMAP - ipcp_sdb_release(sdb); -#endif - continue; + goto fail_frame; } #ifdef BUILD_ETH_LLC @@ -1006,10 +967,7 @@ static void * eth_ipcp_packet_reader(void * o) || memcmp(eth_data.fd_to_ef[fd].r_addr, e_frame->src_hwaddr, MAC_SIZE)) { pthread_rwlock_unlock(ð_data.flows_lock); -#ifndef HAVE_NETMAP - ipcp_sdb_release(sdb); -#endif - continue; + goto fail_frame; } #endif pthread_rwlock_unlock(ð_data.flows_lock); @@ -1017,9 +975,20 @@ static void * eth_ipcp_packet_reader(void * o) #ifndef HAVE_NETMAP shm_du_buff_head_release(sdb, ETH_HEADER_TOT_SIZE); shm_du_buff_truncate(sdb, length); - ipcp_flow_write(fd, sdb); #else - flow_write(fd, &e_frame->payload, length); + if (ipcp_sdb_reserve(&sdb, length)) + continue; + + buf = shm_du_buff_head(sdb); + memcpy(buf, &e_frame->payload, length); +#endif + if (np1_flow_write(fd, sdb) < 0) + ipcp_sdb_release(sdb); + + continue; + fail_frame: +#ifndef HAVE_NETMAP + ipcp_sdb_release(sdb); #endif } } @@ -1053,27 +1022,28 @@ static void * eth_ipcp_packet_writer(void * o) (void) o; - pthread_cleanup_push(cleanup_writer, fq); - ipcp_lock_to_core(); + pthread_cleanup_push(cleanup_writer, fq); + while (true) { fevent(eth_data.np1_flows, fq, NULL); while ((fd = fqueue_next(fq)) >= 0) { if (fqueue_type(fq) != FLOW_PKT) continue; - if (ipcp_flow_read(fd, &sdb)) { + if (np1_flow_read(fd, &sdb)) { log_dbg("Bad read from fd %d.", fd); continue; } - len = shm_du_buff_tail(sdb) - shm_du_buff_head(sdb); + len = shm_du_buff_len(sdb); if (shm_du_buff_head_alloc(sdb, ETH_HEADER_TOT_SIZE) == NULL) { log_dbg("Failed to allocate header."); ipcp_sdb_release(sdb); + continue; } pthread_rwlock_rdlock(ð_data.flows_lock); @@ -1089,14 +1059,15 @@ static void * eth_ipcp_packet_writer(void * o) pthread_rwlock_unlock(ð_data.flows_lock); - eth_ipcp_send_frame(r_addr, + if (eth_ipcp_send_frame(r_addr, #if defined(BUILD_ETH_DIX) deid, #elif defined(BUILD_ETH_LLC) dsap, ssap, #endif shm_du_buff_head(sdb), - len); + len)) + log_dbg("Failed to send frame."); ipcp_sdb_release(sdb); } } @@ -1276,32 +1247,23 @@ static int eth_ipcp_bootstrap(const struct ipcp_config * conf) assert(conf); assert(conf->type == THIS_TYPE); - ipcpi.dir_hash_algo = conf->layer_info.dir_hash_algo; - ipcpi.layer_name = strdup(conf->layer_info.layer_name); - if (ipcpi.layer_name == NULL) { - log_err("Failed to set layer name"); - return -ENOMEM; - } - - if (conf->dev == NULL) { - log_err("Device name is NULL."); - return -1; - } + ipcpi.dir_hash_algo = (enum hash_algo) conf->layer_info.dir_hash_algo; + strcpy(ipcpi.layer_name, conf->layer_info.name); - if (strlen(conf->dev) >= IFNAMSIZ) { - log_err("Invalid device name: %s.", conf->dev); + if (strlen(conf->eth.dev) >= IFNAMSIZ) { + log_err("Invalid device name: %s.", conf->eth.dev); return -1; } memset(&ifr, 0, sizeof(ifr)); - strcpy(ifr.ifr_name, conf->dev); + strcpy(ifr.ifr_name, conf->eth.dev); #ifdef BUILD_ETH_DIX - if (conf->ethertype < 0x0600 || conf->ethertype == 0xFFFF) { - log_err("Invalid Ethertype."); + if (conf->eth.ethertype < 0x0600 || conf->eth.ethertype == 0xFFFF) { + log_err("Invalid Ethertype: %d.", conf->eth.ethertype); return -1; } - eth_data.ethertype = htons(conf->ethertype); + eth_data.ethertype = htons(conf->eth.ethertype); #endif #if defined(__FreeBSD__) || defined(__APPLE__) @@ -1311,9 +1273,9 @@ static int eth_ipcp_bootstrap(const struct ipcp_config * conf) } for (ifa = ifaddr, idx = 0; ifa != NULL; ifa = ifa->ifa_next, ++idx) { - if (strcmp(ifa->ifa_name, conf->dev)) + if (strcmp(ifa->ifa_name, conf->eth.dev)) continue; - log_dbg("Interface %s found.", conf->dev); + log_dbg("Interface %s found.", conf->eth.dev); #if defined(HAVE_NETMAP) || defined(HAVE_BPF) memcpy(eth_data.hw_addr, @@ -1348,7 +1310,8 @@ static int eth_ipcp_bootstrap(const struct ipcp_config * conf) log_dbg("Device MTU is %d.", ifr.ifr_mtu); eth_data.mtu = MIN((int) ETH_MTU_MAX, ifr.ifr_mtu); - if (memcmp(conf->dev, "lo", 2) == 0 && eth_data.mtu > IPCP_ETH_LO_MTU) { + if (memcmp(conf->eth.dev, "lo", 2) == 0 && + eth_data.mtu > IPCP_ETH_LO_MTU) { log_dbg("Using loopback interface. MTU restricted to %d.", IPCP_ETH_LO_MTU); eth_data.mtu = IPCP_ETH_LO_MTU; @@ -1372,7 +1335,7 @@ static int eth_ipcp_bootstrap(const struct ipcp_config * conf) close(skfd); - idx = if_nametoindex(conf->dev); + idx = if_nametoindex(conf->eth.dev); if (idx == 0) { log_err("Failed to retrieve interface index."); return -1; @@ -1382,7 +1345,7 @@ static int eth_ipcp_bootstrap(const struct ipcp_config * conf) #if defined(HAVE_NETMAP) strcpy(ifn, "netmap:"); - strcat(ifn, conf->dev); + strcat(ifn, conf->eth.dev); eth_data.nmd = nm_open(ifn, NULL, 0, NULL); if (eth_data.nmd == NULL) { @@ -1452,7 +1415,7 @@ static int eth_ipcp_bootstrap(const struct ipcp_config * conf) if (eth_data.s_fd < 0) { log_err("Failed to create socket."); - return -1; + goto fail_socket; } flags = fcntl(eth_data.s_fd, F_GETFL, 0); @@ -1474,60 +1437,52 @@ static int eth_ipcp_bootstrap(const struct ipcp_config * conf) #endif if (bind(eth_data.s_fd, (struct sockaddr *) ð_data.device, - sizeof(eth_data.device))) { + sizeof(eth_data.device)) < 0) { log_err("Failed to bind socket to interface."); goto fail_device; } - #endif /* HAVE_NETMAP */ - ipcp_set_state(IPCP_OPERATIONAL); - #if defined(__linux__) - if (pthread_create(ð_data.if_monitor, - NULL, - eth_ipcp_if_monitor, - NULL)) { - ipcp_set_state(IPCP_INIT); + if (pthread_create(ð_data.if_monitor, NULL, + eth_ipcp_if_monitor, NULL)) { + log_err("Failed to create monitor thread: %s.", + strerror(errno)); goto fail_device; } #endif - if (pthread_create(ð_data.mgmt_handler, - NULL, - eth_ipcp_mgmt_handler, - NULL)) { - ipcp_set_state(IPCP_INIT); + if (pthread_create(ð_data.mgmt_handler, NULL, + eth_ipcp_mgmt_handler, NULL)) { + log_err("Failed to create mgmt handler thread: %s.", + strerror(errno)); goto fail_mgmt_handler; } for (idx = 0; idx < IPCP_ETH_RD_THR; ++idx) { - if (pthread_create(ð_data.packet_reader[idx], - NULL, - eth_ipcp_packet_reader, - NULL)) { - ipcp_set_state(IPCP_INIT); + if (pthread_create(ð_data.packet_reader[idx], NULL, + eth_ipcp_packet_reader, NULL)) { + log_err("Failed to create packet reader thread: %s", + strerror(errno)); goto fail_packet_reader; } } for (idx = 0; idx < IPCP_ETH_WR_THR; ++idx) { - if (pthread_create(ð_data.packet_writer[idx], - NULL, - eth_ipcp_packet_writer, - NULL)) { - ipcp_set_state(IPCP_INIT); + if (pthread_create(ð_data.packet_writer[idx], NULL, + eth_ipcp_packet_writer, NULL)) { + log_err("Failed to create packet writer thread: %s", + strerror(errno)); goto fail_packet_writer; } } #if defined(BUILD_ETH_DIX) log_dbg("Bootstrapped IPCP over DIX Ethernet with pid %d " - "and Ethertype 0x%X.", getpid(), conf->ethertype); + "and Ethertype 0x%X.", getpid(), conf->eth.ethertype); #elif defined(BUILD_ETH_LLC) log_dbg("Bootstrapped IPCP over Ethernet with LLC with pid %d.", getpid()); #endif - return 0; fail_packet_writer: @@ -1558,19 +1513,18 @@ static int eth_ipcp_bootstrap(const struct ipcp_config * conf) #elif defined(HAVE_RAW_SOCKETS) close(eth_data.s_fd); #endif + fail_socket: return -1; } static int eth_ipcp_reg(const uint8_t * hash) { if (shim_data_reg_add_entry(eth_data.shim_data, hash)) { - log_err("Failed to add " HASH_FMT " to local registry.", - HASH_VAL(hash)); + log_err("Failed to add " HASH_FMT32 " to local registry.", + HASH_VAL32(hash)); return -1; } - log_dbg("Registered " HASH_FMT ".", HASH_VAL(hash)); - return 0; } @@ -1584,8 +1538,7 @@ static int eth_ipcp_unreg(const uint8_t * hash) static int eth_ipcp_query(const uint8_t * hash) { uint8_t r_addr[MAC_SIZE]; - struct timespec timeout = {(NAME_QUERY_TIMEO / 1000), - (NAME_QUERY_TIMEO % 1000) * MILLION}; + struct timespec timeout = TIMESPEC_INIT_MS(NAME_QUERY_TIMEO); struct dir_query * query; int ret; uint8_t * buf; @@ -1637,11 +1590,10 @@ static int eth_ipcp_query(const uint8_t * hash) return ret; } -static int eth_ipcp_flow_alloc(int fd, - const uint8_t * hash, - qosspec_t qs, - const void * data, - size_t len) +static int eth_ipcp_flow_alloc(int fd, + const uint8_t * hash, + qosspec_t qs, + const buffer_t * data) { #ifdef BUILD_ETH_LLC uint8_t ssap = 0; @@ -1649,12 +1601,11 @@ static int eth_ipcp_flow_alloc(int fd, uint8_t r_addr[MAC_SIZE]; uint64_t addr = 0; - log_dbg("Allocating flow to " HASH_FMT ".", HASH_VAL(hash)); - assert(hash); if (!shim_data_dir_has(eth_data.shim_data, hash)) { - log_err("Destination unreachable."); + log_err("Destination "HASH_FMT32 "unreachable.", + HASH_VAL32(hash)); return -1; } addr = shim_data_dir_get_addr(eth_data.shim_data, hash); @@ -1664,6 +1615,7 @@ static int eth_ipcp_flow_alloc(int fd, ssap = bmp_allocate(eth_data.saps); if (!bmp_is_id_valid(eth_data.saps, ssap)) { pthread_rwlock_unlock(ð_data.flows_lock); + log_err("Failed to allocate SSAP."); return -1; } @@ -1682,34 +1634,29 @@ static int eth_ipcp_flow_alloc(int fd, #endif hash, qs, - data, - len) < 0) { + data) < 0) { #ifdef BUILD_ETH_LLC pthread_rwlock_wrlock(ð_data.flows_lock); bmp_release(eth_data.saps, eth_data.fd_to_ef[fd].sap); eth_data.fd_to_ef[fd].sap = -1; eth_data.ef_to_fd[ssap] = -1; pthread_rwlock_unlock(ð_data.flows_lock); + log_err("Failed to allocate with peer."); #endif return -1; } fset_add(eth_data.np1_flows, fd); -#if defined(BUILD_ETH_DIX) - log_dbg("Pending flow with fd %d.", fd); -#elif defined(BUILD_ETH_LLC) - log_dbg("Pending flow with fd %d on SAP %d.", fd, ssap); +#if defined(BUILD_ETH_LLC) + log_dbg("Assigned SAP %d for fd %d.", ssap, fd); #endif return 0; } -static int eth_ipcp_flow_alloc_resp(int fd, - int response, - const void * data, - size_t len) +static int eth_ipcp_flow_alloc_resp(int fd, + int response, + const buffer_t * data) { - struct timespec ts = {0, ALLOC_TIMEO * MILLION}; - struct timespec abstime; #if defined(BUILD_ETH_DIX) uint16_t r_eid; #elif defined(BUILD_ETH_LLC) @@ -1718,27 +1665,11 @@ static int eth_ipcp_flow_alloc_resp(int fd, #endif uint8_t r_addr[MAC_SIZE]; - clock_gettime(PTHREAD_COND_CLOCK, &abstime); - - pthread_mutex_lock(&ipcpi.alloc_lock); - - while (ipcpi.alloc_id != fd && ipcp_get_state() == IPCP_OPERATIONAL) { - ts_add(&abstime, &ts, &abstime); - pthread_cond_timedwait(&ipcpi.alloc_cond, - &ipcpi.alloc_lock, - &abstime); - } - - if (ipcp_get_state() != IPCP_OPERATIONAL) { - pthread_mutex_unlock(&ipcpi.alloc_lock); + if (ipcp_wait_flow_resp(fd) < 0) { + log_err("Failed to wait for flow response."); return -1; } - ipcpi.alloc_id = -1; - pthread_cond_broadcast(&ipcpi.alloc_cond); - - pthread_mutex_unlock(&ipcpi.alloc_lock); - pthread_rwlock_wrlock(ð_data.flows_lock); #if defined(BUILD_ETH_DIX) r_eid = eth_data.fd_to_ef[fd].r_eid; @@ -1746,6 +1677,7 @@ static int eth_ipcp_flow_alloc_resp(int fd, ssap = bmp_allocate(eth_data.saps); if (!bmp_is_id_valid(eth_data.saps, ssap)) { pthread_rwlock_unlock(ð_data.flows_lock); + log_err("Failed to allocate SSAP."); return -1; } @@ -1764,21 +1696,19 @@ static int eth_ipcp_flow_alloc_resp(int fd, ssap, r_sap, #endif response, - data, - len) < 0) { + data) < 0) { #ifdef BUILD_ETH_LLC pthread_rwlock_wrlock(ð_data.flows_lock); bmp_release(eth_data.saps, eth_data.fd_to_ef[fd].sap); pthread_rwlock_unlock(ð_data.flows_lock); #endif + log_err("Failed to respond to peer."); return -1; } fset_add(eth_data.np1_flows, fd); -#if defined(BUILD_ETH_DIX) - log_dbg("Accepted flow, fd %d.", fd); -#elif defined(BUILD_ETH_LLC) - log_dbg("Accepted flow, fd %d, SAP %d.", fd, (uint8_t)ssap); +#if defined(BUILD_ETH_LLC) + log_dbg("Assigned SAP %d for fd %d.", ssap, fd); #endif return 0; } @@ -1807,9 +1737,7 @@ static int eth_ipcp_flow_dealloc(int fd) pthread_rwlock_unlock(ð_data.flows_lock); - flow_dealloc(fd); - - log_dbg("Flow with fd %d deallocated.", fd); + ipcp_flow_dealloc(fd); return 0; } @@ -1833,9 +1761,6 @@ int main(int argc, { int i; - if (ipcp_init(argc, argv, ð_ops, THIS_TYPE) < 0) - goto fail_init; - if (eth_data_init() < 0) { #if defined(BUILD_ETH_DIX) log_err("Failed to init eth-llc data."); @@ -1845,18 +1770,17 @@ int main(int argc, goto fail_data_init; } - if (ipcp_boot() < 0) { - log_err("Failed to boot IPCP."); - goto fail_boot; + if (ipcp_init(argc, argv, ð_ops, THIS_TYPE) < 0) { + log_err("Failed to initialize IPCP."); + goto fail_init; } - if (ipcp_create_r(0)) { - log_err("Failed to notify IRMd we are initialized."); - ipcp_set_state(IPCP_NULL); - goto fail_create_r; + if (ipcp_start() < 0) { + log_err("Failed to start IPCP."); + goto fail_start; } - ipcp_shutdown(); + ipcp_sigwait(); if (ipcp_get_state() == IPCP_SHUTDOWN) { for (i = 0; i < IPCP_ETH_WR_THR; ++i) @@ -1879,19 +1803,18 @@ int main(int argc, #endif } - eth_data_fini(); + ipcp_stop(); ipcp_fini(); + eth_data_fini(); + exit(EXIT_SUCCESS); - fail_create_r: - ipcp_shutdown(); - fail_boot: - eth_data_fini(); - fail_data_init: + fail_start: ipcp_fini(); fail_init: - ipcp_create_r(-1); + eth_data_fini(); + fail_data_init: exit(EXIT_FAILURE); } diff --git a/src/ipcpd/eth/llc.c b/src/ipcpd/eth/llc.c index d1e8bbdc..c900dcab 100644 --- a/src/ipcpd/eth/llc.c +++ b/src/ipcpd/eth/llc.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * IPC processes over Ethernet - LLC * diff --git a/src/ipcpd/ipcp.c b/src/ipcpd/ipcp.c index cd09a7cf..966c4920 100644 --- a/src/ipcpd/ipcp.c +++ b/src/ipcpd/ipcp.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * IPC process main loop * @@ -35,18 +35,21 @@ #define OUROBOROS_PREFIX "ipcpd/ipcp" #define IPCP_INFO "info" +#define ALLOC_TIMEOUT 50 /* ms */ +#include <ouroboros/bitmap.h> +#include <ouroboros/dev.h> +#include <ouroboros/errno.h> #include <ouroboros/hash.h> +#include <ouroboros/ipcp-dev.h> #include <ouroboros/logs.h> -#include <ouroboros/time_utils.h> -#include <ouroboros/utils.h> -#include <ouroboros/sockets.h> -#include <ouroboros/errno.h> -#include <ouroboros/dev.h> -#include <ouroboros/bitmap.h> #include <ouroboros/np1_flow.h> -#include <ouroboros/rib.h> +#include <ouroboros/protobuf.h> #include <ouroboros/pthread.h> +#include <ouroboros/rib.h> +#include <ouroboros/sockets.h> +#include <ouroboros/time.h> +#include <ouroboros/utils.h> #include "ipcp.h" @@ -173,15 +176,15 @@ static int ipcp_rib_readdir(char *** buf) while (info[i] != NULL) { (*buf)[i] = strdup(info[i]); - if (*buf == NULL) + if ((*buf)[i] == NULL) goto fail_dup; i++; } return i; fail_dup: - while (--i > 0) - free((*buf)[i]); + while (i > 0) + free((*buf)[--i]); fail: free(*buf); @@ -191,9 +194,13 @@ static int ipcp_rib_readdir(char *** buf) static int ipcp_rib_getattr(const char * path, struct rib_attr * attr) { - (void) path; + char buf[LAYER_NAME_SIZE + 2]; + struct timespec now; - attr->size = LAYER_NAME_SIZE; + clock_gettime(CLOCK_REALTIME_COARSE, &now); + + attr->size = ipcp_rib_read(path, buf, LAYER_NAME_SIZE + 2); + attr->mtime = now.tv_sec; return 0; } @@ -206,9 +213,7 @@ static struct rib_ops r_ops = { static void * acceptloop(void * o) { - int csockfd; - struct timeval tv = {(SOCKET_TIMEOUT / 1000), - (SOCKET_TIMEOUT % 1000) * 1000}; + int csockfd; (void) o; @@ -220,10 +225,6 @@ static void * acceptloop(void * o) if (csockfd < 0) continue; - if (setsockopt(csockfd, SOL_SOCKET, SO_RCVTIMEO, - (void *) &tv, sizeof(tv))) - log_warn("Failed to set timeout on socket."); - cmd = malloc(sizeof(*cmd)); if (cmd == NULL) { log_err("Out of memory"); @@ -260,28 +261,395 @@ static void * acceptloop(void * o) return (void *) 0; } +int ipcp_wait_flow_req_arr(const uint8_t * dst, + qosspec_t qs, + time_t mpl, + const buffer_t * data) +{ + struct timespec ts = TIMESPEC_INIT_MS(ALLOC_TIMEOUT); + struct timespec abstime; + int fd; + buffer_t hash; + + hash.data = (uint8_t *) dst; + hash.len = ipcp_dir_hash_len(); + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + + pthread_mutex_lock(&ipcpi.alloc_lock); + + while (ipcpi.alloc_id != -1 && ipcp_get_state() == IPCP_OPERATIONAL) { + ts_add(&abstime, &ts, &abstime); + pthread_cond_timedwait(&ipcpi.alloc_cond, + &ipcpi.alloc_lock, + &abstime); + } + + if (ipcp_get_state() != IPCP_OPERATIONAL) { + pthread_mutex_unlock(&ipcpi.alloc_lock); + log_err("Won't allocate over non-operational IPCP."); + return -EIPCPSTATE; + } + + assert(ipcpi.alloc_id == -1); + + fd = ipcp_flow_req_arr(&hash, qs, mpl, data); + if (fd < 0) { + pthread_mutex_unlock(&ipcpi.alloc_lock); + log_err("Failed to get fd for flow."); + return fd; + } + + ipcpi.alloc_id = fd; + pthread_cond_broadcast(&ipcpi.alloc_cond); + + pthread_mutex_unlock(&ipcpi.alloc_lock); + + return fd; + +} + +int ipcp_wait_flow_resp(const int fd) +{ + struct timespec ts = TIMESPEC_INIT_MS(ALLOC_TIMEOUT); + struct timespec abstime; + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + + pthread_mutex_lock(&ipcpi.alloc_lock); + + while (ipcpi.alloc_id != fd && ipcp_get_state() == IPCP_OPERATIONAL) { + ts_add(&abstime, &ts, &abstime); + pthread_cond_timedwait(&ipcpi.alloc_cond, + &ipcpi.alloc_lock, + &abstime); + } + + if (ipcp_get_state() != IPCP_OPERATIONAL) { + pthread_mutex_unlock(&ipcpi.alloc_lock); + return -1; + } + + assert(ipcpi.alloc_id == fd); + + ipcpi.alloc_id = -1; + pthread_cond_broadcast(&ipcpi.alloc_cond); + + pthread_mutex_unlock(&ipcpi.alloc_lock); + + return 0; +} + static void free_msg(void * o) { ipcp_msg__free_unpacked((ipcp_msg_t *) o, NULL); } + +static void do_bootstrap(ipcp_config_msg_t * conf_msg, + ipcp_msg_t * ret_msg) +{ + struct ipcp_config conf; + + log_info("Bootstrapping..."); + + if (ipcpi.ops->ipcp_bootstrap == NULL) { + log_err("Bootstrap unsupported."); + ret_msg->result = -ENOTSUP; + goto finish; + } + + if (ipcp_get_state() != IPCP_INIT) { + log_err("IPCP in wrong state."); + ret_msg->result = -EIPCPSTATE; + goto finish; + } + + conf = ipcp_config_msg_to_s(conf_msg); + ret_msg->result = ipcpi.ops->ipcp_bootstrap(&conf); + if (ret_msg->result == 0) { + ret_msg->layer_info = layer_info_s_to_msg(&conf.layer_info); + ipcp_set_state(IPCP_OPERATIONAL); + } + finish: + log_info("Finished bootstrapping: %d.", ret_msg->result); +} + +static void do_enroll(const char * dst, + ipcp_msg_t * ret_msg) +{ + struct layer_info info; + + log_info("Enrolling with %s...", dst); + + if (ipcpi.ops->ipcp_enroll == NULL) { + log_err("Enroll unsupported."); + ret_msg->result = -ENOTSUP; + goto finish; + } + + if (ipcp_get_state() != IPCP_INIT) { + log_err("IPCP in wrong state."); + ret_msg->result = -EIPCPSTATE; + goto finish; + } + + ret_msg->result = ipcpi.ops->ipcp_enroll(dst, &info); + if (ret_msg->result == 0) { + ret_msg->layer_info = layer_info_s_to_msg(&info); + ipcp_set_state(IPCP_OPERATIONAL); + } + finish: + log_info("Finished enrolling with %s: %d.", dst, ret_msg->result); +} + +static void do_connect(const char * dst, + const char * comp, + qosspec_t qs, + ipcp_msg_t * ret_msg) +{ + log_info("Connecting %s to %s...", comp, dst); + + if (ipcpi.ops->ipcp_connect == NULL) { + log_err("Connect unsupported."); + ret_msg->result = -ENOTSUP; + goto finish; + } + + ret_msg->result = ipcpi.ops->ipcp_connect(dst, comp, qs); + finish: + log_info("Finished connecting: %d.", ret_msg->result); +} + +static void do_disconnect(const char * dst, + const char * comp, + ipcp_msg_t * ret_msg) +{ + log_info("Disconnecting %s from %s...", comp, dst); + + if (ipcpi.ops->ipcp_disconnect == NULL) { + log_err("Disconnect unsupported."); + ret_msg->result = -ENOTSUP; + goto finish; + } + + ret_msg->result = ipcpi.ops->ipcp_disconnect(dst, comp); + + finish: + log_info("Finished disconnecting %s from %s: %d.", + comp, dst, ret_msg->result); +} + +static void do_reg(const uint8_t * hash, + ipcp_msg_t * ret_msg) +{ + + log_info("Registering " HASH_FMT32 "...", HASH_VAL32(hash)); + + if (ipcpi.ops->ipcp_reg == NULL) { + log_err("Registration unsupported."); + ret_msg->result = -ENOTSUP; + goto finish; + } + + ret_msg->result = ipcpi.ops->ipcp_reg(hash); + finish: + log_info("Finished registering " HASH_FMT32 " : %d.", + HASH_VAL32(hash), ret_msg->result); +} + +static void do_unreg(const uint8_t * hash, + ipcp_msg_t * ret_msg) +{ + log_info("Unregistering " HASH_FMT32 "...", HASH_VAL32(hash)); + + if (ipcpi.ops->ipcp_unreg == NULL) { + log_err("Unregistration unsupported."); + ret_msg->result = -ENOTSUP; + goto finish; + } + + ret_msg->result = ipcpi.ops->ipcp_unreg(hash); + finish: + log_info("Finished unregistering " HASH_FMT32 ": %d.", + HASH_VAL32(hash), ret_msg->result); +} + +static void do_query(const uint8_t * hash, + ipcp_msg_t * ret_msg) +{ + /* TODO: Log this operation when IRMd has internal caches. */ + + if (ipcpi.ops->ipcp_query == NULL) { + log_err("Directory query unsupported."); + ret_msg->result = -ENOTSUP; + return; + } + + if (ipcp_get_state() != IPCP_OPERATIONAL) { + log_err("IPCP in wrong state."); + ret_msg->result = -EIPCPSTATE; + return; + } + + ret_msg->result = ipcpi.ops->ipcp_query(hash); +} + +static void do_flow_alloc(pid_t pid, + int flow_id, + uint8_t * dst, + qosspec_t qs, + const buffer_t * data, + ipcp_msg_t * ret_msg) +{ + int fd; + + log_info("Allocating flow %d for %d to " HASH_FMT32 ".", + flow_id, pid, HASH_VAL32(dst)); + + if (ipcpi.ops->ipcp_flow_alloc == NULL) { + log_err("Flow allocation unsupported."); + ret_msg->result = -ENOTSUP; + goto finish; + } + + if (ipcp_get_state() != IPCP_OPERATIONAL) { + log_err("IPCP in wrong state."); + ret_msg->result = -EIPCPSTATE; + goto finish; + } + + fd = np1_flow_alloc(pid, flow_id); + if (fd < 0) { + log_err("Failed allocating n + 1 fd on flow_id %d: %d", + flow_id, fd); + ret_msg->result = -EFLOWDOWN; + goto finish; + } + + ret_msg->result = ipcpi.ops->ipcp_flow_alloc(fd, dst, qs, data); + finish: + log_info("Finished allocating flow %d to " HASH_FMT32 ": %d.", + flow_id, HASH_VAL32(dst), ret_msg->result); +} + + +static void do_flow_join(pid_t pid, + int flow_id, + const uint8_t * dst, + qosspec_t qs, + ipcp_msg_t * ret_msg) +{ + int fd; + + log_info("Joining layer " HASH_FMT32 ".", HASH_VAL32(dst)); + + if (ipcpi.ops->ipcp_flow_join == NULL) { + log_err("Broadcast unsupported."); + ret_msg->result = -ENOTSUP; + goto finish; + } + + if (ipcp_get_state() != IPCP_OPERATIONAL) { + log_err("IPCP in wrong state."); + ret_msg->result = -EIPCPSTATE; + goto finish; + } + + fd = np1_flow_alloc(pid, flow_id); + if (fd < 0) { + log_err("Failed allocating n + 1 fd on flow_id %d.", flow_id); + ret_msg->result = -1; + goto finish; + } + + ret_msg->result = ipcpi.ops->ipcp_flow_join(fd, dst, qs); + finish: + log_info("Finished joining layer " HASH_FMT32 ".", HASH_VAL32(dst)); +} + +static void do_flow_alloc_resp(int resp, + int flow_id, + const buffer_t * data, + ipcp_msg_t * ret_msg) +{ + int fd = -1; + + log_info("Responding %d to alloc on flow_id %d.", resp, flow_id); + + if (ipcpi.ops->ipcp_flow_alloc_resp == NULL) { + log_err("Flow_alloc_resp unsupported."); + ret_msg->result = -ENOTSUP; + goto finish; + } + + if (ipcp_get_state() != IPCP_OPERATIONAL) { + log_err("IPCP in wrong state."); + ret_msg->result = -EIPCPSTATE; + goto finish; + } + + if (resp == 0) { + fd = np1_flow_resp(flow_id); + if (fd < 0) { + log_warn("Flow_id %d is not known.", flow_id); + ret_msg->result = -1; + goto finish; + } + } + + ret_msg->result = ipcpi.ops->ipcp_flow_alloc_resp(fd, resp, data); + finish: + log_info("Finished responding to allocation request: %d", + ret_msg->result); +} + +static void do_flow_dealloc(int flow_id, + int timeo_sec, + ipcp_msg_t * ret_msg) +{ + int fd; + + log_info("Deallocating flow %d.", flow_id); + + if (ipcpi.ops->ipcp_flow_dealloc == NULL) { + log_err("Flow deallocation unsupported."); + ret_msg->result = -ENOTSUP; + goto finish; + } + + if (ipcp_get_state() != IPCP_OPERATIONAL) { + log_err("IPCP in wrong state."); + ret_msg->result = -EIPCPSTATE; + goto finish; + } + + fd = np1_flow_dealloc(flow_id, timeo_sec); + if (fd < 0) { + log_warn("Could not deallocate flow_id %d.", flow_id); + ret_msg->result = -1; + goto finish; + } + + ret_msg->result = ipcpi.ops->ipcp_flow_dealloc(fd); + finish: + log_info("Finished deallocating flow %d: %d.", + flow_id, ret_msg->result); +} + static void * mainloop(void * o) { int sfd; buffer_t buffer; - struct ipcp_config conf; - struct layer_info info; - ipcp_config_msg_t * conf_msg; ipcp_msg_t * msg; (void) o; while (true) { - ipcp_msg_t ret_msg = IPCP_MSG__INIT; - layer_info_msg_t layer_info = LAYER_INFO_MSG__INIT; - int fd = -1; - struct cmd * cmd; - qosspec_t qs; + ipcp_msg_t ret_msg = IPCP_MSG__INIT; + qosspec_t qs; + struct cmd * cmd; + buffer_t data; ret_msg.code = IPCP_MSG_CODE__IPCP_REPLY; @@ -312,329 +680,68 @@ static void * mainloop(void * o) pthread_cleanup_push(__cleanup_close_ptr, &sfd); pthread_cleanup_push(free_msg, msg); + ret_msg.has_result = true; + switch (msg->code) { case IPCP_MSG_CODE__IPCP_BOOTSTRAP: - ret_msg.has_result = true; - - if (ipcpi.ops->ipcp_bootstrap == NULL) { - log_err("Bootstrap unsupported."); - ret_msg.result = -ENOTSUP; - break; - } - - if (ipcp_get_state() != IPCP_INIT) { - log_err("IPCP in wrong state."); - ret_msg.result = -EIPCPSTATE; - break; - } - - conf_msg = msg->conf; - conf.type = conf_msg->ipcp_type; - strcpy(conf.layer_info.layer_name, - conf_msg->layer_info->layer_name); - - switch(conf_msg->ipcp_type) { - case IPCP_LOCAL: - break; - case IPCP_UNICAST: - conf.addr_size = conf_msg->addr_size; - conf.eid_size = conf_msg->eid_size; - conf.max_ttl = conf_msg->max_ttl; - conf.addr_auth_type = conf_msg->addr_auth_type; - conf.routing_type = conf_msg->routing_type; - conf.cong_avoid = conf_msg->cong_avoid; - break; - case IPCP_ETH_DIX: - conf.ethertype = conf_msg->ethertype; - /* FALLTHRU */ - case IPCP_ETH_LLC: - conf.dev = conf_msg->dev; - break; - case IPCP_UDP: - conf.ip_addr = conf_msg->ip_addr; - conf.dns_addr = conf_msg->dns_addr; - conf.port = conf_msg->port; - conf.layer_info.dir_hash_algo = HASH_MD5; - layer_info.dir_hash_algo = HASH_MD5; - break; - case IPCP_BROADCAST: - conf.layer_info.dir_hash_algo = HASH_SHA3_256; - layer_info.dir_hash_algo = HASH_SHA3_256; - break; - default: - log_err("Unknown IPCP type: %d.", - conf_msg->ipcp_type); - ret_msg.result = -EIPCP; - goto exit; /* break from outer switch/case */ - } - - /* UDP and broadcast use fixed hash algorithm. */ - if (conf_msg->ipcp_type != IPCP_UDP && - conf_msg->ipcp_type != IPCP_BROADCAST) { - switch(conf_msg->layer_info->dir_hash_algo) { - case DIR_HASH_SHA3_224: - conf.layer_info.dir_hash_algo = - HASH_SHA3_224; - break; - case DIR_HASH_SHA3_256: - conf.layer_info.dir_hash_algo = - HASH_SHA3_256; - break; - case DIR_HASH_SHA3_384: - conf.layer_info.dir_hash_algo = - HASH_SHA3_384; - break; - case DIR_HASH_SHA3_512: - conf.layer_info.dir_hash_algo = - HASH_SHA3_512; - break; - default: - assert(false); - } - - layer_info.dir_hash_algo = - conf.layer_info.dir_hash_algo; - } - - ret_msg.result = ipcpi.ops->ipcp_bootstrap(&conf); - if (ret_msg.result == 0) { - ret_msg.layer_info = &layer_info; - layer_info.layer_name = - conf.layer_info.layer_name; - } + do_bootstrap(msg->conf, &ret_msg); break; case IPCP_MSG_CODE__IPCP_ENROLL: - ret_msg.has_result = true; - - if (ipcpi.ops->ipcp_enroll == NULL) { - log_err("Enroll unsupported."); - ret_msg.result = -ENOTSUP; - break; - } - - if (ipcp_get_state() != IPCP_INIT) { - log_err("IPCP in wrong state."); - ret_msg.result = -EIPCPSTATE; - break; - } - - ret_msg.result = ipcpi.ops->ipcp_enroll(msg->dst, - &info); - if (ret_msg.result == 0) { - ret_msg.layer_info = &layer_info; - layer_info.dir_hash_algo = info.dir_hash_algo; - layer_info.layer_name = info.layer_name; - } + do_enroll(msg->dst, &ret_msg); break; case IPCP_MSG_CODE__IPCP_CONNECT: - ret_msg.has_result = true; - - if (ipcpi.ops->ipcp_connect == NULL) { - log_err("Connect unsupported."); - ret_msg.result = -ENOTSUP; - break; - } - - qs = msg_to_spec(msg->qosspec); - ret_msg.result = ipcpi.ops->ipcp_connect(msg->dst, - msg->comp, - qs); + qs = qos_spec_msg_to_s(msg->qosspec); + do_connect(msg->dst, msg->comp, qs, &ret_msg); break; case IPCP_MSG_CODE__IPCP_DISCONNECT: - ret_msg.has_result = true; - - if (ipcpi.ops->ipcp_disconnect == NULL) { - log_err("Disconnect unsupported."); - ret_msg.result = -ENOTSUP; - break; - } - - ret_msg.result = ipcpi.ops->ipcp_disconnect(msg->dst, - msg->comp); + do_disconnect(msg->dst, msg->comp, &ret_msg); break; case IPCP_MSG_CODE__IPCP_REG: - ret_msg.has_result = true; - - if (ipcpi.ops->ipcp_reg == NULL) { - log_err("Registration unsupported."); - ret_msg.result = -ENOTSUP; - break; - } - assert(msg->hash.len == ipcp_dir_hash_len()); - - ret_msg.result = - ipcpi.ops->ipcp_reg(msg->hash.data); + do_reg(msg->hash.data, &ret_msg); break; case IPCP_MSG_CODE__IPCP_UNREG: - ret_msg.has_result = true; - - if (ipcpi.ops->ipcp_unreg == NULL) { - log_err("Unregistration unsupported."); - ret_msg.result = -ENOTSUP; - break; - } - assert(msg->hash.len == ipcp_dir_hash_len()); - - ret_msg.result = - ipcpi.ops->ipcp_unreg(msg->hash.data); + do_unreg(msg->hash.data, &ret_msg); break; case IPCP_MSG_CODE__IPCP_QUERY: - ret_msg.has_result = true; - - if (ipcpi.ops->ipcp_query == NULL) { - log_err("Directory query unsupported."); - ret_msg.result = -ENOTSUP; - break; - } - assert(msg->hash.len == ipcp_dir_hash_len()); - - if (ipcp_get_state() != IPCP_OPERATIONAL) { - log_err("IPCP in wrong state."); - ret_msg.result = -EIPCPSTATE; - break; - } - - ret_msg.result = - ipcpi.ops->ipcp_query(msg->hash.data); + do_query(msg->hash.data, &ret_msg); break; case IPCP_MSG_CODE__IPCP_FLOW_ALLOC: - ret_msg.has_result = true; - - if (ipcpi.ops->ipcp_flow_alloc == NULL) { - log_err("Flow allocation unsupported."); - ret_msg.result = -ENOTSUP; - break; - } - assert(msg->hash.len == ipcp_dir_hash_len()); assert(msg->pk.len > 0 ? msg->pk.data != NULL : msg->pk.data == NULL); - - if (ipcp_get_state() != IPCP_OPERATIONAL) { - log_err("IPCP in wrong state."); - ret_msg.result = -EIPCPSTATE; - break; - } - - qs = msg_to_spec(msg->qosspec); - fd = np1_flow_alloc(msg->pid, - msg->flow_id, - qs); - if (fd < 0) { - log_err("Failed allocating fd on flow_id %d.", - msg->flow_id); - ret_msg.result = -1; - break; - } - - ret_msg.result = - ipcpi.ops->ipcp_flow_alloc(fd, - msg->hash.data, - qs, - msg->pk.data, - msg->pk.len); + data.len = msg->pk.len; + data.data = msg->pk.data; + qs = qos_spec_msg_to_s(msg->qosspec); + do_flow_alloc(msg->pid, msg->flow_id, + msg->hash.data, qs, + &data, &ret_msg); break; case IPCP_MSG_CODE__IPCP_FLOW_JOIN: - ret_msg.has_result = true; - - if (ipcpi.ops->ipcp_flow_join == NULL) { - log_err("Broadcast unsupported."); - ret_msg.result = -ENOTSUP; - break; - } - assert(msg->hash.len == ipcp_dir_hash_len()); - - if (ipcp_get_state() != IPCP_OPERATIONAL) { - log_err("IPCP in wrong state."); - ret_msg.result = -EIPCPSTATE; - break; - } - - qs = msg_to_spec(msg->qosspec); - fd = np1_flow_alloc(msg->pid, - msg->flow_id, - qs); - if (fd < 0) { - log_err("Failed allocating fd on flow_id %d.", - msg->flow_id); - ret_msg.result = -1; - break; - } - - ret_msg.result = - ipcpi.ops->ipcp_flow_join(fd, - msg->hash.data, - qs); + qs = qos_spec_msg_to_s(msg->qosspec); + do_flow_join(msg->pid, msg->flow_id, + msg->hash.data, qs, &ret_msg); break; case IPCP_MSG_CODE__IPCP_FLOW_ALLOC_RESP: - ret_msg.has_result = true; - if (ipcpi.ops->ipcp_flow_alloc_resp == NULL) { - log_err("Flow_alloc_resp unsupported."); - ret_msg.result = -ENOTSUP; - break; - } - - if (ipcp_get_state() != IPCP_OPERATIONAL) { - log_err("IPCP in wrong state."); - ret_msg.result = -EIPCPSTATE; - break; - } - - if (!msg->response) { - fd = np1_flow_resp(msg->flow_id); - if (fd < 0) { - log_warn("Port_id %d is not known.", - msg->flow_id); - ret_msg.result = -1; - break; - } - } - assert(msg->pk.len > 0 ? msg->pk.data != NULL - : msg->pk.data == NULL); - - ret_msg.result = - ipcpi.ops->ipcp_flow_alloc_resp(fd, - msg->response, - msg->pk.data, - msg->pk.len); + : msg->pk.data == NULL); + data.len = msg->pk.len; + data.data = msg->pk.data; + do_flow_alloc_resp(msg->response, msg->flow_id, + &data, &ret_msg); break; case IPCP_MSG_CODE__IPCP_FLOW_DEALLOC: - ret_msg.has_result = true; - if (ipcpi.ops->ipcp_flow_dealloc == NULL) { - log_err("Flow deallocation unsupported."); - ret_msg.result = -ENOTSUP; - break; - } - - if (ipcp_get_state() != IPCP_OPERATIONAL) { - log_err("IPCP in wrong state."); - ret_msg.result = -EIPCPSTATE; - break; - } - - fd = np1_flow_dealloc(msg->flow_id, msg->timeo_sec); - if (fd < 0) { - log_warn("Could not deallocate flow_id %d.", - msg->flow_id); - ret_msg.result = -1; - break; - } - - ret_msg.result = - ipcpi.ops->ipcp_flow_dealloc(fd); + do_flow_dealloc(msg->flow_id, msg->timeo_sec, &ret_msg); break; default: - ret_msg.has_result = true; - ret_msg.result = -1; - log_err("Don't know that message code"); + ret_msg.result = -1; + log_err("Unknown message code: %d.", msg->code); break; } - exit: + pthread_cleanup_pop(true); pthread_cleanup_pop(false); @@ -656,12 +763,16 @@ static void * mainloop(void * o) ipcp_msg__pack(&ret_msg, buffer.data); + if (ret_msg.layer_info != NULL) + layer_info_msg__free_unpacked(ret_msg.layer_info, NULL); + pthread_cleanup_push(__cleanup_close_ptr, &sfd); + pthread_cleanup_push(free, buffer.data) if (write(sfd, buffer.data, buffer.len) == -1) log_warn("Failed to send reply message"); - free(buffer.data); + pthread_cleanup_pop(true); pthread_cleanup_pop(true); tpm_inc(ipcpi.tpm); @@ -771,14 +882,32 @@ int ipcp_init(int argc, goto fail_rib_init; } + if (rib_reg(IPCP_INFO, &r_ops)) { + log_err("Failed to register rib."); + goto fail_rib_reg; + } + + ipcpi.tpm = tpm_create(IPCP_MIN_THREADS, IPCP_ADD_THREADS, + mainloop, NULL); + if (ipcpi.tpm == NULL) { + log_err("Failed to create threadpool manager."); + goto fail_tpm_create; + } + list_head_init(&ipcpi.cmds); ipcpi.alloc_id = -1; pthread_condattr_destroy(&cattr); + ipcp_set_state(IPCP_INIT); + return 0; + fail_tpm_create: + rib_unreg(IPCP_INFO); + fail_rib_reg: + rib_fini(); fail_rib_init: pthread_cond_destroy(&ipcpi.cmd_cond); fail_cmd_cond: @@ -801,50 +930,55 @@ int ipcp_init(int argc, return ret; } -int ipcp_boot() +int ipcp_start(void) { - sigset_t sigset; + sigset_t sigset; + struct ipcp_info info; + sigemptyset(&sigset); sigaddset(&sigset, SIGINT); sigaddset(&sigset, SIGQUIT); sigaddset(&sigset, SIGHUP); sigaddset(&sigset, SIGPIPE); - ipcpi.tpm = tpm_create(IPCP_MIN_THREADS, IPCP_ADD_THREADS, - mainloop, NULL); - if (ipcpi.tpm == NULL) - goto fail_tpm_create; - pthread_sigmask(SIG_BLOCK, &sigset, NULL); + info.pid = getpid(); + info.type = ipcpi.type; + strcpy(info.name, ipcpi.name); + info.state = IPCP_OPERATIONAL; + if (tpm_start(ipcpi.tpm)) goto fail_tpm_start; - ipcp_set_state(IPCP_INIT); - - if (rib_reg(IPCP_INFO, &r_ops)) - goto fail_rib_reg; - if (pthread_create(&ipcpi.acceptor, NULL, acceptloop, NULL)) { log_err("Failed to create acceptor thread."); - ipcp_set_state(IPCP_NULL); goto fail_acceptor; } - return 0; + info.state = IPCP_OPERATIONAL; + + if (ipcp_create_r(&info)) { + log_err("Failed to notify IRMd we are initialized."); + goto fail_create_r; + } + return 0; + fail_create_r: + pthread_cancel(ipcpi.acceptor); + pthread_join(ipcpi.acceptor, NULL); fail_acceptor: - rib_unreg(IPCP_INFO); - fail_rib_reg: tpm_stop(ipcpi.tpm); fail_tpm_start: tpm_destroy(ipcpi.tpm); - fail_tpm_create: + ipcp_set_state(IPCP_NULL); + info.state = IPCP_NULL; + ipcp_create_r(&info); return -1; } -void ipcp_shutdown() +void ipcp_sigwait(void) { siginfo_t info; @@ -898,19 +1032,25 @@ void ipcp_shutdown() continue; } } +} - pthread_cancel(ipcpi.acceptor); +void ipcp_stop(void) +{ + log_info("IPCP %d shutting down.", getpid()); + pthread_cancel(ipcpi.acceptor); pthread_join(ipcpi.acceptor, NULL); - tpm_stop(ipcpi.tpm); - tpm_destroy(ipcpi.tpm); - log_info("IPCP %d shutting down.", getpid()); + tpm_stop(ipcpi.tpm); } -void ipcp_fini() +void ipcp_fini(void) { + tpm_destroy(ipcpi.tpm); + + rib_unreg(IPCP_INFO); + rib_fini(); close(ipcpi.sockfd); @@ -941,7 +1081,7 @@ void ipcp_set_state(enum ipcp_state state) pthread_mutex_unlock(&ipcpi.state_mtx); } -enum ipcp_state ipcp_get_state() +enum ipcp_state ipcp_get_state(void) { enum ipcp_state state; @@ -954,37 +1094,6 @@ enum ipcp_state ipcp_get_state() return state; } -int ipcp_wait_state(enum ipcp_state state, - const struct timespec * timeout) -{ - struct timespec abstime; - int ret = 0; - - clock_gettime(PTHREAD_COND_CLOCK, &abstime); - ts_add(&abstime, timeout, &abstime); - - pthread_mutex_lock(&ipcpi.state_mtx); - - pthread_cleanup_push(__cleanup_mutex_unlock, &ipcpi.state_mtx); - - while (ipcpi.state != state - && ipcpi.state != IPCP_SHUTDOWN - && ipcpi.state != IPCP_NULL - && ret != -ETIMEDOUT) { - if (timeout == NULL) - ret = -pthread_cond_wait(&ipcpi.state_cond, - &ipcpi.state_mtx); - else - ret = -pthread_cond_timedwait(&ipcpi.state_cond, - &ipcpi.state_mtx, - &abstime); - } - - pthread_cleanup_pop(true); - - return ret; -} - void ipcp_lock_to_core(void) { #if defined(__linux__) && !defined(DISABLE_CORE_LOCK) diff --git a/src/ipcpd/ipcp.h b/src/ipcpd/ipcp.h index eff2ae12..aab490c7 100644 --- a/src/ipcpd/ipcp.h +++ b/src/ipcpd/ipcp.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * IPC process structure * @@ -26,20 +26,14 @@ #include <ouroboros/hash.h> #include <ouroboros/ipcp.h> #include <ouroboros/list.h> +#include <ouroboros/protobuf.h> +#include <ouroboros/qos.h> #include <ouroboros/sockets.h> #include <ouroboros/tpm.h> #include <pthread.h> #include <time.h> -enum ipcp_state { - IPCP_NULL = 0, - IPCP_INIT, - /* Layer name must be set for states below. */ - IPCP_OPERATIONAL, - IPCP_SHUTDOWN -}; - struct ipcp_ops { int (* ipcp_bootstrap)(const struct ipcp_config * conf); @@ -59,20 +53,18 @@ struct ipcp_ops { int (* ipcp_query)(const uint8_t * hash); - int (* ipcp_flow_alloc)(int fd, - const uint8_t * dst, - qosspec_t qs, - const void * data, - size_t len); + int (* ipcp_flow_alloc)(int fd, + const uint8_t * dst, + qosspec_t qs, + const buffer_t * data); int (* ipcp_flow_join)(int fd, const uint8_t * dst, qosspec_t qs); - int (* ipcp_flow_alloc_resp)(int fd, - int response, - const void * data, - size_t len); + int (* ipcp_flow_alloc_resp)(int fd, + int response, + const buffer_t * data); int (* ipcp_flow_dealloc)(int fd); }; @@ -85,7 +77,7 @@ extern struct ipcp { char * name; enum ipcp_type type; - char * layer_name; + char layer_name[LAYER_NAME_SIZE + 1]; uint64_t dt_addr; @@ -95,9 +87,8 @@ extern struct ipcp { int irmd_fd; enum ipcp_state state; - pthread_rwlock_t state_lock; - pthread_mutex_t state_mtx; pthread_cond_t state_cond; + pthread_mutex_t state_mtx; int sockfd; char * sock_path; @@ -120,9 +111,11 @@ int ipcp_init(int argc, struct ipcp_ops * ops, enum ipcp_type type); -int ipcp_boot(void); +int ipcp_start(void); -void ipcp_shutdown(void); +void ipcp_sigwait(void); + +void ipcp_stop(void); void ipcp_fini(void); @@ -130,12 +123,17 @@ void ipcp_set_state(enum ipcp_state state); enum ipcp_state ipcp_get_state(void); -int ipcp_wait_state(enum ipcp_state state, - const struct timespec * timeout); - int ipcp_parse_arg(int argc, char * argv[]); +/* Helper functions to handle races during flow allocation */ +int ipcp_wait_flow_req_arr(const uint8_t * dst, + qosspec_t qs, + time_t mpl, + const buffer_t * data); + +int ipcp_wait_flow_resp(const int fd); + /* Helper functions for directory entries, could be moved */ uint8_t * ipcp_hash_dup(const uint8_t * hash); diff --git a/src/ipcpd/local/CMakeLists.txt b/src/ipcpd/local/CMakeLists.txt index a84f4f1b..10fd0120 100644 --- a/src/ipcpd/local/CMakeLists.txt +++ b/src/ipcpd/local/CMakeLists.txt @@ -13,6 +13,8 @@ include_directories(${CMAKE_SOURCE_DIR}/include) include_directories(${CMAKE_BINARY_DIR}/include) set(IPCP_LOCAL_TARGET ipcpd-local CACHE INTERNAL "") +set(IPCP_LOCAL_MPL 2 CACHE STRING + "Default maximum packet lifetime for the Ethernet IPCPs, in seconds") set(LOCAL_SOURCES # Add source files here diff --git a/src/ipcpd/local/main.c b/src/ipcpd/local/main.c index 9c62c3cc..160e07e0 100644 --- a/src/ipcpd/local/main.c +++ b/src/ipcpd/local/main.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Local IPC process * @@ -48,8 +48,7 @@ #include <sys/wait.h> #include <assert.h> -#define THIS_TYPE IPCP_LOCAL -#define ALLOC_TIMEOUT 10 /* ms */ +#define THIS_TYPE IPCP_LOCAL struct ipcp ipcpi; @@ -72,34 +71,39 @@ static int local_data_init(void) local_data.flows = fset_create(); if (local_data.flows == NULL) - return -ENFILE; + goto fail_fset; local_data.fq = fqueue_create(); - if (local_data.fq == NULL) { - fset_destroy(local_data.flows); - return -ENOMEM; - } + if (local_data.fq == NULL) + goto fail_fqueue; local_data.shim_data = shim_data_create(); - if (local_data.shim_data == NULL) { - fqueue_destroy(local_data.fq); - fset_destroy(local_data.flows); - return -ENOMEM; - } + if (local_data.shim_data == NULL) + goto fail_shim_data; - pthread_rwlock_init(&local_data.lock, NULL); + if (pthread_rwlock_init(&local_data.lock, NULL) < 0) + goto fail_rwlock_init; return 0; + + fail_rwlock_init: + shim_data_destroy(local_data.shim_data); + fail_shim_data: + fqueue_destroy(local_data.fq); + fail_fqueue: + fset_destroy(local_data.flows); + fail_fset: + return -ENOMEM; } static void local_data_fini(void){ + pthread_rwlock_destroy(&local_data.lock); shim_data_destroy(local_data.shim_data); - fset_destroy(local_data.flows); fqueue_destroy(local_data.fq); - pthread_rwlock_destroy(&local_data.lock); + fset_destroy(local_data.flows); } -static void * ipcp_local_packet_loop(void * o) +static void * local_ipcp_packet_loop(void * o) { (void) o; @@ -135,54 +139,45 @@ static void * ipcp_local_packet_loop(void * o) return (void *) 0; } -static int ipcp_local_bootstrap(const struct ipcp_config * conf) +static int local_ipcp_bootstrap(const struct ipcp_config * conf) { assert(conf); assert(conf->type == THIS_TYPE); - ipcpi.dir_hash_algo = conf->layer_info.dir_hash_algo; - ipcpi.layer_name = strdup(conf->layer_info.layer_name); - if (ipcpi.layer_name == NULL) { - log_err("Failed to set layer name"); - return -ENOMEM; - } - - ipcp_set_state(IPCP_OPERATIONAL); + ipcpi.dir_hash_algo = (enum hash_algo) conf->layer_info.dir_hash_algo; + strcpy(ipcpi.layer_name,conf->layer_info.name); if (pthread_create(&local_data.packet_loop, NULL, - ipcp_local_packet_loop, NULL)) { + local_ipcp_packet_loop, NULL)) { + log_err("Failed to create pthread: %s", strerror(errno)); ipcp_set_state(IPCP_INIT); return -1; } - log_info("Bootstrapped local IPCP with pid %d.", getpid()); - return 0; } -static int ipcp_local_reg(const uint8_t * hash) +static int local_ipcp_reg(const uint8_t * hash) { if (shim_data_reg_add_entry(local_data.shim_data, hash)) { - log_dbg("Failed to add " HASH_FMT " to local registry.", - HASH_VAL(hash)); + log_err("Failed to add " HASH_FMT32 " to local registry.", + HASH_VAL32(hash)); return -1; } - log_info("Registered " HASH_FMT ".", HASH_VAL(hash)); - return 0; } -static int ipcp_local_unreg(const uint8_t * hash) +static int local_ipcp_unreg(const uint8_t * hash) { shim_data_reg_del_entry(local_data.shim_data, hash); - log_info("Unregistered " HASH_FMT ".", HASH_VAL(hash)); + log_info("Unregistered " HASH_FMT32 ".", HASH_VAL32(hash)); return 0; } -static int ipcp_local_query(const uint8_t * hash) +static int local_ipcp_query(const uint8_t * hash) { int ret; @@ -191,41 +186,19 @@ static int ipcp_local_query(const uint8_t * hash) return ret; } -static int ipcp_local_flow_alloc(int fd, - const uint8_t * dst, - qosspec_t qs, - const void * data, - size_t len) +static int local_ipcp_flow_alloc(int fd, + const uint8_t * dst, + qosspec_t qs, + const buffer_t * data) { - struct timespec ts = {0, ALLOC_TIMEOUT * MILLION}; - struct timespec abstime; - int out_fd = -1; + int out_fd = -1; - log_dbg("Allocating flow to " HASH_FMT " on fd %d.", HASH_VAL(dst), fd); + log_dbg("Allocating flow to " HASH_FMT32 " on fd %d.", + HASH_VAL32(dst), fd); assert(dst); - clock_gettime(PTHREAD_COND_CLOCK, &abstime); - - pthread_mutex_lock(&ipcpi.alloc_lock); - - while (ipcpi.alloc_id != -1 && ipcp_get_state() == IPCP_OPERATIONAL) { - ts_add(&abstime, &ts, &abstime); - pthread_cond_timedwait(&ipcpi.alloc_cond, - &ipcpi.alloc_lock, - &abstime); - } - - if (ipcp_get_state() != IPCP_OPERATIONAL) { - log_dbg("Won't allocate over non-operational IPCP."); - pthread_mutex_unlock(&ipcpi.alloc_lock); - return -1; - } - - assert(ipcpi.alloc_id == -1); - - out_fd = ipcp_flow_req_arr(dst, ipcp_dir_hash_len(), qs, data, len); + out_fd = ipcp_wait_flow_req_arr(dst, qs, IPCP_LOCAL_MPL, data); if (out_fd < 0) { - pthread_mutex_unlock(&ipcpi.alloc_lock); log_dbg("Flow allocation failed: %d", out_fd); return -1; } @@ -237,11 +210,6 @@ static int ipcp_local_flow_alloc(int fd, pthread_rwlock_unlock(&local_data.lock); - ipcpi.alloc_id = out_fd; - pthread_cond_broadcast(&ipcpi.alloc_cond); - - pthread_mutex_unlock(&ipcpi.alloc_lock); - fset_add(local_data.flows, fd); log_info("Pending local allocation request on fd %d.", fd); @@ -249,39 +217,21 @@ static int ipcp_local_flow_alloc(int fd, return 0; } -static int ipcp_local_flow_alloc_resp(int fd, - int response, - const void * data, - size_t len) +static int local_ipcp_flow_alloc_resp(int fd, + int response, + const buffer_t * data) { - struct timespec ts = {0, ALLOC_TIMEOUT * MILLION}; - struct timespec abstime; - int out_fd = -1; + struct timespec wait = TIMESPEC_INIT_MS(1); + time_t mpl = IPCP_LOCAL_MPL; + int out_fd; - clock_gettime(PTHREAD_COND_CLOCK, &abstime); - - pthread_mutex_lock(&ipcpi.alloc_lock); - - while (ipcpi.alloc_id != fd && ipcp_get_state() == IPCP_OPERATIONAL) { - ts_add(&abstime, &ts, &abstime); - pthread_cond_timedwait(&ipcpi.alloc_cond, - &ipcpi.alloc_lock, - &abstime); - } - - if (ipcp_get_state() != IPCP_OPERATIONAL) { - pthread_mutex_unlock(&ipcpi.alloc_lock); + if (ipcp_wait_flow_resp(fd) < 0) { + log_err("Failed waiting for IRMd response."); return -1; } - ipcpi.alloc_id = -1; - pthread_cond_broadcast(&ipcpi.alloc_cond); - - pthread_mutex_unlock(&ipcpi.alloc_lock); - - pthread_rwlock_wrlock(&local_data.lock); - - if (response) { + if (response < 0) { + pthread_rwlock_wrlock(&local_data.lock); if (local_data.in_out[fd] != -1) local_data.in_out[local_data.in_out[fd]] = fd; local_data.in_out[fd] = -1; @@ -289,25 +239,38 @@ static int ipcp_local_flow_alloc_resp(int fd, return 0; } + pthread_rwlock_rdlock(&local_data.lock); + out_fd = local_data.in_out[fd]; if (out_fd == -1) { pthread_rwlock_unlock(&local_data.lock); - return -1; + log_dbg("Potential race detected"); + nanosleep(&wait, NULL); + pthread_rwlock_rdlock(&local_data.lock); + out_fd = local_data.in_out[fd]; } pthread_rwlock_unlock(&local_data.lock); + if (out_fd == -1) { + log_err("Invalid out_fd."); + return -1; + } + fset_add(local_data.flows, fd); - if (ipcp_flow_alloc_reply(out_fd, response, data, len) < 0) + if (ipcp_flow_alloc_reply(out_fd, response, mpl, data) < 0) { + log_err("Failed to reply to allocation"); + fset_del(local_data.flows, fd); return -1; + } log_info("Flow allocation completed, fds (%d, %d).", out_fd, fd); return 0; } -static int ipcp_local_flow_dealloc(int fd) +static int local_ipcp_flow_dealloc(int fd) { assert(!(fd < 0)); @@ -321,7 +284,7 @@ static int ipcp_local_flow_dealloc(int fd) pthread_rwlock_unlock(&local_data.lock); - flow_dealloc(fd); + ipcp_flow_dealloc(fd); log_info("Flow with fd %d deallocated.", fd); @@ -329,60 +292,54 @@ static int ipcp_local_flow_dealloc(int fd) } static struct ipcp_ops local_ops = { - .ipcp_bootstrap = ipcp_local_bootstrap, + .ipcp_bootstrap = local_ipcp_bootstrap, .ipcp_enroll = NULL, .ipcp_connect = NULL, .ipcp_disconnect = NULL, - .ipcp_reg = ipcp_local_reg, - .ipcp_unreg = ipcp_local_unreg, - .ipcp_query = ipcp_local_query, - .ipcp_flow_alloc = ipcp_local_flow_alloc, + .ipcp_reg = local_ipcp_reg, + .ipcp_unreg = local_ipcp_unreg, + .ipcp_query = local_ipcp_query, + .ipcp_flow_alloc = local_ipcp_flow_alloc, .ipcp_flow_join = NULL, - .ipcp_flow_alloc_resp = ipcp_local_flow_alloc_resp, - .ipcp_flow_dealloc = ipcp_local_flow_dealloc + .ipcp_flow_alloc_resp = local_ipcp_flow_alloc_resp, + .ipcp_flow_dealloc = local_ipcp_flow_dealloc }; int main(int argc, char * argv[]) { - if (ipcp_init(argc, argv, &local_ops, THIS_TYPE) < 0) - goto fail_init; - if (local_data_init() < 0) { log_err("Failed to init local data."); goto fail_data_init; } - if (ipcp_boot() < 0) { - log_err("Failed to boot IPCP."); - goto fail_boot; - } + if (ipcp_init(argc, argv, &local_ops, THIS_TYPE) < 0) + goto fail_init; - if (ipcp_create_r(0)) { - log_err("Failed to notify IRMd we are initialized."); - goto fail_create_r; + if (ipcp_start() < 0) { + log_err("Failed to start IPCP."); + goto fail_start; } - ipcp_shutdown(); + ipcp_sigwait(); if (ipcp_get_state() == IPCP_SHUTDOWN) { pthread_cancel(local_data.packet_loop); pthread_join(local_data.packet_loop, NULL); } - local_data_fini(); + ipcp_stop(); ipcp_fini(); - exit(EXIT_SUCCESS); - fail_create_r: - ipcp_set_state(IPCP_NULL); - ipcp_shutdown(); - fail_boot: local_data_fini(); - fail_data_init: + + exit(EXIT_SUCCESS); + + fail_start: ipcp_fini(); fail_init: - ipcp_create_r(-1); + local_data_fini(); + fail_data_init: exit(EXIT_FAILURE); } diff --git a/src/ipcpd/shim-data.c b/src/ipcpd/shim-data.c index ade157ce..1fac63ac 100644 --- a/src/ipcpd/shim-data.c +++ b/src/ipcpd/shim-data.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * IPC process utilities * @@ -30,18 +30,18 @@ #define OUROBOROS_PREFIX "shim-data" -#include <ouroboros/endian.h> -#include <ouroboros/logs.h> -#include <ouroboros/list.h> -#include <ouroboros/time_utils.h> #include <ouroboros/errno.h> +#include <ouroboros/hash.h> +#include <ouroboros/list.h> +#include <ouroboros/logs.h> +#include <ouroboros/time.h> #include "shim-data.h" #include "ipcp.h" -#include <string.h> -#include <stdlib.h> #include <assert.h> +#include <stdlib.h> +#include <string.h> struct reg_entry { struct list_head list; @@ -139,9 +139,11 @@ static void dir_entry_destroy(struct dir_entry * entry) free(entry); } -struct shim_data * shim_data_create() +struct shim_data * shim_data_create(void) { - struct shim_data * sd = malloc(sizeof(*sd)); + struct shim_data * sd; + + sd = malloc(sizeof(*sd)); if (sd == NULL) return NULL; @@ -151,11 +153,23 @@ struct shim_data * shim_data_create() list_head_init(&sd->dir_queries); /* init the locks */ - pthread_rwlock_init(&sd->reg_lock, NULL); - pthread_rwlock_init(&sd->dir_lock, NULL); - pthread_mutex_init(&sd->dir_queries_lock, NULL); + if (pthread_rwlock_init(&sd->reg_lock, NULL) < 0) + goto fail_reg_lock_init; + + if (pthread_rwlock_init(&sd->dir_lock, NULL) < 0) + goto fail_dir_lock_init; + + if (pthread_mutex_init(&sd->dir_queries_lock, NULL) < 0) + goto fail_mutex_init; return sd; + + fail_mutex_init: + pthread_rwlock_destroy(&sd->dir_lock); + fail_dir_lock_init: + pthread_rwlock_destroy(&sd->reg_lock); + fail_reg_lock_init: + return NULL; } static void clear_registry(struct shim_data * data) @@ -283,8 +297,8 @@ int shim_data_reg_add_entry(struct shim_data * data, if (find_reg_entry_by_hash(data, hash)) { pthread_rwlock_unlock(&data->reg_lock); - log_dbg(HASH_FMT " was already in the directory.", - HASH_VAL(hash)); + log_dbg(HASH_FMT32 " was already in the directory.", + HASH_VAL32(hash)); return 0; } @@ -432,9 +446,9 @@ uint64_t shim_data_dir_get_addr(struct shim_data * data, pthread_rwlock_rdlock(&data->dir_lock); entry = find_dir_entry_any(data, hash); - if (entry == NULL) { pthread_rwlock_unlock(&data->dir_lock); + log_warn("No address for " HASH_FMT32 ".", HASH_VAL32(hash)); return 0; /* undefined behaviour, 0 may be a valid address */ } diff --git a/src/ipcpd/shim-data.h b/src/ipcpd/shim-data.h index 12a4b02e..372b4ea7 100644 --- a/src/ipcpd/shim-data.h +++ b/src/ipcpd/shim-data.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Utitilies for building IPC processes * diff --git a/src/ipcpd/udp/CMakeLists.txt b/src/ipcpd/udp/CMakeLists.txt index f1a29ef6..8ae5518e 100644 --- a/src/ipcpd/udp/CMakeLists.txt +++ b/src/ipcpd/udp/CMakeLists.txt @@ -58,6 +58,8 @@ set(IPCP_UDP_RD_THR 3 CACHE STRING "Number of reader threads in UDP IPCP") set(IPCP_UDP_WR_THR 3 CACHE STRING "Number of writer threads in UDP IPCP") +set(IPCP_UDP_MPL 60 CACHE STRING + "Default maximum packet lifetime for the UDP IPCP, in seconds") include(AddCompileFlags) if (CMAKE_BUILD_TYPE MATCHES "Debug*") diff --git a/src/ipcpd/udp/main.c b/src/ipcpd/udp/main.c index 6a3fb24a..2e8d84ce 100644 --- a/src/ipcpd/udp/main.c +++ b/src/ipcpd/udp/main.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * IPC process over UDP * @@ -31,6 +31,7 @@ #define OUROBOROS_PREFIX "ipcpd/udp" #include <ouroboros/bitmap.h> +#include <ouroboros/endian.h> #include <ouroboros/hash.h> #include <ouroboros/list.h> #include <ouroboros/utils.h> @@ -65,7 +66,6 @@ #define IPCP_UDP_BUF_SIZE 8980 #define IPCP_UDP_MSG_SIZE 8980 #define DNS_TTL 86400 -#define FD_UPDATE_TIMEOUT 100 /* microseconds */ #define SADDR ((struct sockaddr *) &udp_data.s_saddr) #define SADDR_SIZE (sizeof(udp_data.s_saddr)) @@ -98,7 +98,9 @@ struct mgmt_msg { uint32_t loss; uint32_t ber; uint32_t max_gap; + uint32_t timeout; uint16_t cypher_s; + } __attribute__((packed)); struct mgmt_frame { @@ -138,12 +140,18 @@ struct { static int udp_data_init(void) { - int i; + int i; + pthread_condattr_t cattr; if (pthread_rwlock_init(&udp_data.flows_lock, NULL)) goto fail_rwlock_init; - if (pthread_cond_init(&udp_data.mgmt_cond, NULL)) + if (pthread_condattr_init(&cattr)) + goto fail_condattr; +#ifndef __APPLE__ + pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK); +#endif + if (pthread_cond_init(&udp_data.mgmt_cond, &cattr)) goto fail_mgmt_cond; if (pthread_mutex_init(&udp_data.mgmt_lock, NULL)) @@ -160,9 +168,12 @@ static int udp_data_init(void) if (udp_data.shim_data == NULL) goto fail_data; + pthread_condattr_destroy(&cattr); + list_head_init(&udp_data.mgmt_frames); return 0; + fail_data: fset_destroy(udp_data.np1_flows); fail_fset: @@ -170,6 +181,8 @@ static int udp_data_init(void) fail_mgmt_lock: pthread_cond_destroy(&udp_data.mgmt_cond); fail_mgmt_cond: + pthread_condattr_destroy(&cattr); + fail_condattr: pthread_rwlock_destroy(&udp_data.flows_lock); fail_rwlock_init: return -1; @@ -186,22 +199,21 @@ static void udp_data_fini(void) pthread_mutex_destroy(&udp_data.mgmt_lock); } -static int ipcp_udp_port_alloc(const struct sockaddr_in * r_saddr, +static int udp_ipcp_port_alloc(const struct sockaddr_in * r_saddr, uint32_t s_eid, const uint8_t * dst, qosspec_t qs, - const void * data, - size_t dlen) + const buffer_t * data) { uint8_t * buf; struct mgmt_msg * msg; size_t len; - assert(dlen > 0 ? data != NULL : data == NULL); + assert(data->len > 0 ? data->data != NULL : data->data == NULL); len = sizeof(*msg) + ipcp_dir_hash_len(); - buf = malloc(len + dlen); + buf = malloc(len + data->len); if (buf == NULL) return -1; @@ -217,11 +229,13 @@ static int ipcp_udp_port_alloc(const struct sockaddr_in * r_saddr, msg->in_order = qs.in_order; msg->max_gap = hton32(qs.max_gap); msg->cypher_s = hton16(qs.cypher_s); + msg->timeout = hton32(qs.timeout); memcpy(msg + 1, dst, ipcp_dir_hash_len()); - memcpy(buf + len, data, dlen); + if (data->len > 0) + memcpy(buf + len, data->data, data->len); - if (sendto(udp_data.s_fd, msg, len + dlen, + if (sendto(udp_data.s_fd, msg, len + data->len, SENDTO_FLAGS, (const struct sockaddr *) r_saddr, sizeof(*r_saddr)) < 0) { free(buf); @@ -233,16 +247,15 @@ static int ipcp_udp_port_alloc(const struct sockaddr_in * r_saddr, return 0; } -static int ipcp_udp_port_alloc_resp(const struct sockaddr_in * r_saddr, +static int udp_ipcp_port_alloc_resp(const struct sockaddr_in * r_saddr, uint32_t s_eid, uint32_t d_eid, int8_t response, - const void * data, - size_t len) + const buffer_t * data) { struct mgmt_msg * msg; - msg = malloc(sizeof(*msg) + len); + msg = malloc(sizeof(*msg) + data->len); if (msg == NULL) return -1; @@ -252,9 +265,10 @@ static int ipcp_udp_port_alloc_resp(const struct sockaddr_in * r_saddr, msg->d_eid = hton32(d_eid); msg->response = response; - memcpy(msg + 1, data, len); + if (data->len > 0) + memcpy(msg + 1, data->data, data->len); - if (sendto(udp_data.s_fd, msg, sizeof(*msg) + len, + if (sendto(udp_data.s_fd, msg, sizeof(*msg) + data->len, SENDTO_FLAGS, (const struct sockaddr *) r_saddr, sizeof(*r_saddr)) < 0 ) { free(msg); @@ -266,37 +280,16 @@ static int ipcp_udp_port_alloc_resp(const struct sockaddr_in * r_saddr, return 0; } -static int ipcp_udp_port_req(struct sockaddr_in * c_saddr, +static int udp_ipcp_port_req(struct sockaddr_in * c_saddr, int d_eid, const uint8_t * dst, qosspec_t qs, - const void * data, - size_t len) + const buffer_t * data) { - struct timespec ts = {0, FD_UPDATE_TIMEOUT * 1000}; - struct timespec abstime; - int fd; - - clock_gettime(PTHREAD_COND_CLOCK, &abstime); - - pthread_mutex_lock(&ipcpi.alloc_lock); - - while (ipcpi.alloc_id != -1 && ipcp_get_state() == IPCP_OPERATIONAL) { - ts_add(&abstime, &ts, &abstime); - pthread_cond_timedwait(&ipcpi.alloc_cond, &ipcpi.alloc_lock, - &abstime); - } - - if (ipcp_get_state() != IPCP_OPERATIONAL) { - log_dbg("Won't allocate over non-operational IPCP."); - pthread_mutex_unlock(&ipcpi.alloc_lock); - return -1; - } + int fd; - /* reply to IRM */ - fd = ipcp_flow_req_arr(dst, ipcp_dir_hash_len(), qs, data, len); + fd = ipcp_wait_flow_req_arr(dst, qs, IPCP_UDP_MPL, data); if (fd < 0) { - pthread_mutex_unlock(&ipcpi.alloc_lock); log_err("Could not get new flow from IRMd."); return -1; } @@ -308,30 +301,26 @@ static int ipcp_udp_port_req(struct sockaddr_in * c_saddr, pthread_rwlock_unlock(&udp_data.flows_lock); - ipcpi.alloc_id = fd; - pthread_cond_broadcast(&ipcpi.alloc_cond); - - pthread_mutex_unlock(&ipcpi.alloc_lock); - log_dbg("Pending allocation request, fd %d, remote eid %d.", fd, d_eid); return 0; } -static int ipcp_udp_port_alloc_reply(const struct sockaddr_in * saddr, +static int udp_ipcp_port_alloc_reply(const struct sockaddr_in * saddr, uint32_t s_eid, uint32_t d_eid, int8_t response, - const void * data, - size_t len) + const buffer_t * data) { + time_t mpl = IPCP_UDP_MPL; + pthread_rwlock_wrlock(&udp_data.flows_lock); if (memcmp(&udp_data.fd_to_uf[s_eid].r_saddr, saddr, sizeof(*saddr))) { pthread_rwlock_unlock(&udp_data.flows_lock); - log_warn("Flow allocation reply for %u from wrong source.", - s_eid); + log_err("Flow allocation reply for %u from wrong source.", + s_eid); return -1; } @@ -340,8 +329,8 @@ static int ipcp_udp_port_alloc_reply(const struct sockaddr_in * saddr, pthread_rwlock_unlock(&udp_data.flows_lock); - if (ipcp_flow_alloc_reply(s_eid, response, data, len) < 0) { - log_dbg("Failed to reply to flow allocation."); + if (ipcp_flow_alloc_reply(s_eid, response, mpl, data) < 0) { + log_err("Failed to reply to flow allocation."); return -1; } @@ -351,13 +340,14 @@ static int ipcp_udp_port_alloc_reply(const struct sockaddr_in * saddr, return 0; } -static int ipcp_udp_mgmt_frame(const uint8_t * buf, +static int udp_ipcp_mgmt_frame(const uint8_t * buf, size_t len, struct sockaddr_in c_saddr) { struct mgmt_msg * msg; size_t msg_len; qosspec_t qs; + buffer_t data; msg = (struct mgmt_msg *) buf; @@ -367,6 +357,10 @@ static int ipcp_udp_mgmt_frame(const uint8_t * buf, assert(len >= msg_len); + data.len = len - msg_len; + data.data = (uint8_t *) buf + msg_len; + + qs.delay = ntoh32(msg->delay); qs.bandwidth = ntoh64(msg->bandwidth); qs.availability = msg->availability; @@ -375,27 +369,29 @@ static int ipcp_udp_mgmt_frame(const uint8_t * buf, qs.in_order = msg->in_order; qs.max_gap = ntoh32(msg->max_gap); qs.cypher_s = ntoh16(msg->cypher_s); + qs.timeout = ntoh32(msg->timeout); - return ipcp_udp_port_req(&c_saddr, ntoh32(msg->s_eid), + return udp_ipcp_port_req(&c_saddr, ntoh32(msg->s_eid), (uint8_t *) (msg + 1), qs, - buf + msg_len, - len - msg_len); + &data); case FLOW_REPLY: assert(len >= sizeof(*msg)); - return ipcp_udp_port_alloc_reply(&c_saddr, + data.len = len - sizeof(*msg); + data.data = (uint8_t *) buf + sizeof(*msg); + + return udp_ipcp_port_alloc_reply(&c_saddr, ntoh32(msg->s_eid), ntoh32(msg->d_eid), msg->response, - buf + sizeof(*msg), - len - sizeof(*msg)); + &data); default: log_err("Unknown message received %d.", msg->code); return -1; } } -static void * ipcp_udp_mgmt_handler(void * o) +static void * udp_ipcp_mgmt_handler(void * o) { (void) o; @@ -417,7 +413,7 @@ static void * ipcp_udp_mgmt_handler(void * o) pthread_mutex_unlock(&udp_data.mgmt_lock); - ipcp_udp_mgmt_frame(frame->buf, frame->len, frame->r_saddr); + udp_ipcp_mgmt_frame(frame->buf, frame->len, frame->r_saddr); free(frame); } @@ -427,7 +423,7 @@ static void * ipcp_udp_mgmt_handler(void * o) return (void *) 0; } -static void * ipcp_udp_packet_reader(void * o) +static void * udp_ipcp_packet_reader(void * o) { uint8_t buf[IPCP_UDP_MAX_PACKET_SIZE]; uint8_t * data; @@ -437,13 +433,17 @@ static void * ipcp_udp_packet_reader(void * o) (void) o; + ipcp_lock_to_core(); + data = buf + sizeof(uint32_t); eid_p = (uint32_t *) buf; while (true) { - struct mgmt_frame * frame; - struct sockaddr_in r_saddr; - socklen_t len; + struct mgmt_frame * frame; + struct sockaddr_in r_saddr; + socklen_t len; + struct shm_du_buff * sdb; + uint8_t * head; len = sizeof(r_saddr); @@ -484,10 +484,18 @@ static void * ipcp_udp_packet_reader(void * o) continue; } - flow_write(eid, data, n - sizeof(eid)); + n-= sizeof(eid); + + if (ipcp_sdb_reserve(&sdb, n)) + continue; + + head = shm_du_buff_head(sdb); + memcpy(head, data, n); + if (np1_flow_write(eid, sdb) < 0) + ipcp_sdb_release(sdb); } - return 0; + return (void *) 0; } static void cleanup_fqueue(void * fq) @@ -500,7 +508,7 @@ static void cleanup_sdb(void * sdb) ipcp_sdb_release((struct shm_du_buff *) sdb); } -static void * ipcp_udp_packet_writer(void * o) +static void * udp_ipcp_packet_writer(void * o) { fqueue_t * fq; @@ -527,12 +535,12 @@ static void * ipcp_udp_packet_writer(void * o) if (fqueue_type(fq) != FLOW_PKT) continue; - if (ipcp_flow_read(fd, &sdb)) { + if (np1_flow_read(fd, &sdb)) { log_dbg("Bad read from fd %d.", fd); continue; } - len = shm_du_buff_tail(sdb) - shm_du_buff_head(sdb); + len = shm_du_buff_len(sdb); if (len > IPCP_UDP_MAX_PACKET_SIZE) { log_dbg("Packet length exceeds MTU."); ipcp_sdb_release(sdb); @@ -572,37 +580,36 @@ static void * ipcp_udp_packet_writer(void * o) return (void *) 1; } -static int ipcp_udp_bootstrap(const struct ipcp_config * conf) +static const char * inet4_ntop(const void * addr, + char * buf) +{ + return inet_ntop(AF_INET, addr, buf, INET_ADDRSTRLEN); +} + +static int udp_ipcp_bootstrap(const struct ipcp_config * conf) { char ipstr[INET_ADDRSTRLEN]; char dnsstr[INET_ADDRSTRLEN]; - char portstr[128]; /* port is max 64535 = 5 chars */ int i = 1; assert(conf); assert(conf->type == THIS_TYPE); - ipcpi.dir_hash_algo = conf->layer_info.dir_hash_algo; - ipcpi.layer_name = strdup(conf->layer_info.layer_name); - if (ipcpi.layer_name == NULL) { - log_err("Failed to set layer name"); - return -ENOMEM; - } + ipcpi.dir_hash_algo = HASH_MD5; + strcpy(ipcpi.layer_name, conf->layer_info.name); - if (inet_ntop(AF_INET, &conf->ip_addr, ipstr, INET_ADDRSTRLEN) - == NULL) { - log_err("Failed to convert IP address"); + if (inet4_ntop(&conf->udp.ip_addr, ipstr) == NULL) { + log_err("Failed to convert IP address."); return -1; } - if (conf->dns_addr != 0) { - if (inet_ntop(AF_INET, &conf->dns_addr, dnsstr, INET_ADDRSTRLEN) - == NULL) { - log_err("Failed to convert DNS address"); + if (conf->udp.dns_addr != 0) { + if (inet4_ntop(&conf->udp.dns_addr, dnsstr) == NULL) { + log_err("Failed to convert DNS address."); return -1; } #ifndef HAVE_DDNS - log_warn("DNS disabled at compile time, address ignored"); + log_warn("DNS disabled at compile time, address ignored."); #endif } else { strcpy(dnsstr, "not set"); @@ -617,46 +624,46 @@ static int ipcp_udp_bootstrap(const struct ipcp_config * conf) memset((char *) &udp_data.s_saddr, 0, sizeof(udp_data.s_saddr)); udp_data.s_saddr.sin_family = AF_INET; - udp_data.s_saddr.sin_addr.s_addr = conf->ip_addr; - udp_data.s_saddr.sin_port = htons(conf->port); + udp_data.s_saddr.sin_addr.s_addr = conf->udp.ip_addr; + udp_data.s_saddr.sin_port = htons(conf->udp.port); if (bind(udp_data.s_fd, SADDR, SADDR_SIZE) < 0) { - log_err("Couldn't bind to %s.", ipstr); + log_err("Couldn't bind to %s:%d. %s.", + ipstr, conf->udp.port, strerror(errno)); goto fail_bind; } - udp_data.dns_addr = conf->dns_addr; - - ipcp_set_state(IPCP_OPERATIONAL); + udp_data.dns_addr = conf->udp.dns_addr; if (pthread_create(&udp_data.mgmt_handler, NULL, - ipcp_udp_mgmt_handler, NULL)) { - ipcp_set_state(IPCP_INIT); + udp_ipcp_mgmt_handler, NULL)) { + log_err("Failed to create management thread."); goto fail_bind; } for (i = 0; i < IPCP_UDP_RD_THR; ++i) { if (pthread_create(&udp_data.packet_reader[i], NULL, - ipcp_udp_packet_reader, NULL)) { - ipcp_set_state(IPCP_INIT); + udp_ipcp_packet_reader, NULL)) { + log_err("Failed to create reader thread."); goto fail_packet_reader; } } for (i = 0; i < IPCP_UDP_WR_THR; ++i) { if (pthread_create(&udp_data.packet_writer[i], NULL, - ipcp_udp_packet_writer, NULL)) { - ipcp_set_state(IPCP_INIT); + udp_ipcp_packet_writer, NULL)) { + log_err("Failed to create writer thread."); goto fail_packet_writer; } } - sprintf(portstr, "%d", conf->port); - log_dbg("Bootstrapped IPCP over UDP with pid %d.", getpid()); log_dbg("Bound to IP address %s.", ipstr); - log_dbg("Using port %u.", conf->port); - log_dbg("DNS server address is %s.", dnsstr); + log_dbg("Using port %u.", conf->udp.port); + if (conf->udp.dns_addr != 0) + log_dbg("DNS server address is %s.", dnsstr); + else + log_dbg("DNS server not in use."); return 0; @@ -684,20 +691,22 @@ static int ipcp_udp_bootstrap(const struct ipcp_config * conf) /* NOTE: Disgusted with this crap */ static int ddns_send(char * cmd) { - pid_t pid = -1; + pid_t pid; int wstatus; int pipe_fd[2]; char * argv[] = {NSUPDATE_EXEC, 0}; char * envp[] = {0}; if (pipe(pipe_fd)) { - log_err("Failed to create pipe."); + log_err("Failed to create pipe: %s.", strerror(errno)); return -1; } pid = fork(); if (pid == -1) { - log_err("Failed to fork."); + log_err("Failed to fork: %s.", strerror(errno)); + close(pipe_fd[0]); + close(pipe_fd[1]); return -1; } @@ -705,12 +714,15 @@ static int ddns_send(char * cmd) close(pipe_fd[1]); dup2(pipe_fd[0], 0); execve(argv[0], &argv[0], envp); + log_err("Failed to execute: %s", strerror(errno)); + exit(1); } close(pipe_fd[0]); if (write(pipe_fd[1], cmd, strlen(cmd)) == -1) { - log_err("Failed to communicate with nsupdate."); + log_err("Failed to communicate with nsupdate: %s.", + strerror(errno)); close(pipe_fd[1]); return -1; } @@ -740,18 +752,20 @@ static uint32_t ddns_resolve(char * name, char * addr_str = "Address:"; uint32_t ip_addr = 0; - if (inet_ntop(AF_INET, &dns_addr, dnsstr, INET_ADDRSTRLEN) == NULL) + if (inet4_ntop(&dns_addr, dnsstr) == NULL) return 0; if (pipe(pipe_fd)) { - log_err("Failed to create pipe."); + log_err("Failed to create pipe: %s.", strerror(errno)); return 0; } pid = fork(); if (pid == -1) { - log_err("Failed to fork."); - return 0; + log_err("Failed to fork: %s.", strerror(errno)); + close(pipe_fd[0]); + close(pipe_fd[1]); + return -1; } if (pid == 0) { @@ -761,11 +775,13 @@ static uint32_t ddns_resolve(char * name, close(pipe_fd[0]); dup2(pipe_fd[1], 1); execve(argv[0], &argv[0], envp); + log_err("Failed to execute: %s", strerror(errno)); + exit(1); } close(pipe_fd[1]); - count = read(pipe_fd[0], buf, IPCP_UDP_BUF_SIZE); + count = read(pipe_fd[0], buf, IPCP_UDP_BUF_SIZE - 1); if (count <= 0) { log_err("Failed to communicate with nslookup."); close(pipe_fd[0]); @@ -776,7 +792,7 @@ static uint32_t ddns_resolve(char * name, waitpid(pid, &wstatus, 0); if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0 && - count != IPCP_UDP_BUF_SIZE) + count != IPCP_UDP_BUF_SIZE - 1) log_dbg("Succesfully communicated with nslookup."); else log_err("Failed to resolve DNS address."); @@ -802,7 +818,7 @@ static uint32_t ddns_resolve(char * name, } #endif -static int ipcp_udp_reg(const uint8_t * hash) +static int udp_ipcp_reg(const uint8_t * hash) { #ifdef HAVE_DDNS char ipstr[INET_ADDRSTRLEN]; @@ -814,16 +830,18 @@ static int ipcp_udp_reg(const uint8_t * hash) char * hashstr; hashstr = malloc(ipcp_dir_hash_strlen() + 1); - if (hashstr == NULL) + if (hashstr == NULL) { + log_err("Failed to malloc hashstr."); return -1; + } assert(hash); ipcp_hash_str(hashstr, hash); if (shim_data_reg_add_entry(udp_data.shim_data, hash)) { - log_err("Failed to add " HASH_FMT " to local registry.", - HASH_VAL(hash)); + log_err("Failed to add " HASH_FMT32 " to local registry.", + HASH_VAL32(hash)); free(hashstr); return -1; } @@ -836,14 +854,14 @@ static int ipcp_udp_reg(const uint8_t * hash) if (dns_addr != 0) { ip_addr = udp_data.s_saddr.sin_addr.s_addr; - if (inet_ntop(AF_INET, &ip_addr, - ipstr, INET_ADDRSTRLEN) == NULL) { + if (inet4_ntop(&ip_addr, ipstr) == NULL) { + log_err("Failed to convert IP address to string."); free(hashstr); return -1; } - if (inet_ntop(AF_INET, &dns_addr, - dnsstr, INET_ADDRSTRLEN) == NULL) { + if (inet4_ntop(&dns_addr, dnsstr) == NULL) { + log_err("Failed to convert DNS address to string."); free(hashstr); return -1; } @@ -852,20 +870,19 @@ static int ipcp_udp_reg(const uint8_t * hash) dnsstr, hashstr, DNS_TTL, ipstr); if (ddns_send(cmd)) { + log_err("Failed to send DDNS message."); shim_data_reg_del_entry(udp_data.shim_data, hash); free(hashstr); return -1; } } #endif - log_dbg("Registered " HASH_FMT ".", HASH_VAL(hash)); - free(hashstr); return 0; } -static int ipcp_udp_unreg(const uint8_t * hash) +static int udp_ipcp_unreg(const uint8_t * hash) { #ifdef HAVE_DDNS char dnsstr[INET_ADDRSTRLEN]; @@ -878,8 +895,10 @@ static int ipcp_udp_unreg(const uint8_t * hash) assert(hash); hashstr = malloc(ipcp_dir_hash_strlen() + 1); - if (hashstr == NULL) + if (hashstr == NULL) { + log_err("Failed to malloc hashstr."); return -1; + } ipcp_hash_str(hashstr, hash); @@ -889,8 +908,8 @@ static int ipcp_udp_unreg(const uint8_t * hash) dns_addr = udp_data.dns_addr; if (dns_addr != 0) { - if (inet_ntop(AF_INET, &dns_addr, dnsstr, INET_ADDRSTRLEN) - == NULL) { + if (inet4_ntop(&dns_addr, dnsstr) == NULL) { + log_err("Failed to convert DNS address to string."); free(hashstr); return -1; } @@ -903,14 +922,12 @@ static int ipcp_udp_unreg(const uint8_t * hash) shim_data_reg_del_entry(udp_data.shim_data, hash); - log_dbg("Unregistered " HASH_FMT ".", HASH_VAL(hash)); - free(hashstr); return 0; } -static int ipcp_udp_query(const uint8_t * hash) +static int udp_ipcp_query(const uint8_t * hash) { uint32_t ip_addr = 0; char * hashstr; @@ -921,8 +938,10 @@ static int ipcp_udp_query(const uint8_t * hash) assert(hash); hashstr = malloc(ipcp_dir_hash_strlen() + 1); - if (hashstr == NULL) + if (hashstr == NULL) { + log_err("Failed to malloc hashstr."); return -ENOMEM; + } ipcp_hash_str(hashstr, hash); @@ -937,7 +956,7 @@ static int ipcp_udp_query(const uint8_t * hash) if (dns_addr != 0) { ip_addr = ddns_resolve(hashstr, dns_addr); if (ip_addr == 0) { - log_dbg("Could not resolve %s.", hashstr); + log_err("Could not resolve %s.", hashstr); free(hashstr); return -1; } @@ -945,7 +964,7 @@ static int ipcp_udp_query(const uint8_t * hash) #endif h = gethostbyname(hashstr); if (h == NULL) { - log_dbg("Could not resolve %s.", hashstr); + log_err("Could not resolve %s.", hashstr); free(hashstr); return -1; } @@ -966,38 +985,40 @@ static int ipcp_udp_query(const uint8_t * hash) return 0; } -static int ipcp_udp_flow_alloc(int fd, - const uint8_t * dst, - qosspec_t qs, - const void * data, - size_t len) +static int udp_ipcp_flow_alloc(int fd, + const uint8_t * dst, + qosspec_t qs, + const buffer_t * data) { struct sockaddr_in r_saddr; /* Server address */ uint32_t ip_addr = 0; char ipstr[INET_ADDRSTRLEN]; - log_dbg("Allocating flow to " HASH_FMT ".", HASH_VAL(dst)); - (void) qs; assert(dst); if (!shim_data_dir_has(udp_data.shim_data, dst)) { - log_dbg("Could not resolve destination."); + log_err("Could not resolve destination."); return -1; } ip_addr = (uint32_t) shim_data_dir_get_addr(udp_data.shim_data, dst); - inet_ntop(AF_INET, &ip_addr, ipstr, INET_ADDRSTRLEN); - log_dbg("Destination UDP ipcp resolved at %s.", ipstr); + if (inet4_ntop(&ip_addr, ipstr) == NULL) { + log_err("Could not convert IP address."); + return -1; + } + + log_dbg("Destination " HASH_FMT32 " resolved at IP %s.", + HASH_VAL32(dst), ipstr); memset((char *) &r_saddr, 0, sizeof(r_saddr)); r_saddr.sin_family = AF_INET; r_saddr.sin_addr.s_addr = ip_addr; r_saddr.sin_port = udp_data.s_saddr.sin_port; - if (ipcp_udp_port_alloc(&r_saddr, fd, dst, qs, data, len) < 0) { + if (udp_ipcp_port_alloc(&r_saddr, fd, dst, qs, data) < 0) { log_err("Could not allocate port."); return -1; } @@ -1011,45 +1032,21 @@ static int ipcp_udp_flow_alloc(int fd, fset_add(udp_data.np1_flows, fd); - log_dbg("Flow to %s pending on fd %d.", ipstr, fd); - return 0; } -static int ipcp_udp_flow_alloc_resp(int fd, - int resp, - const void * data, - size_t len) +static int udp_ipcp_flow_alloc_resp(int fd, + int resp, + const buffer_t * data) { - struct timespec ts = {0, FD_UPDATE_TIMEOUT * 1000}; - struct timespec abstime; struct sockaddr_in saddr; int d_eid; - if (resp) - return 0; - - clock_gettime(PTHREAD_COND_CLOCK, &abstime); - - pthread_mutex_lock(&ipcpi.alloc_lock); - - while (ipcpi.alloc_id != fd && ipcp_get_state() == IPCP_OPERATIONAL) { - ts_add(&abstime, &ts, &abstime); - pthread_cond_timedwait(&ipcpi.alloc_cond, - &ipcpi.alloc_lock, - &abstime); - } - - if (ipcp_get_state() != IPCP_OPERATIONAL) { - pthread_mutex_unlock(&ipcpi.alloc_lock); + if (ipcp_wait_flow_resp(fd) < 0) { + log_err("Failed to wait for flow response."); return -1; } - ipcpi.alloc_id = -1; - pthread_cond_broadcast(&ipcpi.alloc_cond); - - pthread_mutex_unlock(&ipcpi.alloc_lock); - pthread_rwlock_rdlock(&udp_data.flows_lock); saddr = udp_data.fd_to_uf[fd].r_saddr; @@ -1057,7 +1054,7 @@ static int ipcp_udp_flow_alloc_resp(int fd, pthread_rwlock_unlock(&udp_data.flows_lock); - if (ipcp_udp_port_alloc_resp(&saddr, d_eid, fd, resp, data, len) < 0) { + if (udp_ipcp_port_alloc_resp(&saddr, d_eid, fd, resp, data) < 0) { fset_del(udp_data.np1_flows, fd); log_err("Failed to respond to flow request."); return -1; @@ -1065,13 +1062,10 @@ static int ipcp_udp_flow_alloc_resp(int fd, fset_add(udp_data.np1_flows, fd); - log_dbg("Accepted flow, fd %d on eid %d.", - fd, d_eid); - return 0; } -static int ipcp_udp_flow_dealloc(int fd) +static int udp_ipcp_flow_dealloc(int fd) { ipcp_flow_fini(fd); @@ -1084,25 +1078,23 @@ static int ipcp_udp_flow_dealloc(int fd) pthread_rwlock_unlock(&udp_data.flows_lock); - flow_dealloc(fd); - - log_dbg("Flow with fd %d deallocated.", fd); + ipcp_flow_dealloc(fd); return 0; } static struct ipcp_ops udp_ops = { - .ipcp_bootstrap = ipcp_udp_bootstrap, + .ipcp_bootstrap = udp_ipcp_bootstrap, .ipcp_enroll = NULL, .ipcp_connect = NULL, .ipcp_disconnect = NULL, - .ipcp_reg = ipcp_udp_reg, - .ipcp_unreg = ipcp_udp_unreg, - .ipcp_query = ipcp_udp_query, - .ipcp_flow_alloc = ipcp_udp_flow_alloc, + .ipcp_reg = udp_ipcp_reg, + .ipcp_unreg = udp_ipcp_unreg, + .ipcp_query = udp_ipcp_query, + .ipcp_flow_alloc = udp_ipcp_flow_alloc, .ipcp_flow_join = NULL, - .ipcp_flow_alloc_resp = ipcp_udp_flow_alloc_resp, - .ipcp_flow_dealloc = ipcp_udp_flow_dealloc + .ipcp_flow_alloc_resp = udp_ipcp_flow_alloc_resp, + .ipcp_flow_dealloc = udp_ipcp_flow_dealloc }; int main(int argc, @@ -1110,53 +1102,51 @@ int main(int argc, { int i; - if (ipcp_init(argc, argv, &udp_ops, THIS_TYPE) < 0) - goto fail_init; if (udp_data_init() < 0) { log_err("Failed to init udp data."); goto fail_data_init; } - if (ipcp_boot() < 0) { - log_err("Failed to boot IPCP."); - goto fail_boot; + if (ipcp_init(argc, argv, &udp_ops, THIS_TYPE) < 0) { + log_err("Failed to initialize IPCP."); + goto fail_init; } - if (ipcp_create_r(0)) { - log_err("Failed to notify IRMd we are initialized."); - goto fail_create_r; + if (ipcp_start() < 0) { + log_err("Failed to start IPCP."); + goto fail_start; } - ipcp_shutdown(); + ipcp_sigwait(); if (ipcp_get_state() == IPCP_SHUTDOWN) { - for (i = 0; i < IPCP_UDP_RD_THR; ++i) - pthread_cancel(udp_data.packet_reader[i]); for (i = 0; i < IPCP_UDP_WR_THR; ++i) pthread_cancel(udp_data.packet_writer[i]); + for (i = 0; i < IPCP_UDP_RD_THR; ++i) + pthread_cancel(udp_data.packet_reader[i]); pthread_cancel(udp_data.mgmt_handler); - for (i = 0; i < IPCP_UDP_RD_THR; ++i) - pthread_join(udp_data.packet_reader[i], NULL); for (i = 0; i < IPCP_UDP_WR_THR; ++i) pthread_join(udp_data.packet_writer[i], NULL); + for (i = 0; i < IPCP_UDP_RD_THR; ++i) + pthread_join(udp_data.packet_reader[i], NULL); pthread_join(udp_data.mgmt_handler, NULL); + close(udp_data.s_fd); } - udp_data_fini(); + ipcp_stop(); ipcp_fini(); - exit(EXIT_SUCCESS); - fail_create_r: - ipcp_set_state(IPCP_NULL); - ipcp_shutdown(); - fail_boot: udp_data_fini(); - fail_data_init: + + exit(EXIT_SUCCESS); + + fail_start: ipcp_fini(); fail_init: - ipcp_create_r(-1); + udp_data_fini(); + fail_data_init: exit(EXIT_FAILURE); } diff --git a/src/ipcpd/unicast/CMakeLists.txt b/src/ipcpd/unicast/CMakeLists.txt index f4887160..ca742871 100644 --- a/src/ipcpd/unicast/CMakeLists.txt +++ b/src/ipcpd/unicast/CMakeLists.txt @@ -13,8 +13,10 @@ include_directories(${CMAKE_SOURCE_DIR}/include) include_directories(${CMAKE_BINARY_DIR}/include) set(IPCP_UNICAST_TARGET ipcpd-unicast CACHE INTERNAL "") +set(IPCP_UNICAST_MPL 60 CACHE STRING + "Default maximum packet lifetime for the unicast IPCP, in seconds") -protobuf_generate_c(KAD_PROTO_SRCS KAD_PROTO_HDRS dir/kademlia.proto) +protobuf_generate_c(DHT_PROTO_SRCS DHT_PROTO_HDRS dir/dht.proto) math(EXPR PFT_EXPR "1 << 12") set(PFT_SIZE ${PFT_EXPR} CACHE STRING @@ -36,7 +38,6 @@ set(SOURCE_FILES connmgr.c dir.c dt.c - enroll.c fa.c main.c pff.c @@ -55,8 +56,8 @@ set(SOURCE_FILES routing/graph.c ) -add_executable(ipcpd-unicast ${SOURCE_FILES} ${IPCP_SOURCES} - ${KAD_PROTO_SRCS} ${LAYER_CONFIG_PROTO_SRCS}) +add_executable(ipcpd-unicast ${SOURCE_FILES} ${IPCP_SOURCES} ${COMMON_SOURCES} + ${DHT_PROTO_SRCS} ${LAYER_CONFIG_PROTO_SRCS}) target_link_libraries(ipcpd-unicast LINK_PUBLIC ouroboros-dev) include(AddCompileFlags) diff --git a/src/ipcpd/unicast/addr-auth.c b/src/ipcpd/unicast/addr-auth.c index 01c2188f..908a4aa1 100644 --- a/src/ipcpd/unicast/addr-auth.c +++ b/src/ipcpd/unicast/addr-auth.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Address authority * diff --git a/src/ipcpd/unicast/addr-auth.h b/src/ipcpd/unicast/addr-auth.h index d26d3eb7..e119dff3 100644 --- a/src/ipcpd/unicast/addr-auth.h +++ b/src/ipcpd/unicast/addr-auth.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Address authority * diff --git a/src/ipcpd/unicast/addr-auth/flat.c b/src/ipcpd/unicast/addr-auth/flat.c index af245a5d..c4562935 100644 --- a/src/ipcpd/unicast/addr-auth/flat.c +++ b/src/ipcpd/unicast/addr-auth/flat.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Policy for flat addresses in a distributed way * @@ -29,19 +29,11 @@ #define OUROBOROS_PREFIX "flat-addr-auth" #include <ouroboros/logs.h> -#include <ouroboros/errno.h> -#include <ouroboros/time_utils.h> -#include <ouroboros/utils.h> +#include <ouroboros/random.h> #include "ipcp.h" #include "flat.h" -#include <time.h> -#include <stdlib.h> -#include <math.h> -#include <string.h> -#include <assert.h> - #define NAME_LEN 8 struct { @@ -75,13 +67,13 @@ int flat_fini(void) uint64_t flat_address(void) { - struct timespec t; - uint32_t addr; - - clock_gettime(CLOCK_REALTIME, &t); - srand(t.tv_nsec); - - addr = (rand() % (RAND_MAX - 1) + 1) & 0xFFFFFFFF; + uint32_t addr = INVALID_ADDRESS; +#if defined (CONFIG_OUROBOROS_DEBUG) && defined (IPCP_DEBUG_LOCAL) + addr = getpid(); +#else + while (addr == INVALID_ADDRESS) + random_buffer(&addr,sizeof(addr)); +#endif return addr; } diff --git a/src/ipcpd/unicast/addr-auth/flat.h b/src/ipcpd/unicast/addr-auth/flat.h index 96642dcc..d4b672c7 100644 --- a/src/ipcpd/unicast/addr-auth/flat.h +++ b/src/ipcpd/unicast/addr-auth/flat.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Policy for flat addresses in a distributed way * diff --git a/src/ipcpd/unicast/addr-auth/ops.h b/src/ipcpd/unicast/addr-auth/ops.h index e1069706..06b24cec 100644 --- a/src/ipcpd/unicast/addr-auth/ops.h +++ b/src/ipcpd/unicast/addr-auth/ops.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Address authority policy ops * diff --git a/src/ipcpd/unicast/addr-auth/pol.h b/src/ipcpd/unicast/addr-auth/pol.h index 70747af9..844308c6 100644 --- a/src/ipcpd/unicast/addr-auth/pol.h +++ b/src/ipcpd/unicast/addr-auth/pol.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Address Authority policies * diff --git a/src/ipcpd/unicast/ca.c b/src/ipcpd/unicast/ca.c index 3dbc93d5..287eaf41 100644 --- a/src/ipcpd/unicast/ca.c +++ b/src/ipcpd/unicast/ca.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Congestion Avoidance * diff --git a/src/ipcpd/unicast/ca.h b/src/ipcpd/unicast/ca.h index 8b221790..ea803e17 100644 --- a/src/ipcpd/unicast/ca.h +++ b/src/ipcpd/unicast/ca.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Congestion avoidance * diff --git a/src/ipcpd/unicast/ca/mb-ecn.c b/src/ipcpd/unicast/ca/mb-ecn.c index 38305a39..d9a204b0 100644 --- a/src/ipcpd/unicast/ca/mb-ecn.c +++ b/src/ipcpd/unicast/ca/mb-ecn.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Multi-bit ECN Congestion Avoidance * @@ -29,7 +29,7 @@ #include "config.h" #include <ouroboros/ipcp-dev.h> -#include <ouroboros/time_utils.h> +#include <ouroboros/time.h> #include "mb-ecn.h" @@ -187,7 +187,7 @@ ca_wnd_t mb_ecn_ctx_update_snd(void * _ctx, void mb_ecn_wnd_wait(ca_wnd_t wnd) { if (wnd.wait > 0) { - struct timespec s = {0, 0}; + struct timespec s = TIMESPEC_INIT_S(0); if (wnd.wait > BILLION) /* Don't care throttling < 1s */ s.tv_sec = 1; else diff --git a/src/ipcpd/unicast/ca/mb-ecn.h b/src/ipcpd/unicast/ca/mb-ecn.h index 53f23179..9a2c8b49 100644 --- a/src/ipcpd/unicast/ca/mb-ecn.h +++ b/src/ipcpd/unicast/ca/mb-ecn.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Multi-bit ECN Congestion Avoidance * diff --git a/src/ipcpd/unicast/ca/nop.c b/src/ipcpd/unicast/ca/nop.c index 5be826d4..617fc15b 100644 --- a/src/ipcpd/unicast/ca/nop.c +++ b/src/ipcpd/unicast/ca/nop.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Dummy Congestion Avoidance * diff --git a/src/ipcpd/unicast/ca/nop.h b/src/ipcpd/unicast/ca/nop.h index 25996552..248b198d 100644 --- a/src/ipcpd/unicast/ca/nop.h +++ b/src/ipcpd/unicast/ca/nop.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Dummy Congestion Avoidance * diff --git a/src/ipcpd/unicast/ca/ops.h b/src/ipcpd/unicast/ca/ops.h index ee0f028b..3a7b7248 100644 --- a/src/ipcpd/unicast/ca/ops.h +++ b/src/ipcpd/unicast/ca/ops.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Congestion avoidance policy ops * diff --git a/src/ipcpd/unicast/ca/pol.h b/src/ipcpd/unicast/ca/pol.h index 713c59b9..db0a1a11 100644 --- a/src/ipcpd/unicast/ca/pol.h +++ b/src/ipcpd/unicast/ca/pol.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Congestion avoidance policies * diff --git a/src/ipcpd/unicast/connmgr.c b/src/ipcpd/unicast/connmgr.c index 904deff8..11c5d5b6 100644 --- a/src/ipcpd/unicast/connmgr.c +++ b/src/ipcpd/unicast/connmgr.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Handles connections between components * diff --git a/src/ipcpd/unicast/dir.c b/src/ipcpd/unicast/dir.c index 870d0e10..e0cb09fc 100644 --- a/src/ipcpd/unicast/dir.c +++ b/src/ipcpd/unicast/dir.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Directory Management * diff --git a/src/ipcpd/unicast/dir.h b/src/ipcpd/unicast/dir.h index 8aa79638..b261ea2c 100644 --- a/src/ipcpd/unicast/dir.h +++ b/src/ipcpd/unicast/dir.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Directory * diff --git a/src/ipcpd/unicast/dir/dht.c b/src/ipcpd/unicast/dir/dht.c index 1742267b..08a5a5a9 100644 --- a/src/ipcpd/unicast/dir/dht.c +++ b/src/ipcpd/unicast/dir/dht.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Distributed Hash Table based on Kademlia * @@ -31,6 +31,7 @@ #define DHT "dht" #define OUROBOROS_PREFIX DHT +#include <ouroboros/endian.h> #include <ouroboros/hash.h> #include <ouroboros/ipcp-dev.h> #include <ouroboros/bitmap.h> @@ -39,7 +40,7 @@ #include <ouroboros/list.h> #include <ouroboros/notifier.h> #include <ouroboros/random.h> -#include <ouroboros/time_utils.h> +#include <ouroboros/time.h> #include <ouroboros/tpm.h> #include <ouroboros/utils.h> #include <ouroboros/pthread.h> @@ -56,9 +57,9 @@ #include <inttypes.h> #include <limits.h> -#include "kademlia.pb-c.h" -typedef KadMsg kad_msg_t; -typedef KadContactMsg kad_contact_msg_t; +#include "dht.pb-c.h" +typedef DhtMsg dht_msg_t; +typedef DhtContactMsg dht_contact_msg_t; #ifndef CLOCK_REALTIME_COARSE #define CLOCK_REALTIME_COARSE CLOCK_REALTIME @@ -353,7 +354,7 @@ static uint8_t * create_id(size_t len) } static void kad_req_create(struct dht * dht, - kad_msg_t * msg, + dht_msg_t * msg, uint64_t addr) { struct kad_req * req; @@ -361,14 +362,14 @@ static void kad_req_create(struct dht * dht, struct timespec t; size_t b; + clock_gettime(CLOCK_REALTIME_COARSE, &t); + req = malloc(sizeof(*req)); if (req == NULL) - return; + goto fail_malloc; list_head_init(&req->next); - clock_gettime(CLOCK_REALTIME_COARSE, &t); - req->t_exp = t.tv_sec + KAD_T_RESP; req->addr = addr; req->state = REQ_INIT; @@ -382,30 +383,22 @@ static void kad_req_create(struct dht * dht, if (msg->has_key) { req->key = dht_dup_key(msg->key.data, b); - if (req->key == NULL) { - free(req); - return; - } + if (req->key == NULL) + goto fail_dup_key; } - if (pthread_mutex_init(&req->lock, NULL)) { - free(req->key); - free(req); - return; - } + if (pthread_mutex_init(&req->lock, NULL)) + goto fail_mutex; - pthread_condattr_init(&cattr); + + if (pthread_condattr_init(&cattr)) + goto fail_condattr; #ifndef __APPLE__ pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK); #endif - if (pthread_cond_init(&req->cond, &cattr)) { - pthread_condattr_destroy(&cattr); - pthread_mutex_destroy(&req->lock); - free(req->key); - free(req); - return; - } + if (pthread_cond_init(&req->cond, &cattr)) + goto fail_cond_init; pthread_condattr_destroy(&cattr); @@ -414,6 +407,19 @@ static void kad_req_create(struct dht * dht, list_add(&req->next, &dht->requests); pthread_rwlock_unlock(&dht->lock); + + return; + + fail_cond_init: + pthread_condattr_destroy(&cattr); + fail_condattr: + pthread_mutex_destroy(&req->lock); + fail_mutex: + free(req->key); + fail_dup_key: + free(req); + fail_malloc: + return; } static void cancel_req_destroy(void * o) @@ -443,7 +449,7 @@ static void kad_req_destroy(struct kad_req * req) return; case REQ_PENDING: req->state = REQ_DESTROY; - pthread_cond_signal(&req->cond); + pthread_cond_broadcast(&req->cond); break; case REQ_INIT: case REQ_DONE: @@ -466,12 +472,14 @@ static void kad_req_destroy(struct kad_req * req) static int kad_req_wait(struct kad_req * req, time_t t) { - struct timespec timeo = {t, 0}; + struct timespec timeo = TIMESPEC_INIT_S(0); struct timespec abs; int ret = 0; assert(req); + timeo.tv_sec = t; + clock_gettime(PTHREAD_COND_CLOCK, &abs); ts_add(&abs, &timeo, &abs); @@ -787,7 +795,7 @@ static void lookup_destroy(struct lookup * lu) static void lookup_update(struct dht * dht, struct lookup * lu, - kad_msg_t * msg) + dht_msg_t * msg) { struct list_head * p = NULL; struct list_head * h; @@ -989,7 +997,7 @@ static void cancel_lookup_wait(void * o) static enum lookup_state lookup_wait(struct lookup * lu) { - struct timespec timeo = {KAD_T_RESP, 0}; + struct timespec timeo = TIMESPEC_INIT_S(KAD_T_RESP); struct timespec abs; enum lookup_state state; int ret = 0; @@ -1021,7 +1029,7 @@ static enum lookup_state lookup_wait(struct lookup * lu) } static struct kad_req * dht_find_request(struct dht * dht, - kad_msg_t * msg) + dht_msg_t * msg) { struct list_head * p; @@ -1269,7 +1277,7 @@ static void bucket_refresh(struct dht * dht, struct contact * d; c = list_first_entry(&b->contacts, struct contact, next); d = contact_create(c->id, dht->b, c->addr); - if (c != NULL) + if (d != NULL) list_add(&d->next, r); return; } @@ -1458,7 +1466,7 @@ static int dht_update_bucket(struct dht * dht, } static int send_msg(struct dht * dht, - kad_msg_t * msg, + dht_msg_t * msg, uint64_t addr) { #ifndef __DHT_TEST__ @@ -1491,7 +1499,7 @@ static int send_msg(struct dht * dht, pthread_rwlock_unlock(&dht->lock); #ifndef __DHT_TEST__ - len = kad_msg__get_packed_size(msg); + len = dht_msg__get_packed_size(msg); if (len == 0) goto fail_msg; @@ -1499,7 +1507,7 @@ static int send_msg(struct dht * dht, if (ipcp_sdb_reserve(&sdb, len)) goto fail_msg; - kad_msg__pack(msg, shm_du_buff_head(sdb)); + dht_msg__pack(msg, shm_du_buff_head(sdb)); if (dt_write_packet(addr, QOS_CUBE_BE, dht->eid, sdb) == 0) break; @@ -1546,7 +1554,7 @@ static struct dht_entry * dht_find_entry(struct dht * dht, } static int kad_add(struct dht * dht, - const kad_contact_msg_t * contacts, + const dht_contact_msg_t * contacts, ssize_t n, time_t exp) { @@ -1585,7 +1593,7 @@ static int kad_add(struct dht * dht, } static int wait_resp(struct dht * dht, - kad_msg_t * msg, + dht_msg_t * msg, time_t timeo) { struct kad_req * req; @@ -1612,9 +1620,9 @@ static int kad_store(struct dht * dht, uint64_t r_addr, time_t ttl) { - kad_msg_t msg = KAD_MSG__INIT; - kad_contact_msg_t cmsg = KAD_CONTACT_MSG__INIT; - kad_contact_msg_t * cmsgp[1]; + dht_msg_t msg = DHT_MSG__INIT; + dht_contact_msg_t cmsg = DHT_CONTACT_MSG__INIT; + dht_contact_msg_t * cmsgp[1]; cmsg.id.data = (uint8_t *) key; cmsg.addr = addr; @@ -1644,7 +1652,7 @@ static ssize_t kad_find(struct dht * dht, const uint64_t * addrs, enum kad_code code) { - kad_msg_t msg = KAD_MSG__INIT; + dht_msg_t msg = DHT_MSG__INIT; ssize_t sent = 0; assert(dht); @@ -1784,7 +1792,7 @@ static void kad_publish(struct dht * dht, while (n-- > 0) { if (addrs[n] == dht->addr) { - kad_contact_msg_t msg = KAD_CONTACT_MSG__INIT; + dht_contact_msg_t msg = DHT_CONTACT_MSG__INIT; msg.id.data = (uint8_t *) key; msg.id.len = dht->b; msg.addr = addr; @@ -1803,7 +1811,7 @@ static void kad_publish(struct dht * dht, static int kad_join(struct dht * dht, uint64_t addr) { - kad_msg_t msg = KAD_MSG__INIT; + dht_msg_t msg = DHT_MSG__INIT; msg.code = KAD_JOIN; @@ -1883,18 +1891,13 @@ static int dht_del(struct dht * dht, { struct dht_entry * e; - pthread_rwlock_wrlock(&dht->lock); - e = dht_find_entry(dht, key); if (e == NULL) { - pthread_rwlock_unlock(&dht->lock); return -EPERM; } dht_entry_del_addr(e, addr); - pthread_rwlock_unlock(&dht->lock); - return 0; } @@ -1936,14 +1939,14 @@ static buffer_t dht_retrieve(struct dht * dht, fail: pthread_rwlock_unlock(&dht->lock); - buf.len = 0; - + buf.len = 0; + buf.data = NULL; return buf; } static ssize_t dht_get_contacts(struct dht * dht, const uint8_t * key, - kad_contact_msg_t *** msgs) + dht_contact_msg_t *** msgs) { struct list_head l; struct list_head * p; @@ -1980,7 +1983,7 @@ static ssize_t dht_get_contacts(struct dht * dht, return 0; } - kad_contact_msg__init((*msgs)[i]); + dht_contact_msg__init((*msgs)[i]); (*msgs)[i]->id.data = c->id; (*msgs)[i]->id.len = dht->b; @@ -2117,7 +2120,7 @@ static void * work(void * o) static int kad_handle_join_resp(struct dht * dht, struct kad_req * req, - kad_msg_t * msg) + dht_msg_t * msg) { assert(dht); assert(req); @@ -2177,7 +2180,7 @@ static int kad_handle_join_resp(struct dht * dht, static int kad_handle_find_resp(struct dht * dht, struct kad_req * req, - kad_msg_t * msg) + dht_msg_t * msg) { struct lookup * lu; @@ -2201,7 +2204,7 @@ static int kad_handle_find_resp(struct dht * dht, } static void kad_handle_response(struct dht * dht, - kad_msg_t * msg) + dht_msg_t * msg) { struct kad_req * req; @@ -2249,6 +2252,12 @@ int dht_bootstrap(void * dir) pthread_rwlock_wrlock(&dht->lock); +#ifndef __DHT_TEST__ + dht->b = hash_len(ipcpi.dir_hash_algo); +#else + dht->b = DHT_TEST_KEY_LEN; +#endif + dht->id = create_id(dht->b); if (dht->id == NULL) goto fail_id; @@ -2259,11 +2268,7 @@ int dht_bootstrap(void * dir) dht->buckets->depth = 0; dht->buckets->mask = 0; -#ifndef __DHT_TEST__ - dht->b = hash_len(ipcpi.dir_hash_algo); -#else - dht->b = DHT_TEST_KEY_LEN; -#endif + dht->t_expire = 86400; /* 1 day */ dht->t_repub = dht->t_expire - 10; dht->k = KAD_K; @@ -2437,9 +2442,9 @@ static void * dht_handle_packet(void * o) assert(dht); while (true) { - kad_msg_t * msg; - kad_contact_msg_t ** cmsgs; - kad_msg_t resp_msg = KAD_MSG__INIT; + dht_msg_t * msg; + dht_contact_msg_t ** cmsgs; + dht_msg_t resp_msg = DHT_MSG__INIT; uint64_t addr; buffer_t buf; size_t i; @@ -2459,9 +2464,9 @@ static void * dht_handle_packet(void * o) pthread_cleanup_pop(true); - i = shm_du_buff_tail(cmd->sdb) - shm_du_buff_head(cmd->sdb); + i = shm_du_buff_len(cmd->sdb); - msg = kad_msg__unpack(NULL, i, shm_du_buff_head(cmd->sdb)); + msg = dht_msg__unpack(NULL, i, shm_du_buff_head(cmd->sdb)); #ifndef __DHT_TEST__ ipcp_sdb_release(cmd->sdb); #endif @@ -2473,7 +2478,7 @@ static void * dht_handle_packet(void * o) } if (msg->code != KAD_RESPONSE && dht_wait_running(dht)) { - kad_msg__free_unpacked(msg, NULL); + dht_msg__free_unpacked(msg, NULL); log_dbg("Got a request message when not running."); continue; } @@ -2486,13 +2491,13 @@ static void * dht_handle_packet(void * o) pthread_rwlock_unlock(&dht->lock); if (msg->has_key && msg->key.len != b) { - kad_msg__free_unpacked(msg, NULL); + dht_msg__free_unpacked(msg, NULL); log_warn("Bad key in message."); continue; } if (msg->has_s_id && !msg->has_b && msg->s_id.len != b) { - kad_msg__free_unpacked(msg, NULL); + dht_msg__free_unpacked(msg, NULL); log_warn("Bad source ID in message of type %d.", msg->code); continue; @@ -2593,7 +2598,7 @@ static void * dht_handle_packet(void * o) log_warn("Failed to send response."); finish: - kad_msg__free_unpacked(msg, NULL); + dht_msg__free_unpacked(msg, NULL); if (resp_msg.n_addrs > 0) free(resp_msg.addrs); @@ -2604,7 +2609,7 @@ static void * dht_handle_packet(void * o) } for (i = 0; i < resp_msg.n_contacts; ++i) - kad_contact_msg__free_unpacked(resp_msg.contacts[i], + dht_contact_msg__free_unpacked(resp_msg.contacts[i], NULL); free(resp_msg.contacts); @@ -2761,7 +2766,7 @@ static void handle_event(void * self, pthread_t thr; struct join_info * inf; struct conn * c = (struct conn *) o; - struct timespec slack = {0, DHT_ENROLL_SLACK * MILLION}; + struct timespec slack = TIMESPEC_INIT_MS(DHT_ENROLL_SLACK); /* Give the pff some time to update for the new link. */ nanosleep(&slack, NULL); @@ -2847,7 +2852,8 @@ void * dht_create(void) if ((int) dht->eid < 0) goto fail_tpm_start; - notifier_reg(handle_event, dht); + if (notifier_reg(handle_event, dht)) + goto fail_notifier_reg; #else (void) handle_event; (void) dht_handle_packet; @@ -2857,6 +2863,8 @@ void * dht_create(void) return (void *) dht; #ifndef __DHT_TEST__ + fail_notifier_reg: + tpm_stop(dht->tpm); fail_tpm_start: tpm_destroy(dht->tpm); fail_tpm_create: diff --git a/src/ipcpd/unicast/dir/dht.h b/src/ipcpd/unicast/dir/dht.h index a6e9c2c8..311c6b23 100644 --- a/src/ipcpd/unicast/dir/dht.h +++ b/src/ipcpd/unicast/dir/dht.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Distributed Hash Table based on Kademlia * diff --git a/src/ipcpd/unicast/dir/kademlia.proto b/src/ipcpd/unicast/dir/dht.proto index 58f5e787..4c5b06db 100644 --- a/src/ipcpd/unicast/dir/kademlia.proto +++ b/src/ipcpd/unicast/dir/dht.proto @@ -1,7 +1,7 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * - * KAD protocol + * DHT protocol, based on Kademlia * * Dimitri Staessens <dimitri@ouroboros.rocks> * Sander Vrijders <sander@ouroboros.rocks> @@ -22,19 +22,19 @@ syntax = "proto2"; -message kad_contact_msg { +message dht_contact_msg { required bytes id = 1; required uint64 addr = 2; -}; +} -message kad_msg { +message dht_msg { required uint32 code = 1; required uint32 cookie = 2; required uint64 s_addr = 3; optional bytes s_id = 4; optional bytes key = 5; repeated uint64 addrs = 6; - repeated kad_contact_msg contacts = 7; + repeated dht_contact_msg contacts = 7; // enrolment parameters optional uint32 alpha = 8; optional uint32 b = 9; @@ -42,4 +42,4 @@ message kad_msg { optional uint32 t_expire = 11; optional uint32 t_refresh = 12; optional uint32 t_replicate = 13; -};
\ No newline at end of file +} diff --git a/src/ipcpd/unicast/dir/ops.h b/src/ipcpd/unicast/dir/ops.h index e74324da..6ff61ce6 100644 --- a/src/ipcpd/unicast/dir/ops.h +++ b/src/ipcpd/unicast/dir/ops.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Directory policy ops * diff --git a/src/ipcpd/unicast/dir/pol.h b/src/ipcpd/unicast/dir/pol.h index 3aa2d59f..eae4b2e7 100644 --- a/src/ipcpd/unicast/dir/pol.h +++ b/src/ipcpd/unicast/dir/pol.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Directory policies * diff --git a/src/ipcpd/unicast/dir/tests/CMakeLists.txt b/src/ipcpd/unicast/dir/tests/CMakeLists.txt index 482711d5..f62ed993 100644 --- a/src/ipcpd/unicast/dir/tests/CMakeLists.txt +++ b/src/ipcpd/unicast/dir/tests/CMakeLists.txt @@ -20,16 +20,19 @@ create_test_sourcelist(${PARENT_DIR}_tests test_suite.c dht_test.c ) -protobuf_generate_c(KAD_PROTO_SRCS KAD_PROTO_HDRS ../kademlia.proto) - +protobuf_generate_c(DHT_PROTO_SRCS KAD_PROTO_HDRS ../dht.proto) add_executable(${PARENT_DIR}_test EXCLUDE_FROM_ALL ${${PARENT_DIR}_tests} - ${KAD_PROTO_SRCS}) + ${DHT_PROTO_SRCS}) target_link_libraries(${PARENT_DIR}_test ouroboros-common) add_dependencies(check ${PARENT_DIR}_test) set(tests_to_run ${${PARENT_DIR}_tests}) -remove(tests_to_run test_suite.c) +if(CMAKE_VERSION VERSION_LESS "3.29.0") + remove(tests_to_run test_suite.c) +else () + list(POP_FRONT tests_to_run) +endif() foreach (test ${tests_to_run}) get_filename_component(test_name ${test} NAME_WE) diff --git a/src/ipcpd/unicast/dir/tests/dht_test.c b/src/ipcpd/unicast/dir/tests/dht_test.c index 3f4c3b87..bea2c3e7 100644 --- a/src/ipcpd/unicast/dir/tests/dht_test.c +++ b/src/ipcpd/unicast/dir/tests/dht_test.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Unit tests of the DHT * diff --git a/src/ipcpd/unicast/dt.c b/src/ipcpd/unicast/dt.c index 0f504daa..2bb5ed2f 100644 --- a/src/ipcpd/unicast/dt.c +++ b/src/ipcpd/unicast/dt.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Data Transfer Component * @@ -312,7 +312,7 @@ static int dt_rib_readdir(char *** buf) if ((*buf)[idx] == NULL) { while (idx-- > 0) free((*buf)[idx]); - free(buf); + free(*buf); pthread_mutex_unlock(&dt.stat[i].lock); pthread_rwlock_unlock(&dt.lock); return -ENOMEM; @@ -399,6 +399,7 @@ static void handle_event(void * self, const void * o) { struct conn * c; + int fd; (void) self; @@ -406,19 +407,20 @@ static void handle_event(void * self, switch (event) { case NOTIFY_DT_CONN_ADD: + fd = c->flow_info.fd; #ifdef IPCP_FLOW_STATS - stat_used(c->flow_info.fd, c->conn_info.addr); + stat_used(fd, c->conn_info.addr); #endif - psched_add(dt.psched, c->flow_info.fd); - log_dbg("Added fd %d to packet scheduler.", c->flow_info.fd); + psched_add(dt.psched, fd); + log_dbg("Added fd %d to packet scheduler.", fd); break; case NOTIFY_DT_CONN_DEL: + fd = c->flow_info.fd; #ifdef IPCP_FLOW_STATS - stat_used(c->flow_info.fd, INVALID_ADDR); + stat_used(fd, INVALID_ADDR); #endif - psched_del(dt.psched, c->flow_info.fd); - log_dbg("Removed fd %d from " - "packet scheduler.", c->flow_info.fd); + psched_del(dt.psched, fd); + log_dbg("Removed fd %d from packet scheduler.", fd); break; default: break; @@ -435,7 +437,7 @@ static void packet_handler(int fd, uint8_t * head; size_t len; - len = shm_du_buff_tail(sdb) - shm_du_buff_head(sdb); + len = shm_du_buff_len(sdb); #ifndef IPCP_FLOW_STATS (void) fd; @@ -563,10 +565,7 @@ static void * dt_conn_handle(void * o) return 0; } -int dt_init(enum pol_routing pr, - uint8_t addr_size, - uint8_t eid_size, - uint8_t max_ttl) +int dt_init(struct dt_config cfg) { int i; int j; @@ -582,14 +581,14 @@ int dt_init(enum pol_routing pr, info.pref_syntax = PROTO_FIXED; info.addr = ipcpi.dt_addr; - if (eid_size != 8) { /* only support 64 bits from now */ + if (cfg.eid_size != 8) { /* only support 64 bits from now */ log_warn("Invalid EID size. Only 64 bit is supported."); - eid_size = 8; + cfg.eid_size = 8; } - dt_pci_info.addr_size = addr_size; - dt_pci_info.eid_size = eid_size; - dt_pci_info.max_ttl = max_ttl; + dt_pci_info.addr_size = cfg.addr_size; + dt_pci_info.eid_size = cfg.eid_size; + dt_pci_info.max_ttl = cfg.max_ttl; dt_pci_info.qc_o = dt_pci_info.addr_size; dt_pci_info.ttl_o = dt_pci_info.qc_o + QOS_LEN; @@ -597,17 +596,12 @@ int dt_init(enum pol_routing pr, dt_pci_info.eid_o = dt_pci_info.ecn_o + ECN_LEN; dt_pci_info.head_size = dt_pci_info.eid_o + dt_pci_info.eid_size; - if (notifier_reg(handle_event, NULL)) { - log_err("Failed to register with notifier."); - goto fail_notifier_reg; - } - if (connmgr_comp_init(COMPID_DT, &info)) { log_err("Failed to register with connmgr."); goto fail_connmgr_comp_init; } - pp = routing_init(pr); + pp = routing_init(cfg.routing_type); if (pp < 0) { log_err("Failed to init routing."); goto fail_routing; @@ -645,6 +639,7 @@ int dt_init(enum pol_routing pr, for (i = 0; i < PROG_MAX_FLOWS; ++i) if (pthread_mutex_init(&dt.stat[i].lock, NULL)) { + log_err("Failed to init mutex for flow %d.", i); for (j = 0; j < i; ++j) pthread_mutex_destroy(&dt.stat[j].lock); goto fail_stat_lock; @@ -653,8 +648,10 @@ int dt_init(enum pol_routing pr, dt.n_flows = 0; #endif sprintf(dtstr, "%s.%" PRIu64, DT, ipcpi.dt_addr); - if (rib_reg(dtstr, &r_ops)) + if (rib_reg(dtstr, &r_ops)) { + log_err("Failed to register RIB."); goto fail_rib_reg; + } return 0; @@ -678,8 +675,6 @@ int dt_init(enum pol_routing pr, fail_routing: connmgr_comp_fini(COMPID_DT); fail_connmgr_comp_init: - notifier_unreg(&handle_event); - fail_notifier_reg: return -1; } @@ -707,16 +702,19 @@ void dt_fini(void) routing_fini(); connmgr_comp_fini(COMPID_DT); - - notifier_unreg(&handle_event); } int dt_start(void) { - dt.psched = psched_create(packet_handler); + dt.psched = psched_create(packet_handler, ipcp_flow_read); if (dt.psched == NULL) { log_err("Failed to create N-1 packet scheduler."); - return -1; + goto fail_psched; + } + + if (notifier_reg(handle_event, NULL)) { + log_err("Failed to register with notifier."); + goto fail_notifier_reg; } if (pthread_create(&dt.listener, NULL, dt_conn_handle, NULL)) { @@ -726,12 +724,21 @@ int dt_start(void) } return 0; + + fail_notifier_reg: + psched_destroy(dt.psched); + fail_psched: + return -1; + } void dt_stop(void) { pthread_cancel(dt.listener); pthread_join(dt.listener, NULL); + + notifier_unreg(&handle_event); + psched_destroy(dt.psched); } @@ -747,7 +754,7 @@ int dt_reg_comp(void * comp, eid = bmp_allocate(dt.res_fds); if (!bmp_is_id_valid(dt.res_fds, eid)) { - log_warn("Reserved EIDs depleted."); + log_err("Cannot allocate EID."); pthread_rwlock_unlock(&dt.lock); return -EBADF; } @@ -781,7 +788,7 @@ int dt_write_packet(uint64_t dst_addr, assert(sdb); assert(dst_addr != ipcpi.dt_addr); - len = shm_du_buff_tail(sdb) - shm_du_buff_head(sdb); + len = shm_du_buff_len(sdb); #ifdef IPCP_FLOW_STATS if (eid < PROG_RES_FDS) { @@ -815,7 +822,7 @@ int dt_write_packet(uint64_t dst_addr, goto fail_write; } - len = shm_du_buff_tail(sdb) - shm_du_buff_head(sdb); + len = shm_du_buff_len(sdb); dt_pci.dst_addr = dst_addr; dt_pci.qc = qc; diff --git a/src/ipcpd/unicast/dt.h b/src/ipcpd/unicast/dt.h index e1abbe28..7198a013 100644 --- a/src/ipcpd/unicast/dt.h +++ b/src/ipcpd/unicast/dt.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Data Transfer component * @@ -31,11 +31,7 @@ #define DT_PROTO "dtp" #define INVALID_ADDR 0 -int dt_init(enum pol_routing pr, - uint8_t addr_size, - uint8_t eid_size, - uint8_t max_ttl -); +int dt_init(struct dt_config cfg); void dt_fini(void); diff --git a/src/ipcpd/unicast/enroll.c b/src/ipcpd/unicast/enroll.c deleted file mode 100644 index 500a3895..00000000 --- a/src/ipcpd/unicast/enroll.c +++ /dev/null @@ -1,3 +0,0 @@ -#define BUILD_IPCP_UNICAST - -#include "common/enroll.c" diff --git a/src/ipcpd/unicast/fa.c b/src/ipcpd/unicast/fa.c index 6e6d52f0..3631fd7b 100644 --- a/src/ipcpd/unicast/fa.c +++ b/src/ipcpd/unicast/fa.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Flow allocator of the IPC Process * @@ -31,6 +31,7 @@ #define FA "flow-allocator" #define OUROBOROS_PREFIX FA +#include <ouroboros/endian.h> #include <ouroboros/logs.h> #include <ouroboros/fqueue.h> #include <ouroboros/errno.h> @@ -55,7 +56,7 @@ #define CLOCK_REALTIME_COARSE CLOCK_REALTIME #endif -#define TIMEOUT 10000 /* nanoseconds */ +#define TIMEOUT 10 * MILLION /* nanoseconds */ #define FLOW_REQ 0 #define FLOW_REPLY 1 @@ -72,14 +73,15 @@ struct fa_msg { int8_t response; uint16_t ece; /* QoS parameters from spec, aligned */ - uint8_t availability; - uint8_t in_order; uint32_t delay; uint64_t bandwidth; uint32_t loss; uint32_t ber; uint32_t max_gap; + uint32_t timeout; uint16_t cypher_s; + uint8_t availability; + uint8_t in_order; } __attribute__((packed)); struct cmd { @@ -143,7 +145,7 @@ static int fa_rib_read(const char * path, fd = atoi(entry); - if (fd < 0 || fd > PROG_MAX_FLOWS) + if (fd < 0 || fd >= PROG_MAX_FLOWS) return -1; if (len < 1536) @@ -238,7 +240,7 @@ static int fa_rib_readdir(char *** buf) if ((*buf)[idx] == NULL) { while (idx-- > 0) free((*buf)[idx]); - free(buf); + free(*buf); pthread_rwlock_unlock(&fa.flows_lock); return -ENOMEM; } @@ -303,7 +305,7 @@ static int eid_to_fd(uint64_t eid) fd = eid & 0xFFFFFFFF; - if (fd < 0 || fd > PROG_MAX_FLOWS) + if (fd < 0 || fd >= PROG_MAX_FLOWS) return -1; flow = &fa.flows[fd]; @@ -340,7 +342,7 @@ static void packet_handler(int fd, pthread_rwlock_wrlock(&fa.flows_lock); - len = shm_du_buff_tail(sdb) - shm_du_buff_head(sdb); + len = shm_du_buff_len(sdb); #ifdef IPCP_FLOW_STATS ++flow->p_snd; @@ -357,7 +359,7 @@ static void packet_handler(int fd, if (dt_write_packet(r_addr, qc, r_eid, sdb)) { ipcp_sdb_release(sdb); - log_warn("Failed to forward packet."); + log_dbg("Failed to forward packet."); #ifdef IPCP_FLOW_STATS pthread_rwlock_wrlock(&fa.flows_lock); ++flow->p_snd_f; @@ -435,167 +437,190 @@ static void fa_post_packet(void * comp, pthread_mutex_unlock(&fa.mtx); } -static void * fa_handle_packet(void * o) +static size_t fa_wait_for_fa_msg(struct fa_msg * msg) { - struct timespec ts = {0, TIMEOUT * 1000}; - - (void) o; + struct cmd * cmd; + size_t len; - while (true) { - struct timespec abstime; - int fd; - uint8_t buf[MSGBUFSZ]; - struct fa_msg * msg; - qosspec_t qs; - struct cmd * cmd; - size_t len; - size_t msg_len; - struct fa_flow * flow; + pthread_mutex_lock(&fa.mtx); - pthread_mutex_lock(&fa.mtx); + pthread_cleanup_push(__cleanup_mutex_unlock, &fa.mtx); - pthread_cleanup_push(__cleanup_mutex_unlock, &fa.mtx); + while (list_is_empty(&fa.cmds)) + pthread_cond_wait(&fa.cond, &fa.mtx); - while (list_is_empty(&fa.cmds)) - pthread_cond_wait(&fa.cond, &fa.mtx); + cmd = list_last_entry(&fa.cmds, struct cmd, next); + list_del(&cmd->next); - cmd = list_last_entry(&fa.cmds, struct cmd, next); - list_del(&cmd->next); + pthread_cleanup_pop(true); - pthread_cleanup_pop(true); + len = shm_du_buff_len(cmd->sdb); + if (len > MSGBUFSZ || len < sizeof(*msg)) { + log_warn("Invalid flow allocation message (len: %zd).", len); + free(cmd); + return 0; /* No valid message */ + } - len = shm_du_buff_tail(cmd->sdb) - shm_du_buff_head(cmd->sdb); + memcpy(msg, shm_du_buff_head(cmd->sdb), len); - if (len > MSGBUFSZ) { - log_err("Message over buffer size."); - free(cmd); - continue; - } + ipcp_sdb_release(cmd->sdb); - msg = (struct fa_msg *) buf; + free(cmd); - /* Depending on the message call the function in ipcp-dev.h */ + return len; +} - memcpy(msg, shm_du_buff_head(cmd->sdb), len); +static int fa_handle_flow_req(struct fa_msg * msg, + size_t len) +{ + size_t msg_len; + int fd; + qosspec_t qs; + struct fa_flow * flow; + uint8_t * dst; + buffer_t data; /* Piggbacked data on flow alloc request. */ - ipcp_sdb_release(cmd->sdb); + msg_len = sizeof(*msg) + ipcp_dir_hash_len(); + if (len < msg_len) { + log_err("Invalid flow allocation request"); + return -EPERM; + } - free(cmd); + dst = (uint8_t *)(msg + 1); + data.data = (uint8_t *) msg + msg_len; + data.len = len - msg_len; + + qs.delay = ntoh32(msg->delay); + qs.bandwidth = ntoh64(msg->bandwidth); + qs.availability = msg->availability; + qs.loss = ntoh32(msg->loss); + qs.ber = ntoh32(msg->ber); + qs.in_order = msg->in_order; + qs.max_gap = ntoh32(msg->max_gap); + qs.cypher_s = ntoh16(msg->cypher_s); + qs.timeout = ntoh32(msg->timeout); + + fd = ipcp_wait_flow_req_arr(dst, qs, IPCP_UNICAST_MPL, &data); + if (fd < 0) + return fd; - switch (msg->code) { - case FLOW_REQ: - msg_len = sizeof(*msg) + ipcp_dir_hash_len(); + flow = &fa.flows[fd]; - assert(len >= msg_len); + pthread_rwlock_wrlock(&fa.flows_lock); - clock_gettime(PTHREAD_COND_CLOCK, &abstime); + fa_flow_init(flow); - pthread_mutex_lock(&ipcpi.alloc_lock); + flow->s_eid = gen_eid(fd); + flow->r_eid = ntoh64(msg->s_eid); + flow->r_addr = ntoh64(msg->s_addr); - while (ipcpi.alloc_id != -1 && - ipcp_get_state() == IPCP_OPERATIONAL) { - ts_add(&abstime, &ts, &abstime); - pthread_cond_timedwait(&ipcpi.alloc_cond, - &ipcpi.alloc_lock, - &abstime); - } + pthread_rwlock_unlock(&fa.flows_lock); - if (ipcp_get_state() != IPCP_OPERATIONAL) { - pthread_mutex_unlock(&ipcpi.alloc_lock); - log_dbg("Won't allocate over non-operational" - "IPCP."); - continue; - } + return fd; +} - assert(ipcpi.alloc_id == -1); +static int fa_handle_flow_reply(struct fa_msg * msg, + size_t len) +{ + int fd; + struct fa_flow * flow; + buffer_t data; /* Piggbacked data on flow alloc request. */ + time_t mpl = IPCP_UNICAST_MPL; - qs.delay = ntoh32(msg->delay); - qs.bandwidth = ntoh64(msg->bandwidth); - qs.availability = msg->availability; - qs.loss = ntoh32(msg->loss); - qs.ber = ntoh32(msg->ber); - qs.in_order = msg->in_order; - qs.max_gap = ntoh32(msg->max_gap); - qs.cypher_s = ntoh16(msg->cypher_s); + assert(len >= sizeof(*msg)); - fd = ipcp_flow_req_arr((uint8_t *) (msg + 1), - ipcp_dir_hash_len(), - qs, - buf + msg_len, - len - msg_len); - if (fd < 0) { - pthread_mutex_unlock(&ipcpi.alloc_lock); - log_err("Failed to get fd for flow."); - continue; - } + data.data = (uint8_t *) msg + sizeof(*msg); + data.len = len - sizeof(*msg); - flow = &fa.flows[fd]; + pthread_rwlock_wrlock(&fa.flows_lock); - pthread_rwlock_wrlock(&fa.flows_lock); + fd = eid_to_fd(ntoh64(msg->r_eid)); + if (fd < 0) { + pthread_rwlock_unlock(&fa.flows_lock); + log_err("Flow reply for unknown EID %" PRIu64 ".", + ntoh64(msg->r_eid)); + return -ENOTALLOC; + } - fa_flow_init(flow); + flow = &fa.flows[fd]; - flow->s_eid = gen_eid(fd); - flow->r_eid = ntoh64(msg->s_eid); - flow->r_addr = ntoh64(msg->s_addr); + flow->r_eid = ntoh64(msg->s_eid); - pthread_rwlock_unlock(&fa.flows_lock); + if (msg->response < 0) + fa_flow_fini(flow); + else + psched_add(fa.psched, fd); - ipcpi.alloc_id = fd; - pthread_cond_broadcast(&ipcpi.alloc_cond); + pthread_rwlock_unlock(&fa.flows_lock); - pthread_mutex_unlock(&ipcpi.alloc_lock); + if (ipcp_flow_alloc_reply(fd, msg->response, mpl, &data) < 0) { + log_err("Failed to reply for flow allocation on fd %d.", fd); + return -EIRMD; + } - break; - case FLOW_REPLY: - assert(len >= sizeof(*msg)); + return 0; +} - pthread_rwlock_wrlock(&fa.flows_lock); +static int fa_handle_flow_update(struct fa_msg * msg, + size_t len) +{ + struct fa_flow * flow; + int fd; - fd = eid_to_fd(ntoh64(msg->r_eid)); - if (fd < 0) { - pthread_rwlock_unlock(&fa.flows_lock); - break; - } + (void) len; + assert(len >= sizeof(*msg)); - flow = &fa.flows[fd]; + pthread_rwlock_wrlock(&fa.flows_lock); - flow->r_eid = ntoh64(msg->s_eid); + fd = eid_to_fd(ntoh64(msg->r_eid)); + if (fd < 0) { + pthread_rwlock_unlock(&fa.flows_lock); + log_err("Flow update for unknown EID %" PRIu64 ".", + ntoh64(msg->r_eid)); + return -EPERM; + } - if (msg->response < 0) - fa_flow_fini(flow); - else - psched_add(fa.psched, fd); + flow = &fa.flows[fd]; +#ifdef IPCP_FLOW_STATS + flow->u_rcv++; +#endif + ca_ctx_update_ece(flow->ctx, ntoh16(msg->ece)); - pthread_rwlock_unlock(&fa.flows_lock); + pthread_rwlock_unlock(&fa.flows_lock); - ipcp_flow_alloc_reply(fd, - msg->response, - buf + sizeof(*msg), - len - sizeof(*msg)); - break; - case FLOW_UPDATE: - assert(len >= sizeof(*msg)); + return 0; +} - pthread_rwlock_wrlock(&fa.flows_lock); +static void * fa_handle_packet(void * o) +{ + (void) o; - fd = eid_to_fd(ntoh64(msg->r_eid)); - if (fd < 0) { - pthread_rwlock_unlock(&fa.flows_lock); - break; - } + while (true) { + uint8_t buf[MSGBUFSZ]; + struct fa_msg * msg; + size_t len; - flow = &fa.flows[fd]; -#ifdef IPCP_FLOW_STATS - flow->u_rcv++; -#endif - ca_ctx_update_ece(flow->ctx, ntoh16(msg->ece)); + msg = (struct fa_msg *) buf; - pthread_rwlock_unlock(&fa.flows_lock); + len = fa_wait_for_fa_msg(msg); + if (len == 0) + continue; + switch (msg->code) { + case FLOW_REQ: + if (fa_handle_flow_req(msg, len) < 0) + log_err("Error handling flow alloc request."); + break; + case FLOW_REPLY: + if (fa_handle_flow_reply(msg, len) < 0) + log_err("Error handling flow reply."); + break; + case FLOW_UPDATE: + if (fa_handle_flow_update(msg, len) < 0) + log_err("Error handling flow update."); break; default: - log_err("Got an unknown flow allocation message."); + log_warn("Recieved unknown flow allocation message."); break; } } @@ -644,7 +669,7 @@ int fa_init(void) fail_mtx: pthread_rwlock_destroy(&fa.flows_lock); fail_rwlock: - log_err("Failed to initialize flow allocator."); + return -1; } @@ -663,7 +688,7 @@ int fa_start(void) int pol; int max; - fa.psched = psched_create(packet_handler); + fa.psched = psched_create(packet_handler, np1_flow_read); if (fa.psched == NULL) { log_err("Failed to start packet scheduler."); goto fail_psched; @@ -700,7 +725,6 @@ int fa_start(void) fail_thread: psched_destroy(fa.psched); fail_psched: - log_err("Failed to start flow allocator."); return -1; } @@ -712,11 +736,10 @@ void fa_stop(void) psched_destroy(fa.psched); } -int fa_alloc(int fd, - const uint8_t * dst, - qosspec_t qs, - const void * data, - size_t dlen) +int fa_alloc(int fd, + const uint8_t * dst, + qosspec_t qs, + const buffer_t * data) { struct fa_msg * msg; struct shm_du_buff * sdb; @@ -732,7 +755,7 @@ int fa_alloc(int fd, len = sizeof(*msg) + ipcp_dir_hash_len(); - if (ipcp_sdb_reserve(&sdb, len + dlen)) + if (ipcp_sdb_reserve(&sdb, len + data->len)) return -1; msg = (struct fa_msg *) shm_du_buff_head(sdb); @@ -751,11 +774,14 @@ int fa_alloc(int fd, msg->in_order = qs.in_order; msg->max_gap = hton32(qs.max_gap); msg->cypher_s = hton16(qs.cypher_s); + msg->timeout = hton32(qs.timeout); memcpy(msg + 1, dst, ipcp_dir_hash_len()); - memcpy(shm_du_buff_head(sdb) + len, data, dlen); + if (data->len > 0) + memcpy(shm_du_buff_head(sdb) + len, data->data, data->len); if (dt_write_packet(addr, qc, fa.eid, sdb)) { + log_err("Failed to send flow allocation request packet."); ipcp_sdb_release(sdb); return -1; } @@ -773,75 +799,66 @@ int fa_alloc(int fd, return 0; } -int fa_alloc_resp(int fd, - int response, - const void * data, - size_t len) +int fa_alloc_resp(int fd, + int response, + const buffer_t * data) { - struct timespec ts = {0, TIMEOUT * 1000}; - struct timespec abstime; struct fa_msg * msg; struct shm_du_buff * sdb; struct fa_flow * flow; qoscube_t qc = QOS_CUBE_BE; - clock_gettime(PTHREAD_COND_CLOCK, &abstime); - flow = &fa.flows[fd]; - pthread_mutex_lock(&ipcpi.alloc_lock); - - while (ipcpi.alloc_id != fd && ipcp_get_state() == IPCP_OPERATIONAL) { - ts_add(&abstime, &ts, &abstime); - pthread_cond_timedwait(&ipcpi.alloc_cond, - &ipcpi.alloc_lock, - &abstime); + if (ipcp_wait_flow_resp(fd) < 0) { + log_err("Failed to wait for flow response."); + goto fail_alloc_resp; } - if (ipcp_get_state() != IPCP_OPERATIONAL) { - pthread_mutex_unlock(&ipcpi.alloc_lock); - return -1; - } - - ipcpi.alloc_id = -1; - pthread_cond_broadcast(&ipcpi.alloc_cond); - - pthread_mutex_unlock(&ipcpi.alloc_lock); - - if (ipcp_sdb_reserve(&sdb, sizeof(*msg) + len)) { - fa_flow_fini(flow); - return -1; + if (ipcp_sdb_reserve(&sdb, sizeof(*msg) + data->len)) { + log_err("Failed to reserve sdb (%zu bytes).", + sizeof(*msg) + data->len); + goto fail_reserve; } msg = (struct fa_msg *) shm_du_buff_head(sdb); memset(msg, 0, sizeof(*msg)); - pthread_rwlock_wrlock(&fa.flows_lock); - msg->code = FLOW_REPLY; + msg->response = response; + if (data->len > 0) + memcpy(msg + 1, data->data, data->len); + + pthread_rwlock_rdlock(&fa.flows_lock); + msg->r_eid = hton64(flow->r_eid); msg->s_eid = hton64(flow->s_eid); - msg->response = response; - memcpy(msg + 1, data, len); + pthread_rwlock_unlock(&fa.flows_lock); + + if (dt_write_packet(flow->r_addr, qc, fa.eid, sdb)) { + log_err("Failed to send flow allocation response packet."); + goto fail_packet; + } if (response < 0) { + pthread_rwlock_rdlock(&fa.flows_lock); fa_flow_fini(flow); - ipcp_sdb_release(sdb); + pthread_rwlock_unlock(&fa.flows_lock); } else { psched_add(fa.psched, fd); } - if (dt_write_packet(flow->r_addr, qc, fa.eid, sdb)) { - fa_flow_fini(flow); - pthread_rwlock_unlock(&fa.flows_lock); - ipcp_sdb_release(sdb); - return -1; - } + return 0; + fail_packet: + ipcp_sdb_release(sdb); + fail_reserve: + pthread_rwlock_wrlock(&fa.flows_lock); + fa_flow_fini(flow); pthread_rwlock_unlock(&fa.flows_lock); - - return 0; + fail_alloc_resp: + return -1; } int fa_dealloc(int fd) @@ -857,7 +874,7 @@ int fa_dealloc(int fd) pthread_rwlock_unlock(&fa.flows_lock); - flow_dealloc(fd); + ipcp_flow_dealloc(fd); return 0; } @@ -872,6 +889,7 @@ static int fa_update_remote(int fd, uint64_t r_addr; if (ipcp_sdb_reserve(&sdb, sizeof(*msg))) { + log_err("Failed to reserve sdb (%zu bytes).", sizeof(*msg)); return -1; } @@ -895,6 +913,7 @@ static int fa_update_remote(int fd, if (dt_write_packet(r_addr, qc, fa.eid, sdb)) { + log_err("Failed to send flow update packet."); ipcp_sdb_release(sdb); return -1; } @@ -912,13 +931,14 @@ void fa_np1_rcv(uint64_t eid, int fd; size_t len; - len = shm_du_buff_tail(sdb) - shm_du_buff_head(sdb); + len = shm_du_buff_len(sdb); pthread_rwlock_wrlock(&fa.flows_lock); fd = eid_to_fd(eid); if (fd < 0) { pthread_rwlock_unlock(&fa.flows_lock); + log_dbg("Received packet for unknown EID %" PRIu64 ".", eid); ipcp_sdb_release(sdb); return; } @@ -934,6 +954,7 @@ void fa_np1_rcv(uint64_t eid, pthread_rwlock_unlock(&fa.flows_lock); if (ipcp_flow_write(fd, sdb) < 0) { + log_dbg("Failed to write to flow %d.", fd); ipcp_sdb_release(sdb); #ifdef IPCP_FLOW_STATS pthread_rwlock_wrlock(&fa.flows_lock); diff --git a/src/ipcpd/unicast/fa.h b/src/ipcpd/unicast/fa.h index 376fdb20..1e716966 100644 --- a/src/ipcpd/unicast/fa.h +++ b/src/ipcpd/unicast/fa.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Flow allocator of the IPC Process * @@ -34,16 +34,14 @@ int fa_start(void); void fa_stop(void); -int fa_alloc(int fd, - const uint8_t * dst, - qosspec_t qs, - const void * data, - size_t len); +int fa_alloc(int fd, + const uint8_t * dst, + qosspec_t qs, + const buffer_t * data); -int fa_alloc_resp(int fd, - int response, - const void * data, - size_t len); +int fa_alloc_resp(int fd, + int response, + const buffer_t * data); int fa_dealloc(int fd); diff --git a/src/ipcpd/unicast/main.c b/src/ipcpd/unicast/main.c index eafb7f2a..e6cb2994 100644 --- a/src/ipcpd/unicast/main.c +++ b/src/ipcpd/unicast/main.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Unicast IPC Process * @@ -32,12 +32,12 @@ #define THIS_TYPE IPCP_UNICAST #include <ouroboros/errno.h> -#include <ouroboros/hash.h> #include <ouroboros/ipcp-dev.h> #include <ouroboros/logs.h> #include <ouroboros/notifier.h> +#include <ouroboros/random.h> #include <ouroboros/rib.h> -#include <ouroboros/time_utils.h> +#include <ouroboros/time.h> #include "common/connmgr.h" #include "common/enroll.h" @@ -59,18 +59,13 @@ struct ipcp ipcpi; static int initialize_components(const struct ipcp_config * conf) { - ipcpi.layer_name = strdup(conf->layer_info.layer_name); - if (ipcpi.layer_name == NULL) { - log_err("Failed to set layer name."); - goto fail_layer_name; - } - - ipcpi.dir_hash_algo = conf->layer_info.dir_hash_algo; + strcpy(ipcpi.layer_name, conf->layer_info.name); + ipcpi.dir_hash_algo = (enum hash_algo) conf->layer_info.dir_hash_algo; assert(ipcp_dir_hash_len() != 0); - if (addr_auth_init(conf->addr_auth_type, - &conf->addr_size)) { + if (addr_auth_init(conf->unicast.addr_auth_type, + &conf->unicast.dt.addr_size)) { log_err("Failed to init address authority."); goto fail_addr_auth; } @@ -81,17 +76,14 @@ static int initialize_components(const struct ipcp_config * conf) goto fail_addr_auth; } - log_dbg("IPCP got address %" PRIu64 ".", ipcpi.dt_addr); + log_info("IPCP got address %" PRIu64 ".", ipcpi.dt_addr); - if (ca_init(conf->cong_avoid)) { + if (ca_init(conf->unicast.cong_avoid)) { log_err("Failed to initialize congestion avoidance."); goto fail_ca; } - if (dt_init(conf->routing_type, - conf->addr_size, - conf->eid_size, - conf->max_ttl)) { + if (dt_init(conf->unicast.dt)) { log_err("Failed to initialize data transfer component."); goto fail_dt; } @@ -119,8 +111,6 @@ static int initialize_components(const struct ipcp_config * conf) fail_ca: addr_auth_fini(); fail_addr_auth: - free(ipcpi.layer_name); - fail_layer_name: return -1; } @@ -135,32 +125,26 @@ static void finalize_components(void) ca_fini(); addr_auth_fini(); - - free(ipcpi.layer_name); } static int start_components(void) { - assert(ipcp_get_state() == IPCP_INIT); - - ipcp_set_state(IPCP_OPERATIONAL); - - if (dt_start()) { + if (dt_start() < 0) { log_err("Failed to start data transfer."); goto fail_dt_start; } - if (fa_start()) { + if (fa_start() < 0) { log_err("Failed to start flow allocator."); goto fail_fa_start; } - if (enroll_start()) { + if (enroll_start() < 0) { log_err("Failed to start enrollment."); goto fail_enroll_start; } - if (connmgr_start()) { + if (connmgr_start() < 0) { log_err("Failed to start AP connection manager."); goto fail_connmgr_start; } @@ -180,9 +164,6 @@ static int start_components(void) static void stop_components(void) { - assert(ipcp_get_state() == IPCP_OPERATIONAL || - ipcp_get_state() == IPCP_SHUTDOWN); - connmgr_stop(); enroll_stop(); @@ -208,38 +189,46 @@ static int unicast_ipcp_enroll(const char * dst, struct layer_info * info) { struct conn conn; + uint8_t id[ENROLL_ID_LEN]; + + if (random_buffer(id, ENROLL_ID_LEN) < 0) { + log_err("Failed to generate enrollment ID."); + goto fail_id; + } - if (connmgr_alloc(COMPID_ENROLL, dst, NULL, &conn)) { - log_err("Failed to get connection."); - goto fail_er_flow; + log_info_id(id, "Requesting enrollment."); + + if (connmgr_alloc(COMPID_ENROLL, dst, NULL, &conn) < 0) { + log_err_id(id, "Failed to get connection."); + goto fail_id; } /* Get boot state from peer. */ - if (enroll_boot(&conn)) { - log_err("Failed to get boot information."); + if (enroll_boot(&conn, id) < 0) { + log_err_id(id, "Failed to get boot information."); goto fail_enroll_boot; } - if (initialize_components(enroll_get_conf())) { - log_err("Failed to initialize IPCP components."); + if (initialize_components(enroll_get_conf()) < 0) { + log_err_id(id, "Failed to initialize components."); goto fail_enroll_boot; } - if (start_components()) { - log_err("Failed to start components."); + if (start_components() < 0) { + log_err_id(id, "Failed to start components."); goto fail_start_comp; } - if (enroll_done(&conn, 0)) - log_warn("Failed to confirm enrollment with peer."); + if (enroll_ack(&conn, id, 0) < 0) + log_err_id(id, "Failed to confirm enrollment."); - if (connmgr_dealloc(COMPID_ENROLL, &conn)) - log_warn("Failed to deallocate enrollment flow."); + if (connmgr_dealloc(COMPID_ENROLL, &conn) < 0) + log_warn_id(id, "Failed to dealloc enrollment flow."); - log_info("Enrolled with %s.", dst); + log_info_id(id, "Enrolled with %s.", dst); - info->dir_hash_algo = ipcpi.dir_hash_algo; - strcpy(info->layer_name, ipcpi.layer_name); + info->dir_hash_algo = (enum pol_dir_hash) ipcpi.dir_hash_algo; + strcpy(info->name, ipcpi.layer_name); return 0; @@ -247,7 +236,7 @@ static int unicast_ipcp_enroll(const char * dst, finalize_components(); fail_enroll_boot: connmgr_dealloc(COMPID_ENROLL, &conn); - fail_er_flow: + fail_id: return -1; } @@ -258,23 +247,21 @@ static int unicast_ipcp_bootstrap(const struct ipcp_config * conf) enroll_bootstrap(conf); - if (initialize_components(conf)) { + if (initialize_components(conf) < 0) { log_err("Failed to init IPCP components."); goto fail_init; } - if (start_components()) { + if (start_components() < 0) { log_err("Failed to init IPCP components."); goto fail_start; } - if (bootstrap_components()) { + if (bootstrap_components() < 0) { log_err("Failed to bootstrap IPCP components."); goto fail_bootstrap; } - log_dbg("Bootstrapped in layer %s.", conf->layer_info.layer_name); - return 0; fail_bootstrap: @@ -312,39 +299,35 @@ int main(int argc, goto fail_init; } - if (notifier_init()) { + if (notifier_init() < 0) { log_err("Failed to initialize notifier component."); goto fail_notifier_init; } - if (connmgr_init()) { + if (connmgr_init() < 0) { log_err("Failed to initialize connection manager."); goto fail_connmgr_init; } - if (enroll_init()) { + if (enroll_init() < 0) { log_err("Failed to initialize enrollment component."); goto fail_enroll_init; } - if (ipcp_boot() < 0) { - log_err("Failed to boot IPCP."); - goto fail_boot; - } - - if (ipcp_create_r(0)) { - log_err("Failed to notify IRMd we are initialized."); - ipcp_set_state(IPCP_NULL); - goto fail_create_r; + if (ipcp_start() < 0) { + log_err("Failed to start IPCP."); + goto fail_start; } - ipcp_shutdown(); + ipcp_sigwait(); if (ipcp_get_state() == IPCP_SHUTDOWN) { stop_components(); finalize_components(); } + ipcp_stop(); + enroll_fini(); connmgr_fini(); @@ -355,17 +338,14 @@ int main(int argc, exit(EXIT_SUCCESS); - fail_create_r: - ipcp_shutdown(); - fail_boot: + fail_start: enroll_fini(); fail_enroll_init: connmgr_fini(); fail_connmgr_init: notifier_fini(); fail_notifier_init: - ipcp_fini(); + ipcp_fini(); fail_init: - ipcp_create_r(-1); exit(EXIT_FAILURE); } diff --git a/src/ipcpd/unicast/pff.c b/src/ipcpd/unicast/pff.c index 3d36e6b0..9b2aa2b4 100644 --- a/src/ipcpd/unicast/pff.c +++ b/src/ipcpd/unicast/pff.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * PDU Forwarding Function * @@ -59,8 +59,10 @@ struct pff * pff_create(enum pol_pff pol) } pff->pff_i = pff->ops->create(); - if (pff->pff_i == NULL) + if (pff->pff_i == NULL) { + log_err("Failed to create PFF instance."); goto err; + } return pff; err: diff --git a/src/ipcpd/unicast/pff.h b/src/ipcpd/unicast/pff.h index 3ac9d5fb..f44e5531 100644 --- a/src/ipcpd/unicast/pff.h +++ b/src/ipcpd/unicast/pff.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * PDU Forwarding Function * diff --git a/src/ipcpd/unicast/pff/alternate.c b/src/ipcpd/unicast/pff/alternate.c index 9f0a6279..85e85914 100644 --- a/src/ipcpd/unicast/pff/alternate.c +++ b/src/ipcpd/unicast/pff/alternate.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Policy for PFF with alternate next hops * diff --git a/src/ipcpd/unicast/pff/alternate.h b/src/ipcpd/unicast/pff/alternate.h index 294f48d9..96207e74 100644 --- a/src/ipcpd/unicast/pff/alternate.h +++ b/src/ipcpd/unicast/pff/alternate.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Policy for PFF with alternate next hops * diff --git a/src/ipcpd/unicast/pff/multipath.c b/src/ipcpd/unicast/pff/multipath.c index b03ce32f..cbab0f5f 100644 --- a/src/ipcpd/unicast/pff/multipath.c +++ b/src/ipcpd/unicast/pff/multipath.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Policy for PFF supporting multipath routing * @@ -191,7 +191,7 @@ int multipath_pff_nhop(struct pff_i * pff_i, assert(len > 0); /* Rotate fds left. */ - memcpy(fds, fds + 1, (len - 1) * sizeof(*fds)); + memmove(fds, fds + 1, (len - 1) * sizeof(*fds)); fds[len - 1] = fd; pthread_rwlock_unlock(&pff_i->lock); diff --git a/src/ipcpd/unicast/pff/multipath.h b/src/ipcpd/unicast/pff/multipath.h index 4a5bcefb..0eb03476 100644 --- a/src/ipcpd/unicast/pff/multipath.h +++ b/src/ipcpd/unicast/pff/multipath.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Policy for PFF supporting multipath routing * diff --git a/src/ipcpd/unicast/pff/ops.h b/src/ipcpd/unicast/pff/ops.h index a46f3da8..16a31273 100644 --- a/src/ipcpd/unicast/pff/ops.h +++ b/src/ipcpd/unicast/pff/ops.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Pff policy ops * diff --git a/src/ipcpd/unicast/pff/pft.c b/src/ipcpd/unicast/pff/pft.c index e42b4a98..8c436113 100644 --- a/src/ipcpd/unicast/pff/pft.c +++ b/src/ipcpd/unicast/pff/pft.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Packet forwarding table (PFT) with chaining on collisions * @@ -115,19 +115,11 @@ void pft_flush(struct pft * pft) static uint64_t hash(uint64_t key) { - void * res; - uint64_t ret; - uint8_t keys[4]; + uint64_t res[2]; - memcpy(keys, &key, 4); + mem_hash(HASH_MD5, res, (uint8_t *) &key, sizeof(key)); - mem_hash(HASH_MD5, &res, keys, 4); - - ret = (* (uint64_t *) res); - - free(res); - - return ret; + return res[0]; } static uint64_t calc_key(struct pft * pft, diff --git a/src/ipcpd/unicast/pff/pft.h b/src/ipcpd/unicast/pff/pft.h index 011ad414..711dabcb 100644 --- a/src/ipcpd/unicast/pff/pft.h +++ b/src/ipcpd/unicast/pff/pft.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Packet forwarding table (PFT) with chaining on collisions * diff --git a/src/ipcpd/unicast/pff/pol.h b/src/ipcpd/unicast/pff/pol.h index 322d6dd6..245b03c4 100644 --- a/src/ipcpd/unicast/pff/pol.h +++ b/src/ipcpd/unicast/pff/pol.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * PDU Forwarding Function policies * diff --git a/src/ipcpd/unicast/pff/simple.c b/src/ipcpd/unicast/pff/simple.c index a007bcec..5f95e3ce 100644 --- a/src/ipcpd/unicast/pff/simple.c +++ b/src/ipcpd/unicast/pff/simple.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Simple PDU Forwarding Function * diff --git a/src/ipcpd/unicast/pff/simple.h b/src/ipcpd/unicast/pff/simple.h index e9083cf5..0966a186 100644 --- a/src/ipcpd/unicast/pff/simple.h +++ b/src/ipcpd/unicast/pff/simple.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Simple policy for PFF * diff --git a/src/ipcpd/unicast/pff/tests/CMakeLists.txt b/src/ipcpd/unicast/pff/tests/CMakeLists.txt index e7082372..65705714 100644 --- a/src/ipcpd/unicast/pff/tests/CMakeLists.txt +++ b/src/ipcpd/unicast/pff/tests/CMakeLists.txt @@ -26,7 +26,11 @@ target_link_libraries(${PARENT_DIR}_test ouroboros-common) add_dependencies(check ${PARENT_DIR}_test) set(tests_to_run ${${PARENT_DIR}_tests}) -remove(tests_to_run test_suite.c) +if(CMAKE_VERSION VERSION_LESS "3.29.0") + remove(tests_to_run test_suite.c) +else () + list(POP_FRONT tests_to_run) +endif() foreach (test ${tests_to_run}) get_filename_component(test_name ${test} NAME_WE) diff --git a/src/ipcpd/unicast/pff/tests/pft_test.c b/src/ipcpd/unicast/pff/tests/pft_test.c index c48267eb..18287fb8 100644 --- a/src/ipcpd/unicast/pff/tests/pft_test.c +++ b/src/ipcpd/unicast/pff/tests/pft_test.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Test of the hash table * diff --git a/src/ipcpd/unicast/psched.c b/src/ipcpd/unicast/psched.c index 33ac5afe..7e12148b 100644 --- a/src/ipcpd/unicast/psched.c +++ b/src/ipcpd/unicast/psched.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Packet scheduler component * @@ -50,6 +50,7 @@ static int qos_prio [] = { struct psched { fset_t * set[QOS_CUBE_MAX]; next_packet_fn_t callback; + read_fn_t read; pthread_t readers[QOS_CUBE_MAX * IPCP_SCHED_THR_MUL]; }; @@ -101,7 +102,7 @@ static void * packet_reader(void * o) notifier_event(NOTIFY_DT_FLOW_UP, &fd); break; case FLOW_PKT: - if (ipcp_flow_read(fd, &sdb)) + if (sched->read(fd, &sdb) < 0) continue; sched->callback(fd, qc, sdb); @@ -117,7 +118,8 @@ static void * packet_reader(void * o) return (void *) 0; } -struct psched * psched_create(next_packet_fn_t callback) +struct psched * psched_create(next_packet_fn_t callback, + read_fn_t read) { struct psched * psched; struct sched_info * infos[QOS_CUBE_MAX * IPCP_SCHED_THR_MUL]; @@ -131,6 +133,7 @@ struct psched * psched_create(next_packet_fn_t callback) goto fail_malloc; psched->callback = callback; + psched->read = read; for (i = 0; i < QOS_CUBE_MAX; ++i) { psched->set[i] = fset_create(); diff --git a/src/ipcpd/unicast/psched.h b/src/ipcpd/unicast/psched.h index 1f22b34b..831f8084 100644 --- a/src/ipcpd/unicast/psched.h +++ b/src/ipcpd/unicast/psched.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Packet scheduler component * @@ -30,7 +30,11 @@ typedef void (* next_packet_fn_t)(int fd, qoscube_t qc, struct shm_du_buff * sdb); -struct psched * psched_create(next_packet_fn_t callback); +typedef int (* read_fn_t)(int fd, + struct shm_du_buff ** sdb); + +struct psched * psched_create(next_packet_fn_t callback, + read_fn_t read); void psched_destroy(struct psched * psched); diff --git a/src/ipcpd/unicast/routing.c b/src/ipcpd/unicast/routing.c index 09f7d711..f5417c24 100644 --- a/src/ipcpd/unicast/routing.c +++ b/src/ipcpd/unicast/routing.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Routing component of the IPCP * diff --git a/src/ipcpd/unicast/routing.h b/src/ipcpd/unicast/routing.h index 2eaaeb68..d5d833ae 100644 --- a/src/ipcpd/unicast/routing.h +++ b/src/ipcpd/unicast/routing.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Routing component of the IPCP * diff --git a/src/ipcpd/unicast/routing/graph.c b/src/ipcpd/unicast/routing/graph.c index 6ea5c507..32f3e6fb 100644 --- a/src/ipcpd/unicast/routing/graph.c +++ b/src/ipcpd/unicast/routing/graph.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Undirected graph structure * diff --git a/src/ipcpd/unicast/routing/graph.h b/src/ipcpd/unicast/routing/graph.h index 632cc5a0..8190cc6c 100644 --- a/src/ipcpd/unicast/routing/graph.h +++ b/src/ipcpd/unicast/routing/graph.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Undirected graph structure * diff --git a/src/ipcpd/unicast/routing/link-state.c b/src/ipcpd/unicast/routing/link-state.c index 7ceb86a1..57c0c7cb 100644 --- a/src/ipcpd/unicast/routing/link-state.c +++ b/src/ipcpd/unicast/routing/link-state.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Link state routing policy * @@ -273,7 +273,7 @@ static int lsdb_rib_readdir(char *** buf) if ((*buf)[idx] == NULL) { while (idx-- > 0) free((*buf)[idx]); - free(buf); + free(*buf); pthread_rwlock_unlock(&ls.db_lock); return -ENOMEM; } diff --git a/src/ipcpd/unicast/routing/link-state.h b/src/ipcpd/unicast/routing/link-state.h index c6e573ff..d77d72df 100644 --- a/src/ipcpd/unicast/routing/link-state.h +++ b/src/ipcpd/unicast/routing/link-state.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Link state routing policy * diff --git a/src/ipcpd/unicast/routing/ops.h b/src/ipcpd/unicast/routing/ops.h index 1522ccd9..8a79b7ec 100644 --- a/src/ipcpd/unicast/routing/ops.h +++ b/src/ipcpd/unicast/routing/ops.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Routing policy ops * diff --git a/src/ipcpd/unicast/routing/pol.h b/src/ipcpd/unicast/routing/pol.h index f6e5274e..b6a6f150 100644 --- a/src/ipcpd/unicast/routing/pol.h +++ b/src/ipcpd/unicast/routing/pol.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Routing policies * diff --git a/src/ipcpd/unicast/routing/tests/CMakeLists.txt b/src/ipcpd/unicast/routing/tests/CMakeLists.txt index d0652533..9d24bf03 100644 --- a/src/ipcpd/unicast/routing/tests/CMakeLists.txt +++ b/src/ipcpd/unicast/routing/tests/CMakeLists.txt @@ -26,7 +26,11 @@ target_link_libraries(${PARENT_DIR}_test ouroboros-common) add_dependencies(check ${PARENT_DIR}_test) set(tests_to_run ${${PARENT_DIR}_tests}) -remove(tests_to_run test_suite.c) +if(CMAKE_VERSION VERSION_LESS "3.29.0") + remove(tests_to_run test_suite.c) +else () + list(POP_FRONT tests_to_run) +endif() foreach (test ${tests_to_run}) get_filename_component(test_name ${test} NAME_WE) diff --git a/src/ipcpd/unicast/routing/tests/graph_test.c b/src/ipcpd/unicast/routing/tests/graph_test.c index 217c7eab..d805640c 100644 --- a/src/ipcpd/unicast/routing/tests/graph_test.c +++ b/src/ipcpd/unicast/routing/tests/graph_test.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Test of the graph structure * diff --git a/src/irmd/CMakeLists.txt b/src/irmd/CMakeLists.txt index 59d0d103..c9c2e553 100644 --- a/src/irmd/CMakeLists.txt +++ b/src/irmd/CMakeLists.txt @@ -4,42 +4,84 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_SOURCE_DIR}/include) include_directories(${CMAKE_BINARY_DIR}/include) -set(IRMD_REQ_ARR_TIMEOUT 500 CACHE STRING +find_library(LIBTOML_LIBRARIES toml QUIET) +if (LIBTOML_LIBRARIES) + set(DISABLE_CONFIGFILE FALSE CACHE BOOL + "Disable configuration file support") + if (NOT DISABLE_CONFIGFILE) + set(OUROBOROS_CONFIG_DIR /etc/ouroboros/ CACHE STRING + "Configuration directory") + set(OUROBOROS_CONFIG_FILE irmd.conf CACHE STRING + "Name of the IRMd configuration file") + set(HAVE_TOML TRUE) + message(STATUS "Found TOML C99 library: " ${LIBTOML_LIBRARIES}) + message(STATUS "Configuration file support enabled") + message(STATUS "Configuration directory: ${OUROBOROS_CONFIG_DIR}") + set(INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}") + configure_file("${CMAKE_SOURCE_DIR}/irmd.conf.in" + "${CMAKE_BINARY_DIR}/irmd.conf.example" @ONLY) + install(FILES "${CMAKE_BINARY_DIR}/irmd.conf.example" + DESTINATION "${OUROBOROS_CONFIG_DIR}") + unset(INSTALL_DIR) + mark_as_advanced(LIBTOML_LIBRARIES) + else () + message(STATUS "Configuration file support disabled by user") + unset(OUROBOROS_CONFIG_FILE CACHE) + unset(OUROBOROS_CONFIG_DIR CACHE) + set(HAVE_TOML FALSE) + endif () +else () + message(STATUS "Install tomlc99 for config file support") + message(STATUS " https://github.com/cktan/tomlc99") + set(LIBTOML_LIBRARIES "") + unset(DISABLE_CONFIGFILE CACHE) + unset(HAVE_TOML) +endif () + +set(IRMD_REQ_ARR_TIMEOUT 1000 CACHE STRING "Timeout for an application to respond to a new flow (ms)") -set(IRMD_FLOW_TIMEOUT 5000 CACHE STRING - "Timeout for a flow allocation response (ms)") + set(BOOTSTRAP_TIMEOUT 5000 CACHE STRING "Timeout for an IPCP to bootstrap (ms)") -set(ENROLL_TIMEOUT 60000 CACHE STRING +set(ENROLL_TIMEOUT 20000 CACHE STRING "Timeout for an IPCP to enroll (ms)") -set(REG_TIMEOUT 10000 CACHE STRING +set(REG_TIMEOUT 20000 CACHE STRING "Timeout for registering a name (ms)") -set(QUERY_TIMEOUT 3000 CACHE STRING +set(QUERY_TIMEOUT 20000 CACHE STRING "Timeout to query a name with an IPCP (ms)") -set(CONNECT_TIMEOUT 60000 CACHE STRING +set(CONNECT_TIMEOUT 20000 CACHE STRING "Timeout to connect an IPCP to another IPCP (ms)") +set(FLOW_ALLOC_TIMEOUT 20000 CACHE STRING + "Timeout for a flow allocation response (ms)") set(IRMD_MIN_THREADS 8 CACHE STRING - "Minimum number of worker threads in the IRMd.") + "Minimum number of worker threads in the IRMd") set(IRMD_ADD_THREADS 8 CACHE STRING "Number of extra threads to start when the IRMD faces thread starvation") +set(IRMD_PKILL_TIMEOUT 30 CACHE STRING + "Number of seconds to wait before sending SIGKILL to subprocesses on exit") +set(IRMD_KILL_ALL_PROCESSES TRUE CACHE BOOL + "Kill all processes on exit") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/config.h" @ONLY) set(SOURCE_FILES # Add source files here - proc_table.c - prog_table.c ipcp.c - irm_flow.c + configfile.c main.c - registry.c - utils.c + reg/flow.c + reg/ipcp.c + reg/proc.c + reg/prog.c + reg/name.c + reg/reg.c ) add_executable (irmd ${SOURCE_FILES}) -target_link_libraries (irmd LINK_PUBLIC ouroboros-common) +target_link_libraries (irmd LINK_PUBLIC ouroboros-common + ${LIBTOML_LIBRARIES}) include(AddCompileFlags) if (CMAKE_BUILD_TYPE MATCHES "Debug*") @@ -49,4 +91,5 @@ endif () install(TARGETS irmd RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}) # Enable once irmd has tests -# add_subdirectory(tests) +#add_subdirectory(tests) +add_subdirectory(reg) diff --git a/src/irmd/config.h.in b/src/irmd/config.h.in index a7bd9066..fa1156b9 100644 --- a/src/irmd/config.h.in +++ b/src/irmd/config.h.in @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Configuration for the IPC Resource Manager * @@ -20,36 +20,78 @@ * Foundation, Inc., http://www.fsf.org/about/contact/. */ -#define IPCP_UDP_EXEC "@IPCP_UDP_TARGET@" -#define IPCP_ETH_LLC_EXEC "@IPCP_ETH_LLC_TARGET@" -#define IPCP_ETH_DIX_EXEC "@IPCP_ETH_DIX_TARGET@" -#define IPCP_UNICAST_EXEC "@IPCP_UNICAST_TARGET@" -#define IPCP_BROADCAST_EXEC "@IPCP_BROADCAST_TARGET@" -#define IPCP_LOCAL_EXEC "@IPCP_LOCAL_TARGET@" -#define INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@" +#define IPCP_UDP_EXEC "@IPCP_UDP_TARGET@" +#define IPCP_ETH_LLC_EXEC "@IPCP_ETH_LLC_TARGET@" +#define IPCP_ETH_DIX_EXEC "@IPCP_ETH_DIX_TARGET@" +#define IPCP_UNICAST_EXEC "@IPCP_UNICAST_TARGET@" +#define IPCP_BROADCAST_EXEC "@IPCP_BROADCAST_TARGET@" +#define IPCP_LOCAL_EXEC "@IPCP_LOCAL_TARGET@" -#define PTHREAD_COND_CLOCK @PTHREAD_COND_CLOCK@ +#define INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@" +#define INSTALL_SBINDIR "@CMAKE_INSTALL_SBINDIR@" -#define SOCKET_TIMEOUT @SOCKET_TIMEOUT@ +#define PTHREAD_COND_CLOCK @PTHREAD_COND_CLOCK@ -#define IRMD_REQ_ARR_TIMEOUT @IRMD_REQ_ARR_TIMEOUT@ -#define IRMD_FLOW_TIMEOUT @IRMD_FLOW_TIMEOUT@ +#define SOCKET_TIMEOUT @SOCKET_TIMEOUT@ -#define BOOTSTRAP_TIMEOUT @BOOTSTRAP_TIMEOUT@ -#define ENROLL_TIMEOUT @ENROLL_TIMEOUT@ -#define REG_TIMEOUT @REG_TIMEOUT@ -#define QUERY_TIMEOUT @QUERY_TIMEOUT@ -#define CONNECT_TIMEOUT @CONNECT_TIMEOUT@ +#define IRMD_REQ_ARR_TIMEOUT @IRMD_REQ_ARR_TIMEOUT@ -#define SYS_MAX_FLOWS @SYS_MAX_FLOWS@ +#define FLOW_ALLOC_TIMEOUT @FLOW_ALLOC_TIMEOUT@ +#define FLOW_DEALLOC_TIMEOUT @FLOW_DEALLOC_TIMEOUT@ + +#define BOOTSTRAP_TIMEOUT @BOOTSTRAP_TIMEOUT@ +#define ENROLL_TIMEOUT @ENROLL_TIMEOUT@ +#define REG_TIMEOUT @REG_TIMEOUT@ +#define QUERY_TIMEOUT @QUERY_TIMEOUT@ +#define CONNECT_TIMEOUT @CONNECT_TIMEOUT@ + +#define SYS_MAX_FLOWS @SYS_MAX_FLOWS@ +#define IRMD_MIN_THREADS @IRMD_MIN_THREADS@ +#define IRMD_ADD_THREADS @IRMD_ADD_THREADS@ -#define IRMD_MIN_THREADS @IRMD_MIN_THREADS@ -#define IRMD_ADD_THREADS @IRMD_ADD_THREADS@ #cmakedefine HAVE_FUSE #ifdef HAVE_FUSE -#define FUSE_PREFIX "@FUSE_PREFIX@" +#define FUSE_PREFIX "@FUSE_PREFIX@" +#endif + +#cmakedefine HAVE_TOML +#ifdef HAVE_TOML +#define OUROBOROS_CONFIG_DIR "@OUROBOROS_CONFIG_DIR@" +#define OUROBOROS_CONFIG_FILE "@OUROBOROS_CONFIG_FILE@" #endif +#define IRMD_PKILL_TIMEOUT @IRMD_PKILL_TIMEOUT@ + +#cmakedefine IRMD_KILL_ALL_PROCESSES #cmakedefine HAVE_LIBGCRYPT + +#define O7S_ASCII_ART \ +"[0m\n" \ +" [38;5;4m▄▄█████▄▄▄[38;5;7m\n" \ +" [38;5;4m▄█▀▀[38;5;7m [38;5;4m▀▀███▄[38;5;7m " \ +"[38;5;8mâ–ˆ[38;5;7m\n" \ +" [38;5;4m██[38;5;7m [38;5;4mâ–„â–„â–„[38;5;7m [38;5;4m▄███▄[38;5;7m " \ +"[38;5;8mâ–„[38;5;7m [38;5;8mâ–„[38;5;7m [38;5;8mâ–„[38;5;7m [38;5;8mâ–„â–„" \ +"[38;5;7m [38;5;8mâ–„â–„[38;5;7m [38;5;8mâ–ˆ[38;5;7m [38;5;8mâ–„â–„[38;5;7m " \ +" [38;5;8mâ–„â–„[38;5;7m [38;5;8mâ–„[38;5;7m [38;5;8mâ–„â–„[38;5;7m [38;5;8m" \ +"â–„â–„[38;5;7m [38;5;8mâ–„â–„[38;5;7m\n" \ +" [38;5;4m██[38;5;7m [38;5;4mâ–ˆ[38;5;7m [38;5;4mâ–ˆ[38;5;7m " \ +"[38;5;4m█████[38;5;7m [38;5;8mâ–ˆ[38;5;7m [38;5;8mâ–ˆ[38;5;7m " \ +"[38;5;8m█▀[38;5;7m [38;5;8mâ–€[38;5;7m [38;5;8mâ–ˆ[38;5;7m [38;5;8mâ–ˆ" \ +"[38;5;7m [38;5;8m█▀[38;5;7m [38;5;8mâ–ˆ[38;5;7m [38;5;8mâ–ˆ[38;5;7m " \ +"[38;5;8mâ–ˆ[38;5;7m [38;5;8m█▀[38;5;7m [38;5;8mâ–€[38;5;7m [38;5;8mâ–ˆ" \ +"[38;5;7m [38;5;8mâ–ˆ[38;5;7m [38;5;8m▀▄[38;5;7m [38;5;8mâ–€[38;5;7m\n" \ +" [38;5;4m██[38;5;7m [38;5;4m▀▄▄▄▀[38;5;7m [38;5;4m▀█▀[38;5;7m " \ +"[38;5;8mâ–ˆ[38;5;7m [38;5;8mâ–ˆ[38;5;7m [38;5;8mâ–ˆ[38;5;7m [38;5;8m" \ +"â–ˆ[38;5;7m [38;5;8mâ–ˆ[38;5;7m [38;5;8m█▄[38;5;7m [38;5;8mâ–ˆ[38;5;7m " \ +"[38;5;8mâ–ˆ[38;5;7m [38;5;8mâ–ˆ[38;5;7m [38;5;8mâ–ˆ[38;5;7m [38;5;8mâ–ˆ" \ +"[38;5;7m [38;5;8mâ–ˆ[38;5;7m [38;5;8mâ–„[38;5;7m [38;5;8m▀▄[38;5;7m\n" \ +" [38;5;4m█▄[38;5;7m [38;5;4mâ–ˆ[38;5;7m [38;5;8m▀▀▀" \ +"[38;5;7m [38;5;8mâ–€[38;5;7m [38;5;8mâ–€[38;5;7m [38;5;8m▀▀[38;5;7m" \ +" [38;5;8mâ–€[38;5;7m [38;5;8m▀▀[38;5;7m [38;5;8m▀▀[38;5;7m " \ +"[38;5;8mâ–€[38;5;7m [38;5;8m▀▀[38;5;7m [38;5;8m▀▀[38;5;7m\n" \ +" [38;5;4m▀█▄▄▄▄▄▄▄▄▀[38;5;7m\n" \ +" [38;5;4m▀▀▀▀▀▀[38;5;7m\n" \ +"\n" diff --git a/src/irmd/configfile.c b/src/irmd/configfile.c new file mode 100644 index 00000000..688c4ade --- /dev/null +++ b/src/irmd/configfile.c @@ -0,0 +1,871 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager / Configuration from file + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + + +#include "config.h" + +#if defined (HAVE_TOML) + +#define _POSIX_C_SOURCE 200809L +#define _XOPEN_SOURCE 500 + +#define OUROBOROS_PREFIX "irmd/configuration" + +#include <ouroboros/errno.h> +#include <ouroboros/ipcp.h> +#include <ouroboros/logs.h> +#include <ouroboros/utils.h> + +#include "irmd.h" +#include "configfile.h" + +#include "reg/reg.h" + +#include <assert.h> +#include <errno.h> +#include <inttypes.h> +#include <stdlib.h> +#include <string.h> +#include <toml.h> +#include <arpa/inet.h> + +#define ERRBUFSZ 200 + +static int toml_hash(toml_table_t * table, + struct layer_info * info) +{ + toml_datum_t hash; + + hash = toml_string_in(table, "hash"); + if (!hash.ok) { + log_dbg("No hash specified, using default."); + return 0; + } + + if (strcmp(hash.u.s, "SHA3_224") == 0) { + info->dir_hash_algo = DIR_HASH_SHA3_224; + } else if (strcmp(hash.u.s, "SHA3_256") == 0) { + info->dir_hash_algo = DIR_HASH_SHA3_256; + } else if (strcmp(hash.u.s, "SHA3_384") == 0) { + info->dir_hash_algo = DIR_HASH_SHA3_384; + } else if (strcmp(hash.u.s, "SHA3_512") == 0) { + info->dir_hash_algo = DIR_HASH_SHA3_512; + } else { + log_err("Unknown hash algorithm: %s.", hash.u.s); + free(hash.u.s); + return -1; + } + + free(hash.u.s); + + return 0; +} + +static int toml_local(toml_table_t * table, + struct ipcp_config * conf) +{ + *conf = local_default_conf; + + return toml_hash(table, &conf->layer_info); +} + +static int toml_eth_dev(toml_table_t * table, + struct eth_config * conf) +{ + toml_datum_t dev; + + dev = toml_string_in(table, "dev"); + if (!dev.ok) { + log_err("Missing device."); + return -1; + } + + if (strlen(dev.u.s) > DEV_NAME_SIZE) { + log_err("Device name too long: %s", dev.u.s); + free(dev.u.s); + return -1; + } + + strcpy(conf->dev, dev.u.s); + free(dev.u.s); + + return 0; +} + +static int toml_eth_llc(toml_table_t * table, + struct ipcp_config * conf) +{ + *conf = eth_llc_default_conf; + + if (toml_hash(table, &conf->layer_info) < 0) + return -1; + + return toml_eth_dev(table, &conf->eth); +} + + +static int toml_ethertype(toml_table_t * table, + struct eth_config * conf) +{ + toml_datum_t ethertype; + + ethertype = toml_int_in(table, "ethertype"); + if (ethertype.ok) + conf->ethertype = ethertype.u.i; + + if (conf->ethertype < 0x0600 || conf->ethertype == 0xFFFF) + return -1; + + return 0; +} + +static int toml_eth_dix(toml_table_t * table, + struct ipcp_config * conf) +{ + *conf = eth_dix_default_conf; + + if (toml_hash(table, &conf->layer_info) < 0) + return -1; + + if (toml_eth_dev(table, &conf->eth) < 0) + return -1; + + if (toml_ethertype(table, &conf->eth) < 0) { + log_err("Ethertype not in valid range."); + return -1; + } + + return 0; +} + +static int toml_udp(toml_table_t * table, + struct ipcp_config * conf) +{ + toml_datum_t ip; + toml_datum_t port; + toml_datum_t dns; + + *conf = udp_default_conf; + + ip = toml_string_in(table, "ip"); + if (!ip.ok) { + log_err("No IP address specified!"); + goto fail_ip; + } + + if (inet_pton (AF_INET, ip.u.s, &conf->udp.ip_addr) != 1) { + log_err("Failed to parse IPv4 address %s.", ip.u.s); + goto fail_addr; + } + + port = toml_int_in(table, "port"); + if (port.ok) + conf->udp.port = port.u.i; + + dns = toml_string_in(table, "dns"); + if (dns.ok) { + if (inet_pton(AF_INET, dns.u.s, &conf->udp.dns_addr) < 0) { + log_err("Failed to parse DNS address %s.", ip.u.s); + goto fail_dns; + } + + free(dns.u.s); + } + + free(ip.u.s); + + return 0; + + fail_dns: + free(dns.u.s); + fail_addr: + free(ip.u.s); + fail_ip: + return -1; +} + +static int toml_broadcast(toml_table_t * table, + struct ipcp_config * conf) +{ + (void) table; + (void) conf; + + /* Nothing to do here. */ + + return 0; +} + +static int toml_routing(toml_table_t * table, + struct dt_config * conf) +{ + toml_datum_t routing; + + routing = toml_string_in(table, "routing"); + if (routing.ok) { + if (strcmp(routing.u.s, "link-state") == 0) + conf->routing_type = ROUTING_LINK_STATE; + else if (strcmp(routing.u.s, "lfa") == 0) + conf->routing_type = ROUTING_LINK_STATE_LFA; + else if (strcmp(routing.u.s, "ecmp") == 0) + conf->routing_type = ROUTING_LINK_STATE_ECMP; + else + conf->routing_type = ROUTING_INVALID; + free(routing.u.s); + } + + if (conf->routing_type == ROUTING_INVALID) + return -1; + + return 0; +} + +static int toml_addr_auth(toml_table_t * table, + struct uni_config * conf) +{ + toml_datum_t addr_auth; + + addr_auth = toml_string_in(table, "addr-auth"); + if (addr_auth.ok) { + if (strcmp(addr_auth.u.s, "flat") == 0) + conf->addr_auth_type = ADDR_AUTH_FLAT_RANDOM; + else + conf->addr_auth_type = ADDR_AUTH_INVALID; + free(addr_auth.u.s); + } + + if (conf->addr_auth_type == ADDR_AUTH_INVALID) + return -1; + + return 0; +} + +static int toml_congestion(toml_table_t * table, + struct uni_config * conf) +{ + toml_datum_t congestion; + + congestion = toml_string_in(table, "congestion"); + if (congestion.ok) { + if (strcmp(congestion.u.s, "none") == 0) + conf->cong_avoid = CA_NONE; + else if (strcmp(congestion.u.s, "lfa") == 0) + conf->cong_avoid = CA_MB_ECN; + else + conf->cong_avoid = CA_INVALID; + free(congestion.u.s); + + } + + if (conf->cong_avoid == CA_INVALID) + return -1; + + return 0; +} + +static int toml_dt(toml_table_t * table, + struct dt_config * conf) +{ + toml_datum_t addr; + toml_datum_t eid; + toml_datum_t ttl; + + addr = toml_int_in(table, "addr_size"); + if (addr.ok) + conf->addr_size = addr.u.i; + + eid = toml_int_in(table, "eid_size"); + if (eid.ok) + conf->eid_size = eid.u.i; + + ttl = toml_int_in(table, "max_ttl"); + if (ttl.ok) + conf->max_ttl = ttl.u.i; + + if (toml_routing(table, conf) < 0) { + log_err("Invalid routing option."); + return -1; + } + + return 0; +} + +static int toml_unicast(toml_table_t * table, + struct ipcp_config * conf) +{ + + + *conf = uni_default_conf; + + if (toml_hash(table, &conf->layer_info) < 0) + return -1; + + if (toml_dt(table, &conf->unicast.dt) < 0) { + log_err("Invalid DT configuration."); + return -1; + } + + if (toml_addr_auth(table, &conf->unicast) < 0) { + log_err("Invalid address authority"); + return -1; + } + + if (toml_congestion(table, &conf->unicast) < 0) { + log_err("Invalid congestion avoidance algorithm."); + return -1; + } + + return 0; +} + +static int toml_autobind(toml_table_t * table, + pid_t pid, + const char * name, + const char * layer) +{ + toml_datum_t autobind; + + autobind = toml_bool_in(table, "autobind"); + if (!autobind.ok) + return 0; + + if (bind_process(pid, name) < 0) { + log_err("Failed to bind IPCP process %d to %s.", pid, name); + return -1; + } + + if (layer != NULL && bind_process(pid, layer) < 0) { + log_err("Failed to bind IPCP process %d to %s.", pid, layer); + return -1; + } + + return 0; +} + +static int toml_register(toml_table_t * table, + pid_t pid) +{ + toml_array_t * reg; + int i; + int ret = 0; + struct name_info info = { + .pol_lb = LB_SPILL + }; + + reg = toml_array_in(table, "reg"); + if (reg == NULL) + return 0; + + for (i = 0; ret == 0; i++) { + toml_datum_t name; + + name = toml_string_at(reg, i); + if (!name.ok) + break; + + log_dbg("Registering %s in %d", name.u.s, pid); + + strcpy(info.name, name.u.s); + + ret = name_create(&info); + if (ret < 0 && ret != -ENAME) { + free(name.u.s); + break; + } + + ret = name_reg(name.u.s, pid); + free(name.u.s); + } + + return ret; +} + +static int toml_connect(toml_table_t * table, + pid_t pid) +{ + toml_array_t * conn; + int i; + int ret = 0; + + conn = toml_array_in(table, "conn"); + if (conn == NULL) + return 0; + + for (i=0; ret == 0; i++) { + toml_datum_t dst; + qosspec_t qs = qos_raw; + + dst = toml_string_at(conn, i); + if (!dst.ok) + break; + + log_dbg("Connecting %d to %s", pid, dst.u.s); + + ret = connect_ipcp(pid, dst.u.s, MGMT_COMP, qs); + if (ret == 0) + ret = connect_ipcp(pid, dst.u.s, DT_COMP, qs); + + free(dst.u.s); + } + + return ret; +} + +static int toml_ipcp(toml_table_t * table, + struct ipcp_info * info, + struct ipcp_config * conf) +{ + toml_datum_t bootstrap; + toml_datum_t enrol; + int ret; + + log_dbg("Found IPCP %s in configuration file.", info->name); + + if (create_ipcp(info) < 0) { + log_err("Failed to create IPCP %s.", info->name); + return -1; + } + + bootstrap = toml_string_in(table, "bootstrap"); + enrol = toml_string_in(table, "enrol"); + + if (bootstrap.ok && enrol.ok) { + log_err("Ignoring bootstrap for IPCP %s.", info->name); + free(bootstrap.u.s); + bootstrap.ok = false; + } + + if (!bootstrap.ok && !enrol.ok) { + log_dbg("Nothing more to do for %s.", info->name); + return 0; + } + + if (enrol.ok) { + struct layer_info layer; + ret = enroll_ipcp(info->pid, enrol.u.s); + free(enrol.u.s); + if (ret < 0) { + log_err("Failed to enrol %s.", info->name); + return -1; + } + + if (reg_get_ipcp(info, &layer) < 0) + return -1; + + if (toml_autobind(table, info->pid, info->name, layer.name)) + return -1; + + if (toml_register(table, info->pid) < 0) { + log_err("Failed to register names."); + return -1; + } + + if (toml_connect(table, info->pid) < 0) { + log_err("Failed to register names."); + return -1; + } + + return 0; + } + + assert(bootstrap.ok); + + if (strlen(bootstrap.u.s) > LAYER_NAME_SIZE) { + log_err("Layer name too long: %s", bootstrap.u.s); + free(bootstrap.u.s); + return -1; + } + + switch (conf->type) { + case IPCP_LOCAL: + ret = toml_local(table, conf); + break; + case IPCP_ETH_DIX: + ret = toml_eth_dix(table, conf); + break; + case IPCP_ETH_LLC: + ret = toml_eth_llc(table, conf); + break; + case IPCP_UDP: + ret = toml_udp(table, conf); + break; + case IPCP_BROADCAST: + ret = toml_broadcast(table, conf); + break; + case IPCP_UNICAST: + ret = toml_unicast(table, conf); + break; + default: + log_err("Invalid IPCP type"); + ret = -1; + } + + if (ret < 0) + return -1; + + strcpy(conf->layer_info.name, bootstrap.u.s); + free(bootstrap.u.s); + + if (bootstrap_ipcp(info->pid, conf) < 0) + return -1; + + if (toml_autobind(table, info->pid, info->name, + conf->layer_info.name) < 0) + return -1; + + if (toml_register(table, info->pid) < 0) { + log_err("Failed to register names."); + return -1; + } + + return 0; +} + +static int toml_ipcp_list(toml_table_t * table, + enum ipcp_type type) +{ + int i = 0; + int ret = 0; + + for (i = 0; ret == 0; i++) { + const char * key; + struct ipcp_info info; + struct ipcp_config conf; + + memset(&conf, 0, sizeof(conf)); + memset(&info, 0, sizeof(info)); + + key = toml_key_in(table, i); + if (key == NULL) + break; + + if (strlen(key) > IPCP_NAME_SIZE) { + log_err("IPCP name too long: %s,", key); + return -1; + } + + info.type = type; + strcpy(info.name,key); + conf.type = type; + + ret = toml_ipcp(toml_table_in(table, key), &info, &conf); + } + + return ret; +} + +static int args_to_argv(const char * prog, + const char * args, + char *** argv) +{ + char * tok; + char * str; + int argc = 0; + + str = (char *) args; + + if (str != NULL) { + tok = str; + while (*(tok += strspn(tok, " ")) != '\0') { + tok += strcspn(tok, " "); + argc++; + } + } + + *argv = malloc((argc + 2) * sizeof(**argv)); + if (*argv == NULL) + goto fail_malloc; + + (*argv)[0] = strdup(prog); + if ((*argv)[0] == NULL) + goto fail_malloc2; + + argc = 1; + + if (str == NULL) + goto finish; + + tok = str; + while (*(tok += strspn(tok, " ")) != '\0') { + size_t toklen = strcspn(tok, " "); + (*argv)[argc] = malloc((toklen + 1) * sizeof(***argv)); + if ((*argv)[argc] == NULL) + goto fail_malloc2; + + strncpy((*argv)[argc], tok, toklen); + (*argv)[argc++][toklen] = '\0'; + tok += toklen; + } + + finish: + (*argv)[argc] = NULL; + + return argc; + + fail_malloc2: + argvfree(*argv); + fail_malloc: + return -1; + +} + +static int toml_prog(const char * prog, + const char * args, + const char * name) +{ + uint16_t flags = 0; + int argc; + char ** exec; + int ret; + + if (args != NULL) + flags |= BIND_AUTO; + + argc = args_to_argv(prog, args, &exec); + if (argc < 0) { + log_err("Failed to parse arguments: %s", args); + return -1; + } + + ret = bind_program(exec, name, flags); + if (ret < 0) + log_err("Failed to bind program %s %s for name %s.", + prog, args, name); + + argvfree(exec); + + return ret; +} + +static int toml_prog_list(toml_array_t * progs, + toml_array_t * args, + const char * name) +{ + int ret = 0; + int i; + + for (i = 0; ret == 0; i++) { + toml_datum_t prog; + toml_datum_t arg; + + prog = toml_string_at(progs, i); + if (!prog.ok) + break; + + if (args == NULL) { + ret = toml_prog(prog.u.s, NULL, name); + } else { + arg = toml_string_at(args, i); + if (!arg.ok) { + args = NULL; /* no more arguments in list. */ + assert(arg.u.s == NULL); + } + + ret = toml_prog(prog.u.s, arg.u.s, name); + + if (arg.ok) + free(arg.u.s); + } + + free(prog.u.s); + } + + return ret; +} + +static int toml_name(toml_table_t * table, + const char * name) +{ + toml_array_t * progs; + toml_array_t * args; + toml_datum_t lb; + struct name_info info = { + .pol_lb = LB_SPILL + }; + + log_dbg("Found service name %s in configuration file.", name); + + lb = toml_string_in(table, "lb"); + if (lb.ok) { + if (strcmp(lb.u.s, "spill") == 0) + info.pol_lb = LB_SPILL; + else if (strcmp(lb.u.s, "round-robin") == 0) + info.pol_lb = LB_RR; + else + info.pol_lb = LB_INVALID; + free(lb.u.s); + } + + if (info.pol_lb == LB_INVALID) { + log_err("Invalid load-balancing policy for %s.", name); + return -1; + } + + strcpy(info.name, name); + + if (name_create(&info) < 0) { + log_err("Failed to create name %s.", name); + return -1; + } + + progs = toml_array_in(table, "prog"); + if (progs == NULL) + return 0; + + args = toml_array_in(table, "args"); + if (toml_prog_list(progs, args, name) < 0) + return -1; + + return 0; +} + +static int toml_name_list(toml_table_t * table) +{ + int i = 0; + int ret = 0; + + for (i = 0; ret == 0; i++) { + const char * key; + + key = toml_key_in(table, i); + if (key == NULL) + break; + + ret = toml_name(toml_table_in(table, key), key); + } + + return ret; + return 0; +} + +static int toml_toplevel(toml_table_t * table, + const char * key) +{ + toml_table_t * subtable; + + subtable = toml_table_in(table, key); + + if (strcmp(key, "local") == 0) + return toml_ipcp_list(subtable, IPCP_LOCAL); + else if (strcmp(key, "eth-dix") == 0) + return toml_ipcp_list(subtable, IPCP_ETH_DIX); + else if (strcmp(key, "eth-llc") == 0) + return toml_ipcp_list(subtable, IPCP_ETH_LLC); + else if (strcmp(key, "udp") == 0) + return toml_ipcp_list(subtable, IPCP_UDP); + else if (strcmp(key, "broadcast") == 0) + return toml_ipcp_list(subtable, IPCP_BROADCAST); + else if (strcmp(key, "unicast") == 0) + return toml_ipcp_list(subtable, IPCP_UNICAST); + else if (strcmp(key, "name") == 0) + return toml_name_list(subtable); + + log_err("Unkown toplevel key: %s.", key); + return -1; +} + +static int toml_load(toml_table_t * table) +{ + int i = 0; + int ret = 0; + + for (i = 0; ret == 0; i++) { + const char * key; + + key = toml_key_in(table, i); + if (key == NULL) + break; + + ret = toml_toplevel(table, key); + } + + return ret; +} + +static int toml_cfg(FILE * fp) +{ + toml_table_t * table; + char errbuf[ERRBUFSZ + 1]; + + assert(fp != NULL); + + table = toml_parse_file(fp, errbuf, sizeof(errbuf)); + if (table == NULL) { + log_err("Failed to parse config file: %s.", errbuf); + goto fail_parse; + } + + if (toml_load(table) < 0) { + log_err("Failed to load configuration."); + goto fail_load; + } + + toml_free(table); + + return 0; + + fail_load: + toml_free(table); + fail_parse: + return -1; +} + +int irm_configure(const char * path) +{ + FILE * fp; + char * rp; + + if (path == NULL) + return 0; + + rp = realpath(path, NULL); + if (rp == NULL) { + log_err("Failed to resolve path for %s", path); + goto fail_resolve; + } + + log_info("Reading configuration from file %s", rp); + + fp = fopen(rp, "r"); + if (fp == NULL) { + log_err("Failed to open config file: %s\n", strerror(errno)); + goto fail_fopen; + } + + if (toml_cfg(fp) < 0) { + log_err("Failed to load config file."); + goto fail_cfg; + } + + fclose(fp); + free(rp); + + return 0; + + fail_cfg: + fclose(fp); + fail_fopen: + free(rp); + fail_resolve: + return -1; +} + +#endif /* HAVE_TOML */ diff --git a/src/ipcpd/broadcast/enroll.c b/src/irmd/configfile.h index 143f16d5..3ccf53fd 100644 --- a/src/ipcpd/broadcast/enroll.c +++ b/src/irmd/configfile.h @@ -1,7 +1,7 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * - * Enrollment Task + * The IPC Resource Manager / Configuration from file * * Dimitri Staessens <dimitri@ouroboros.rocks> * Sander Vrijders <sander@ouroboros.rocks> @@ -19,6 +19,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., http://www.fsf.org/about/contact/. */ -#define BUILD_IPCP_BROADCAST -#include "common/enroll.c" + +#ifndef OUROBOROS_IRMD_CONFIGURATION_H +#define OUROBOROS_IRMD_CONFIGURATION_H + +int irm_configure(const char * path); + +#endif /* OUROBOROS_IRMD_CONFIGURATION_H */ diff --git a/src/irmd/ipcp.c b/src/irmd/ipcp.c index ae5325c5..5a9a79d3 100644 --- a/src/irmd/ipcp.c +++ b/src/irmd/ipcp.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * The API to instruct IPCPs * @@ -20,195 +20,182 @@ * Foundation, Inc., http://www.fsf.org/about/contact/. */ -#define _POSIX_C_SOURCE 199309L +#define _POSIX_C_SOURCE 200112L #include "config.h" #define OUROBOROS_PREFIX "irmd/ipcp" -#include <ouroboros/logs.h> #include <ouroboros/errno.h> -#include <ouroboros/utils.h> +#include <ouroboros/flow.h> +#include <ouroboros/logs.h> #include <ouroboros/sockets.h> +#include <ouroboros/time.h> +#include <ouroboros/utils.h> #include "ipcp.h" -#include <stdlib.h> -#include <string.h> +#include <fcntl.h> +#include <pthread.h> #include <signal.h> +#include <spawn.h> #include <stdbool.h> -#include <pthread.h> +#include <stdlib.h> +#include <string.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/socket.h> #include <sys/time.h> -#include <spawn.h> -ipcp_msg_t * send_recv_ipcp_msg(pid_t pid, - ipcp_msg_t * msg) +static char * str_ipcp_cmd(int code) { - int sockfd = 0; - uint8_t buf[SOCK_BUF_SIZE]; - char * sock_path = NULL; - ssize_t len; - ipcp_msg_t * recv_msg = NULL; - struct timeval tv; - - if (kill(pid, 0) < 0) - return NULL; - - sock_path = ipcp_sock_path(pid); - if (sock_path == NULL) - return NULL; - - sockfd = client_socket_open(sock_path); - if (sockfd < 0) { - free(sock_path); - return NULL; - } - - free(sock_path); - - len = ipcp_msg__get_packed_size(msg); - if (len == 0) { - close(sockfd); - return NULL; - } - - switch (msg->code) { - case IPCP_MSG_CODE__IPCP_BOOTSTRAP: - tv.tv_sec = BOOTSTRAP_TIMEOUT / 1000; - tv.tv_usec = (BOOTSTRAP_TIMEOUT % 1000) * 1000; - break; - case IPCP_MSG_CODE__IPCP_ENROLL: - tv.tv_sec = ENROLL_TIMEOUT / 1000; - tv.tv_usec = (ENROLL_TIMEOUT % 1000) * 1000; - break; - case IPCP_MSG_CODE__IPCP_REG: - tv.tv_sec = REG_TIMEOUT / 1000; - tv.tv_usec = (REG_TIMEOUT % 1000) * 1000; - break; - case IPCP_MSG_CODE__IPCP_QUERY: - tv.tv_sec = QUERY_TIMEOUT / 1000; - tv.tv_usec = (QUERY_TIMEOUT % 1000) * 1000; - break; - case IPCP_MSG_CODE__IPCP_CONNECT: - tv.tv_sec = CONNECT_TIMEOUT / 1000; - tv.tv_usec = (CONNECT_TIMEOUT % 1000) * 1000; - break; - default: - tv.tv_sec = SOCKET_TIMEOUT / 1000; - tv.tv_usec = (SOCKET_TIMEOUT % 1000) * 1000; - break; - } - - if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, - (void *) &tv, sizeof(tv))) - log_warn("Failed to set timeout on socket."); - - pthread_cleanup_push(__cleanup_close_ptr, (void *) &sockfd); - - ipcp_msg__pack(msg, buf); - - if (write(sockfd, buf, len) != -1) - len = read(sockfd, buf, SOCK_BUF_SIZE); - - if (len > 0) - recv_msg = ipcp_msg__unpack(NULL, len, buf); - - pthread_cleanup_pop(true); - - return recv_msg; + switch (code) { + case IPCP_MSG_CODE__IPCP_BOOTSTRAP: + return "bootstrap"; + case IPCP_MSG_CODE__IPCP_ENROLL: + return "enroll"; + case IPCP_MSG_CODE__IPCP_CONNECT: + return "connect"; + case IPCP_MSG_CODE__IPCP_DISCONNECT: + return "disconnect"; + case IPCP_MSG_CODE__IPCP_REG: + return "reg"; + case IPCP_MSG_CODE__IPCP_UNREG: + return "unreg"; + case IPCP_MSG_CODE__IPCP_QUERY: + return "query"; + case IPCP_MSG_CODE__IPCP_FLOW_JOIN: + return "join"; + case IPCP_MSG_CODE__IPCP_FLOW_ALLOC: + return "alloc"; + case IPCP_MSG_CODE__IPCP_FLOW_ALLOC_RESP: + return "alloc_resp"; + case IPCP_MSG_CODE__IPCP_FLOW_DEALLOC: + return "dealloc"; + default: + assert(false); + return "unknown"; + } } -pid_t ipcp_create(const char * name, - enum ipcp_type ipcp_type) +ipcp_msg_t * send_recv_ipcp_msg(pid_t pid, + ipcp_msg_t * msg) { - pid_t pid = -1; - char * ipcp_dir = "/sbin/"; - char * exec_name = NULL; - char irmd_pid[10]; - char full_name[256]; - char * argv[5]; - - switch(ipcp_type) { - case IPCP_UNICAST: - exec_name = IPCP_UNICAST_EXEC; + int sockfd; + uint8_t buf[SOCK_BUF_SIZE]; + char * sock_path; + ssize_t len; + ipcp_msg_t * recv_msg; + struct timeval tv; + struct timespec tic; + struct timespec toc; + bool dealloc = false; + + if (kill(pid, 0) < 0) + return NULL; + + sock_path = ipcp_sock_path(pid); + if (sock_path == NULL) + return NULL; + + sockfd = client_socket_open(sock_path); + if (sockfd < 0) { + free(sock_path); + return NULL; + } + + free(sock_path); + + len = ipcp_msg__get_packed_size(msg); + if (len == 0 || len >= SOCK_BUF_SIZE) { + log_warn("IPCP message has invalid size: %zd.", len); + close(sockfd); + return NULL; + } + + switch (msg->code) { + case IPCP_MSG_CODE__IPCP_BOOTSTRAP: + tv.tv_sec = BOOTSTRAP_TIMEOUT / 1000; + tv.tv_usec = (BOOTSTRAP_TIMEOUT % 1000) * 1000; + break; + case IPCP_MSG_CODE__IPCP_ENROLL: + tv.tv_sec = ENROLL_TIMEOUT / 1000; + tv.tv_usec = (ENROLL_TIMEOUT % 1000) * 1000; break; - case IPCP_BROADCAST: - exec_name = IPCP_BROADCAST_EXEC; + case IPCP_MSG_CODE__IPCP_REG: + tv.tv_sec = REG_TIMEOUT / 1000; + tv.tv_usec = (REG_TIMEOUT % 1000) * 1000; break; - case IPCP_UDP: - exec_name = IPCP_UDP_EXEC; + case IPCP_MSG_CODE__IPCP_QUERY: + tv.tv_sec = QUERY_TIMEOUT / 1000; + tv.tv_usec = (QUERY_TIMEOUT % 1000) * 1000; break; - case IPCP_ETH_LLC: - exec_name = IPCP_ETH_LLC_EXEC; + case IPCP_MSG_CODE__IPCP_CONNECT: + tv.tv_sec = CONNECT_TIMEOUT / 1000; + tv.tv_usec = (CONNECT_TIMEOUT % 1000) * 1000; break; - case IPCP_ETH_DIX: - exec_name = IPCP_ETH_DIX_EXEC; + case IPCP_MSG_CODE__IPCP_FLOW_ALLOC: + tv.tv_sec = FLOW_ALLOC_TIMEOUT / 1000; + tv.tv_usec = (FLOW_ALLOC_TIMEOUT % 1000) * 1000; break; - case IPCP_LOCAL: - exec_name = IPCP_LOCAL_EXEC; + case IPCP_MSG_CODE__IPCP_FLOW_DEALLOC: + dealloc = true; + tv.tv_sec = 0; /* FIX DEALLOC: don't wait for dealloc */ + tv.tv_usec = 500; break; default: - return -1; + tv.tv_sec = SOCKET_TIMEOUT / 1000; + tv.tv_usec = (SOCKET_TIMEOUT % 1000) * 1000; + break; } - if (strlen(exec_name) == 0) { - log_err("IPCP type not installed."); - return -1; - } + if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, + (void *) &tv, sizeof(tv))) + log_warn("Failed to set timeout on socket."); - sprintf(irmd_pid, "%u", getpid()); + pthread_cleanup_push(__cleanup_close_ptr, (void *) &sockfd); - strcpy(full_name, INSTALL_PREFIX); - strcat(full_name, ipcp_dir); - strcat(full_name, exec_name); + ipcp_msg__pack(msg, buf); - /* log_file to be placed at the end */ - argv[0] = full_name; - argv[1] = irmd_pid; - argv[2] = (char *) name; - if (log_syslog) - argv[3] = "1"; - else - argv[3] = NULL; + clock_gettime(CLOCK_REALTIME, &tic); - argv[4] = NULL; + if (write(sockfd, buf, len) != -1) + len = read(sockfd, buf, SOCK_BUF_SIZE); - if (posix_spawn(&pid, argv[0], NULL, NULL, argv, NULL)) { - log_err("Failed to spawn new process"); - return -1; - } + clock_gettime(CLOCK_REALTIME, &toc); - return pid; -} + pthread_cleanup_pop(true); /* close socket */ -int ipcp_destroy(pid_t pid) -{ - if (kill(pid, SIGTERM)) { - log_err("Failed to destroy IPCP"); - return -1; + if (len > 0) + recv_msg = ipcp_msg__unpack(NULL, len, buf); + else { + if (errno == EAGAIN && !dealloc) { + int diff = ts_diff_ms(&tic, &toc); + log_warn("IPCP %s timed out after %d ms.", + str_ipcp_cmd(msg->code), diff); + } + return NULL; } - return 0; + return recv_msg; } -int ipcp_bootstrap(pid_t pid, - ipcp_config_msg_t * conf, - struct layer_info * info) +int ipcp_bootstrap(pid_t pid, + struct ipcp_config * conf, + struct layer_info * info) { - ipcp_msg_t msg = IPCP_MSG__INIT; - ipcp_msg_t * recv_msg = NULL; - int ret = -1; + ipcp_msg_t msg = IPCP_MSG__INIT; + ipcp_msg_t * recv_msg; + int ret; if (conf == NULL) return -EINVAL; msg.code = IPCP_MSG_CODE__IPCP_BOOTSTRAP; - msg.conf = conf; + msg.conf = ipcp_config_s_to_msg(conf); recv_msg = send_recv_ipcp_msg(pid, &msg); + ipcp_config_msg__free_unpacked(msg.conf, NULL); if (recv_msg == NULL) return -EIPCP; @@ -229,7 +216,7 @@ int ipcp_bootstrap(pid_t pid, } info->dir_hash_algo = recv_msg->layer_info->dir_hash_algo; - strcpy(info->layer_name, recv_msg->layer_info->layer_name); + strcpy(info->name, recv_msg->layer_info->name); ret = recv_msg->result; ipcp_msg__free_unpacked(recv_msg, NULL); @@ -241,9 +228,9 @@ int ipcp_enroll(pid_t pid, const char * dst, struct layer_info * info) { - ipcp_msg_t msg = IPCP_MSG__INIT; - ipcp_msg_t * recv_msg = NULL; - int ret = -1; + ipcp_msg_t msg = IPCP_MSG__INIT; + ipcp_msg_t * recv_msg; + int ret; if (dst == NULL) return -EINVAL; @@ -272,7 +259,7 @@ int ipcp_enroll(pid_t pid, } info->dir_hash_algo = recv_msg->layer_info->dir_hash_algo; - strcpy(info->layer_name, recv_msg->layer_info->layer_name); + strcpy(info->name, recv_msg->layer_info->name); ipcp_msg__free_unpacked(recv_msg, NULL); @@ -284,20 +271,19 @@ int ipcp_connect(pid_t pid, const char * component, qosspec_t qs) { - ipcp_msg_t msg = IPCP_MSG__INIT; - qosspec_msg_t qs_msg = QOSSPEC_MSG__INIT; - int ret = -1; + ipcp_msg_t msg = IPCP_MSG__INIT; ipcp_msg_t * recv_msg; + int ret; msg.code = IPCP_MSG_CODE__IPCP_CONNECT; msg.dst = (char *) dst; msg.comp = (char *) component; msg.has_pid = true; msg.pid = pid; - qs_msg = spec_to_msg(&qs); - msg.qosspec = &qs_msg; + msg.qosspec = qos_spec_s_to_msg(&qs); recv_msg = send_recv_ipcp_msg(pid, &msg); + free(msg.qosspec); if (recv_msg == NULL) return -EIPCP; @@ -316,9 +302,9 @@ int ipcp_disconnect(pid_t pid, const char * dst, const char * component) { - ipcp_msg_t msg = IPCP_MSG__INIT; - ipcp_msg_t * recv_msg = NULL; - int ret = -1; + ipcp_msg_t msg = IPCP_MSG__INIT; + ipcp_msg_t * recv_msg; + int ret; msg.code = IPCP_MSG_CODE__IPCP_DISCONNECT; msg.dst = (char *) dst; @@ -341,20 +327,17 @@ int ipcp_disconnect(pid_t pid, return ret; } -int ipcp_reg(pid_t pid, - const uint8_t * hash, - size_t len) +int ipcp_reg(pid_t pid, + const buffer_t hash) { - ipcp_msg_t msg = IPCP_MSG__INIT; - ipcp_msg_t * recv_msg = NULL; - int ret = -1; - - assert(hash); + ipcp_msg_t msg = IPCP_MSG__INIT; + ipcp_msg_t * recv_msg; + int ret; msg.code = IPCP_MSG_CODE__IPCP_REG; msg.has_hash = true; - msg.hash.len = len; - msg.hash.data = (uint8_t *)hash; + msg.hash.data = (uint8_t *) hash.data; + msg.hash.len = hash.len; recv_msg = send_recv_ipcp_msg(pid, &msg); if (recv_msg == NULL) @@ -371,18 +354,17 @@ int ipcp_reg(pid_t pid, return ret; } -int ipcp_unreg(pid_t pid, - const uint8_t * hash, - size_t len) +int ipcp_unreg(pid_t pid, + const buffer_t hash) { - ipcp_msg_t msg = IPCP_MSG__INIT; - ipcp_msg_t * recv_msg = NULL; - int ret = -1; + ipcp_msg_t msg = IPCP_MSG__INIT; + ipcp_msg_t * recv_msg; + int ret; msg.code = IPCP_MSG_CODE__IPCP_UNREG; msg.has_hash = true; - msg.hash.len = len; - msg.hash.data = (uint8_t *) hash; + msg.hash.data = (uint8_t *) hash.data; + msg.hash.len = hash.len; recv_msg = send_recv_ipcp_msg(pid, &msg); if (recv_msg == NULL) @@ -399,18 +381,17 @@ int ipcp_unreg(pid_t pid, return ret; } -int ipcp_query(pid_t pid, - const uint8_t * hash, - size_t len) +int ipcp_query(pid_t pid, + const buffer_t dst) { - ipcp_msg_t msg = IPCP_MSG__INIT; - ipcp_msg_t * recv_msg = NULL; - int ret = -1; + ipcp_msg_t msg = IPCP_MSG__INIT; + ipcp_msg_t * recv_msg; + int ret; msg.code = IPCP_MSG_CODE__IPCP_QUERY; msg.has_hash = true; - msg.hash.len = len; - msg.hash.data = (uint8_t *) hash; + msg.hash.data = (uint8_t *) dst.data; + msg.hash.len = dst.len; recv_msg = send_recv_ipcp_msg(pid, &msg); if (recv_msg == NULL) @@ -427,39 +408,25 @@ int ipcp_query(pid_t pid, return ret; } -static int __ipcp_flow_alloc(pid_t pid, - int flow_id, - pid_t n_pid, - const uint8_t * dst, - size_t len, - qosspec_t qs, - bool join, - const void * data, - size_t dlen) +int ipcp_flow_join(const struct flow_info * flow, + const buffer_t dst) { - ipcp_msg_t msg = IPCP_MSG__INIT; - qosspec_msg_t qs_msg; - ipcp_msg_t * recv_msg = NULL; - int ret = -1; + ipcp_msg_t msg = IPCP_MSG__INIT; + ipcp_msg_t * recv_msg; + int ret; - assert(dst); - - msg.code = join ? IPCP_MSG_CODE__IPCP_FLOW_JOIN - : IPCP_MSG_CODE__IPCP_FLOW_ALLOC; + msg.code = IPCP_MSG_CODE__IPCP_FLOW_JOIN; msg.has_flow_id = true; - msg.flow_id = flow_id; + msg.flow_id = flow->id; msg.has_pid = true; - msg.pid = n_pid; + msg.pid = flow->n_pid; msg.has_hash = true; - msg.hash.len = len; - msg.hash.data = (uint8_t *) dst; - qs_msg = spec_to_msg(&qs); - msg.qosspec = &qs_msg; - msg.has_pk = true; - msg.pk.data = (uint8_t *) data; - msg.pk.len = (uint32_t) dlen; + msg.hash.data = (uint8_t *) dst.data; + msg.hash.len = dst.len; + msg.has_pk = false; - recv_msg = send_recv_ipcp_msg(pid, &msg); + recv_msg = send_recv_ipcp_msg(flow->n_1_pid, &msg); + free(msg.qosspec); if (recv_msg == NULL) return -EIPCP; @@ -474,53 +441,66 @@ static int __ipcp_flow_alloc(pid_t pid, return ret; } -int ipcp_flow_alloc(pid_t pid, - int flow_id, - pid_t n_pid, - const uint8_t * dst, - size_t len, - qosspec_t qs, - const void * data, - size_t dlen) +int ipcp_flow_alloc(const struct flow_info * flow, + const buffer_t dst, + const buffer_t data) { - return __ipcp_flow_alloc(pid, flow_id, n_pid, dst, len, qs, false, - data, dlen); -} + ipcp_msg_t msg = IPCP_MSG__INIT; + ipcp_msg_t * recv_msg; + int ret; -int ipcp_flow_join(pid_t pid, - int flow_id, - pid_t n_pid, - const uint8_t * dst, - size_t len, - qosspec_t qs) -{ - return __ipcp_flow_alloc(pid, flow_id, n_pid, dst, len, qs, true, - NULL, 0); + msg.code = IPCP_MSG_CODE__IPCP_FLOW_ALLOC; + msg.has_flow_id = true; + msg.flow_id = flow->id; + msg.has_pid = true; + msg.pid = flow->n_pid; + msg.qosspec = qos_spec_s_to_msg(&flow->qs); + msg.has_hash = true; + msg.hash.data = (uint8_t *) dst.data; + msg.hash.len = dst.len; + msg.has_pk = true; + msg.pk.data = data.data; + msg.pk.len = data.len; + + recv_msg = send_recv_ipcp_msg(flow->n_1_pid, &msg); + free(msg.qosspec); + if (recv_msg == NULL) { + log_err("Did not receive message."); + return -EIPCP; + } + + if (!recv_msg->has_result) { + log_err("Message has no result"); + ipcp_msg__free_unpacked(recv_msg, NULL); + return -EIPCP; + } + + ret = recv_msg->result; + ipcp_msg__free_unpacked(recv_msg, NULL); + + return ret; } -int ipcp_flow_alloc_resp(pid_t pid, - int flow_id, - pid_t n_pid, - int response, - const void * data, - size_t len) +int ipcp_flow_alloc_resp(const struct flow_info * flow, + int response, + const buffer_t data) { - ipcp_msg_t msg = IPCP_MSG__INIT; - ipcp_msg_t * recv_msg = NULL; - int ret = -1; + ipcp_msg_t msg = IPCP_MSG__INIT; + ipcp_msg_t * recv_msg; + int ret; msg.code = IPCP_MSG_CODE__IPCP_FLOW_ALLOC_RESP; msg.has_flow_id = true; - msg.flow_id = flow_id; + msg.flow_id = flow->id; msg.has_pid = true; - msg.pid = n_pid; + msg.pid = flow->n_pid; msg.has_response = true; msg.response = response; msg.has_pk = true; - msg.pk.data = (uint8_t *) data; - msg.pk.len = (uint32_t) len; + msg.pk.data = data.data; + msg.pk.len = data.len; - recv_msg = send_recv_ipcp_msg(pid, &msg); + recv_msg = send_recv_ipcp_msg(flow->n_1_pid, &msg); if (recv_msg == NULL) return -EIPCP; @@ -539,9 +519,9 @@ int ipcp_flow_dealloc(pid_t pid, int flow_id, time_t timeo) { - ipcp_msg_t msg = IPCP_MSG__INIT; - ipcp_msg_t * recv_msg = NULL; - int ret = -1; + ipcp_msg_t msg = IPCP_MSG__INIT; + ipcp_msg_t * recv_msg; + int ret; msg.code = IPCP_MSG_CODE__IPCP_FLOW_DEALLOC; msg.has_flow_id = true; diff --git a/src/irmd/ipcp.h b/src/irmd/ipcp.h index eb2361c7..b7413cd2 100644 --- a/src/irmd/ipcp.h +++ b/src/irmd/ipcp.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * The API for the IRM to instruct IPCPs * @@ -21,25 +21,19 @@ */ #include <ouroboros/ipcp.h> +#include <ouroboros/protobuf.h> #include <ouroboros/sockets.h> -#include <sys/types.h> - #ifndef OUROBOROS_IRMD_IPCP_H #define OUROBOROS_IRMD_IPCP_H -pid_t ipcp_create(const char * name, - enum ipcp_type ipcp_type); - -int ipcp_destroy(pid_t pid); - int ipcp_enroll(pid_t pid, const char * dst, struct layer_info * info); -int ipcp_bootstrap(pid_t pid, - ipcp_config_msg_t * conf, - struct layer_info * info); +int ipcp_bootstrap(pid_t pid, + struct ipcp_config * conf, + struct layer_info * info); int ipcp_connect(pid_t pid, const char * dst, @@ -50,40 +44,25 @@ int ipcp_disconnect(pid_t pid, const char * dst, const char * component); -int ipcp_reg(pid_t pid, - const uint8_t * hash, - size_t len); +int ipcp_reg(pid_t pid, + const buffer_t hash); -int ipcp_unreg(pid_t pid, - const uint8_t * hash, - size_t len); +int ipcp_unreg(pid_t pid, + const buffer_t hash); -int ipcp_query(pid_t pid, - const uint8_t * hash, - size_t len); +int ipcp_query(pid_t pid, + const buffer_t dst); -int ipcp_flow_alloc(pid_t pid, - int flow_id, - pid_t n_pid, - const uint8_t * dst, - size_t len, - qosspec_t qs, - const void * data, - size_t dlen); +int ipcp_flow_alloc(const struct flow_info * flow, + const buffer_t hash, + const buffer_t data); -int ipcp_flow_join(pid_t pid, - int flow_id, - pid_t n_pid, - const uint8_t * dst, - size_t len, - qosspec_t qs); +int ipcp_flow_join(const struct flow_info * flow, + const buffer_t dst); -int ipcp_flow_alloc_resp(pid_t pid, - int flow_id, - pid_t n_pid, - int response, - const void * data, - size_t len); +int ipcp_flow_alloc_resp(const struct flow_info * flow, + int response, + const buffer_t data); int ipcp_flow_dealloc(pid_t pid, int flow_id, diff --git a/src/irmd/irm_flow.c b/src/irmd/irm_flow.c deleted file mode 100644 index 75df7a80..00000000 --- a/src/irmd/irm_flow.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Ouroboros - Copyright (C) 2016 - 2021 - * - * The IPC Resource Manager - Flows - * - * Dimitri Staessens <dimitri@ouroboros.rocks> - * Sander Vrijders <sander@ouroboros.rocks> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., http://www.fsf.org/about/contact/. - */ - -#define _POSIX_C_SOURCE 200112L - -#include "config.h" - -#define OUROBOROS_PREFIX "irm_flow" - -#include <ouroboros/errno.h> -#include <ouroboros/logs.h> -#include <ouroboros/time_utils.h> -#include <ouroboros/pthread.h> - -#include "irm_flow.h" - -#include <stdlib.h> -#include <stdbool.h> -#include <assert.h> - -struct irm_flow * irm_flow_create(pid_t n_pid, - pid_t n_1_pid, - int flow_id, - qosspec_t qs) -{ - pthread_condattr_t cattr; - struct irm_flow * f = malloc(sizeof(*f)); - if (f == NULL) - goto fail_malloc; - - if (pthread_condattr_init(&cattr)) - goto fail_cattr; - -#ifndef __APPLE__ - pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK); -#endif - if (pthread_cond_init(&f->state_cond, &cattr)) - goto fail_state_cond; - - if (pthread_mutex_init(&f->state_lock, NULL)) - goto fail_mutex; - - f->n_pid = n_pid; - f->n_1_pid = n_1_pid; - f->flow_id = flow_id; - f->qs = qs; - f->data = NULL; - f->len = 0; - - f->n_rb = shm_rbuff_create(n_pid, flow_id); - if (f->n_rb == NULL) { - log_err("Could not create ringbuffer for process %d.", n_pid); - goto fail_n_rbuff; - } - - f->n_1_rb = shm_rbuff_create(n_1_pid, flow_id); - if (f->n_1_rb == NULL) { - log_err("Could not create ringbuffer for process %d.", n_1_pid); - goto fail_n_1_rbuff; - } - - f->state = FLOW_ALLOC_PENDING; - - if (clock_gettime(CLOCK_MONOTONIC, &f->t0) < 0) - log_warn("Failed to set timestamp."); - - pthread_condattr_destroy(&cattr); - - return f; - - fail_n_1_rbuff: - shm_rbuff_destroy(f->n_rb); - fail_n_rbuff: - pthread_mutex_destroy(&f->state_lock); - fail_mutex: - pthread_cond_destroy(&f->state_cond); - fail_state_cond: - pthread_condattr_destroy(&cattr); - fail_cattr: - free(f); - fail_malloc: - return NULL; -} - -static void cancel_irm_destroy(void * o) -{ - struct irm_flow * f = (struct irm_flow *) o; - - pthread_mutex_unlock(&f->state_lock); - - pthread_cond_destroy(&f->state_cond); - pthread_mutex_destroy(&f->state_lock); - - shm_rbuff_destroy(f->n_rb); - shm_rbuff_destroy(f->n_1_rb); - - free(f); -} - -void irm_flow_destroy(struct irm_flow * f) -{ - assert(f); - - pthread_mutex_lock(&f->state_lock); - - assert(f->len == 0); - - if (f->state == FLOW_DESTROY) { - pthread_mutex_unlock(&f->state_lock); - return; - } - - if (f->state == FLOW_ALLOC_PENDING) - f->state = FLOW_DESTROY; - else - f->state = FLOW_NULL; - - pthread_cond_signal(&f->state_cond); - - pthread_cleanup_push(cancel_irm_destroy, f); - - while (f->state != FLOW_NULL) - pthread_cond_wait(&f->state_cond, &f->state_lock); - - pthread_cleanup_pop(true); -} - -enum flow_state irm_flow_get_state(struct irm_flow * f) -{ - enum flow_state state; - - assert(f); - - pthread_mutex_lock(&f->state_lock); - - state = f->state; - - pthread_mutex_unlock(&f->state_lock); - - return state; -} - -void irm_flow_set_state(struct irm_flow * f, - enum flow_state state) -{ - assert(f); - assert(state != FLOW_DESTROY); - - pthread_mutex_lock(&f->state_lock); - - f->state = state; - pthread_cond_broadcast(&f->state_cond); - - pthread_mutex_unlock(&f->state_lock); -} - -int irm_flow_wait_state(struct irm_flow * f, - enum flow_state state, - struct timespec * timeo) -{ - int ret = 0; - int s; - - struct timespec dl; - - assert(f); - assert(state != FLOW_NULL); - assert(state != FLOW_DESTROY); - assert(state != FLOW_DEALLOC_PENDING); - - if (timeo != NULL) { - clock_gettime(PTHREAD_COND_CLOCK, &dl); - ts_add(&dl, timeo, &dl); - } - - pthread_mutex_lock(&f->state_lock); - - assert(f->state != FLOW_NULL); - - pthread_cleanup_push(__cleanup_mutex_unlock, &f->state_lock); - - while (!(f->state == state || - f->state == FLOW_DESTROY || - f->state == FLOW_DEALLOC_PENDING) && - ret != -ETIMEDOUT) { - if (timeo == NULL) - ret = -pthread_cond_wait(&f->state_cond, - &f->state_lock); - else - ret = -pthread_cond_timedwait(&f->state_cond, - &f->state_lock, - &dl); - } - - if (f->state == FLOW_DESTROY || - f->state == FLOW_DEALLOC_PENDING || - ret == -ETIMEDOUT) { - f->state = FLOW_NULL; - pthread_cond_broadcast(&f->state_cond); - } - - s = f->state; - - pthread_cleanup_pop(true); - - return ret ? ret : s; -} diff --git a/src/irmd/irm_flow.h b/src/irmd/irm_flow.h deleted file mode 100644 index 35e7dc2c..00000000 --- a/src/irmd/irm_flow.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Ouroboros - Copyright (C) 2016 - 2021 - * - * The IPC Resource Manager - Flows - * - * Dimitri Staessens <dimitri@ouroboros.rocks> - * Sander Vrijders <sander@ouroboros.rocks> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., http://www.fsf.org/about/contact/. - */ - -#ifndef OUROBOROS_IRMD_IRM_FLOW_H -#define OUROBOROS_IRMD_IRM_FLOW_H - -#include <ouroboros/list.h> -#include <ouroboros/qos.h> -#include <ouroboros/shm_rbuff.h> - -#include <sys/types.h> -#include <pthread.h> -#include <time.h> - -enum flow_state { - FLOW_NULL = 0, - FLOW_ALLOC_PENDING, - FLOW_ALLOCATED, - FLOW_DEALLOC_PENDING, - FLOW_DESTROY -}; - -struct irm_flow { - struct list_head next; - - int flow_id; - - pid_t n_pid; - pid_t n_1_pid; - - qosspec_t qs; - void * data; - size_t len; - - struct shm_rbuff * n_rb; - struct shm_rbuff * n_1_rb; - - struct timespec t0; - - enum flow_state state; - pthread_cond_t state_cond; - pthread_mutex_t state_lock; -}; - -struct irm_flow * irm_flow_create(pid_t n_pid, - pid_t n_1_pid, - int flow_id, - qosspec_t qs); - -void irm_flow_destroy(struct irm_flow * f); - -enum flow_state irm_flow_get_state(struct irm_flow * f); - - -void irm_flow_set_state(struct irm_flow * f, - enum flow_state state); - -int irm_flow_wait_state(struct irm_flow * f, - enum flow_state state, - struct timespec * timeo); - -#endif /* OUROBOROS_IRMD_IRM_FLOW_H */ diff --git a/src/irmd/irmd.h b/src/irmd/irmd.h new file mode 100644 index 00000000..cf8f6953 --- /dev/null +++ b/src/irmd/irmd.h @@ -0,0 +1,54 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#ifndef OUROBOROS_IRMD_H +#define OUROBOROS_IRMD_H + +#include <ouroboros/ipcp.h> +#include <ouroboros/irm.h> + +int create_ipcp(struct ipcp_info * info); + +int bootstrap_ipcp(pid_t pid, + struct ipcp_config * conf); + +int enroll_ipcp(pid_t pid, + const char * dst); + +int connect_ipcp(pid_t pid, + const char * dst, + const char * component, + qosspec_t qs); + +int name_create(const struct name_info * info); + +int name_reg(const char * name, + pid_t pid); + +int bind_process(pid_t pid, + const char * name); + +int bind_program(char ** exec, + const char * name, + uint8_t flags); + +#endif /* OUROBOROS_IRMD_H*/ diff --git a/src/irmd/main.c b/src/irmd/main.c index 22d94136..5d91baf0 100644 --- a/src/irmd/main.c +++ b/src/irmd/main.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * The IPC Resource Manager * @@ -30,28 +30,28 @@ #define OUROBOROS_PREFIX "irmd" -#include <ouroboros/hash.h> +#include <ouroboros/bitmap.h> +#include <ouroboros/crypt.h> #include <ouroboros/errno.h> -#include <ouroboros/sockets.h> -#include <ouroboros/list.h> -#include <ouroboros/utils.h> +#include <ouroboros/flow.h> +#include <ouroboros/hash.h> #include <ouroboros/irm.h> +#include <ouroboros/list.h> #include <ouroboros/lockfile.h> -#include <ouroboros/shm_rbuff.h> +#include <ouroboros/logs.h> +#include <ouroboros/pthread.h> +#include <ouroboros/rib.h> #include <ouroboros/shm_rdrbuff.h> -#include <ouroboros/bitmap.h> -#include <ouroboros/qos.h> -#include <ouroboros/time_utils.h> +#include <ouroboros/sockets.h> +#include <ouroboros/time.h> #include <ouroboros/tpm.h> -#include <ouroboros/logs.h> +#include <ouroboros/utils.h> #include <ouroboros/version.h> -#include <ouroboros/pthread.h> -#include "utils.h" -#include "registry.h" -#include "irm_flow.h" -#include "proc_table.h" +#include "irmd.h" #include "ipcp.h" +#include "reg/reg.h" +#include "configfile.h" #include <sys/socket.h> #include <sys/un.h> @@ -69,60 +69,30 @@ #define IRMD_CLEANUP_TIMER ((IRMD_FLOW_TIMEOUT / 20) * MILLION) /* ns */ #define SHM_SAN_HOLDOFF 1000 /* ms */ -#define IPCP_HASH_LEN(e) hash_len(e->dir_hash_algo) -#define IB_LEN SOCK_BUF_SIZE +#define IPCP_HASH_LEN(p) hash_len((p)->dir_hash_algo) #define BIND_TIMEOUT 10 /* ms */ #define DEALLOC_TIME 300 /* s */ - -enum init_state { - IPCP_NULL = 0, - IPCP_BOOT, - IPCP_LIVE -}; - -struct ipcp_entry { - struct list_head next; - - char * name; - pid_t pid; - enum ipcp_type type; - enum hash_algo dir_hash_algo; - char * layer; - - enum init_state state; - pthread_cond_t cond; - pthread_mutex_t lock; -}; +#define MSGBUFSZ 2048 enum irm_state { IRMD_NULL = 0, - IRMD_RUNNING + IRMD_RUNNING, + IRMD_SHUTDOWN }; struct cmd { struct list_head next; - uint8_t cbuf[IB_LEN]; + uint8_t cbuf[SOCK_BUF_SIZE]; size_t len; int fd; }; struct { - struct list_head registry; /* registered names known */ - size_t n_names; /* number of names */ - - struct list_head ipcps; /* list of ipcps in system */ - size_t n_ipcps; /* number of ipcps */ - - struct list_head proc_table; /* processes */ - struct list_head prog_table; /* programs known */ - struct list_head spawned_pids; /* child processes */ - pthread_rwlock_t reg_lock; /* lock for registration info */ - - struct bmp * flow_ids; /* flow_ids for flows */ - struct list_head irm_flows; /* flow information */ - pthread_rwlock_t flows_lock; /* lock for flows */ - + bool log_stdout; /* log to stdout */ +#ifdef HAVE_TOML + char * cfg_file; /* configuration file path */ +#endif struct lockfile * lf; /* single irmd per system */ struct shm_rdrbuff * rdrb; /* rdrbuff for packets */ @@ -163,492 +133,260 @@ static void irmd_set_state(enum irm_state state) pthread_rwlock_unlock(&irmd.state_lock); } -static void clear_irm_flow(struct irm_flow * f) { - ssize_t idx; - - assert(f); - - if (f->len != 0) { - free(f->data); - f->len = 0; - } - - while ((idx = shm_rbuff_read(f->n_rb)) >= 0) - shm_rdrbuff_remove(irmd.rdrb, idx); - - while ((idx = shm_rbuff_read(f->n_1_rb)) >= 0) - shm_rdrbuff_remove(irmd.rdrb, idx); -} - -static struct irm_flow * get_irm_flow(int flow_id) +static pid_t spawn_program(char ** argv) { - struct list_head * pos = NULL; + pid_t pid; + struct stat s; - list_for_each(pos, &irmd.irm_flows) { - struct irm_flow * e = list_entry(pos, struct irm_flow, next); - if (e->flow_id == flow_id) - return e; + if (stat(argv[0], &s) != 0) { + log_warn("Program %s does not exist.", argv[0]); + return -1; } - return NULL; -} - -static struct irm_flow * get_irm_flow_n(pid_t n_pid) -{ - struct list_head * pos = NULL; - - list_for_each(pos, &irmd.irm_flows) { - struct irm_flow * e = list_entry(pos, struct irm_flow, next); - if (e->n_pid == n_pid && - irm_flow_get_state(e) == FLOW_ALLOC_PENDING) - return e; + if (!(s.st_mode & S_IXUSR)) { + log_warn("Program %s is not executable.", argv[0]); + return -1; } - return NULL; -} - -static struct ipcp_entry * ipcp_entry_create(const char * name, - enum ipcp_type type) -{ - struct ipcp_entry * e; - pthread_condattr_t cattr; - - e = malloc(sizeof(*e)); - if (e == NULL) - goto fail_malloc; - - e->layer = NULL; - e->type = type; - e->state = IPCP_BOOT; - e->name = strdup(name); - if (e->name == NULL) - goto fail_name; - - if (pthread_condattr_init(&cattr)) - goto fail_cattr; -#ifndef __APPLE__ - pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK); -#endif - if (pthread_cond_init(&e->cond, &cattr)) - goto fail_cond; - - if (pthread_mutex_init(&e->lock, NULL)) - goto fail_mutex; - - - list_head_init(&e->next); - - pthread_condattr_destroy(&cattr); - - return e; - - fail_mutex: - pthread_cond_destroy(&e->cond); - fail_cond: - pthread_condattr_destroy(&cattr); - fail_cattr: - free(e->name); - fail_name: - free(e); - fail_malloc: - return NULL; -} - -static void ipcp_entry_destroy(struct ipcp_entry * e) -{ - assert(e); - - pthread_mutex_lock(&e->lock); - - while (e->state == IPCP_BOOT) - pthread_cond_wait(&e->cond, &e->lock); - - pthread_mutex_unlock(&e->lock); + if (posix_spawn(&pid, argv[0], NULL, NULL, argv, NULL)) { + log_err("Failed to spawn new process for %s.", argv[0]); + return -1; + } - free(e->name); - free(e->layer); - free(e); -} + log_info("Instantiated %s as process %d.", argv[0], pid); -static void ipcp_entry_set_state(struct ipcp_entry * e, - enum init_state state) -{ - pthread_mutex_lock(&e->lock); - e->state = state; - pthread_cond_broadcast(&e->cond); - pthread_mutex_unlock(&e->lock); + return pid; } -static int ipcp_entry_wait_boot(struct ipcp_entry * e) +static pid_t spawn_ipcp(struct ipcp_info * info) { - int ret = 0; - struct timespec dl; - struct timespec to = {SOCKET_TIMEOUT / 1000, - (SOCKET_TIMEOUT % 1000) * MILLION}; - - clock_gettime(PTHREAD_COND_CLOCK, &dl); - ts_add(&dl, &to, &dl); - - pthread_mutex_lock(&e->lock); - - while (e->state == IPCP_BOOT && ret != ETIMEDOUT) - ret = pthread_cond_timedwait(&e->cond, &e->lock, &dl); - - if (ret == ETIMEDOUT) { - kill(e->pid, SIGTERM); - e->state = IPCP_NULL; - pthread_cond_signal(&e->cond); + char * exec_name = NULL; + char irmd_pid[10]; + char full_name[256]; + char * argv[5]; + pid_t pid; + + switch(info->type) { + case IPCP_UNICAST: + exec_name = IPCP_UNICAST_EXEC; + break; + case IPCP_BROADCAST: + exec_name = IPCP_BROADCAST_EXEC; + break; + case IPCP_UDP: + exec_name = IPCP_UDP_EXEC; + break; + case IPCP_ETH_LLC: + exec_name = IPCP_ETH_LLC_EXEC; + break; + case IPCP_ETH_DIX: + exec_name = IPCP_ETH_DIX_EXEC; + break; + case IPCP_LOCAL: + exec_name = IPCP_LOCAL_EXEC; + break; + default: + assert(false); } - if (e->state != IPCP_LIVE) { - pthread_mutex_unlock(&e->lock); + if (exec_name == NULL) { + log_err("IPCP type not installed."); return -1; } - pthread_mutex_unlock(&e->lock); - - return 0; -} - -static struct ipcp_entry * get_ipcp_entry_by_pid(pid_t pid) -{ - struct list_head * p; + sprintf(irmd_pid, "%u", getpid()); - list_for_each(p, &irmd.ipcps) { - struct ipcp_entry * e = list_entry(p, struct ipcp_entry, next); - if (e->pid == pid) - return e; - } + strcpy(full_name, INSTALL_PREFIX"/"INSTALL_SBINDIR"/"); + strcat(full_name, exec_name); - return NULL; -} + /* log_file to be placed at the end */ + argv[0] = full_name; + argv[1] = irmd_pid; + argv[2] = (char *) info->name; + if (log_syslog) + argv[3] = "1"; + else + argv[3] = NULL; -static struct ipcp_entry * get_ipcp_entry_by_name(const char * name) -{ - struct list_head * p; + argv[4] = NULL; - list_for_each(p, &irmd.ipcps) { - struct ipcp_entry * e = list_entry(p, struct ipcp_entry, next); - if (strcmp(name, e->name) == 0) - return e; + pid = spawn_program(argv); + if (pid < 0) { + log_err("Failed to spawn IPCP %s.", info->name); + return -1; } - return NULL; -} - -static struct ipcp_entry * get_ipcp_entry_by_layer(const char * layer) -{ - struct list_head * p; + info->pid = pid; + info->state = IPCP_BOOT; - list_for_each(p, &irmd.ipcps) { - struct ipcp_entry * e = list_entry(p, struct ipcp_entry, next); - if (strcmp(layer, e->layer) == 0) - return e; - } - - return NULL; + return 0; } -static struct ipcp_entry * get_ipcp_by_dst_name(const char * name, - pid_t src) +static int kill_ipcp(pid_t pid) { - struct list_head * p; - struct list_head * h; - uint8_t * hash; - pid_t pid; - size_t len; - - pthread_rwlock_rdlock(&irmd.reg_lock); + int status; - list_for_each_safe(p, h, &irmd.ipcps) { - struct ipcp_entry * e = list_entry(p, struct ipcp_entry, next); - if (e->layer == NULL || e->pid == src) - continue; - - len = IPCP_HASH_LEN(e); - - hash = malloc(len); - if (hash == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - return NULL; - } - - str_hash(e->dir_hash_algo, hash, name); - - pid = e->pid; - - pthread_rwlock_unlock(&irmd.reg_lock); - - if (ipcp_query(pid, hash, len) == 0) { - free(hash); - return e; - } - - free(hash); - - pthread_rwlock_rdlock(&irmd.reg_lock); + if (kill(pid, SIGTERM) < 0) { + log_err("Failed to destroy IPCP: %s.", strerror(errno)); + return -1; } - pthread_rwlock_unlock(&irmd.reg_lock); + waitpid(pid, &status, 0); - return NULL; + return 0; } -static pid_t create_ipcp(const char * name, - enum ipcp_type type) +int create_ipcp(struct ipcp_info * info) { - struct pid_el * ppid; - struct ipcp_entry * entry; - struct list_head * p; - pid_t pid; + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_MS(SOCKET_TIMEOUT); + int status; - pthread_rwlock_rdlock(&irmd.reg_lock); + assert(info->pid == 0); - entry = get_ipcp_entry_by_name(name); - if (entry != NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("IPCP by that name already exists."); - return -EPERM; - } - - pthread_rwlock_unlock(&irmd.reg_lock); - - ppid = malloc(sizeof(*ppid)); - if (ppid == NULL) - goto fail_ppid; - - entry = ipcp_entry_create(name, type); - if (entry == NULL) { - log_err("Failed to create IPCP entry."); - goto fail_ipcp_entry; - } + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); - pid = ipcp_create(name, type); - if (pid == -1) { + if (spawn_ipcp(info) < 0) { log_err("Failed to create IPCP."); goto fail_ipcp; } - entry->pid = pid; - - pthread_rwlock_wrlock(&irmd.reg_lock); - - list_for_each(p, &irmd.ipcps) { - if (list_entry(p, struct ipcp_entry, next)->type > type) - break; + if (reg_create_ipcp(info) < 0) { + log_err("Failed to create IPCP entry."); + goto fail_reg_ipcp; } - list_add_tail(&entry->next, p); - ++irmd.n_ipcps; - - ppid->pid = entry->pid; - list_add(&ppid->next, &irmd.spawned_pids); - - pthread_rwlock_unlock(&irmd.reg_lock); - - /* IRMd maintenance will clean up if booting fails. */ - if (ipcp_entry_wait_boot(entry)) { - log_err("IPCP %d failed to boot.", pid); - return -1; + if (reg_wait_ipcp_boot(info, &abstime)) { + log_err("IPCP %d failed to boot.", info->pid); + goto fail_boot; } - log_info("Created IPCP %d.", pid); + log_info("Created IPCP %d.", info->pid); - return pid; + return 0; - fail_ipcp: - ipcp_entry_destroy(entry); - fail_ipcp_entry: - free(ppid); - fail_ppid: + fail_boot: + waitpid(info->pid, &status, 0); + reg_destroy_proc(info->pid); return -1; -} - -static int create_ipcp_r(pid_t pid, - int result) -{ - struct list_head * p; - - pthread_rwlock_rdlock(&irmd.reg_lock); - - list_for_each(p, &irmd.ipcps) { - struct ipcp_entry * e = list_entry(p, struct ipcp_entry, next); - if (e->pid == pid) { - ipcp_entry_set_state(e, result ? IPCP_NULL : IPCP_LIVE); - break; - } - } - - pthread_rwlock_unlock(&irmd.reg_lock); - return 0; + fail_reg_ipcp: + kill_ipcp(info->pid); + fail_ipcp: + return -1; } -static void clear_spawned_process(pid_t pid) +static int create_ipcp_r(struct ipcp_info * info) { - struct list_head * p; - struct list_head * h; - - list_for_each_safe(p, h, &(irmd.spawned_pids)) { - struct pid_el * a = list_entry(p, struct pid_el, next); - if (a->pid == pid) { - list_del(&a->next); - free(a); - } - } + return reg_respond_ipcp(info); } static int destroy_ipcp(pid_t pid) { - struct list_head * p; - struct list_head * h; - - pthread_rwlock_wrlock(&irmd.reg_lock); - - list_for_each_safe(p, h, &irmd.ipcps) { - struct ipcp_entry * e = list_entry(p, struct ipcp_entry, next); - if (e->pid == pid) { - clear_spawned_process(pid); - if (ipcp_destroy(pid)) - log_err("Could not destroy IPCP."); - list_del(&e->next); - ipcp_entry_destroy(e); - --irmd.n_ipcps; - log_info("Destroyed IPCP %d.", pid); - } + if (kill_ipcp(pid)) { + log_err("Could not destroy IPCP."); + goto fail; } - pthread_rwlock_unlock(&irmd.reg_lock); + if (reg_destroy_proc(pid)) { + log_err("Failed to remove IPCP from registry."); + goto fail; + } return 0; + fail: + return -1; } -static int bootstrap_ipcp(pid_t pid, - ipcp_config_msg_t * conf) +int bootstrap_ipcp(pid_t pid, + struct ipcp_config * conf) { - struct ipcp_entry * entry; - struct layer_info info; + struct ipcp_info info; + struct layer_info layer; - pthread_rwlock_wrlock(&irmd.reg_lock); + info.pid = pid; - entry = get_ipcp_entry_by_pid(pid); - if (entry == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("No such IPCP."); - return -1; + if (reg_get_ipcp(&info, NULL) < 0) { + log_err("Could not find IPCP %d.", pid); + goto fail; } - if (entry->type != (enum ipcp_type) conf->ipcp_type) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Configuration does not match IPCP type."); - return -1; - } + if (conf->type == IPCP_UDP) + conf->layer_info.dir_hash_algo = (enum pol_dir_hash) HASH_MD5; - if (ipcp_bootstrap(entry->pid, conf, &info)) { - pthread_rwlock_unlock(&irmd.reg_lock); + if (ipcp_bootstrap(pid, conf, &layer)) { log_err("Could not bootstrap IPCP."); - return -1; - } - - entry->layer = strdup(info.layer_name); - if (entry->layer == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_warn("Failed to set name of layer."); - return -ENOMEM; + goto fail; } - entry->dir_hash_algo = info.dir_hash_algo; + info.state = IPCP_BOOTSTRAPPED; - pthread_rwlock_unlock(&irmd.reg_lock); + if (reg_set_layer_for_ipcp(&info, &layer) < 0) { + log_err("Failed to set layer info for IPCP."); + goto fail; + } - log_info("Bootstrapped IPCP %d in layer %s.", - pid, conf->layer_info->layer_name); + log_info("Bootstrapped IPCP %d.", pid); return 0; + fail: + return -1; } -static int enroll_ipcp(pid_t pid, - char * dst) +int enroll_ipcp(pid_t pid, + const char * dst) { - struct ipcp_entry * entry = NULL; - struct layer_info info; + struct layer_info layer; + struct ipcp_info info; - pthread_rwlock_wrlock(&irmd.reg_lock); + info.pid = pid; - entry = get_ipcp_entry_by_pid(pid); - if (entry == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("No such IPCP."); - return -1; - } - - if (entry->layer != NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("IPCP in wrong state"); - return -1; + if (reg_get_ipcp(&info, NULL) < 0) { + log_err("Could not find IPCP."); + goto fail; } - pthread_rwlock_unlock(&irmd.reg_lock); - - if (ipcp_enroll(pid, dst, &info) < 0) { + if (ipcp_enroll(pid, dst, &layer) < 0) { log_err("Could not enroll IPCP %d.", pid); - return -1; - } - - pthread_rwlock_wrlock(&irmd.reg_lock); - - entry = get_ipcp_entry_by_pid(pid); - if (entry == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("No such IPCP."); - return -1; + goto fail; } - entry->layer = strdup(info.layer_name); - if (entry->layer == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Failed to strdup layer_name."); - return -ENOMEM; + if (reg_set_layer_for_ipcp(&info, &layer) < 0) { + log_err("Failed to set layer info for IPCP."); + goto fail; } - entry->dir_hash_algo = info.dir_hash_algo; - - pthread_rwlock_unlock(&irmd.reg_lock); - - log_info("Enrolled IPCP %d in layer %s.", - pid, info.layer_name); + log_info("Enrolled IPCP %d in layer %s.", pid, layer.name); return 0; + fail: + return -1; } -static int connect_ipcp(pid_t pid, - const char * dst, - const char * component, - qosspec_t qs) +int connect_ipcp(pid_t pid, + const char * dst, + const char * component, + qosspec_t qs) { - struct ipcp_entry * entry = NULL; + struct ipcp_info info; - pthread_rwlock_rdlock(&irmd.reg_lock); + info.pid = pid; - entry = get_ipcp_entry_by_pid(pid); - if (entry == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); + if (reg_get_ipcp(&info, NULL) < 0) { log_err("No such IPCP."); return -EIPCP; } - if (entry->type != IPCP_UNICAST && entry->type != IPCP_BROADCAST) { - pthread_rwlock_unlock(&irmd.reg_lock); + if (info.type != IPCP_UNICAST && info.type != IPCP_BROADCAST) { log_err("Cannot establish connections for this IPCP type."); return -EIPCP; } - pthread_rwlock_unlock(&irmd.reg_lock); - log_dbg("Connecting %s to %s.", component, dst); if (ipcp_connect(pid, dst, component, qs)) { - log_err("Could not connect IPCP."); + log_err("Could not connect IPCP %d to %s.", pid, dst); return -EPERM; } @@ -662,25 +400,20 @@ static int disconnect_ipcp(pid_t pid, const char * dst, const char * component) { - struct ipcp_entry * entry = NULL; + struct ipcp_info info; - pthread_rwlock_rdlock(&irmd.reg_lock); + info.pid = pid; - entry = get_ipcp_entry_by_pid(pid); - if (entry == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); + if (reg_get_ipcp(&info, NULL) < 0) { log_err("No such IPCP."); return -EIPCP; } - if (entry->type != IPCP_UNICAST) { - pthread_rwlock_unlock(&irmd.reg_lock); + if (info.type != IPCP_UNICAST && info.type != IPCP_BROADCAST) { log_err("Cannot tear down connections for this IPCP type."); return -EIPCP; } - pthread_rwlock_unlock(&irmd.reg_lock); - if (ipcp_disconnect(pid, dst, component)) { log_err("Could not disconnect IPCP."); return -EPERM; @@ -692,173 +425,120 @@ static int disconnect_ipcp(pid_t pid, return 0; } -static int bind_program(char * prog, - char * name, - uint16_t flags, - int argc, - char ** argv) +int bind_program(char ** exec, + const char * name, + uint8_t flags) { - char * progs; - char ** argv_dup = NULL; - int i; - char * name_dup = NULL; - struct prog_entry * e = NULL; - struct reg_entry * re = NULL; - - if (prog == NULL || name == NULL) - return -EINVAL; + struct prog_info prog; + struct name_info ni; - pthread_rwlock_wrlock(&irmd.reg_lock); + if (name == NULL || exec == NULL || exec[0] == NULL) + return -EINVAL; - e = prog_table_get(&irmd.prog_table, path_strip(prog)); - if (e == NULL) { - progs = strdup(path_strip(prog)); - if (progs == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - return -ENOMEM; - } + memset(&prog, 0, sizeof(prog)); + memset(&ni, 0, sizeof(ni)); - if ((flags & BIND_AUTO) && argc) { - /* We need to duplicate argv and set argv[0] to prog. */ - argv_dup = malloc((argc + 2) * sizeof(*argv_dup)); - argv_dup[0] = strdup(prog); - for (i = 1; i <= argc; ++i) { - argv_dup[i] = strdup(argv[i - 1]); - if (argv_dup[i] == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - argvfree(argv_dup); - log_err("Failed to bind program " - "%s to %s.", - prog, name); - free(progs); - return -ENOMEM; - } - } - argv_dup[argc + 1] = NULL; - } - e = prog_entry_create(progs, flags, argv_dup); - if (e == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - free(progs); - argvfree(argv_dup); - return -ENOMEM; - } - prog_table_add(&irmd.prog_table, e); + if (!reg_has_prog(exec[0])) { + strcpy(prog.name, path_strip(exec[0])); + strcpy(prog.path, exec[0]); + if (reg_create_prog(&prog) < 0) + goto fail_prog; } - name_dup = strdup(name); - if (name_dup == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - return -ENOMEM; + if (!reg_has_name(name)) { + ni.pol_lb = LB_SPILL; + strcpy(ni.name, name); + if (reg_create_name(&ni) < 0) { + log_err("Failed to create name %s.", name); + goto fail_name; + } } - if (prog_entry_add_name(e, name_dup)) { - log_err("Failed adding name."); - pthread_rwlock_unlock(&irmd.reg_lock); - free(name_dup); - return -ENOMEM; + if (reg_bind_prog(name, exec, flags) < 0) { + log_err("Failed to bind program %s to name %s", exec[0], name); + goto fail_bind; } - re = registry_get_entry(&irmd.registry, name); - if (re != NULL && reg_entry_add_prog(re, e) < 0) - log_err("Failed adding program %s for name %s.", prog, name); - - pthread_rwlock_unlock(&irmd.reg_lock); - - log_info("Bound program %s to name %s.", prog, name); + log_info("Bound program %s to name %s.", exec[0], name); return 0; + + fail_bind: + if (strlen(ni.name) > 0) + reg_destroy_name(name); + fail_name: + if (strlen(prog.name) > 0) + reg_destroy_prog(exec[0]); + fail_prog: + return -1; } -static int bind_process(pid_t pid, - char * name) +int bind_process(pid_t pid, + const char * name) { - char * name_dup = NULL; - struct proc_entry * e = NULL; - struct reg_entry * re = NULL; - struct timespec now; - struct timespec dl = {0, 10 * MILLION}; + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_MS(10); + struct name_info ni; if (name == NULL) return -EINVAL; - clock_gettime(PTHREAD_COND_CLOCK, &now); - - ts_add(&dl, &now, &dl); + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); - pthread_rwlock_wrlock(&irmd.reg_lock); - - while (!kill(pid, 0)) { - e = proc_table_get(&irmd.proc_table, pid); - if (e != NULL || ts_diff_ms(&now, &dl) > 0) - break; - clock_gettime(PTHREAD_COND_CLOCK, &now); - sched_yield(); - } - - if (e == NULL) { + if (reg_wait_proc(pid, &abstime) < 0) { log_err("Process %d does not %s.", pid, kill(pid, 0) ? "exist" : "respond"); - pthread_rwlock_unlock(&irmd.reg_lock); - return -1; + goto fail; } - name_dup = strdup(name); - if (name_dup == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - return -ENOMEM; + memset(&ni, 0, sizeof(ni)); + + if (!reg_has_name(name)) { + ni.pol_lb = LB_SPILL; + strcpy(ni.name, name); + if (reg_create_name(&ni) < 0) { + log_err("Failed to create name %s.", name); + goto fail; + } } - if (proc_entry_add_name(e, name_dup)) { - pthread_rwlock_unlock(&irmd.reg_lock); + if (reg_bind_proc(name, pid) < 0) { log_err("Failed to add name %s to process %d.", name, pid); - free(name_dup); - return -1; + goto fail_bind; } - re = registry_get_entry(&irmd.registry, name); - if (re != NULL && reg_entry_add_pid(re, pid) < 0) - log_err("Failed adding process %d for name %s.", pid, name); - - pthread_rwlock_unlock(&irmd.reg_lock); - log_info("Bound process %d to name %s.", pid, name); return 0; + + fail_bind: + if (strlen(ni.name) > 0) + reg_destroy_name(name); + fail: + return -1; + } -static int unbind_program(char * prog, - char * name) +static int unbind_program(const char * prog, + const char * name) { - struct reg_entry * e; - if (prog == NULL) return -EINVAL; - pthread_rwlock_wrlock(&irmd.reg_lock); - - if (name == NULL) - prog_table_del(&irmd.prog_table, prog); - else { - struct prog_entry * en = prog_table_get(&irmd.prog_table, prog); - if (en == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - return -EINVAL; + if (name == NULL) { + if (reg_destroy_prog(prog) < 0) { + log_err("Failed to unbind %s.", prog); + return -1; } - - prog_entry_del_name(en, name); - - e = registry_get_entry(&irmd.registry, name); - if (e != NULL) - reg_entry_del_prog(e, prog); - } - - pthread_rwlock_unlock(&irmd.reg_lock); - - if (name == NULL) log_info("Program %s unbound.", prog); - else - log_info("All names matching %s unbound for %s.", name, prog); + } else { + if (reg_unbind_prog(name, prog) < 0) { + log_err("Failed to unbind %s from %s", prog, name); + return -1; + } + log_info("Name %s unbound for %s.", name, prog); + } return 0; } @@ -866,1128 +546,732 @@ static int unbind_program(char * prog, static int unbind_process(pid_t pid, const char * name) { - struct reg_entry * e; - - pthread_rwlock_wrlock(&irmd.reg_lock); - - if (name == NULL) - proc_table_del(&irmd.proc_table, pid); - else { - struct proc_entry * en = proc_table_get(&irmd.proc_table, pid); - if (en != NULL) - proc_entry_del_name(en, name); - - e = registry_get_entry(&irmd.registry, name); - if (e != NULL) - reg_entry_del_pid(e, pid); - } - - pthread_rwlock_unlock(&irmd.reg_lock); - - if (name == NULL) + if (name == NULL) { + if (reg_destroy_proc(pid) < 0) { + log_err("Failed to unbind %d.", pid); + return -1; + } log_info("Process %d unbound.", pid); - else - log_info("All names matching %s unbound for %d.", name, pid); + } else { + if (reg_unbind_proc(name, pid) < 0) { + log_err("Failed to unbind %d from %s", pid, name); + return -1; + } + log_info("Name %s unbound for process %d.", name, pid); + } return 0; } -static ssize_t list_ipcps(ipcp_info_msg_t *** ipcps, - size_t * n_ipcps) +static int list_ipcps(ipcp_list_msg_t *** ipcps, + size_t * n_ipcps) { - struct list_head * p; - int i = 0; - - pthread_rwlock_rdlock(&irmd.reg_lock); - - *n_ipcps = irmd.n_ipcps; - if (*n_ipcps == 0) { - pthread_rwlock_unlock(&irmd.reg_lock); - return 0; - } - - *ipcps = malloc(irmd.n_ipcps * sizeof(**ipcps)); - if (*ipcps == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - *n_ipcps = 0; - return -ENOMEM; - } + int n; - list_for_each(p, &irmd.ipcps) { - struct ipcp_entry * e = list_entry(p, struct ipcp_entry, next); - (*ipcps)[i] = malloc(sizeof(***ipcps)); - if ((*ipcps)[i] == NULL) { - --i; - goto fail; - } - - ipcp_info_msg__init((*ipcps)[i]); - (*ipcps)[i]->name = strdup(e->name); - if ((*ipcps)[i]->name == NULL) - goto fail; - - (*ipcps)[i]->layer = strdup( - e->layer != NULL ? e->layer : "Not enrolled"); - if ((*ipcps)[i]->layer == NULL) - goto fail; - - (*ipcps)[i]->pid = e->pid; - (*ipcps)[i++]->type = e->type; - } + n = reg_list_ipcps(ipcps); + if (n < 0) + goto fail; - pthread_rwlock_unlock(&irmd.reg_lock); + *n_ipcps = (size_t) n; return 0; - fail: - pthread_rwlock_unlock(&irmd.reg_lock); - while (i >= 0) { - free((*ipcps)[i]->layer); - free((*ipcps)[i]->name); - free(*ipcps[i--]); - } - free(*ipcps); + *ipcps = NULL; *n_ipcps = 0; - return -ENOMEM; + return -1; } -static int name_create(const char * name, - enum pol_balance pol) +int name_create(const struct name_info * info) { - struct reg_entry * re; - struct list_head * p; + int ret; - assert(name); + assert(info != NULL); - pthread_rwlock_wrlock(&irmd.reg_lock); - - if (registry_has_name(&irmd.registry, name)) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Registry entry for %s already exists.", name); - return -ENAME; - } - - re = registry_add_name(&irmd.registry, name); - if (re == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Failed creating registry entry for %s.", name); - return -ENOMEM; - } - ++irmd.n_names; - reg_entry_set_policy(re, pol); - - /* check the tables for existing bindings */ - list_for_each(p, &irmd.proc_table) { - struct list_head * q; - struct proc_entry * e; - e = list_entry(p, struct proc_entry, next); - list_for_each(q, &e->names) { - struct str_el * s; - s = list_entry(q, struct str_el, next); - if (!strcmp(s->str, name)) - reg_entry_add_pid(re, e->pid); - } + ret = reg_create_name(info); + if (ret == -EEXIST) { + log_info("Name %s already exists.", info->name); + return 0; } - list_for_each(p, &irmd.prog_table) { - struct list_head * q; - struct prog_entry * e; - e = list_entry(p, struct prog_entry, next); - list_for_each(q, &e->names) { - struct str_el * s; - s = list_entry(q, struct str_el, next); - if (!strcmp(s->str, name)) - reg_entry_add_prog(re, e); - } + if (ret < 0) { + log_err("Failed to create name %s.", info->name); + return -1; } - pthread_rwlock_unlock(&irmd.reg_lock); - - log_info("Created new name: %s.", name); + log_info("Created new name: %s.", info->name); return 0; } static int name_destroy(const char * name) { - assert(name); - pthread_rwlock_wrlock(&irmd.reg_lock); + assert(name != NULL); - if (!registry_has_name(&irmd.registry, name)) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_warn("Registry entry for %s does not exist.", name); - return -ENAME; + if (reg_destroy_name(name) < 0) { + log_err("Failed to destroy name %s.", name); + return -1; } - registry_del_name(&irmd.registry, name); - --irmd.n_names; - - pthread_rwlock_unlock(&irmd.reg_lock); - log_info("Destroyed name: %s.", name); return 0; } -static ssize_t list_names(name_info_msg_t *** names, - size_t * n_names) +static int list_names(name_info_msg_t *** names, + size_t * n_names) { - struct list_head * p; - int i = 0; - - pthread_rwlock_rdlock(&irmd.reg_lock); + int n; - *n_names = irmd.n_names; - if (*n_names == 0) { - pthread_rwlock_unlock(&irmd.reg_lock); - return 0; - } - - *names = malloc(irmd.n_names * sizeof(**names)); - if (*names == NULL) { - *n_names = 0; - pthread_rwlock_unlock(&irmd.reg_lock); - return -ENOMEM; - } - - list_for_each(p, &irmd.registry) { - struct reg_entry * e = list_entry(p, struct reg_entry, next); - - (*names)[i] = malloc(sizeof(***names)); - if ((*names)[i] == NULL) { - --i; - goto fail; - } - - name_info_msg__init((*names)[i]); - (*names)[i]->name = strdup(e->name); - if ((*names)[i]->name == NULL) - goto fail; - - (*names)[i++]->pol_lb = e->pol_lb; - } + n = reg_list_names(names); + if (n < 0) + goto fail; - pthread_rwlock_unlock(&irmd.reg_lock); + *n_names = (size_t) n; return 0; - fail: - pthread_rwlock_unlock(&irmd.reg_lock); - while (i >= 0) { - free((*names)[i]->name); - free(*names[i--]); - } - free(*names); + *names = NULL; *n_names = 0; - return -ENOMEM; + return -1; } -static int name_reg(const char * name, - pid_t pid) +int name_reg(const char * name, + pid_t pid) { - size_t len; - struct ipcp_entry * ipcp; - uint8_t * hash; - int err; + struct ipcp_info info; + struct layer_info layer; + buffer_t hash; assert(name); - pthread_rwlock_wrlock(&irmd.reg_lock); - - if (!registry_has_name(&irmd.registry, name)) { - err = -ENAME; - goto fail; - } + info.pid = pid; - ipcp = get_ipcp_entry_by_pid(pid); - if (ipcp == NULL) { - err = -EIPCP; - goto fail; + if (!reg_has_name(name)) { + log_err("Failed to get name %s.", name); + return -ENAME; } - if (ipcp->layer == NULL) { - err = -EPERM; - goto fail; + if (reg_get_ipcp(&info, &layer) < 0) { + log_err("Failed to get IPCP %d.", pid); + return -EIPCP; } - len = IPCP_HASH_LEN(ipcp); - - hash = malloc(len); - if (hash == NULL) { - err = -ENOMEM; - goto fail; + hash.len = hash_len((enum hash_algo) layer.dir_hash_algo); + hash.data = malloc(hash.len); + if (hash.data == NULL) { + log_err("Failed to malloc hash."); + return -ENOMEM; } - str_hash(ipcp->dir_hash_algo, hash, name); - pthread_rwlock_unlock(&irmd.reg_lock); + str_hash((enum hash_algo) layer.dir_hash_algo, hash.data, name); - if (ipcp_reg(pid, hash, len)) { - log_err("Could not register " HASH_FMT " with IPCP %d.", - HASH_VAL(hash), pid); - free(hash); + if (ipcp_reg(pid, hash)) { + log_err("Could not register " HASH_FMT32 " with IPCP %d.", + HASH_VAL32(hash.data), pid); + freebuf(hash); return -1; } - log_info("Registered %s with IPCP %d as " HASH_FMT ".", - name, pid, HASH_VAL(hash)); + log_info("Registered %s with IPCP %d as " HASH_FMT32 ".", + name, pid, HASH_VAL32(hash.data)); - free(hash); + freebuf(hash); return 0; - -fail: - pthread_rwlock_unlock(&irmd.reg_lock); - return err; } static int name_unreg(const char * name, pid_t pid) { - struct ipcp_entry * ipcp; - int err; - uint8_t * hash; - size_t len; + struct ipcp_info info; + struct layer_info layer; + buffer_t hash; assert(name); - pthread_rwlock_wrlock(&irmd.reg_lock); + info.pid = pid; - ipcp = get_ipcp_entry_by_pid(pid); - if (ipcp == NULL) { - err = -EIPCP; - goto fail; + if (!reg_has_name(name)) { + log_err("Failed to get name %s.", name); + return -ENAME; } - if (ipcp->layer == NULL) { - err = -EPERM; - goto fail; + if (reg_get_ipcp(&info, &layer) < 0) { + log_err("Failed to get IPCP %d.", pid); + return -EIPCP; } - len = IPCP_HASH_LEN(ipcp); - - hash = malloc(len); - if (hash == NULL) { - err = -ENOMEM; - goto fail; + hash.len = hash_len((enum hash_algo) layer.dir_hash_algo); + hash.data = malloc(hash.len); + if (hash.data == NULL) { + log_err("Failed to malloc hash."); + return -ENOMEM; } - str_hash(ipcp->dir_hash_algo, hash, name); + str_hash((enum hash_algo) layer.dir_hash_algo, hash.data, name); - pthread_rwlock_unlock(&irmd.reg_lock); - - if (ipcp_unreg(pid, hash, len)) { + if (ipcp_unreg(pid, hash)) { log_err("Could not unregister %s with IPCP %d.", name, pid); - free(hash); + freebuf(hash); return -1; } log_info("Unregistered %s from %d.", name, pid); - free(hash); + freebuf(hash); return 0; - - fail: - pthread_rwlock_unlock(&irmd.reg_lock); - return err; } -static int proc_announce(pid_t pid, - char * prog) +static int proc_announce(const struct proc_info * info) { - struct proc_entry * e; - struct prog_entry * a; - char * prog_dup; - - assert(prog); - - prog_dup = strdup(prog); - if (prog_dup == NULL) - return -ENOMEM; - - e = proc_entry_create(pid, prog_dup); - if (e == NULL) { - free(prog_dup); - return -ENOMEM; + if (reg_create_proc(info) < 0) { + log_err("Failed to add process %d.", info->pid); + goto fail_proc; } - pthread_rwlock_wrlock(&irmd.reg_lock); + log_info("Process added: %d (%s).", info->pid, info->prog); - proc_table_add(&irmd.proc_table, e); - - /* Copy listen names from program if it exists. */ - a = prog_table_get(&irmd.prog_table, e->prog); - if (a != NULL) { - struct list_head * p; - list_for_each(p, &a->names) { - struct str_el * s = list_entry(p, struct str_el, next); - struct str_el * n = malloc(sizeof(*n)); - if (n == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - return -ENOMEM; - } + return 0; - n->str = strdup(s->str); - if (n->str == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - free(n); - return -ENOMEM; - } + fail_proc: + return -1; +} - list_add(&n->next, &e->names); - log_dbg("Process %d inherits name %s from program %s.", - pid, n->str, e->prog); - } - } +static int proc_exit(pid_t pid) +{ + if (reg_destroy_proc(pid) < 0) + log_err("Failed to remove process %d.", pid); - pthread_rwlock_unlock(&irmd.reg_lock); + log_info("Process removed: %d.", pid); return 0; } -static int flow_accept(pid_t pid, - struct timespec * timeo, - struct irm_flow * f_out, - const void * data, - size_t len) +static void __cleanup_pkp(void * pkp) { - struct irm_flow * f = NULL; - struct proc_entry * pe = NULL; - struct reg_entry * re = NULL; - struct list_head * p = NULL; - - pid_t pid_n; - pid_t pid_n_1; - int flow_id; - int ret; - - pthread_rwlock_wrlock(&irmd.reg_lock); - - pe = proc_table_get(&irmd.proc_table, pid); - if (pe == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Unknown process %d calling accept.", pid); - return -EINVAL; - } - - log_dbg("New instance (%d) of %s added.", pid, pe->prog); - log_dbg("This process accepts flows for:"); - - list_for_each(p, &pe->names) { - struct str_el * s = list_entry(p, struct str_el, next); - log_dbg(" %s", s->str); - re = registry_get_entry(&irmd.registry, s->str); - if (re != NULL) - reg_entry_add_pid(re, pid); - } - - pthread_rwlock_unlock(&irmd.reg_lock); - - ret = proc_entry_sleep(pe, timeo); - if (ret == -ETIMEDOUT) - return -ETIMEDOUT; - - if (ret == -1) - return -EPIPE; - - if (irmd_get_state() != IRMD_RUNNING) - return -EIRMD; + if (pkp != NULL) + crypt_dh_pkp_destroy(pkp); +} - pthread_rwlock_rdlock(&irmd.flows_lock); +static void __cleanup_flow(void * flow) +{ + reg_destroy_flow(((struct flow_info *) flow)->id); +} - f = get_irm_flow_n(pid); - if (f == NULL) { - pthread_rwlock_unlock(&irmd.flows_lock); - log_warn("Port_id was not created yet."); - return -EPERM; +static int flow_accept(struct flow_info * flow, + buffer_t * data, + struct timespec * abstime) +{ + uint8_t buf[MSGBUFSZ]; + buffer_t lpk; /* local public key */ + buffer_t rpk; /* remote public key */ + void * pkp; /* my public/private key pair */ + ssize_t key_len; + uint8_t * s; + int err; + + /* piggyback of user data not yet implemented */ + assert(data != NULL && data->len == 0 && data->data == NULL); + + if (!reg_has_proc(flow->n_pid)) { + log_err("Unknown process %d calling accept.", flow->n_pid); + err = -EINVAL; + goto fail; } - pid_n = f->n_pid; - pid_n_1 = f->n_1_pid; - flow_id = f->flow_id; - - pthread_rwlock_unlock(&irmd.flows_lock); - pthread_rwlock_rdlock(&irmd.reg_lock); - - pe = proc_table_get(&irmd.proc_table, pid); - if (pe == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - pthread_rwlock_wrlock(&irmd.flows_lock); - list_del(&f->next); - bmp_release(irmd.flow_ids, f->flow_id); - pthread_rwlock_unlock(&irmd.flows_lock); - ipcp_flow_alloc_resp(pid_n_1, flow_id, pid_n, -1, NULL, 0); - clear_irm_flow(f); - irm_flow_set_state(f, FLOW_NULL); - irm_flow_destroy(f); - log_dbg("Process gone while accepting flow."); - return -EPERM; + s = malloc(SYMMKEYSZ); + if (s == NULL) { + log_err("Failed to malloc symmkey."); + err = -ENOMEM; + goto fail; } - pthread_mutex_lock(&pe->lock); - - re = pe->re; - - pthread_mutex_unlock(&pe->lock); - - if (reg_entry_get_state(re) != REG_NAME_FLOW_ARRIVED) { - pthread_rwlock_unlock(&irmd.reg_lock); - pthread_rwlock_wrlock(&irmd.flows_lock); - list_del(&f->next); - bmp_release(irmd.flow_ids, f->flow_id); - pthread_rwlock_unlock(&irmd.flows_lock); - ipcp_flow_alloc_resp(pid_n_1, flow_id, pid_n, -1, NULL, 0); - clear_irm_flow(f); - irm_flow_set_state(f, FLOW_NULL); - irm_flow_destroy(f); - log_err("Entry in wrong state."); - return -EPERM; + key_len = crypt_dh_pkp_create(&pkp, buf); + if (key_len < 0) { + log_err("Failed to generate key pair."); + err = -ECRYPT; + goto fail_pkp; } - registry_del_process(&irmd.registry, pid); - - pthread_rwlock_unlock(&irmd.reg_lock); + lpk.data = buf; + lpk.len = (size_t) key_len; - pthread_rwlock_wrlock(&irmd.flows_lock); + log_dbg("Generated ephemeral keys for %d.", flow->n_pid); - f_out->flow_id = f->flow_id; - f_out->n_pid = f->n_pid; - f_out->n_1_pid = f->n_1_pid; - f_out->data = f->data; /* pass owner */ - f_out->len = f->len; - f_out->qs = f->qs; - f->data = NULL; - f->len = 0; - - pthread_rwlock_unlock(&irmd.flows_lock); - - if (f->qs.cypher_s == 0) { /* no crypto requested, don't send pubkey */ - data = NULL; - len = 0; + if (reg_create_flow(flow) < 0) { + log_err("Failed to create flow."); + err = -EBADF; + goto fail_flow; } - if (ipcp_flow_alloc_resp(pid_n_1, flow_id, pid_n, 0, data, len)) { - pthread_rwlock_wrlock(&irmd.flows_lock); - list_del(&f->next); - pthread_rwlock_unlock(&irmd.flows_lock); - log_dbg("Failed to respond to alloc. Port_id invalidated."); - clear_irm_flow(f); - irm_flow_set_state(f, FLOW_NULL); - irm_flow_destroy(f); - return -EPERM; + if (reg_prepare_flow_accept(flow, &lpk) < 0) { + log_err("Failed to prepare accept."); + err = -EBADF; + goto fail_wait; } - irm_flow_set_state(f, FLOW_ALLOCATED); - - log_info("Flow on flow_id %d allocated.", f->flow_id); - - return 0; -} + pthread_cleanup_push(__cleanup_flow, flow); + pthread_cleanup_push(__cleanup_pkp, pkp); + pthread_cleanup_push(free, s); -static int flow_alloc(pid_t pid, - const char * dst, - qosspec_t qs, - struct timespec * timeo, - struct irm_flow * f_out, - bool join, - const void * data, - size_t len) -{ - struct irm_flow * f; - struct ipcp_entry * ipcp; - int flow_id; - int state; - uint8_t * hash; - - ipcp = join ? get_ipcp_entry_by_layer(dst) - : get_ipcp_by_dst_name(dst, pid); - if (ipcp == NULL) { - log_info("Destination %s unreachable.", dst); - return -1; - } + err = reg_wait_flow_accepted(flow, &rpk, abstime); - pthread_rwlock_wrlock(&irmd.flows_lock); + pthread_cleanup_pop(false); + pthread_cleanup_pop(false); + pthread_cleanup_pop(false); - flow_id = bmp_allocate(irmd.flow_ids); - if (!bmp_is_id_valid(irmd.flow_ids, flow_id)) { - pthread_rwlock_unlock(&irmd.flows_lock); - log_err("Could not allocate flow_id."); - return -EBADF; + if (err == -ETIMEDOUT) { + log_err("Flow accept timed out."); + goto fail_wait; } - f = irm_flow_create(pid, ipcp->pid, flow_id, qs); - if (f == NULL) { - bmp_release(irmd.flow_ids, flow_id); - pthread_rwlock_unlock(&irmd.flows_lock); - log_err("Could not allocate flow_id."); - return -ENOMEM; + if (err == -1) { + log_dbg("Flow accept terminated."); + err = -EPIPE; + goto fail_wait; } - list_add(&f->next, &irmd.irm_flows); - - pthread_rwlock_unlock(&irmd.flows_lock); - - assert(irm_flow_get_state(f) == FLOW_ALLOC_PENDING); + assert(err == 0); - hash = malloc(IPCP_HASH_LEN(ipcp)); - if (hash == NULL) - /* sanitizer cleans this */ - return -ENOMEM; - - str_hash(ipcp->dir_hash_algo, hash, dst); - - if (join) { - if (ipcp_flow_join(ipcp->pid, flow_id, pid, hash, - IPCP_HASH_LEN(ipcp), qs)) { - /* sanitizer cleans this */ - log_info("Flow_join failed."); - free(hash); - return -EAGAIN; + if (flow->qs.cypher_s != 0) { /* crypto requested */ + if (crypt_dh_derive(pkp, rpk, s) < 0) { + log_err("Failed to derive secret for %d.", flow->id); + err = -ECRYPT; + goto fail_derive; } + freebuf(rpk); + data->data = s; + data->len = SYMMKEYSZ; + s= NULL; } else { - if (ipcp_flow_alloc(ipcp->pid, flow_id, pid, hash, - IPCP_HASH_LEN(ipcp), qs, data, len)) { - /* sanitizer cleans this */ - log_info("Flow_allocation failed."); - free(hash); - return -EAGAIN; - } + clrbuf(lpk); } - free(hash); - - state = irm_flow_wait_state(f, FLOW_ALLOCATED, timeo); - if (state != FLOW_ALLOCATED) { - if (state == -ETIMEDOUT) { - log_dbg("Flow allocation timed out"); - return -ETIMEDOUT; - } - - log_info("Pending flow to %s torn down.", dst); - return -EPIPE; + if (ipcp_flow_alloc_resp(flow, 0, lpk) < 0) { + log_err("Failed to respond to flow allocation."); + err = -EIPCP; + goto fail_alloc_resp; } - pthread_rwlock_wrlock(&irmd.flows_lock); - - assert(irm_flow_get_state(f) == FLOW_ALLOCATED); - - f_out->flow_id = f->flow_id; - f_out->n_pid = f->n_pid; - f_out->n_1_pid = f->n_1_pid; - f_out->data = f->data; /* pass owner */ - f_out->len = f->len; - f->data = NULL; - f->len = 0; - - pthread_rwlock_unlock(&irmd.flows_lock); - - log_info("Flow on flow_id %d allocated.", flow_id); + crypt_dh_pkp_destroy(pkp); + free(s); return 0; + + fail_derive: + freebuf(rpk); + clrbuf(lpk); + ipcp_flow_alloc_resp(flow, err, lpk); + fail_alloc_resp: + flow->state = FLOW_NULL; + fail_wait: + reg_destroy_flow(flow->id); + fail_flow: + crypt_dh_pkp_destroy(pkp); + fail_pkp: + free(s); + fail: + return err; } -static int flow_dealloc(pid_t pid, - int flow_id, - time_t timeo) +static int flow_join(struct flow_info * flow, + const char * dst, + struct timespec * abstime) { - pid_t n_1_pid = -1; - int ret = 0; - - struct irm_flow * f = NULL; + struct ipcp_info ipcp; + struct layer_info layer; + buffer_t hash; + buffer_t pbuf = {NULL, 0}; /* nothing to piggyback */ + int err; - pthread_rwlock_wrlock(&irmd.flows_lock); + log_info("Allocating flow for %d to %s.", flow->n_pid, dst); - f = get_irm_flow(flow_id); - if (f == NULL) { - pthread_rwlock_unlock(&irmd.flows_lock); - log_dbg("Deallocate unknown port %d by %d.", flow_id, pid); - return 0; + if (reg_create_flow(flow) < 0) { + log_err("Failed to create flow."); + err = -EBADF; + goto fail_flow; } - if (pid == f->n_pid) { - f->n_pid = -1; - n_1_pid = f->n_1_pid; - } else if (pid == f->n_1_pid) { - f->n_1_pid = -1; - } else { - pthread_rwlock_unlock(&irmd.flows_lock); - log_dbg("Dealloc called by wrong process."); - return -EPERM; + strcpy(layer.name, dst); + if (reg_get_ipcp_by_layer(&ipcp, &layer) < 0) { + log_err("Failed to get IPCP for layer %s.", dst); + err = -EIPCP; + goto fail_ipcp; } - if (irm_flow_get_state(f) == FLOW_DEALLOC_PENDING) { - list_del(&f->next); - if ((kill(f->n_pid, 0) < 0 && f->n_1_pid == -1) || - (kill(f->n_1_pid, 0) < 0 && f->n_pid == -1)) - irm_flow_set_state(f, FLOW_NULL); - clear_irm_flow(f); - irm_flow_destroy(f); - bmp_release(irmd.flow_ids, flow_id); - log_info("Completed deallocation of flow_id %d by process %d.", - flow_id, pid); - } else { - irm_flow_set_state(f, FLOW_DEALLOC_PENDING); - log_dbg("Partial deallocation of flow_id %d by process %d.", - flow_id, pid); + hash.len = hash_len((enum hash_algo) layer.dir_hash_algo); + hash.data = malloc(hash.len); + if (hash.data == NULL) { + log_err("Failed to malloc hash buffer."); + err = -ENOMEM; + goto fail_ipcp; } - pthread_rwlock_unlock(&irmd.flows_lock); + reg_prepare_flow_alloc(flow); - if (n_1_pid != -1) - ret = ipcp_flow_dealloc(n_1_pid, flow_id, timeo); + if (ipcp_flow_join(flow, hash)) { + log_err("Flow join with layer %s failed.", dst); + err = -ENOTALLOC; + goto fail_alloc; + } - return ret; -} + pthread_cleanup_push(__cleanup_flow, flow); + pthread_cleanup_push(free, hash.data); -static pid_t auto_execute(char ** argv) -{ - pid_t pid; - struct stat s; + err = reg_wait_flow_allocated(flow, &pbuf, abstime); - if (stat(argv[0], &s) != 0) { - log_warn("Program %s does not exist.", argv[0]); - return -1; - } + pthread_cleanup_pop(false); + pthread_cleanup_pop(false); - if (!(s.st_mode & S_IXUSR)) { - log_warn("Program %s is not executable.", argv[0]); - return -1; + if (err == -ETIMEDOUT) { + log_err("Flow join timed out."); + goto fail_alloc; } - if (posix_spawn(&pid, argv[0], NULL, NULL, argv, NULL)) { - log_err("Failed to spawn new process"); - return -1; + if (err == -1) { + log_dbg("Flow join terminated."); + err = -EPIPE; + goto fail_alloc; } - log_info("Instantiated %s as process %d.", argv[0], pid); + assert(err == 0); - return pid; + freebuf(hash); + + return 0; + + fail_alloc: + freebuf(hash); + fail_ipcp: + reg_destroy_flow(flow->id); + fail_flow: + return err; } -static int flow_req_arr(pid_t pid, - struct irm_flow * f_out, - const uint8_t * hash, - qosspec_t qs, - const void * data, - size_t len) +static int get_ipcp_by_dst(const char * dst, + pid_t * pid, + buffer_t * hash) { - struct reg_entry * re = NULL; - struct prog_entry * a = NULL; - struct proc_entry * pe = NULL; - struct irm_flow * f = NULL; + ipcp_list_msg_t ** ipcps; + int n; + int i; + int err = -EIPCP; - struct pid_el * c_pid; - struct ipcp_entry * ipcp; - pid_t h_pid; - int flow_id; + n = reg_list_ipcps(&ipcps); - struct timespec wt = {IRMD_REQ_ARR_TIMEOUT / 1000, - (IRMD_REQ_ARR_TIMEOUT % 1000) * MILLION}; + /* Clean up the ipcp_msgs in this loop */ + for (i = 0; i < n; ++i) { + enum hash_algo algo; + enum ipcp_type type; + pid_t tmp; + bool enrolled; - log_dbg("Flow req arrived from IPCP %d for " HASH_FMT ".", - pid, HASH_VAL(hash)); + type = ipcps[i]->type; + algo = ipcps[i]->hash_algo; + tmp = ipcps[i]->pid; - pthread_rwlock_rdlock(&irmd.reg_lock); + enrolled = strcmp(ipcps[i]->layer, "Not enrolled.") != 0; - ipcp = get_ipcp_entry_by_pid(pid); - if (ipcp == NULL) { - log_err("IPCP died."); - return -EIPCP; - } + ipcp_list_msg__free_unpacked(ipcps[i], NULL); - re = registry_get_entry_by_hash(&irmd.registry, ipcp->dir_hash_algo, - hash, IPCP_HASH_LEN(ipcp)); - if (re == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Unknown hash: " HASH_FMT ".", HASH_VAL(hash)); - return -1; - } - - log_info("Flow request arrived for %s.", re->name); - - pthread_rwlock_unlock(&irmd.reg_lock); - - /* Give the process a bit of slop time to call accept */ - if (reg_entry_leave_state(re, REG_NAME_IDLE, &wt) == -1) { - log_err("No processes for " HASH_FMT ".", HASH_VAL(hash)); - return -1; - } + if (type == IPCP_BROADCAST) + continue; - pthread_rwlock_wrlock(&irmd.reg_lock); + if (err == 0 /* solution found */ || !enrolled) + continue; - switch (reg_entry_get_state(re)) { - case REG_NAME_IDLE: - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("No processes for " HASH_FMT ".", HASH_VAL(hash)); - return -1; - case REG_NAME_AUTO_ACCEPT: - c_pid = malloc(sizeof(*c_pid)); - if (c_pid == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - return -1; + hash->len = hash_len(algo); + hash->data = malloc(hash->len); + if (hash->data == NULL) { + log_warn("Failed to malloc hash for query."); + err = -ENOMEM; + continue; } - reg_entry_set_state(re, REG_NAME_AUTO_EXEC); - a = prog_table_get(&irmd.prog_table, - reg_entry_get_prog(re)); + str_hash(algo, hash->data, dst); - if (a == NULL || (c_pid->pid = auto_execute(a->argv)) < 0) { - reg_entry_set_state(re, REG_NAME_AUTO_ACCEPT); - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Could not start program for reg_entry %s.", - re->name); - free(c_pid); - return -1; + if (ipcp_query(tmp, *hash) < 0) { + freebuf(*hash); + continue; } - list_add(&c_pid->next, &irmd.spawned_pids); + *pid = tmp; - pthread_rwlock_unlock(&irmd.reg_lock); + err = 0; + } - if (reg_entry_leave_state(re, REG_NAME_AUTO_EXEC, NULL)) - return -1; + free(ipcps); - pthread_rwlock_wrlock(&irmd.reg_lock); - /* FALLTHRU */ - case REG_NAME_FLOW_ACCEPT: - h_pid = reg_entry_get_pid(re); - if (h_pid == -1) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Invalid process id returned."); - return -1; + return err; +} + +static int flow_alloc(struct flow_info * flow, + const char * dst, + buffer_t * data, + struct timespec * abstime) +{ + uint8_t buf[MSGBUFSZ]; + buffer_t lpk ={NULL, 0}; /* local public key */ + buffer_t rpk; /* remote public key */ + void * pkp = NULL; /* my public/private key pair */ + uint8_t * s = NULL; + buffer_t hash; + int err; + /* piggyback of user data not yet implemented */ + assert(data != NULL && data->len == 0 && data->data == NULL); + + log_info("Allocating flow for %d to %s.", flow->n_pid, dst); + + + if (flow->qs.cypher_s > 0) { + ssize_t key_len; + + s = malloc(SYMMKEYSZ); + if (s == NULL) { + log_err("Failed to malloc symmetric key"); + err = -ENOMEM; + goto fail_malloc; } - break; - default: - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("IRMd in wrong state."); - return -1; - } + key_len = crypt_dh_pkp_create(&pkp, buf); + if (key_len < 0) { + log_err("Failed to generate key pair."); + err = -ECRYPT; + goto fail_pkp; + } - pthread_rwlock_unlock(&irmd.reg_lock); - pthread_rwlock_wrlock(&irmd.flows_lock); + lpk.data = buf; + lpk.len = (size_t) key_len; - flow_id = bmp_allocate(irmd.flow_ids); - if (!bmp_is_id_valid(irmd.flow_ids, flow_id)) { - pthread_rwlock_unlock(&irmd.flows_lock); - return -1; + log_dbg("Generated ephemeral keys for %d.", flow->n_pid); } - f = irm_flow_create(h_pid, pid, flow_id, qs); - if (f == NULL) { - bmp_release(irmd.flow_ids, flow_id); - pthread_rwlock_unlock(&irmd.flows_lock); - log_err("Could not allocate flow_id."); - return -1; + if (reg_create_flow(flow) < 0) { + log_err("Failed to create flow."); + err = -EBADF; + goto fail_flow; } - if (len != 0) { - assert(data); - f->data = malloc(len); - if (f->data == NULL) { - bmp_release(irmd.flow_ids, flow_id); - pthread_rwlock_unlock(&irmd.flows_lock); - log_err("Could not piggyback data."); - return -1; - } + if (get_ipcp_by_dst(dst, &flow->n_1_pid, &hash) < 0) { + log_err("Failed to find IPCP for %s.", dst); + err = -EIPCP; + goto fail_ipcp; + } - f->len = len; + reg_prepare_flow_alloc(flow); - memcpy(f->data, data, len); + if (ipcp_flow_alloc(flow, hash, lpk)) { + log_err("Flow allocation %d failed.", flow->id); + err = -ENOTALLOC; + goto fail_alloc; } - list_add(&f->next, &irmd.irm_flows); + pthread_cleanup_push(__cleanup_flow, flow); + pthread_cleanup_push(__cleanup_pkp, pkp); + pthread_cleanup_push(free, hash.data); + pthread_cleanup_push(free, s); - pthread_rwlock_unlock(&irmd.flows_lock); - pthread_rwlock_rdlock(&irmd.reg_lock); + err = reg_wait_flow_allocated(flow, &rpk, abstime); - reg_entry_set_state(re, REG_NAME_FLOW_ARRIVED); + pthread_cleanup_pop(false); + pthread_cleanup_pop(false); + pthread_cleanup_pop(false); + pthread_cleanup_pop(false); - pe = proc_table_get(&irmd.proc_table, h_pid); - if (pe == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - pthread_rwlock_wrlock(&irmd.flows_lock); - clear_irm_flow(f); - bmp_release(irmd.flow_ids, f->flow_id); - list_del(&f->next); - pthread_rwlock_unlock(&irmd.flows_lock); - log_err("Could not get process table entry for %d.", h_pid); - free(f->data); - f->len = 0; - irm_flow_destroy(f); - return -1; + if (err == -ETIMEDOUT) { + log_err("Flow allocation timed out."); + goto fail_alloc; } - proc_entry_wake(pe, re); + if (err == -1) { + log_dbg("Flow allocation terminated."); + err = -EPIPE; + goto fail_alloc; + } - pthread_rwlock_unlock(&irmd.reg_lock); + assert(err == 0); - reg_entry_leave_state(re, REG_NAME_FLOW_ARRIVED, NULL); + if (flow->qs.cypher_s != 0) { /* crypto requested */ + if (crypt_dh_derive(pkp, rpk, s) < 0) { + log_err("Failed to derive secret for %d.", flow->id); + err = -ECRYPT; + goto fail_derive; + } + crypt_dh_pkp_destroy(pkp); + freebuf(rpk); + data->data = s; + data->len = SYMMKEYSZ; + s = NULL; + } - f_out->flow_id = flow_id; - f_out->n_pid = h_pid; + freebuf(hash); + free(s); return 0; + + fail_derive: + freebuf(rpk); + flow->state = FLOW_DEALLOCATED; + fail_alloc: + freebuf(hash); + fail_ipcp: + reg_destroy_flow(flow->id); + fail_flow: + if (flow->qs.cypher_s > 0) + crypt_dh_pkp_destroy(pkp); + fail_pkp: + free(s); + fail_malloc: + return err; } -static int flow_alloc_reply(int flow_id, - int response, - const void * data, - size_t len) +static int wait_for_accept(enum hash_algo algo, + const uint8_t * hash) { - struct irm_flow * f; + struct timespec timeo = TIMESPEC_INIT_MS(IRMD_REQ_ARR_TIMEOUT); + struct timespec abstime; + char ** exec; + int ret; + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); + + ret = reg_wait_flow_accepting(algo, hash, &abstime); + if (ret == -ETIMEDOUT) { + if (reg_get_exec(algo, hash, &exec) < 0) { + log_dbg("No program bound to " HASH_FMT32 ".", + HASH_VAL32(hash)); + goto fail; + } - pthread_rwlock_wrlock(&irmd.flows_lock); + log_info("Autostarting %s.", exec[0]); - f = get_irm_flow(flow_id); - if (f == NULL) { - pthread_rwlock_unlock(&irmd.flows_lock); - return -1; - } + if (spawn_program(exec) < 0) { + log_dbg("Failed to autostart " HASH_FMT32 ".", + HASH_VAL32(hash)); + goto fail_spawn; + } - if (!response) - irm_flow_set_state(f, FLOW_ALLOCATED); - else - irm_flow_set_state(f, FLOW_NULL); + ts_add(&abstime, &timeo, &abstime); - f->data = malloc(len); - if (f->data == NULL) { - pthread_rwlock_unlock(&irmd.flows_lock); - return -1; - } + ret = reg_wait_flow_accepting(algo, hash, &abstime); + if (ret == -ETIMEDOUT) + goto fail_spawn; - memcpy(f->data, data, len); - f->len = len; + argvfree(exec); + } - pthread_rwlock_unlock(&irmd.flows_lock); + return ret; - return 0; + fail_spawn: + argvfree(exec); + fail: + return -1; } -static void irm_fini(void) +static int flow_req_arr(struct flow_info * flow, + const uint8_t * hash, + buffer_t * data) { - struct list_head * p; - struct list_head * h; + struct ipcp_info info; + struct layer_info layer; + enum hash_algo algo; + int ret; - if (irmd_get_state() != IRMD_NULL) - log_warn("Unsafe destroy."); + info.pid = flow->n_1_pid; - pthread_rwlock_wrlock(&irmd.reg_lock); + log_info("Flow req arrived from IPCP %d for " HASH_FMT32 ".", + info.pid, HASH_VAL32(hash)); - /* Clear the lists. */ - list_for_each_safe(p, h, &irmd.ipcps) { - struct ipcp_entry * e = list_entry(p, struct ipcp_entry, next); - list_del(&e->next); - ipcp_entry_destroy(e); + if (reg_get_ipcp(&info, &layer) < 0) { + log_err("No IPCP with pid %d.", info.pid); + ret = -EIPCP; + goto fail; } - list_for_each(p, &irmd.spawned_pids) { - struct pid_el * e = list_entry(p, struct pid_el, next); - if (kill(e->pid, SIGTERM)) - log_dbg("Could not send kill signal to %d.", e->pid); - } + algo = (enum hash_algo) layer.dir_hash_algo; - list_for_each_safe(p, h, &irmd.spawned_pids) { - struct pid_el * e = list_entry(p, struct pid_el, next); - int status; - if (waitpid(e->pid, &status, 0) < 0) - log_dbg("Error waiting for %d to exit.", e->pid); - list_del(&e->next); - registry_del_process(&irmd.registry, e->pid); - free(e); + ret = wait_for_accept(algo, hash); + if (ret < 0) { + log_err("No activeprocess for " HASH_FMT32 ".", + HASH_VAL32(hash)); + goto fail; } - list_for_each_safe(p, h, &irmd.prog_table) { - struct prog_entry * e = list_entry(p, struct prog_entry, next); - list_del(&e->next); - prog_entry_destroy(e); - } + flow->id = ret; + flow->state = FLOW_ALLOCATED; - list_for_each_safe(p, h, &irmd.proc_table) { - struct proc_entry * e = list_entry(p, struct proc_entry, next); - list_del(&e->next); - e->state = PROC_INIT; /* sanitizer already joined */ - proc_entry_destroy(e); + ret = reg_respond_accept(flow, data); + if (ret < 0) { + log_err("Failed to respond to flow %d.", flow->id); + goto fail; } - registry_destroy(&irmd.registry); - - pthread_rwlock_unlock(&irmd.reg_lock); - - close(irmd.sockfd); - - if (unlink(IRM_SOCK_PATH)) - log_dbg("Failed to unlink %s.", IRM_SOCK_PATH); - - pthread_rwlock_wrlock(&irmd.flows_lock); + return 0; + fail: + return ret; +} - if (irmd.flow_ids != NULL) - bmp_destroy(irmd.flow_ids); +static int flow_alloc_reply(struct flow_info * flow, + int response, + buffer_t * data) +{ + flow->state = response ? FLOW_DEALLOCATED : FLOW_ALLOCATED; - list_for_each_safe(p, h, &irmd.irm_flows) { - struct irm_flow * f = list_entry(p, struct irm_flow, next); - list_del(&f->next); - irm_flow_destroy(f); + if (reg_respond_alloc(flow, data) < 0) { + log_err("Failed to reply to flow %d.", flow->id); + flow->state = FLOW_DEALLOCATED; + return -EBADF; } - pthread_rwlock_unlock(&irmd.flows_lock); - - - if (irmd.rdrb != NULL) - shm_rdrbuff_destroy(irmd.rdrb); - - if (irmd.lf != NULL) - lockfile_destroy(irmd.lf); - - pthread_mutex_destroy(&irmd.cmd_lock); - pthread_cond_destroy(&irmd.cmd_cond); - pthread_rwlock_destroy(&irmd.flows_lock); - pthread_rwlock_destroy(&irmd.reg_lock); - pthread_rwlock_destroy(&irmd.state_lock); - -#ifdef HAVE_FUSE - if (rmdir(FUSE_PREFIX)) - log_dbg("Failed to remove " FUSE_PREFIX); -#endif + return 0; } -void * irm_sanitize(void * o) +static int flow_dealloc(struct flow_info * flow, + struct timespec * ts) { - struct timespec now; - struct list_head * p = NULL; - struct list_head * h = NULL; + log_info("Deallocating flow %d for process %d (timeout: %zd s).", + flow->id, flow->n_pid, ts->tv_sec); - struct timespec timeout = {IRMD_CLEANUP_TIMER / BILLION, - IRMD_CLEANUP_TIMER % BILLION}; - int s; + reg_dealloc_flow(flow); - (void) o; - - while (true) { - if (clock_gettime(CLOCK_MONOTONIC, &now) < 0) - log_warn("Failed to get time."); - - if (irmd_get_state() != IRMD_RUNNING) - return (void *) 0; - - pthread_rwlock_wrlock(&irmd.reg_lock); - - list_for_each_safe(p, h, &irmd.spawned_pids) { - struct pid_el * e = list_entry(p, struct pid_el, next); - waitpid(e->pid, &s, WNOHANG); - if (kill(e->pid, 0) >= 0) - continue; - log_dbg("Child process %d died, error %d.", e->pid, s); - list_del(&e->next); - free(e); - } - - list_for_each_safe(p, h, &irmd.proc_table) { - struct proc_entry * e = - list_entry(p, struct proc_entry, next); - if (kill(e->pid, 0) >= 0) - continue; - log_dbg("Dead process removed: %d.", e->pid); - list_del(&e->next); - proc_entry_destroy(e); - } - - list_for_each_safe(p, h, &irmd.ipcps) { - struct ipcp_entry * e = - list_entry(p, struct ipcp_entry, next); - if (kill(e->pid, 0) >= 0) - continue; - log_dbg("Dead IPCP removed: %d.", e->pid); - list_del(&e->next); - ipcp_entry_destroy(e); - } + if (ipcp_flow_dealloc(flow->n_1_pid, flow->id, ts->tv_sec) < 0) { + log_err("Failed to request dealloc from %d.", flow->n_1_pid); + return -EIPCP; + } - list_for_each_safe(p, h, &irmd.registry) { - struct list_head * p2; - struct list_head * h2; - struct reg_entry * e = - list_entry(p, struct reg_entry, next); - list_for_each_safe(p2, h2, &e->reg_pids) { - struct pid_el * a = - list_entry(p2, struct pid_el, next); - if (kill(a->pid, 0) >= 0) - continue; - log_dbg("Dead process removed from: %d %s.", - a->pid, e->name); - reg_entry_del_pid_el(e, a); - } - } + return 0; +} - pthread_rwlock_unlock(&irmd.reg_lock); - pthread_rwlock_wrlock(&irmd.flows_lock); - - list_for_each_safe(p, h, &irmd.irm_flows) { - int ipcpi; - int flow_id; - struct irm_flow * f = - list_entry(p, struct irm_flow, next); - - if (irm_flow_get_state(f) == FLOW_ALLOC_PENDING - && ts_diff_ms(&f->t0, &now) > IRMD_FLOW_TIMEOUT) { - log_dbg("Pending flow_id %d timed out.", - f->flow_id); - f->n_pid = -1; - irm_flow_set_state(f, FLOW_DEALLOC_PENDING); - continue; - } +static int flow_dealloc_resp(struct flow_info * flow) +{ + reg_dealloc_flow_resp(flow); - if (kill(f->n_pid, 0) < 0) { - log_dbg("Process %d gone, deallocating " - "flow %d.", - f->n_pid, f->flow_id); - f->n_pid = -1; - irm_flow_set_state(f, FLOW_DEALLOC_PENDING); - ipcpi = f->n_1_pid; - flow_id = f->flow_id; - pthread_rwlock_unlock(&irmd.flows_lock); - ipcp_flow_dealloc(ipcpi, flow_id, DEALLOC_TIME); - pthread_rwlock_wrlock(&irmd.flows_lock); - continue; - } + assert(flow->state == FLOW_DEALLOCATED); - if (kill(f->n_1_pid, 0) < 0) { - struct shm_flow_set * set; - log_err("IPCP %d gone, flow %d removed.", - f->n_1_pid, f->flow_id); - set = shm_flow_set_open(f->n_pid); - if (set != NULL) - shm_flow_set_destroy(set); - f->n_1_pid = -1; - irm_flow_set_state(f, FLOW_DEALLOC_PENDING); - } - } + reg_destroy_flow(flow->id); - pthread_rwlock_unlock(&irmd.flows_lock); + log_info("Completed deallocation of flow_id %d by process %d.", + flow->id, flow->n_1_pid); - nanosleep(&timeout, NULL); - } + return 0; } static void * acceptloop(void * o) { int csockfd; - struct timeval tv = {(SOCKET_TIMEOUT / 1000), - (SOCKET_TIMEOUT % 1000) * 1000}; (void) o; - while (irmd_get_state() == IRMD_RUNNING) { + while (true) { struct cmd * cmd; csockfd = accept(irmd.sockfd, 0, 0); if (csockfd < 0) continue; - if (setsockopt(csockfd, SOL_SOCKET, SO_RCVTIMEO, - (void *) &tv, sizeof(tv))) - log_warn("Failed to set timeout on socket."); - cmd = malloc(sizeof(*cmd)); if (cmd == NULL) { log_err("Out of memory."); @@ -2029,6 +1313,198 @@ static void free_msg(void * o) irm_msg__free_unpacked((irm_msg_t *) o, NULL); } +static irm_msg_t * do_command_msg(irm_msg_t * msg) +{ + struct ipcp_config conf; + struct ipcp_info ipcp; + struct flow_info flow; + struct proc_info proc; + struct name_info name; + struct timespec * abstime; + struct timespec max = TIMESPEC_INIT_MS(FLOW_ALLOC_TIMEOUT); + struct timespec now; + struct timespec ts = TIMESPEC_INIT_S(0); /* static analysis */ + int res; + irm_msg_t * ret_msg; + buffer_t data; + + memset(&flow, 0, sizeof(flow)); + + clock_gettime(PTHREAD_COND_CLOCK, &now); + + if (msg->timeo != NULL) { + ts = timespec_msg_to_s(msg->timeo); + ts_add(&ts, &now, &ts); + abstime = &ts; + } else { + ts_add(&max, &now, &max); + abstime = NULL; + } + + ret_msg = malloc(sizeof(*ret_msg)); + if (ret_msg == NULL) { + log_err("Failed to malloc return msg."); + return NULL; + } + + irm_msg__init(ret_msg); + + ret_msg->code = IRM_MSG_CODE__IRM_REPLY; + + pthread_cleanup_push(free_msg, ret_msg); + + switch (msg->code) { + case IRM_MSG_CODE__IRM_CREATE_IPCP: + ipcp = ipcp_info_msg_to_s(msg->ipcp_info); + res = create_ipcp(&ipcp); + break; + case IRM_MSG_CODE__IPCP_CREATE_R: + ipcp = ipcp_info_msg_to_s(msg->ipcp_info); + res = create_ipcp_r(&ipcp); + break; + case IRM_MSG_CODE__IRM_DESTROY_IPCP: + res = destroy_ipcp(msg->pid); + break; + case IRM_MSG_CODE__IRM_BOOTSTRAP_IPCP: + conf = ipcp_config_msg_to_s(msg->conf); + res = bootstrap_ipcp(msg->pid, &conf); + break; + case IRM_MSG_CODE__IRM_ENROLL_IPCP: + res = enroll_ipcp(msg->pid, msg->dst); + break; + case IRM_MSG_CODE__IRM_CONNECT_IPCP: + flow.qs = qos_spec_msg_to_s(msg->qosspec); + res = connect_ipcp(msg->pid, msg->dst, msg->comp, flow.qs); + break; + case IRM_MSG_CODE__IRM_DISCONNECT_IPCP: + res = disconnect_ipcp(msg->pid, msg->dst, msg->comp); + break; + case IRM_MSG_CODE__IRM_BIND_PROGRAM: + /* Make exec NULL terminated instead of empty string terminated */ + free(msg->exec[msg->n_exec - 1]); + msg->exec[msg->n_exec - 1] = NULL; + res = bind_program(msg->exec, msg->name, msg->opts); + break; + case IRM_MSG_CODE__IRM_UNBIND_PROGRAM: + res = unbind_program(msg->prog, msg->name); + break; + case IRM_MSG_CODE__IRM_PROC_ANNOUNCE: + proc.pid = msg->pid; + strcpy(proc.prog, msg->prog); + res = proc_announce(&proc); + break; + case IRM_MSG_CODE__IRM_PROC_EXIT: + res = proc_exit(msg->pid); + break; + case IRM_MSG_CODE__IRM_BIND_PROCESS: + res = bind_process(msg->pid, msg->name); + break; + case IRM_MSG_CODE__IRM_UNBIND_PROCESS: + res = unbind_process(msg->pid, msg->name); + break; + case IRM_MSG_CODE__IRM_LIST_IPCPS: + res = list_ipcps(&ret_msg->ipcps, &ret_msg->n_ipcps); + break; + case IRM_MSG_CODE__IRM_CREATE_NAME: + strcpy(name.name, msg->names[0]->name); + name.pol_lb = msg->names[0]->pol_lb; + res = name_create(&name); + break; + case IRM_MSG_CODE__IRM_DESTROY_NAME: + res = name_destroy(msg->name); + break; + case IRM_MSG_CODE__IRM_LIST_NAMES: + res = list_names(&ret_msg->names, &ret_msg->n_names); + break; + case IRM_MSG_CODE__IRM_REG_NAME: + res = name_reg(msg->name, msg->pid); + break; + case IRM_MSG_CODE__IRM_UNREG_NAME: + res = name_unreg(msg->name, msg->pid); + break; + case IRM_MSG_CODE__IRM_FLOW_ACCEPT: + data.len = msg->pk.len; + data.data = msg->pk.data; + msg->has_pk = false; + assert(data.len > 0 ? data.data != NULL : data.data == NULL); + flow = flow_info_msg_to_s(msg->flow_info); + res = flow_accept(&flow, &data, abstime); + if (res == 0) { + ret_msg->flow_info = flow_info_s_to_msg(&flow); + ret_msg->has_symmkey = data.len != 0; + ret_msg->symmkey.data = data.data; + ret_msg->symmkey.len = data.len; + } + break; + case IRM_MSG_CODE__IRM_FLOW_ALLOC: + data.len = msg->pk.len; + data.data = msg->pk.data; + msg->has_pk = false; + assert(data.len > 0 ? data.data != NULL : data.data == NULL); + flow = flow_info_msg_to_s(msg->flow_info); + abstime = abstime == NULL ? &max : abstime; + res = flow_alloc(&flow, msg->dst, &data, abstime); + if (res == 0) { + ret_msg->flow_info = flow_info_s_to_msg(&flow); + ret_msg->has_symmkey = data.len != 0; + ret_msg->symmkey.data = data.data; + ret_msg->symmkey.len = data.len; + } + break; + case IRM_MSG_CODE__IRM_FLOW_JOIN: + assert(msg->pk.len == 0 && msg->pk.data == NULL); + flow = flow_info_msg_to_s(msg->flow_info); + abstime = abstime == NULL ? &max : abstime; + res = flow_join(&flow, msg->dst, abstime); + if (res == 0) + ret_msg->flow_info = flow_info_s_to_msg(&flow); + break; + case IRM_MSG_CODE__IRM_FLOW_DEALLOC: + flow = flow_info_msg_to_s(msg->flow_info); + ts = timespec_msg_to_s(msg->timeo); + res = flow_dealloc(&flow, &ts); + break; + case IRM_MSG_CODE__IPCP_FLOW_DEALLOC: + flow = flow_info_msg_to_s(msg->flow_info); + res = flow_dealloc_resp(&flow); + break; + case IRM_MSG_CODE__IPCP_FLOW_REQ_ARR: + data.len = msg->pk.len; + data.data = msg->pk.data; + msg->pk.data = NULL; /* pass data */ + msg->pk.len = 0; + assert(data.len > 0 ? data.data != NULL : data.data == NULL); + flow = flow_info_msg_to_s(msg->flow_info); + res = flow_req_arr(&flow, msg->hash.data, &data); + if (res == 0) + ret_msg->flow_info = flow_info_s_to_msg(&flow); + break; + case IRM_MSG_CODE__IPCP_FLOW_ALLOC_REPLY: + data.len = msg->pk.len; + data.data = msg->pk.data; + msg->pk.data = NULL; /* pass data */ + msg->pk.len = 0; + assert(data.len > 0 ? data.data != NULL : data.data == NULL); + flow = flow_info_msg_to_s(msg->flow_info); + res = flow_alloc_reply(&flow, msg->response, &data); + break; + default: + log_err("Don't know that message code."); + res = -1; + break; + } + + pthread_cleanup_pop(false); + + ret_msg->has_result = true; + if (abstime == &max && res == -ETIMEDOUT) + ret_msg->result = -EPERM; /* No timeout requested */ + else + ret_msg->result = res; + + return ret_msg; +} + static void * mainloop(void * o) { int sfd; @@ -2038,26 +1514,11 @@ static void * mainloop(void * o) (void) o; while (true) { - irm_msg_t * ret_msg; - struct irm_flow e; - struct timespec * timeo = NULL; - struct timespec ts = {0, 0}; - struct cmd * cmd; - int result; - - memset(&e, 0, sizeof(e)); - - ret_msg = malloc(sizeof(*ret_msg)); - if (ret_msg == NULL) - return (void *) -1; - - irm_msg__init(ret_msg); - - ret_msg->code = IRM_MSG_CODE__IRM_REPLY; + irm_msg_t * ret_msg; + struct cmd * cmd; pthread_mutex_lock(&irmd.cmd_lock); - pthread_cleanup_push(free_msg, ret_msg); pthread_cleanup_push(__cleanup_mutex_unlock, &irmd.cmd_lock); while (list_is_empty(&irmd.cmds)) @@ -2067,7 +1528,6 @@ static void * mainloop(void * o) list_del(&cmd->next); pthread_cleanup_pop(true); - pthread_cleanup_pop(false); msg = irm_msg__unpack(NULL, cmd->len, cmd->cbuf); sfd = cmd->fd; @@ -2075,177 +1535,35 @@ static void * mainloop(void * o) free(cmd); if (msg == NULL) { + log_err("Failed to unpack command message."); close(sfd); - irm_msg__free_unpacked(msg, NULL); continue; } tpm_dec(irmd.tpm); - if (msg->has_timeo_sec) { - assert(msg->has_timeo_nsec); - - ts.tv_sec = msg->timeo_sec; - ts.tv_nsec = msg->timeo_nsec; - timeo = &ts; - } - pthread_cleanup_push(__cleanup_close_ptr, &sfd); pthread_cleanup_push(free_msg, msg); - pthread_cleanup_push(free_msg, ret_msg); - switch (msg->code) { - case IRM_MSG_CODE__IRM_CREATE_IPCP: - result = create_ipcp(msg->name, msg->ipcp_type); - break; - case IRM_MSG_CODE__IPCP_CREATE_R: - result = create_ipcp_r(msg->pid, msg->result); - break; - case IRM_MSG_CODE__IRM_DESTROY_IPCP: - result = destroy_ipcp(msg->pid); - break; - case IRM_MSG_CODE__IRM_BOOTSTRAP_IPCP: - result = bootstrap_ipcp(msg->pid, msg->conf); - break; - case IRM_MSG_CODE__IRM_ENROLL_IPCP: - result = enroll_ipcp(msg->pid, msg->dst); - break; - case IRM_MSG_CODE__IRM_CONNECT_IPCP: - result = connect_ipcp(msg->pid, msg->dst, msg->comp, - msg_to_spec(msg->qosspec)); - break; - case IRM_MSG_CODE__IRM_DISCONNECT_IPCP: - result = disconnect_ipcp(msg->pid, msg->dst, msg->comp); - break; - case IRM_MSG_CODE__IRM_BIND_PROGRAM: - result = bind_program(msg->prog, - msg->name, - msg->opts, - msg->n_args, - msg->args); - break; - case IRM_MSG_CODE__IRM_UNBIND_PROGRAM: - result = unbind_program(msg->prog, msg->name); - break; - case IRM_MSG_CODE__IRM_PROC_ANNOUNCE: - result = proc_announce(msg->pid, msg->prog); - break; - case IRM_MSG_CODE__IRM_BIND_PROCESS: - result = bind_process(msg->pid, msg->name); - break; - case IRM_MSG_CODE__IRM_UNBIND_PROCESS: - result = unbind_process(msg->pid, msg->name); - break; - case IRM_MSG_CODE__IRM_LIST_IPCPS: - result = list_ipcps(&ret_msg->ipcps, &ret_msg->n_ipcps); - break; - case IRM_MSG_CODE__IRM_CREATE_NAME: - result = name_create(msg->names[0]->name, - msg->names[0]->pol_lb); - break; - case IRM_MSG_CODE__IRM_DESTROY_NAME: - result = name_destroy(msg->name); - break; - case IRM_MSG_CODE__IRM_LIST_NAMES: - result = list_names(&ret_msg->names, &ret_msg->n_names); - break; - case IRM_MSG_CODE__IRM_REG_NAME: - result = name_reg(msg->name, msg->pid); - break; - case IRM_MSG_CODE__IRM_UNREG_NAME: - result = name_unreg(msg->name, msg->pid); - break; - case IRM_MSG_CODE__IRM_FLOW_ACCEPT: - assert(msg->pk.len > 0 ? msg->pk.data != NULL - : msg->pk.data == NULL); - result = flow_accept(msg->pid, timeo, &e, - msg->pk.data, msg->pk.len); - if (result == 0) { - qosspec_msg_t qs_msg; - ret_msg->has_flow_id = true; - ret_msg->flow_id = e.flow_id; - ret_msg->has_pid = true; - ret_msg->pid = e.n_1_pid; - qs_msg = spec_to_msg(&e.qs); - ret_msg->qosspec = &qs_msg; - ret_msg->has_pk = true; - ret_msg->pk.data = e.data; - ret_msg->pk.len = e.len; - } - break; - case IRM_MSG_CODE__IRM_FLOW_ALLOC: - assert(msg->pk.len > 0 ? msg->pk.data != NULL - : msg->pk.data == NULL); - result = flow_alloc(msg->pid, msg->dst, - msg_to_spec(msg->qosspec), - timeo, &e, false, msg->pk.data, - msg->pk.len); - if (result == 0) { - ret_msg->has_flow_id = true; - ret_msg->flow_id = e.flow_id; - ret_msg->has_pid = true; - ret_msg->pid = e.n_1_pid; - ret_msg->has_pk = true; - ret_msg->pk.data = e.data; - ret_msg->pk.len = e.len; - } - break; - case IRM_MSG_CODE__IRM_FLOW_JOIN: - assert(msg->pk.len == 0 && msg->pk.data == NULL); - result = flow_alloc(msg->pid, msg->dst, - msg_to_spec(msg->qosspec), - timeo, &e, true, NULL, 0); - if (result == 0) { - ret_msg->has_flow_id = true; - ret_msg->flow_id = e.flow_id; - ret_msg->has_pid = true; - ret_msg->pid = e.n_1_pid; - } - break; - case IRM_MSG_CODE__IRM_FLOW_DEALLOC: - result = flow_dealloc(msg->pid, - msg->flow_id, - msg->timeo_sec); - break; - case IRM_MSG_CODE__IPCP_FLOW_REQ_ARR: - assert(msg->pk.len > 0 ? msg->pk.data != NULL - : msg->pk.data == NULL); - result = flow_req_arr(msg->pid, - &e, - msg->hash.data, - msg_to_spec(msg->qosspec), - msg->pk.data, - msg->pk.len); - if (result == 0) { - ret_msg->has_flow_id = true; - ret_msg->flow_id = e.flow_id; - ret_msg->has_pid = true; - ret_msg->pid = e.n_pid; - } - break; - case IRM_MSG_CODE__IPCP_FLOW_ALLOC_REPLY: - assert(msg->pk.len > 0 ? msg->pk.data != NULL - : msg->pk.data == NULL); - result = flow_alloc_reply(msg->flow_id, - msg->response, - msg->pk.data, - msg->pk.len); - break; - default: - log_err("Don't know that message code."); - result = -1; - break; - } + ret_msg = do_command_msg(msg); - pthread_cleanup_pop(false); pthread_cleanup_pop(true); pthread_cleanup_pop(false); - if (result == -EPIPE) + if (ret_msg == NULL) { + log_err("Failed to create return message."); + goto fail_msg; + } + + if (ret_msg->result == -EPIPE) { + log_dbg("Terminated command: application closed socket."); goto fail; + } - ret_msg->has_result = true; - ret_msg->result = result; + if (ret_msg->result == -EIRMD) { + log_dbg("Terminated command: IRMd not in running state."); + goto fail; + } buffer.len = irm_msg__get_packed_size(ret_msg); if (buffer.len == 0) { @@ -2261,18 +1579,21 @@ static void * mainloop(void * o) irm_msg__pack(ret_msg, buffer.data); - /* Can't free the qosspec. */ - ret_msg->qosspec = NULL; irm_msg__free_unpacked(ret_msg, NULL); pthread_cleanup_push(__cleanup_close_ptr, &sfd); + pthread_cleanup_push(free, buffer.data); + + if (write(sfd, buffer.data, buffer.len) == -1) { + if (errno != EPIPE) + log_warn("Failed to send reply message: %s.", + strerror(errno)); + else + log_dbg("Failed to send reply message: %s.", + strerror(errno)); + } - if (write(sfd, buffer.data, buffer.len) == -1) - if (result != -EIRMD) - log_warn("Failed to send reply message."); - - free(buffer.data); - + pthread_cleanup_pop(true); pthread_cleanup_pop(true); tpm_inc(irmd.tpm); @@ -2280,6 +1601,7 @@ static void * mainloop(void * o) continue; fail: irm_msg__free_unpacked(ret_msg, NULL); + fail_msg: close(sfd); tpm_inc(irmd.tpm); continue; @@ -2288,6 +1610,116 @@ static void * mainloop(void * o) return (void *) 0; } +static void irm_fini(void) +{ +#ifdef HAVE_FUSE + struct timespec wait = TIMESPEC_INIT_MS(1); + int retries = 5; +#endif + if (irmd_get_state() != IRMD_NULL) + log_warn("Unsafe destroy."); + + tpm_destroy(irmd.tpm); + + close(irmd.sockfd); + + if (unlink(IRM_SOCK_PATH)) + log_dbg("Failed to unlink %s.", IRM_SOCK_PATH); + + if (irmd.rdrb != NULL) + shm_rdrbuff_destroy(irmd.rdrb); + + if (irmd.lf != NULL) + lockfile_destroy(irmd.lf); + + pthread_mutex_destroy(&irmd.cmd_lock); + pthread_cond_destroy(&irmd.cmd_cond); + pthread_rwlock_destroy(&irmd.state_lock); + +#ifdef HAVE_FUSE + while (rmdir(FUSE_PREFIX) < 0 && retries-- > 0) + nanosleep(&wait, NULL); + if (retries < 0) + log_err("Failed to remove " FUSE_PREFIX); +#endif +} + +#ifdef HAVE_FUSE +static void destroy_mount(char * mnt) +{ + struct stat st; + + if (stat(mnt, &st) == -1){ + switch(errno) { + case ENOENT: + log_dbg("Fuse mountpoint %s not found: %s", + mnt, strerror(errno)); + break; + case ENOTCONN: + /* FALLTHRU */ + case ECONNABORTED: + log_dbg("Cleaning up fuse mountpoint %s.", + mnt); + rib_cleanup(mnt); + break; + default: + log_err("Unhandled fuse error on mnt %s: %s.", + mnt, strerror(errno)); + } + } +} +#endif + +static int ouroboros_reset(void) +{ + shm_rdrbuff_purge(); + lockfile_destroy(irmd.lf); + + return 0; +} + +static void cleanup_pid(pid_t pid) +{ +#ifdef HAVE_FUSE + char mnt[RIB_PATH_LEN + 1]; + + if (reg_has_ipcp(pid)) { + struct ipcp_info info; + info.pid = pid; + reg_get_ipcp(&info, NULL); + sprintf(mnt, FUSE_PREFIX "/%s", info.name); + } else { + sprintf(mnt, FUSE_PREFIX "/proc.%d", pid); + } + + destroy_mount(mnt); + +#else + (void) pid; +#endif +} + +void * irm_sanitize(void * o) +{ + pid_t pid; + struct timespec ts = TIMESPEC_INIT_MS(FLOW_ALLOC_TIMEOUT / 20); + + (void) o; + + while (true) { + while((pid = reg_get_dead_proc()) != -1) { + log_info("Process %d died.", pid); + cleanup_pid(pid); + reg_destroy_proc(pid); + } + + nanosleep(&ts, NULL); + } + + return (void *) 0; +} + + static int irm_init(void) { struct stat st; @@ -2297,19 +1729,42 @@ static int irm_init(void) #endif memset(&st, 0, sizeof(st)); - if (pthread_rwlock_init(&irmd.state_lock, NULL)) { - log_err("Failed to initialize rwlock."); - goto fail_state_lock; + log_init(!irmd.log_stdout); + + irmd.lf = lockfile_create(); + if (irmd.lf == NULL) { + irmd.lf = lockfile_open(); + if (irmd.lf == NULL) { + log_err("Lockfile error."); + goto fail_lockfile; + } + + if (kill(lockfile_owner(irmd.lf), 0) < 0) { + log_warn("IRMd didn't properly shut down last time."); + if (ouroboros_reset() < 0) { + log_err("Failed to clean stale resources."); + lockfile_close(irmd.lf); + goto fail_lockfile; + } + + log_warn("Stale resources cleaned."); + irmd.lf = lockfile_create(); + } else { + log_warn("IRMd already running (%d), exiting.", + lockfile_owner(irmd.lf)); + lockfile_close(irmd.lf); + goto fail_lockfile; + } } - if (pthread_rwlock_init(&irmd.reg_lock, NULL)) { - log_err("Failed to initialize rwlock."); - goto fail_reg_lock; + if (irmd.lf == NULL) { + log_err("Failed to create lockfile."); + goto fail_lockfile; } - if (pthread_rwlock_init(&irmd.flows_lock, NULL)) { + if (pthread_rwlock_init(&irmd.state_lock, NULL)) { log_err("Failed to initialize rwlock."); - goto fail_flows_lock; + goto fail_state_lock; } if (pthread_mutex_init(&irmd.cmd_lock, NULL)) { @@ -2333,45 +1788,8 @@ static int irm_init(void) pthread_condattr_destroy(&cattr); - list_head_init(&irmd.ipcps); - list_head_init(&irmd.proc_table); - list_head_init(&irmd.prog_table); - list_head_init(&irmd.spawned_pids); - list_head_init(&irmd.registry); - list_head_init(&irmd.irm_flows); list_head_init(&irmd.cmds); - irmd.flow_ids = bmp_create(SYS_MAX_FLOWS, 0); - if (irmd.flow_ids == NULL) { - log_err("Failed to create flow_ids bitmap."); - goto fail_flow_ids; - } - - if ((irmd.lf = lockfile_create()) == NULL) { - if ((irmd.lf = lockfile_open()) == NULL) { - log_err("Lockfile error."); - goto fail_lockfile; - } - - if (kill(lockfile_owner(irmd.lf), 0) < 0) { - log_info("IRMd didn't properly shut down last time."); - shm_rdrbuff_purge(); - log_info("Stale resources cleaned."); - lockfile_destroy(irmd.lf); - irmd.lf = lockfile_create(); - } else { - log_info("IRMd already running (%d), exiting.", - lockfile_owner(irmd.lf)); - lockfile_close(irmd.lf); - goto fail_lockfile; - } - } - - if (irmd.lf == NULL) { - log_err("Failed to create lockfile."); - goto fail_lockfile; - } - if (stat(SOCK_PATH, &st) == -1) { if (mkdir(SOCK_PATH, 0777)) { log_err("Failed to create sockets directory."); @@ -2394,6 +1812,13 @@ static int irm_init(void) log_err("Failed to create rdrbuff."); goto fail_rdrbuff; } + + irmd.tpm = tpm_create(IRMD_MIN_THREADS, IRMD_ADD_THREADS, + mainloop, NULL); + if (irmd.tpm == NULL) { + log_err("Failed to greate thread pool."); + goto fail_tpm_create; + } #ifdef HAVE_FUSE mask = umask(0); @@ -2418,68 +1843,124 @@ static int irm_init(void) gcry_control(GCRYCTL_INITIALIZATION_FINISHED); #endif - irmd_set_state(IRMD_RUNNING); - - log_info("Ouroboros IPC Resource Manager daemon started..."); return 0; #ifdef HAVE_LIBGCRYPT fail_gcry_version: -#ifdef HAVE_FUSE + #ifdef HAVE_FUSE rmdir(FUSE_PREFIX); + #endif + tpm_destroy(irmd.tpm); #endif + fail_tpm_create: shm_rdrbuff_destroy(irmd.rdrb); -#endif fail_rdrbuff: close(irmd.sockfd); fail_sock_path: unlink(IRM_SOCK_PATH); fail_stat: - lockfile_destroy(irmd.lf); - fail_lockfile: - bmp_destroy(irmd.flow_ids); - fail_flow_ids: pthread_cond_destroy(&irmd.cmd_cond); fail_cmd_cond: pthread_mutex_destroy(&irmd.cmd_lock); fail_cmd_lock: - pthread_rwlock_destroy(&irmd.flows_lock); - fail_flows_lock: - pthread_rwlock_destroy(&irmd.reg_lock); - fail_reg_lock: pthread_rwlock_destroy(&irmd.state_lock); fail_state_lock: + lockfile_destroy(irmd.lf); + fail_lockfile: + log_fini(); return -1; } static void usage(void) { printf("Usage: irmd \n" +#ifdef HAVE_TOML + " [--config <path> (Path to configuration file)]\n" +#endif " [--stdout (Log to stdout instead of system log)]\n" " [--version (Print version number and exit)]\n" "\n"); } -int main(int argc, - char ** argv) +static int irm_start(void) { - sigset_t sigset; - bool use_stdout = false; - int sig; + if (tpm_start(irmd.tpm)) + goto fail_tpm_start; - sigemptyset(&sigset); - sigaddset(&sigset, SIGINT); - sigaddset(&sigset, SIGQUIT); - sigaddset(&sigset, SIGHUP); - sigaddset(&sigset, SIGTERM); - sigaddset(&sigset, SIGPIPE); + irmd_set_state(IRMD_RUNNING); + + if (pthread_create(&irmd.irm_sanitize, NULL, irm_sanitize, NULL)) + goto fail_irm_sanitize; + + if (pthread_create(&irmd.acceptor, NULL, acceptloop, NULL)) + goto fail_acceptor; + + log_info("Ouroboros IPC Resource Manager daemon started..."); + + return 0; + + fail_acceptor: + pthread_cancel(irmd.irm_sanitize); + pthread_join(irmd.irm_sanitize, NULL); + fail_irm_sanitize: + irmd_set_state(IRMD_NULL); + tpm_stop(irmd.tpm); + fail_tpm_start: + return -1; +} + +static void irm_sigwait(sigset_t sigset) +{ + int sig; + + while (irmd_get_state() != IRMD_SHUTDOWN) { + if (sigwait(&sigset, &sig) != 0) { + log_warn("Bad signal."); + continue; + } + + switch(sig) { + case SIGINT: + case SIGQUIT: + case SIGTERM: + case SIGHUP: + log_info("IRMd shutting down..."); + irmd_set_state(IRMD_SHUTDOWN); + break; + case SIGPIPE: + log_dbg("Ignored SIGPIPE."); + break; + default: + break; + } + } +} + +static void irm_stop(void) +{ + pthread_cancel(irmd.acceptor); + pthread_cancel(irmd.irm_sanitize); + + pthread_join(irmd.acceptor, NULL); + pthread_join(irmd.irm_sanitize, NULL); + + tpm_stop(irmd.tpm); + + irmd_set_state(IRMD_NULL); +} +static void irm_argparse(int argc, + char ** argv) +{ +#ifdef HAVE_TOML + irmd.cfg_file = NULL; +#endif argc--; argv++; while (argc > 0) { if (strcmp(*argv, "--stdout") == 0) { - use_stdout = true; + irmd.log_stdout = true; argc--; argv++; } else if (strcmp(*argv, "--version") == 0) { @@ -2488,96 +1969,141 @@ int main(int argc, OUROBOROS_VERSION_MINOR, OUROBOROS_VERSION_PATCH); exit(EXIT_SUCCESS); +#ifdef HAVE_TOML + } else if (strcmp (*argv, "--config") == 0) { + irmd.cfg_file = *(argv + 1); + argc -= 2; + argv += 2; +#endif } else { usage(); exit(EXIT_FAILURE); } } +} + +static void * kill_dash_nine(void * o) +{ + time_t slept = 0; +#ifdef IRMD_KILL_ALL_PROCESSES + struct timespec ts = TIMESPEC_INIT_MS(FLOW_ALLOC_TIMEOUT / 19); +#endif + (void) o; + + while (slept < IRMD_PKILL_TIMEOUT) { + time_t intv = 1; + if (reg_first_spawned() == -1) + goto finish; + sleep(intv); + slept += intv; + } + + log_dbg("I am become Death, destroyer of hung processes."); + +#ifdef IRMD_KILL_ALL_PROCESSES + reg_kill_all_proc(SIGKILL); + nanosleep(&ts, NULL); +#else + reg_kill_all_spawned(SIGKILL); +#endif + finish: + return (void *) 0; +} + +static void kill_all_spawned(void) +{ + pid_t pid; + pthread_t grimreaper; + +#ifdef IRMD_KILL_ALL_PROCESSES + reg_kill_all_proc(SIGTERM); +#else + reg_kill_all_spawned(SIGTERM); +#endif + pthread_create(&grimreaper, NULL, kill_dash_nine, NULL); + + pid = reg_first_spawned(); + while (pid != -1) { + int s; + if (kill(pid, 0) == 0) + waitpid(pid, &s, 0); + else { + log_warn("Child process %d died.", pid); + cleanup_pid(pid); + reg_destroy_proc(pid); + } + pid = reg_first_spawned(); + } + + pthread_join(grimreaper, NULL); +} + +int main(int argc, + char ** argv) +{ + sigset_t sigset; + int ret = EXIT_SUCCESS; + + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGQUIT); + sigaddset(&sigset, SIGHUP); + sigaddset(&sigset, SIGTERM); + sigaddset(&sigset, SIGPIPE); + + irm_argparse(argc, argv); + + if (irmd.log_stdout) + printf(O7S_ASCII_ART); if (geteuid() != 0) { printf("IPC Resource Manager must be run as root.\n"); exit(EXIT_FAILURE); } - log_init(!use_stdout); - if (irm_init() < 0) goto fail_irm_init; - irmd.tpm = tpm_create(IRMD_MIN_THREADS, IRMD_ADD_THREADS, - mainloop, NULL); - if (irmd.tpm == NULL) { - irmd_set_state(IRMD_NULL); - goto fail_tpm_create; + if (reg_init() < 0) { + log_err("Failed to initialize registry."); + goto fail_reg; } pthread_sigmask(SIG_BLOCK, &sigset, NULL); - if (tpm_start(irmd.tpm)) { - irmd_set_state(IRMD_NULL); - goto fail_tpm_start; - } + if (irm_start() < 0) + goto fail_irm_start; - if (pthread_create(&irmd.irm_sanitize, NULL, irm_sanitize, NULL)) { - irmd_set_state(IRMD_NULL); - goto fail_irm_sanitize; +#ifdef HAVE_TOML + if (irm_configure(irmd.cfg_file) < 0) { + irmd_set_state(IRMD_SHUTDOWN); + ret = EXIT_FAILURE; } +#endif + irm_sigwait(sigset); - if (pthread_create(&irmd.acceptor, NULL, acceptloop, NULL)) { - irmd_set_state(IRMD_NULL); - goto fail_acceptor; - } + kill_all_spawned(); - while (irmd_get_state() != IRMD_NULL) { - if (sigwait(&sigset, &sig) != 0) { - log_warn("Bad signal."); - continue; - } + irm_stop(); - switch(sig) { - case SIGINT: - case SIGQUIT: - case SIGTERM: - case SIGHUP: - log_info("IRMd shutting down..."); - irmd_set_state(IRMD_NULL); - break; - case SIGPIPE: - log_dbg("Ignored SIGPIPE."); - break; - default: - break; - } - } - - pthread_cancel(irmd.acceptor); - - pthread_join(irmd.acceptor, NULL); - pthread_join(irmd.irm_sanitize, NULL); + pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); - tpm_stop(irmd.tpm); + reg_clear(); - tpm_destroy(irmd.tpm); + reg_fini(); irm_fini(); - pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); - - log_info("Bye."); + log_info("Ouroboros IPC Resource Manager daemon exited. Bye."); log_fini(); - exit(EXIT_SUCCESS); + exit(ret); - fail_acceptor: - pthread_join(irmd.irm_sanitize, NULL); - fail_irm_sanitize: - tpm_stop(irmd.tpm); - fail_tpm_start: - tpm_destroy(irmd.tpm); - fail_tpm_create: + fail_irm_start: + reg_fini(); + fail_reg: irm_fini(); fail_irm_init: - log_fini(); exit(EXIT_FAILURE); } diff --git a/src/irmd/proc_table.c b/src/irmd/proc_table.c deleted file mode 100644 index a80e8d27..00000000 --- a/src/irmd/proc_table.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Ouroboros - Copyright (C) 2016 - 2021 - * - * The IPC Resource Manager - Process Table - * - * Dimitri Staessens <dimitri@ouroboros.rocks> - * Sander Vrijders <sander@ouroboros.rocks> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., http://www.fsf.org/about/contact/. - */ - -#if defined(__linux__) || defined(__CYGWIN__) -#define _DEFAULT_SOURCE -#else -#define _POSIX_C_SOURCE 200112L -#endif - -#include "config.h" - -#include <ouroboros/list.h> -#include <ouroboros/errno.h> -#include <ouroboros/time_utils.h> - -#include "proc_table.h" -#include "registry.h" - -#include <stdlib.h> -#include <unistd.h> -#include <limits.h> -#include <assert.h> - -struct proc_entry * proc_entry_create(pid_t pid, - char * prog) -{ - struct proc_entry * e; - pthread_condattr_t cattr; - - assert(prog); - - e = malloc(sizeof(*e)); - if (e == NULL) - goto fail_malloc; - - if (pthread_condattr_init(&cattr)) - goto fail_condattr; - -#ifndef __APPLE__ - pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK); -#endif - - if (pthread_mutex_init(&e->lock, NULL)) - goto fail_mutex; - - if (pthread_cond_init(&e->cond, &cattr)) - goto fail_cond; - - e->set = shm_flow_set_create(pid); - if (e->set == NULL) - goto fail_set; - - list_head_init(&e->next); - list_head_init(&e->names); - - e->pid = pid; - e->prog = prog; - e->re = NULL; - e->state = PROC_INIT; - - return e; - fail_set: - pthread_cond_destroy(&e->cond);; - fail_cond: - pthread_mutex_destroy(&e->lock); - fail_mutex: - pthread_condattr_destroy(&cattr); - fail_condattr: - free(e); - fail_malloc: - return NULL; -} - -static void cancel_proc_entry(void * o) -{ - struct proc_entry * e = (struct proc_entry *) o; - - e->state = PROC_NULL; - - pthread_mutex_unlock(&e->lock); -} - -void proc_entry_destroy(struct proc_entry * e) -{ - struct list_head * p; - struct list_head * h; - - assert(e); - - pthread_mutex_lock(&e->lock); - - if (e->state == PROC_DESTROY) { - pthread_mutex_unlock(&e->lock); - return; - } - - if (e->state == PROC_SLEEP) - e->state = PROC_DESTROY; - - pthread_cond_signal(&e->cond); - - pthread_cleanup_push(cancel_proc_entry, e); - - while (e->state != PROC_INIT) - pthread_cond_wait(&e->cond, &e->lock); - - pthread_cleanup_pop(false); - - pthread_mutex_unlock(&e->lock); - - shm_flow_set_destroy(e->set); - - pthread_cond_destroy(&e->cond); - pthread_mutex_destroy(&e->lock); - - if (e->prog != NULL) - free(e->prog); - - list_for_each_safe(p, h, &e->names) { - struct str_el * n = list_entry(p, struct str_el, next); - list_del(&n->next); - if (n->str != NULL) - free(n->str); - free(n); - } - - free(e); -} - -int proc_entry_add_name(struct proc_entry * e, - char * name) -{ - struct str_el * s; - - assert(e); - assert(name); - - s = malloc(sizeof(*s)); - if (s == NULL) - return -ENOMEM; - - s->str = name; - list_add(&s->next, &e->names); - - return 0; -} - -void proc_entry_del_name(struct proc_entry * e, - const char * name) -{ - struct list_head * p = NULL; - struct list_head * h = NULL; - - assert(e); - assert(name); - - list_for_each_safe(p, h, &e->names) { - struct str_el * s = list_entry(p, struct str_el, next); - if (!strcmp(name, s->str)) { - list_del(&s->next); - free(s->str); - free(s); - } - } -} - -int proc_entry_sleep(struct proc_entry * e, - struct timespec * timeo) -{ - struct timespec dl; - - int ret = 0; - - assert(e); - - if (timeo != NULL) { - clock_gettime(PTHREAD_COND_CLOCK, &dl); - ts_add(&dl, timeo, &dl); - } - - pthread_mutex_lock(&e->lock); - - if (e->state != PROC_WAKE && e->state != PROC_DESTROY) - e->state = PROC_SLEEP; - - pthread_cleanup_push(cancel_proc_entry, e); - - while (e->state == PROC_SLEEP && ret != -ETIMEDOUT) - if (timeo) - ret = -pthread_cond_timedwait(&e->cond, &e->lock, &dl); - else - ret = -pthread_cond_wait(&e->cond, &e->lock); - - pthread_cleanup_pop(false); - - if (e->state == PROC_DESTROY) { - if (e->re != NULL) - reg_entry_del_pid(e->re, e->pid); - ret = -1; - } - - e->state = PROC_INIT; - - pthread_cond_broadcast(&e->cond); - pthread_mutex_unlock(&e->lock); - - return ret; -} - -void proc_entry_wake(struct proc_entry * e, - struct reg_entry * re) -{ - assert(e); - assert(re); - - pthread_mutex_lock(&e->lock); - - if (e->state != PROC_SLEEP) { - pthread_mutex_unlock(&e->lock); - return; - } - - e->state = PROC_WAKE; - e->re = re; - - pthread_cond_broadcast(&e->cond); - - pthread_cleanup_push(cancel_proc_entry, e); - - while (e->state == PROC_WAKE) - pthread_cond_wait(&e->cond, &e->lock); - - pthread_cleanup_pop(false); - - if (e->state == PROC_DESTROY) - e->state = PROC_INIT; - - pthread_mutex_unlock(&e->lock); -} - -int proc_table_add(struct list_head * proc_table, - struct proc_entry * e) -{ - - assert(proc_table); - assert(e); - - list_add(&e->next, proc_table); - - return 0; -} - -void proc_table_del(struct list_head * proc_table, - pid_t pid) -{ - struct list_head * p; - struct list_head * h; - - assert(proc_table); - - list_for_each_safe(p, h, proc_table) { - struct proc_entry * e = list_entry(p, struct proc_entry, next); - if (pid == e->pid) { - list_del(&e->next); - proc_entry_destroy(e); - } - } -} - -struct proc_entry * proc_table_get(struct list_head * proc_table, - pid_t pid) -{ - struct list_head * h; - - assert(proc_table); - - list_for_each(h, proc_table) { - struct proc_entry * e = list_entry(h, struct proc_entry, next); - if (pid == e->pid) - return e; - } - - return NULL; -} diff --git a/src/irmd/proc_table.h b/src/irmd/proc_table.h deleted file mode 100644 index 9b81a111..00000000 --- a/src/irmd/proc_table.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Ouroboros - Copyright (C) 2016 - 2021 - * - * The IPC Resource Manager - Process Table - * - * Dimitri Staessens <dimitri@ouroboros.rocks> - * Sander Vrijders <sander@ouroboros.rocks> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., http://www.fsf.org/about/contact/. - */ - -#ifndef OUROBOROS_IRMD_PROC_TABLE_H -#define OUROBOROS_IRMD_PROC_TABLE_H - -#include <ouroboros/shm_flow_set.h> - -#include "utils.h" - -#include <unistd.h> -#include <pthread.h> - -enum proc_state { - PROC_NULL = 0, - PROC_INIT, - PROC_SLEEP, - PROC_WAKE, - PROC_DESTROY -}; - -struct proc_entry { - struct list_head next; - pid_t pid; - char * prog; /* program instantiated */ - struct list_head names; /* names for which process accepts flows */ - struct shm_flow_set * set; - - struct reg_entry * re; /* reg_entry for which a flow arrived */ - - /* The process will block on this */ - enum proc_state state; - pthread_cond_t cond; - pthread_mutex_t lock; -}; - -struct proc_entry * proc_entry_create(pid_t proc, - char * prog); - -void proc_entry_destroy(struct proc_entry * e); - -int proc_entry_sleep(struct proc_entry * e, - struct timespec * timeo); - -void proc_entry_wake(struct proc_entry * e, - struct reg_entry * re); - -void proc_entry_cancel(struct proc_entry * e); - -int proc_entry_add_name(struct proc_entry * e, - char * name); - -void proc_entry_del_name(struct proc_entry * e, - const char * name); - -int proc_table_add(struct list_head * proc_table, - struct proc_entry * e); - -void proc_table_del(struct list_head * proc_table, - pid_t pid); - -struct proc_entry * proc_table_get(struct list_head * proc_table, - pid_t pid); - -#endif /* OUROBOROS_IRMD_PROC_TABLE_H */ diff --git a/src/irmd/prog_table.c b/src/irmd/prog_table.c deleted file mode 100644 index eb2b1966..00000000 --- a/src/irmd/prog_table.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Ouroboros - Copyright (C) 2016 - 2021 - * - * The IPC Resource Manager - Program Table - * - * Dimitri Staessens <dimitri@ouroboros.rocks> - * Sander Vrijders <sander@ouroboros.rocks> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., http://www.fsf.org/about/contact/. - */ - -#include <ouroboros/errno.h> -#include <ouroboros/irm.h> - -#include "prog_table.h" -#include "utils.h" - -#include <assert.h> -#include <stdlib.h> -#include <string.h> - -struct prog_entry * prog_entry_create(char * prog, - uint32_t flags, - char ** argv) -{ - struct prog_entry * e; - - assert(prog); - - e = malloc(sizeof(*e)); - if (e == NULL) - return NULL; - - list_head_init(&e->next); - list_head_init(&e->names); - - e->prog = prog; - e->flags = flags; - - if (flags & BIND_AUTO) { - e->argv = argv; - } else { - e->argv = NULL; - argvfree(argv); - argv = NULL; - } - - return e; -} -void prog_entry_destroy(struct prog_entry * e) -{ - struct list_head * p = NULL; - struct list_head * h = NULL; - - if (e == NULL) - return; - - if (e->prog != NULL) - free(e->prog); - - if (e->argv != NULL) - argvfree(e->argv); - - list_for_each_safe(p, h, &e->names) { - struct str_el * s = list_entry(p, struct str_el, next); - list_del(&s->next); - free(s->str); - free(s); - } - - free(e); -} - -int prog_entry_add_name(struct prog_entry * e, - char * name) -{ - struct str_el * s; - - if (e == NULL || name == NULL) - return -EINVAL; - - s = malloc(sizeof(*s)); - if (s == NULL) - return -ENOMEM; - - s->str = name; - list_add(&s->next, &e->names); - - return 0; -} - -void prog_entry_del_name(struct prog_entry * e, - char * name) -{ - struct list_head * p = NULL; - struct list_head * h = NULL; - - list_for_each_safe(p, h, &e->names) { - struct str_el * s = list_entry(p, struct str_el, next); - if (!strcmp(name, s->str)) { - list_del(&s->next); - if (s->str != NULL) - free(s->str); - free(s); - } - } -} - -int prog_table_add(struct list_head * prog_table, - struct prog_entry * e) -{ - assert(prog_table); - assert(e); - - list_add(&e->next, prog_table); - - return 0; -} - -void prog_table_del(struct list_head * prog_table, - char * prog) -{ - struct list_head * p; - struct list_head * h; - - assert(prog_table); - assert(prog); - - list_for_each_safe(p, h, prog_table) { - struct prog_entry * e = list_entry(p, struct prog_entry, next); - if (!strcmp(prog, e->prog)) { - list_del(&e->next); - prog_entry_destroy(e); - } - } -} - -struct prog_entry * prog_table_get(struct list_head * prog_table, - char * prog) -{ - struct list_head * p; - - assert(prog_table); - assert(prog); - - list_for_each(p, prog_table) { - struct prog_entry * e = list_entry(p, struct prog_entry, next); - if (!strcmp(e->prog, prog)) - return e; - } - - return NULL; -} diff --git a/src/irmd/prog_table.h b/src/irmd/prog_table.h deleted file mode 100644 index eed046c8..00000000 --- a/src/irmd/prog_table.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Ouroboros - Copyright (C) 2016 - 2021 - * - * The IPC Resource Manager - Program Table - * - * Dimitri Staessens <dimitri@ouroboros.rocks> - * Sander Vrijders <sander@ouroboros.rocks> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., http://www.fsf.org/about/contact/. - */ - -#ifndef OUROBOROS_IRMD_PROG_TABLE_H -#define OUROBOROS_IRMD_PROG_TABLE_H - -#include <ouroboros/list.h> - -#include <unistd.h> -#include <stdint.h> - -struct prog_entry { - struct list_head next; - char * prog; /* name of binary */ - uint32_t flags; - char ** argv; - struct list_head names; /* names that all instances will listen for */ -}; - -struct prog_entry * prog_entry_create(char * prog, - uint32_t flags, - char ** argv); - -void prog_entry_destroy(struct prog_entry * e); - -int prog_entry_add_name(struct prog_entry * e, - char * name); - -void prog_entry_del_name(struct prog_entry * e, - char * name); - -int prog_table_add(struct list_head * prog_table, - struct prog_entry * e); - -void prog_table_del(struct list_head * prog_table, - char * prog); - -struct prog_entry * prog_table_get(struct list_head * prog_table, - char * prog); - -#endif /* OUROBOROS_IRMD_PROG_TABLE_H */ diff --git a/src/irmd/reg/CMakeLists.txt b/src/irmd/reg/CMakeLists.txt new file mode 100644 index 00000000..ff9d2e99 --- /dev/null +++ b/src/irmd/reg/CMakeLists.txt @@ -0,0 +1,7 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +include_directories(${CMAKE_SOURCE_DIR}/include) +include_directories(${CMAKE_BINARY_DIR}/include) + +add_subdirectory(tests) diff --git a/src/irmd/reg/flow.c b/src/irmd/reg/flow.c new file mode 100644 index 00000000..4d091b23 --- /dev/null +++ b/src/irmd/reg/flow.c @@ -0,0 +1,208 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Flows + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#define _POSIX_C_SOURCE 200809L + +#define OUROBOROS_PREFIX "reg/flow" + +#include <ouroboros/logs.h> + +#include "flow.h" + +#include <assert.h> +#include <errno.h> +#include <stdbool.h> +#include <stdlib.h> + +struct reg_flow * reg_flow_create(const struct flow_info * info) +{ + struct reg_flow * flow; + + assert(info != NULL); + assert(info->id > 0); + assert(info->n_pid != 0); + assert(info->n_1_pid == 0); + assert(info->mpl == 0); + assert(info->state == FLOW_INIT); + + flow = malloc(sizeof(*flow)); + if (flow == NULL) { + log_err("Failed to malloc flow."); + goto fail_malloc; + } + + memset(flow, 0, sizeof(*flow)); + + clock_gettime(PTHREAD_COND_CLOCK, &flow->t0); + list_head_init(&flow->next); + + flow->info = *info; + + return flow; + + fail_malloc: + return NULL; +} + +static void destroy_rbuffs(struct reg_flow * flow) +{ + if (flow->n_rb != NULL) + shm_rbuff_destroy(flow->n_rb); + flow->n_rb = NULL; + + if (flow->n_1_rb != NULL) + shm_rbuff_destroy(flow->n_1_rb); + flow->n_1_rb = NULL; +} + +void reg_flow_destroy(struct reg_flow * flow) +{ + assert(flow != NULL); + + switch(flow->info.state) { + case FLOW_ACCEPT_PENDING: + clrbuf(flow->data); + /* FALLTHRU */ + default: + destroy_rbuffs(flow); + break; + } + + assert(flow->n_rb == NULL); + assert(flow->n_1_rb == NULL); + assert(flow->data.data == NULL); + assert(flow->data.len == 0); + + assert(list_is_empty(&flow->next)); + + free(flow); +} + +static int create_rbuffs(struct reg_flow * flow, + struct flow_info * info) +{ + assert(flow != NULL); + assert(info != NULL); + + flow->n_rb = shm_rbuff_create(info->n_pid, info->id); + if (flow->n_rb == NULL) + goto fail_n_rb; + + assert(flow->info.n_1_pid == 0); + assert(flow->n_1_rb == NULL); + + flow->info.n_1_pid = info->n_1_pid; + flow->n_1_rb = shm_rbuff_create(info->n_1_pid, info->id); + if (flow->n_1_rb == NULL) + goto fail_n_1_rb; + + return 0; + + fail_n_1_rb: + shm_rbuff_destroy(flow->n_rb); + fail_n_rb: + return -ENOMEM; +} + +int reg_flow_update(struct reg_flow * flow, + struct flow_info * info) +{ + assert(flow != NULL); + assert(info != NULL); + + assert(flow->info.id == info->id); + + switch(info->state) { + case FLOW_ACCEPT_PENDING: + assert(flow->info.state == FLOW_INIT); + flow->info.n_pid = info->n_pid; + break; + case FLOW_ALLOC_PENDING: + assert(flow->info.state == FLOW_INIT); + assert(info->n_1_pid != 0); + + if (create_rbuffs(flow, info) < 0) + goto fail; + + break; + case FLOW_ALLOCATED: + assert(info->n_1_pid != 0); + assert(flow->info.state > FLOW_INIT); + assert(flow->info.state < FLOW_ALLOCATED); + assert(flow->info.n_pid != 0); + assert(info->mpl != 0); + + flow->info.mpl = info->mpl; + + if (flow->info.state == FLOW_ALLOC_PENDING) + break; + + flow->info.qs = info->qs; + + if (create_rbuffs(flow, info) < 0) + goto fail; + break; + case FLOW_DEALLOCATED: + destroy_rbuffs(flow); + break; + case FLOW_DEALLOC_PENDING: + break; + default: + assert(false); + return -EPERM; + } + + flow->info.state = info->state; + + *info = flow->info; + + return 0; + fail: + return -ENOMEM; +} + +void reg_flow_set_data(struct reg_flow * flow, + const buffer_t * buf) +{ + assert(flow != NULL); + assert(buf != NULL); + assert(flow->data.data == NULL); + assert(flow->data.len == 0); + + flow->data = *buf; +} + +void reg_flow_get_data(struct reg_flow * flow, + buffer_t * buf) +{ + assert(flow != NULL); + assert(buf != NULL); + + *buf = flow->data; + + clrbuf(flow->data); +} + +void reg_flow_free_data(struct reg_flow * flow) +{ + freebuf(flow->data); +} diff --git a/src/irmd/reg/flow.h b/src/irmd/reg/flow.h new file mode 100644 index 00000000..75ada971 --- /dev/null +++ b/src/irmd/reg/flow.h @@ -0,0 +1,63 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Flows + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#ifndef OUROBOROS_IRMD_REG_FLOW_H +#define OUROBOROS_IRMD_REG_FLOW_H + +#include <ouroboros/list.h> +#include <ouroboros/flow.h> +#include <ouroboros/pthread.h> +#include <ouroboros/qos.h> +#include <ouroboros/shm_rbuff.h> +#include <ouroboros/utils.h> + +#include <sys/types.h> +#include <time.h> + +struct reg_flow { + struct list_head next; + + struct flow_info info; + + buffer_t data; + struct timespec t0; + + struct shm_rbuff * n_rb; + struct shm_rbuff * n_1_rb; +}; + +struct reg_flow * reg_flow_create(const struct flow_info * info); + +void reg_flow_destroy(struct reg_flow * flow); + +int reg_flow_update(struct reg_flow * flow, + struct flow_info * info); + +void reg_flow_set_data(struct reg_flow * flow, + const buffer_t * buf); + +void reg_flow_get_data(struct reg_flow * flow, + buffer_t * buf); + +void reg_flow_free_data(struct reg_flow * flow); + +#endif /* OUROBOROS_IRMD_REG_FLOW_H */ diff --git a/src/irmd/reg/ipcp.c b/src/irmd/reg/ipcp.c new file mode 100644 index 00000000..6580cb5b --- /dev/null +++ b/src/irmd/reg/ipcp.c @@ -0,0 +1,92 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - IPCPs + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#define _POSIX_C_SOURCE 200809L + +#define OUROBOROS_PREFIX "reg/ipcp" + +#include <ouroboros/logs.h> +#include <ouroboros/time.h> + +#include "ipcp.h" + +#include <assert.h> +#include <errno.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +struct reg_ipcp * reg_ipcp_create(const struct ipcp_info * info) +{ + struct reg_ipcp * ipcp; + + assert(info != NULL); + assert(info->state == IPCP_BOOT); + + ipcp = malloc(sizeof(*ipcp)); + if (ipcp == NULL) { + log_err("Failed to malloc ipcp."); + goto fail_malloc; + } + + memset(ipcp, 0, sizeof(*ipcp)); + memset(&ipcp->layer, 0, sizeof(ipcp->layer)); + + list_head_init(&ipcp->next); + + ipcp->info = *info; + ipcp->info.state = IPCP_BOOT; + + strcpy(ipcp->layer.name, "Not enrolled."); + + return ipcp; + + fail_malloc: + return NULL; +} + +void reg_ipcp_destroy(struct reg_ipcp * ipcp) +{ + assert(ipcp != NULL); + + assert(list_is_empty(&ipcp->next)); + + free(ipcp); +} + +void reg_ipcp_update(struct reg_ipcp * ipcp, + const struct ipcp_info * info) +{ + assert(ipcp != NULL); + assert(info->state != IPCP_INIT); + + ipcp->info = *info; +} + +void reg_ipcp_set_layer(struct reg_ipcp * ipcp, + const struct layer_info * info) +{ + assert(ipcp != NULL); + assert(ipcp->info.state == IPCP_OPERATIONAL); + + ipcp->layer = *info; +} diff --git a/src/irmd/utils.h b/src/irmd/reg/ipcp.h index 5af918fd..375973a7 100644 --- a/src/irmd/utils.h +++ b/src/irmd/reg/ipcp.h @@ -1,7 +1,7 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * - * Utils of the IPC Resource Manager + * The IPC Resource Manager - Registry - IPCPs * * Dimitri Staessens <dimitri@ouroboros.rocks> * Sander Vrijders <sander@ouroboros.rocks> @@ -20,24 +20,28 @@ * Foundation, Inc., http://www.fsf.org/about/contact/. */ -#ifndef OUROBOROS_IRMD_UTILS_H -#define OUROBOROS_IRMD_UTILS_H +#ifndef OUROBOROS_IRMD_REG_IPCP_H +#define OUROBOROS_IRMD_REG_IPCP_H -#include <sys/types.h> +#include <ouroboros/list.h> +#include <ouroboros/ipcp.h> -struct str_el { +struct reg_ipcp { struct list_head next; - char * str; -}; -struct pid_el { - struct list_head next; - pid_t pid; + struct ipcp_info info; + + struct layer_info layer; }; -/* functions for copying and destroying arguments list */ -char ** argvdup(char ** argv); +struct reg_ipcp * reg_ipcp_create(const struct ipcp_info * info); + +void reg_ipcp_destroy(struct reg_ipcp * ipcp); + +void reg_ipcp_update(struct reg_ipcp * ipcp, + const struct ipcp_info * info); -void argvfree(char ** argv); +void reg_ipcp_set_layer(struct reg_ipcp * ipcp, + const struct layer_info * info); -#endif /* OUROBOROS_IRM_UTILS_H */ +#endif /* OUROBOROS_IRMD_REG_IPCP_H */ diff --git a/src/irmd/reg/name.c b/src/irmd/reg/name.c new file mode 100644 index 00000000..1ac939a5 --- /dev/null +++ b/src/irmd/reg/name.c @@ -0,0 +1,375 @@ + +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Names + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#define _POSIX_C_SOURCE 200809L + +#define OUROBOROS_PREFIX "reg/name" + +#include <ouroboros/logs.h> +#include <ouroboros/utils.h> + +#include "name.h" + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +struct prog_entry { + struct list_head next; + char ** exec; +}; + +struct proc_entry { + struct list_head next; + pid_t pid; +}; + +static void __free_prog_entry(struct prog_entry * entry) +{ + assert(entry != NULL); + assert(entry->exec != NULL); + + argvfree(entry->exec); + free(entry); +} + +struct reg_name * reg_name_create(const struct name_info * info) +{ + struct reg_name * name; + + assert(info != NULL); + + name = malloc(sizeof(*name)); + if (name == NULL) { + log_err("Failed to malloc name."); + goto fail_malloc; + } + + list_head_init(&name->next); + list_head_init(&name->progs); + list_head_init(&name->procs); + list_head_init(&name->active); + + name->info = *info; + name->n_progs = 0; + name->n_procs = 0; + name->n_active = 0; + + return name; + + fail_malloc: + return NULL; +} + +void reg_name_destroy(struct reg_name * name) +{ + assert(name != NULL); + + assert(list_is_empty(&name->next)); + + assert(name->n_progs == 0); + assert(name->n_procs == 0); + assert(name->n_active == 0); + + assert(list_is_empty(&name->progs)); + assert(list_is_empty(&name->procs)); + assert(list_is_empty(&name->active)); + + free(name); +} + +static struct proc_entry * __reg_name_get_active(const struct reg_name * name, + pid_t pid) +{ + struct list_head * p; + + assert(name != NULL); + assert(pid > 0); + + list_for_each(p, &name->active) { + struct proc_entry * entry; + entry = list_entry(p, struct proc_entry, next); + if (entry->pid == pid) + return entry; + } + + return NULL; +} + +static void __reg_name_del_all_active(struct reg_name * name, + pid_t pid) +{ + struct list_head * p; + struct list_head * h; + + list_for_each_safe(p, h, &name->active) { + struct proc_entry * entry; + entry = list_entry(p, struct proc_entry, next); + if (entry->pid == pid) { + list_del(&entry->next); + free(entry); + name->n_active--; + } + } +} + +static struct proc_entry * __reg_name_get_proc(const struct reg_name * name, + pid_t pid) +{ + struct list_head * p; + + assert(name != NULL); + assert(pid > 0); + + list_for_each(p, &name->procs) { + struct proc_entry * entry; + entry = list_entry(p, struct proc_entry, next); + if (entry->pid == pid) + return entry; + } + + return NULL; +} + +static struct prog_entry * __reg_name_get_prog(const struct reg_name * name, + const char * prog) +{ + struct list_head * p; + + assert(name != NULL); + assert(prog != NULL); + + list_for_each(p, &name->progs) { + struct prog_entry * entry; + entry = list_entry(p, struct prog_entry, next); + if (strcmp(entry->exec[0], prog) == 0) + return entry; + } + + return NULL; +} + +int reg_name_add_active(struct reg_name * name, + pid_t pid) +{ + struct proc_entry * entry; + + assert(name != NULL); + assert(pid > 0); + + assert(__reg_name_get_proc(name, pid) != NULL); + + log_dbg("Process %d accepting flows for %s.", pid, name->info.name); + + if (__reg_name_get_active(name, pid) != NULL) + log_dbg("Process calling accept from multiple threads."); + + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + log_err("Failed to malloc active."); + goto fail_malloc; + } + + entry->pid = pid; + + switch (name->info.pol_lb) { + case LB_RR: /* Round robin policy. */ + list_add_tail(&entry->next, &name->active); + break; + case LB_SPILL: /* Keep accepting flows on the current process */ + list_add(&entry->next, &name->active); + break; + default: + goto fail_unreachable; + } + + name->n_active++; + + return 0; + + fail_unreachable: + free(entry); + assert(false); + fail_malloc: + return -1; +} + +void reg_name_del_active(struct reg_name * name, + pid_t pid) +{ + struct proc_entry * entry; + + entry = __reg_name_get_active(name, pid); + if (entry == NULL) + return; + + list_del(&entry->next); + + name->n_active--; + + free(entry); +} + +pid_t reg_name_get_active(struct reg_name * name) +{ + assert(name != NULL); + + if (list_is_empty(&name->active)) + return -1; + + return list_first_entry(&name->active, struct proc_entry, next)->pid; +} + +int reg_name_add_proc(struct reg_name * name, + pid_t pid) +{ + struct proc_entry * entry; + + assert(name != NULL); + assert(pid > 0); + + assert(__reg_name_get_proc(name, pid) == NULL); + + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + log_err("Failed to malloc proc."); + goto fail_malloc; + } + + entry->pid = pid; + + list_add(&entry->next, &name->procs); + + name->n_procs++; + + return 0; + + fail_malloc: + return -1; +} + +void reg_name_del_proc(struct reg_name * name, + pid_t pid) +{ + struct proc_entry * entry; + + assert(name != NULL); + assert(pid > 0); + + entry = __reg_name_get_proc(name, pid); + if (entry == NULL) + return; + + __reg_name_del_all_active(name, pid); + + list_del(&entry->next); + + free(entry); + + name->n_procs--; + + assert(__reg_name_get_proc(name, pid) == NULL); +} + +bool reg_name_has_proc(const struct reg_name * name, + pid_t pid) +{ + return __reg_name_get_proc(name, pid) != NULL; +} char ** exec; + + +int reg_name_add_prog(struct reg_name * name, + char ** exec) +{ + struct prog_entry * entry; + + assert(name != NULL); + assert(exec != NULL); + assert(exec[0] != NULL); + + assert(__reg_name_get_prog(name, exec[0]) == NULL); + + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + log_err("Failed to malloc prog."); + goto fail_malloc; + } + + entry->exec = argvdup(exec); + if (entry->exec == NULL) { + log_err("Failed to argvdup prog."); + goto fail_exec; + } + + list_add(&entry->next, &name->progs); + + log_dbg("Add prog %s to name %s.", exec[0], name->info.name); + + name->n_progs++; + + return 0; + + fail_exec: + free(entry); + fail_malloc: + return -1; +} + +void reg_name_del_prog(struct reg_name * name, + const char * prog) +{ + struct prog_entry * entry; + + assert(name != NULL); + assert(prog != NULL); + + entry = __reg_name_get_prog(name, prog); + if (entry == NULL) + return; + + list_del(&entry->next); + + __free_prog_entry(entry); + + name->n_progs--; + + assert(__reg_name_get_prog(name, prog) == NULL); +} + +bool reg_name_has_prog(const struct reg_name * name, + const char * prog) +{ + assert(name != NULL); + assert(prog != NULL); + + return __reg_name_get_prog(name, prog) != NULL; +} + +char ** reg_name_get_exec(const struct reg_name * name) +{ + if (list_is_empty(&name->progs)) + return NULL; + + return list_first_entry(&name->progs, struct prog_entry, next)->exec; +} diff --git a/src/irmd/reg/name.h b/src/irmd/reg/name.h new file mode 100644 index 00000000..97ca7f04 --- /dev/null +++ b/src/irmd/reg/name.h @@ -0,0 +1,78 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Names + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#ifndef OUROBOROS_IRMD_REG_NAME_H +#define OUROBOROS_IRMD_REG_NAME_H + +#include <ouroboros/list.h> +#include <ouroboros/name.h> + +#define BIND_AUTO 0x01 + +struct reg_name { + struct list_head next; + + struct name_info info; + + struct list_head progs; /* autostart programs for this name */ + size_t n_progs; /* number of programs */ + + struct list_head procs; /* processes bound to this name */ + size_t n_procs; /* number of processes */ + + struct list_head active; /* processes actively calling accept */ + size_t n_active; /* number of processes accepting */ +}; + +struct reg_name * reg_name_create(const struct name_info * info); + +void reg_name_destroy(struct reg_name * name); + +int reg_name_add_proc(struct reg_name * name, + pid_t proc); + +void reg_name_del_proc(struct reg_name * name, + pid_t proc); + +bool reg_name_has_proc(const struct reg_name * name, + pid_t proc); + +int reg_name_add_prog(struct reg_name * name, + char ** exec); + +void reg_name_del_prog(struct reg_name * name, + const char * prog); + +bool reg_name_has_prog(const struct reg_name * name, + const char * prog); + +char ** reg_name_get_exec(const struct reg_name * name); + +int reg_name_add_active(struct reg_name * name, + pid_t proc); + +pid_t reg_name_get_active(struct reg_name * name); + +void reg_name_del_active(struct reg_name * name, + pid_t proc); + +#endif /* OUROBOROS_IRMD_REG_NAME_H */ diff --git a/src/irmd/reg/proc.c b/src/irmd/reg/proc.c new file mode 100644 index 00000000..9bbdf0eb --- /dev/null +++ b/src/irmd/reg/proc.c @@ -0,0 +1,183 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Processes + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This procram is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This procram 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this procram; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#define _POSIX_C_SOURCE 200809L + +#define OUROBOROS_PREFIX "reg/proc" + +#include <ouroboros/logs.h> + +#include "proc.h" + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +struct name_entry { + struct list_head next; + char * name; +}; + +static void __free_name_entry(struct name_entry * entry) +{ + assert(entry != NULL); + assert(entry->name != NULL); + + free(entry->name); + free(entry); +} + +static void __reg_proc_clear_names(struct reg_proc * proc) +{ + struct list_head * p; + struct list_head * h; + + assert(proc != NULL); + + list_for_each_safe(p, h, &proc->names) { + struct name_entry * entry; + entry = list_entry(p, struct name_entry, next); + list_del(&entry->next); + __free_name_entry(entry); + proc->n_names--; + } +} + +struct reg_proc * reg_proc_create(const struct proc_info * info) +{ + struct reg_proc * proc; + + assert(info != NULL); + + proc = malloc(sizeof(*proc)); + if (proc == NULL) { + log_err("Failed to malloc proc."); + goto fail_malloc; + } + + proc->set = shm_flow_set_create(info->pid); + if (proc->set == NULL) { + log_err("Failed to create flow set for %d.", info->pid); + goto fail_set; + } + + list_head_init(&proc->next); + list_head_init(&proc->names); + + proc->info = *info; + proc->n_names = 0; + + return proc; + + fail_set: + free(proc); + fail_malloc: + return NULL; +} + +void reg_proc_destroy(struct reg_proc * proc) +{ + assert(proc != NULL); + + shm_flow_set_destroy(proc->set); + + __reg_proc_clear_names(proc); + + assert(list_is_empty(&proc->next)); + + assert(proc->n_names == 0); + + assert(list_is_empty(&proc->names)); + + free(proc); +} + +static struct name_entry * __reg_proc_get_name(const struct reg_proc * proc, + const char * name) +{ + struct list_head * p; + + list_for_each(p, &proc->names) { + struct name_entry * entry; + entry = list_entry(p, struct name_entry, next); + if (strcmp(entry->name, name) == 0) + return entry; + } + + return NULL; +} + +int reg_proc_add_name(struct reg_proc * proc, + const char * name) +{ + struct name_entry * entry; + + assert(__reg_proc_get_name(proc, name) == NULL); + + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + log_err("Failed to malloc name."); + goto fail_malloc; + } + + entry->name = strdup(name); + if (entry == NULL) { + log_err("Failed to strdup name."); + goto fail_name; + } + + list_add(&entry->next, &proc->names); + + proc->n_names++; + + return 0; + + fail_name: + free(entry); + fail_malloc: + return -1; +} + +void reg_proc_del_name(struct reg_proc * proc, + const char * name) +{ + struct name_entry * entry; + + entry = __reg_proc_get_name(proc, name); + if(entry == NULL) + return; + + list_del(&entry->next); + + __free_name_entry(entry); + + proc->n_names--; + + assert(__reg_proc_get_name(proc, name) == NULL); +} + +bool reg_proc_has_name(const struct reg_proc * proc, + const char * name) +{ + return __reg_proc_get_name(proc, name) != NULL; +} diff --git a/src/irmd/reg/proc.h b/src/irmd/reg/proc.h new file mode 100644 index 00000000..99f74fef --- /dev/null +++ b/src/irmd/reg/proc.h @@ -0,0 +1,56 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Processes + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#ifndef OUROBOROS_IRMD_REG_PROC_H +#define OUROBOROS_IRMD_REG_PROC_H + +#include <ouroboros/list.h> +#include <ouroboros/proc.h> +#include <ouroboros/shm_flow_set.h> + +struct reg_proc { + struct list_head next; + + struct proc_info info; + + struct list_head names; /* names for which process accepts flows */ + size_t n_names; /* number of names */ + + struct shm_flow_set * set; +}; + +struct reg_proc * reg_proc_create(const struct proc_info * info); + +void reg_proc_destroy(struct reg_proc * proc); + +void reg_proc_clear(struct reg_proc * proc); + +int reg_proc_add_name(struct reg_proc * proc, + const char * name); + +void reg_proc_del_name(struct reg_proc * proc, + const char * name); + +bool reg_proc_has_name(const struct reg_proc * proc, + const char * name); + +#endif /* OUROBOROS_IRMD_REG_PROC_H */ diff --git a/src/irmd/reg/prog.c b/src/irmd/reg/prog.c new file mode 100644 index 00000000..9b9e7510 --- /dev/null +++ b/src/irmd/reg/prog.c @@ -0,0 +1,174 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Programs + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#define _POSIX_C_SOURCE 200809L + +#define OUROBOROS_PREFIX "reg/prog" + +#include <ouroboros/logs.h> +#include <ouroboros/utils.h> + +#include "prog.h" + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +struct name_entry { + struct list_head next; + char * name; +}; + +static void __free_name_entry(struct name_entry * entry) +{ + assert(entry != NULL); + assert(entry->name != NULL); + + free(entry->name); + free(entry); +} + +static void __reg_prog_clear_names(struct reg_prog * prog) +{ + struct list_head * p; + struct list_head * h; + + assert(prog != NULL); + + list_for_each_safe(p, h, &prog->names) { + struct name_entry * entry; + entry = list_entry(p, struct name_entry, next); + list_del(&entry->next); + __free_name_entry(entry); + prog->n_names--; + } +} + +struct reg_prog * reg_prog_create(const struct prog_info * info) +{ + struct reg_prog * p; + + assert(info != NULL); + + p = malloc(sizeof(*p)); + if (p == NULL) { + log_err("Failed to malloc prog."); + goto fail_malloc; + } + + list_head_init(&p->next); + list_head_init(&p->names); + + p->info = *info; + p->n_names = 0; + + return p; + + fail_malloc: + return NULL; +} + +void reg_prog_destroy(struct reg_prog * prog) +{ + assert(prog != NULL); + + __reg_prog_clear_names(prog); + + assert(list_is_empty(&prog->next)); + + assert(prog->n_names == 0); + + assert(list_is_empty(&prog->names)); + + free(prog); +} + +static struct name_entry * __reg_prog_get_name(const struct reg_prog * prog, + const char * name) +{ + struct list_head * p; + + list_for_each(p, &prog->names) { + struct name_entry * entry; + entry = list_entry(p, struct name_entry, next); + if (strcmp(entry->name, name) == 0) + return entry; + } + + return NULL; +} + +int reg_prog_add_name(struct reg_prog * prog, + const char * name) +{ + struct name_entry * entry; + + assert(__reg_prog_get_name(prog, name) == NULL); + + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + log_err("Failed to malloc name."); + goto fail_malloc; + } + + entry->name = strdup(name); + if (entry == NULL) { + log_err("Failed to strdup name."); + goto fail_name; + } + + list_add(&entry->next, &prog->names); + + prog->n_names++; + + return 0; + + fail_name: + free(entry); + fail_malloc: + return -1; +} + +void reg_prog_del_name(struct reg_prog * prog, + const char * name) +{ + struct name_entry * entry; + + entry = __reg_prog_get_name(prog, name); + if (entry == NULL) + return; + + list_del(&entry->next); + + __free_name_entry(entry); + + prog->n_names--; + + assert(__reg_prog_get_name(prog, name) == NULL); +} + +bool reg_prog_has_name(const struct reg_prog * prog, + const char * name) +{ + return __reg_prog_get_name(prog, name) != NULL; +} diff --git a/src/irmd/reg/prog.h b/src/irmd/reg/prog.h new file mode 100644 index 00000000..a98fc6a1 --- /dev/null +++ b/src/irmd/reg/prog.h @@ -0,0 +1,53 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Programs + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#ifndef OUROBOROS_IRMD_REG_PROG_H +#define OUROBOROS_IRMD_REG_PROG_H + +#include <ouroboros/list.h> +#include <ouroboros/proc.h> + +#include <stdint.h> + +struct reg_prog { + struct list_head next; + + struct prog_info info; + + struct list_head names; /* names to listen for */ + size_t n_names; /* number of names in list */ + }; + +struct reg_prog * reg_prog_create(const struct prog_info * info); + +void reg_prog_destroy(struct reg_prog * prog); + +int reg_prog_add_name(struct reg_prog * prog, + const char * name); + +void reg_prog_del_name(struct reg_prog * prog, + const char * name); + +bool reg_prog_has_name(const struct reg_prog * prog, + const char * name); + +#endif /* OUROBOROS_IRMD_REG_PROG_H */ diff --git a/src/irmd/reg/reg.c b/src/irmd/reg/reg.c new file mode 100644 index 00000000..d95a4722 --- /dev/null +++ b/src/irmd/reg/reg.c @@ -0,0 +1,2120 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * +The IPC Resource Manager - Registry + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#define _POSIX_C_SOURCE 200809L + +#define OUROBOROS_PREFIX "reg" + +#include <ouroboros/bitmap.h> +#include <ouroboros/errno.h> +#include <ouroboros/list.h> +#include <ouroboros/logs.h> +#include <ouroboros/pthread.h> + +#include "reg.h" +#include "flow.h" +#include "ipcp.h" +#include "name.h" +#include "proc.h" +#include "prog.h" + +#include <assert.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> + +#define ID_OFFT 1 /* reserve some flow_ids */ + +struct { + struct bmp * flow_ids; /* flow_ids for flows */ + struct list_head flows; /* flow information */ + size_t n_flows; /* number of flows */ + + struct list_head ipcps; /* list of ipcps in system */ + size_t n_ipcps; /* number of ipcps */ + + struct list_head names; /* registered names known */ + size_t n_names; /* number of names */ + + struct list_head procs; /* processes */ + size_t n_procs; /* number of processes */ + + struct list_head progs; /* programs known */ + size_t n_progs; /* number of programs */ + + struct list_head spawned; /* child processes */ + size_t n_spawned; /* number of child processes */ + + pthread_mutex_t mtx; /* registry lock */ + pthread_cond_t cond; /* condvar for reg changes */ +} reg; + +struct pid_entry { + struct list_head next; + pid_t pid; +}; + +static struct reg_flow * __reg_get_flow(int flow_id) +{ + struct list_head * p; + + assert(flow_id >= ID_OFFT); + + list_for_each(p, ®.flows) { + struct reg_flow * entry; + entry = list_entry(p, struct reg_flow, next); + if (entry->info.id == flow_id) + return entry; + } + + return NULL; +} + +static struct reg_flow * __reg_get_accept_flow(pid_t pid) +{ + struct list_head * p; + + list_for_each(p, ®.flows) { + struct reg_flow * entry; + entry = list_entry(p, struct reg_flow, next); + if (entry->info.state != FLOW_ACCEPT_PENDING) + continue; + if (entry->info.n_pid == pid) + return entry; + } + + return NULL; +} + +static struct list_head * __reg_after_flow(int flow_id) +{ + struct list_head * p; + + assert(flow_id >= ID_OFFT); + + list_for_each(p, ®.flows) { + struct reg_flow * entry; + entry = list_entry(p, struct reg_flow, next); + if (entry->info.id > flow_id) + break; + } + + return p; +} + +static struct reg_ipcp * __reg_get_ipcp(pid_t pid) +{ + struct list_head * p; + + assert(pid > 0); + + list_for_each(p, ®.ipcps) { + struct reg_ipcp * entry; + entry = list_entry(p, struct reg_ipcp, next); + if (entry->info.pid == pid) + return entry; + } + + return NULL; +} + +static struct reg_ipcp * __reg_get_ipcp_by_layer(const char * layer) +{ + struct list_head * p; + + list_for_each(p, ®.ipcps) { + struct reg_ipcp * entry; + entry = list_entry(p, struct reg_ipcp, next); + if (strcmp(entry->layer.name, layer) == 0) + return entry; + } + + return NULL; +} + +static struct list_head * __reg_after_ipcp(pid_t pid) +{ + struct list_head * p; + + assert(pid > 0); + + list_for_each(p, ®.ipcps) { + struct reg_ipcp * entry; + entry = list_entry(p, struct reg_ipcp, next); + if (entry->info.pid > pid) + break; + } + + return p; +} + +static struct reg_name * __reg_get_name(const char * name) +{ + struct list_head * p; + + assert(name != NULL); + + list_for_each(p, ®.names) { + struct reg_name * entry; + entry = list_entry(p, struct reg_name, next); + if (strcmp(entry->info.name, name) == 0) + return entry; + } + + return NULL; +} + +static struct reg_name * __reg_get_name_by_hash(enum hash_algo algo, + const uint8_t * hash) +{ + struct list_head * p; + uint8_t * thash; + size_t len; + + len = hash_len(algo); + + thash = malloc(len); + if (thash == NULL) + return NULL; + + list_for_each(p, ®.names) { + struct reg_name * n = list_entry(p, struct reg_name, next); + str_hash(algo, thash, n->info.name); + if (memcmp(thash, hash, len) == 0) { + free(thash); + return n; + } + } + + free(thash); + + return NULL; +} + +static int __reg_get_pending_flow_id_for_hash(enum hash_algo algo, + const uint8_t * hash) +{ + struct reg_name * entry; + struct reg_flow * flow; + pid_t pid; + + entry =__reg_get_name_by_hash(algo, hash); + if (entry == NULL) + return -ENAME; + + pid = reg_name_get_active(entry); + if (pid < 0) + return -EAGAIN; + + flow = __reg_get_accept_flow(pid); + assert(flow != NULL); + + return flow->info.id; +} + +static struct list_head * __reg_after_name(const char * name) +{ + struct list_head * p; + + assert(name != NULL); + + list_for_each(p, ®.names) { + struct reg_name * entry; + entry = list_entry(p, struct reg_name, next); + if (strcmp(entry->info.name, name) > 0) + break; + } + + return p; +} + +static struct reg_proc * __reg_get_proc(pid_t pid) +{ + struct list_head * p; + + list_for_each(p, ®.procs) { + struct reg_proc * entry; + entry = list_entry(p, struct reg_proc, next); + if (entry->info.pid == pid) + return entry; + } + + return NULL; +} + +static struct list_head * __reg_after_proc(pid_t pid) +{ + struct list_head * p; + + list_for_each(p, ®.procs) { + struct reg_proc * entry; + entry = list_entry(p, struct reg_proc, next); + if (entry->info.pid > pid) + break; + } + + return p; +} + +static void __reg_kill_all_proc(int signal) +{ + struct list_head * p; + + list_for_each(p, ®.procs) { + struct reg_proc * entry; + entry = list_entry(p, struct reg_proc, next); + kill(entry->info.pid, signal); + } +} + +static pid_t __reg_get_dead_proc(void) +{ + struct list_head * p; + + list_for_each(p, ®.procs) { + struct reg_proc * entry; + entry = list_entry(p, struct reg_proc, next); + if (kill(entry->info.pid, 0) < 0) + return entry->info.pid; + } + + return -1; +} + +static void __reg_cancel_flows_for_proc(pid_t pid) +{ + struct list_head * p; + bool changed = false; + + list_for_each(p, ®.flows) { + struct reg_flow * entry; + entry = list_entry(p, struct reg_flow, next); + if (entry->info.n_pid != pid) + continue; + + switch (entry->info.state) { + case FLOW_ALLOC_PENDING: + /* FALLTHRU */ + case FLOW_ACCEPT_PENDING: + entry->info.state = FLOW_DEALLOCATED; + changed = true; + break; + default: + continue; + } + } + + if (changed) + pthread_cond_broadcast(®.cond); +} + +static struct pid_entry * __reg_get_spawned(pid_t pid) +{ + struct list_head * p; + + list_for_each(p, ®.spawned) { + struct pid_entry * entry; + entry = list_entry(p, struct pid_entry, next); + if (entry->pid == pid) + return entry; + } + + return NULL; +} + +static struct list_head * __reg_after_spawned(pid_t pid) +{ + struct list_head * p; + + list_for_each(p, ®.spawned) { + struct pid_entry * entry; + entry = list_entry(p, struct pid_entry, next); + if (entry->pid > pid) + break; + } + + return p; +} + +static void __reg_kill_all_spawned(int signal) +{ + struct list_head * p; + + list_for_each(p, ®.spawned) { + struct pid_entry * entry; + entry = list_entry(p, struct pid_entry, next); + kill(entry->pid, signal); + } +} + +static pid_t __reg_first_spawned(void) +{ + if (list_is_empty(®.spawned)) + return -1; + + return list_first_entry(®.spawned, struct pid_entry, next)->pid; +} + +static struct reg_prog * __reg_get_prog(const char * name) +{ + struct list_head * p; + + list_for_each(p, ®.progs) { + struct reg_prog * entry; + entry = list_entry(p, struct reg_prog, next); + if (strcmp(entry->info.name, name) == 0) + return entry; + } + + return NULL; +} + +static char ** __reg_get_exec(enum hash_algo algo, + const uint8_t * hash) +{ + struct list_head * p; + uint8_t * buf; + + buf = malloc(hash_len(algo)); + if (buf == NULL) { + log_err("Failed to malloc hash buffer."); + return NULL; + } + + list_for_each(p, ®.names) { + struct reg_name * entry; + entry = list_entry(p, struct reg_name, next); + str_hash(algo, buf, entry->info.name); + if (memcmp(buf, hash, hash_len(algo)) == 0) { + free(buf); + return reg_name_get_exec(entry); + } + } + + free(buf); + + return NULL; +} + +static struct list_head * __reg_after_prog(const char * name) +{ + struct list_head * p; + + list_for_each(p, ®.progs) { + struct reg_prog * entry; + entry = list_entry(p, struct reg_prog, next); + if (strcmp(entry->info.name, name) > 0) + break; + } + + return p; +} + +static void __reg_del_name_from_procs(const char * name) +{ + struct list_head * p; + + list_for_each(p, ®.procs) { + struct reg_proc * proc; + proc = list_entry(p, struct reg_proc, next); + reg_proc_del_name(proc, name); + } +} + +static void __reg_del_name_from_progs(const char * name) +{ + struct list_head * p; + + list_for_each(p, ®.progs) { + struct reg_prog * prog; + prog = list_entry(p, struct reg_prog, next); + reg_prog_del_name(prog, name); + } +} + +static void __reg_proc_update_names(struct reg_proc * proc) +{ + struct list_head * p; + struct reg_prog * prog; + + assert(list_is_empty(&proc->names)); + + prog = __reg_get_prog(proc->info.prog); + if (prog == NULL) + return; + + list_for_each(p, ®.names) { + struct reg_name * name; + name = list_entry(p, struct reg_name, next); + assert(!reg_name_has_proc(name, proc->info.pid)); + if (reg_prog_has_name(prog, name->info.name)) { + reg_proc_add_name(proc, name->info.name); + reg_name_add_proc(name, proc->info.pid); + } + } +} + +static void __reg_del_proc_from_names(pid_t pid) +{ + struct list_head * p; + + list_for_each(p, ®.names) { + struct reg_name * name; + name = list_entry(p, struct reg_name, next); + reg_name_del_proc(name, pid); + } +} + +static void __reg_del_prog_from_names(const char * prog) +{ + struct list_head * p; + + list_for_each(p, ®.names) { + struct reg_name * name; + name = list_entry(p, struct reg_name, next); + reg_name_del_prog(name, prog); + } +} + +static int __reg_add_active_proc(pid_t pid) +{ + struct list_head * p; + size_t n_names = 0; + size_t failed = 0; + + assert(pid > 0); + + list_for_each(p, ®.names) { + struct reg_name * name; + name = list_entry(p, struct reg_name, next); + if (reg_name_has_proc(name, pid)) { + if (reg_name_add_active(name, pid) < 0) + failed++; + n_names++; + } + } + + if (n_names > 0 && failed == n_names) + return -1; + + return 0; /* some were marked */ +} + +static void __reg_del_active_proc(pid_t pid) +{ + struct list_head * p; + + assert(pid > 0); + + list_for_each(p, ®.names) { + struct reg_name * name; + name = list_entry(p, struct reg_name, next); + reg_name_del_active(name, pid); + } +} + +int reg_init(void) +{ + pthread_condattr_t cattr; + + if (pthread_mutex_init(®.mtx, NULL) != 0) { + log_err("Failed to initialize mutex."); + goto fail_mtx; + } + + if (pthread_condattr_init(&cattr) != 0) { + log_err("Failed to initialize condattr."); + goto fail_cattr; + } + +#ifndef __APPLE__ + pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK); +#endif + if (pthread_cond_init(®.cond, &cattr) != 0) { + log_err("Failed to initialize condvar."); + goto fail_cond; + } + + reg.flow_ids = bmp_create(SYS_MAX_FLOWS -ID_OFFT, ID_OFFT); + if (reg.flow_ids == NULL) { + log_err("Failed to create flow_ids bitmap."); + goto fail_flow_ids; + } + + pthread_condattr_destroy(&cattr); + + list_head_init(®.flows); + list_head_init(®.ipcps); + list_head_init(®.names); + list_head_init(®.procs); + list_head_init(®.progs); + list_head_init(®.spawned); + + return 0; + + fail_flow_ids: + pthread_cond_destroy(®.cond); + fail_cond: + pthread_condattr_destroy(&cattr); + fail_cattr: + pthread_mutex_destroy(®.mtx); + fail_mtx: + return -1; +} + +void reg_clear(void) +{ + struct list_head * p; + struct list_head * h; + + pthread_mutex_lock(®.mtx); + + list_for_each_safe(p, h, ®.spawned) { + struct pid_entry * entry; + entry = list_entry(p, struct pid_entry, next); + list_del(&entry->next); + free(entry); + reg.n_spawned--; + } + + list_for_each_safe(p, h, ®.progs) { + struct reg_prog * entry; + entry = list_entry(p, struct reg_prog, next); + list_del(&entry->next); + __reg_del_prog_from_names(entry->info.path); + reg_prog_destroy(entry); + reg.n_progs--; + } + + list_for_each_safe(p, h, ®.procs) { + struct reg_proc * entry; + entry = list_entry(p, struct reg_proc, next); + list_del(&entry->next); + __reg_del_proc_from_names(entry->info.pid); + reg_proc_destroy(entry); + reg.n_procs--; + } + + list_for_each_safe(p, h, ®.names) { + struct reg_name * entry; + entry = list_entry(p, struct reg_name, next); + list_del(&entry->next); + reg_name_destroy(entry); + reg.n_names--; + } + + list_for_each_safe(p, h, ®.ipcps) { + struct reg_ipcp * entry; + entry = list_entry(p, struct reg_ipcp, next); + list_del(&entry->next); + reg_ipcp_destroy(entry); + reg.n_ipcps--; + } + + list_for_each_safe(p, h, ®.flows) { + struct reg_flow * entry; + entry = list_entry(p, struct reg_flow, next); + list_del(&entry->next); + reg_flow_destroy(entry); + reg.n_flows--; + } + + pthread_mutex_unlock(®.mtx); +} + +void reg_fini(void) +{ + assert(list_is_empty(®.spawned)); + assert(list_is_empty(®.progs)); + assert(list_is_empty(®.procs)); + assert(list_is_empty(®.names)); + assert(list_is_empty(®.ipcps)); + assert(list_is_empty(®.flows)); + + assert(reg.n_spawned == 0); + assert(reg.n_progs == 0); + assert(reg.n_procs == 0); + assert(reg.n_names == 0); + assert(reg.n_ipcps == 0); + assert(reg.n_flows == 0); + + bmp_destroy(reg.flow_ids); + + if (pthread_cond_destroy(®.cond) != 0) + log_warn("Failed to destroy condvar."); + + if (pthread_mutex_destroy(®.mtx) != 0) + log_warn("Failed to destroy mutex."); +} + +int reg_create_flow(struct flow_info * info) +{ + struct reg_flow * f; + + assert(info != NULL); + assert(info->id == 0); + assert(info->n_pid != 0); + assert(info->state == FLOW_INIT); + + pthread_mutex_lock(®.mtx); + + info->id = bmp_allocate(reg.flow_ids); + if (!bmp_is_id_valid(reg.flow_ids, info->id)) { + log_err("Failed to allocate flow id."); + goto fail_id; + } + + f = reg_flow_create(info); + if (f == NULL) { + log_err("Failed to create flow %d.", info->id); + goto fail_flow; + } + + list_add(&f->next, __reg_after_flow(info->id)); + + reg.n_flows++; + + pthread_mutex_unlock(®.mtx); + + return 0; + + fail_flow: + bmp_release(reg.flow_ids, info->id); + info->id = 0; + fail_id: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_destroy_flow(int flow_id) +{ + struct reg_flow * f; + + pthread_mutex_lock(®.mtx); + + f = __reg_get_flow(flow_id); + if (f == NULL) { + log_err("Flow %d does not exist.", flow_id); + goto no_flow; + } + + list_del(&f->next); + + reg.n_flows--; + + bmp_release(reg.flow_ids, flow_id); + + pthread_mutex_unlock(®.mtx); + + pthread_cond_broadcast(®.cond); + + reg_flow_destroy(f); + + return 0; + + no_flow: + pthread_mutex_unlock(®.mtx); + return -1; + +} + +bool reg_has_flow(int flow_id) +{ + bool ret; + + pthread_mutex_lock(®.mtx); + + ret = __reg_get_flow(flow_id) != NULL; + + pthread_mutex_unlock(®.mtx); + + return ret; +} + +int reg_create_ipcp(const struct ipcp_info * info) +{ + struct reg_ipcp * ipcp; + struct pid_entry * entry; + + assert(info != NULL); + assert(info->pid != 0); + assert(info->state == IPCP_BOOT); + + pthread_mutex_lock(®.mtx); + + if (__reg_get_ipcp(info->pid) != NULL) { + log_err("IPCP %d already exists.", info->pid); + goto fail_ipcp; + } + + ipcp = reg_ipcp_create(info); + if (ipcp == NULL) { + log_err("Failed to create ipcp %s.", info->name); + goto fail_ipcp; + } + + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + log_err("Failed to create spawn entry.\n"); + goto fail_spawn; + } + + entry->pid = info->pid; + + list_add(&ipcp->next, __reg_after_ipcp(info->pid)); + list_add(&entry->next, __reg_after_spawned(info->pid)); + + reg.n_ipcps++; + reg.n_spawned++; + + pthread_mutex_unlock(®.mtx); + + return 0; + + fail_spawn: + reg_ipcp_destroy(ipcp); + fail_ipcp: + pthread_mutex_unlock(®.mtx); + return -1; + +} + +int reg_update_ipcp(struct ipcp_info * info) +{ + struct reg_ipcp * ipcp; + + pthread_mutex_lock(®.mtx); + + ipcp = __reg_get_ipcp(info->pid); + if (ipcp == NULL) { + log_err("IPCP %d does not exist.", info->pid); + goto no_ipcp; + + } + + reg_ipcp_update(ipcp, info); + + pthread_mutex_unlock(®.mtx); + + reg_ipcp_destroy(ipcp); + + return 0; + + no_ipcp: + pthread_mutex_unlock(®.mtx); + return -1; +} + +bool reg_has_ipcp(pid_t pid) +{ + bool ret; + + pthread_mutex_lock(®.mtx); + + ret = __reg_get_ipcp(pid) != NULL; + + pthread_mutex_unlock(®.mtx); + + return ret; +} + +static int __get_ipcp_info(ipcp_list_msg_t ** msg, + struct reg_ipcp * ipcp) +{ + *msg = malloc(sizeof(**msg)); + if (*msg == NULL) + goto fail; + + ipcp_list_msg__init(*msg); + + (*msg)->name = strdup(ipcp->info.name); + if ((*msg)->name == NULL) + goto fail_name; + + (*msg)->layer = strdup(ipcp->layer.name); + if ((*msg)->layer == NULL) + goto fail_layer; + + (*msg)->pid = ipcp->info.pid; + (*msg)->type = ipcp->info.type; + (*msg)->hash_algo = ipcp->layer.dir_hash_algo; + + return 0; + + fail_layer: + free((*msg)->name); + fail_name: + free(*msg); + *msg = NULL; + fail: + return -1; +} + +int reg_list_ipcps(ipcp_list_msg_t *** ipcps) +{ + struct list_head * p; + int i = 0; + + pthread_mutex_lock(®.mtx); + + if (reg.n_ipcps == 0) { + *ipcps = NULL; + goto finish; + } + + *ipcps = malloc(reg.n_ipcps * sizeof(**ipcps)); + if (*ipcps == NULL) { + log_err("Failed to malloc ipcps."); + goto fail_malloc; + } + + list_for_each(p, ®.ipcps) { + struct reg_ipcp * entry; + entry = list_entry(p, struct reg_ipcp, next); + if (__get_ipcp_info(&((*ipcps)[i]), entry) < 0) { + log_err("Failed to create ipcp list info."); + goto fail; + } + + ++i; + } + + assert(i == (int) reg.n_ipcps); + finish: + pthread_mutex_unlock(®.mtx); + + return i; + + fail: + while (i > 0) + ipcp_list_msg__free_unpacked((*ipcps)[--i], NULL); + + free(*ipcps); + fail_malloc: + pthread_mutex_unlock(®.mtx); + *ipcps = NULL; + return -ENOMEM; +} + +int reg_create_name(const struct name_info * info) +{ + struct reg_name * n; + + assert(info != NULL); + + pthread_mutex_lock(®.mtx); + + if (__reg_get_name(info->name) != NULL) { + log_dbg("Name %s already exists.", info->name); + goto exists; + } + + n = reg_name_create(info); + if (n == NULL) { + log_err("Failed to create name %s.", info->name); + goto fail_name; + } + + list_add(&n->next, __reg_after_name(info->name)); + + reg.n_names++; + + pthread_mutex_unlock(®.mtx); + return 0; + exists: + pthread_mutex_unlock(®.mtx); + return -EEXIST; + + fail_name: + pthread_mutex_unlock(®.mtx); + return -1; + +} + +int reg_destroy_name(const char * name) +{ + struct reg_name * n; + + pthread_mutex_lock(®.mtx); + + n = __reg_get_name(name); + if (n == NULL) { + log_err("Name %s does not exist.", name); + goto no_name; + } + + __reg_del_name_from_procs(name); + __reg_del_name_from_progs(name); + + list_del(&n->next); + + reg.n_names--; + + pthread_mutex_unlock(®.mtx); + + reg_name_destroy(n); + + return 0; + + no_name: + pthread_mutex_unlock(®.mtx); + return -1; +} + +bool reg_has_name(const char * name) +{ + bool ret; + + pthread_mutex_lock(®.mtx); + + ret = __reg_get_name(name) != NULL; + + pthread_mutex_unlock(®.mtx); + + return ret; +} + +static int __get_name_info(name_info_msg_t ** msg, + struct reg_name * n) +{ + *msg = malloc(sizeof(**msg)); + if (*msg == NULL) + goto fail; + + name_info_msg__init(*msg); + + (*msg)->name = strdup(n->info.name); + if ((*msg)->name == NULL) + goto fail_name; + + (*msg)->pol_lb = n->info.pol_lb; + + return 0; + + fail_name: + free(*msg); + *msg = NULL; + fail: + return -1; +} + +int reg_list_names(name_info_msg_t *** names) +{ + struct list_head * p; + int i = 0; + + pthread_mutex_lock(®.mtx); + + if (reg.n_names == 0) + goto finish; + + *names = malloc(reg.n_names * sizeof(**names)); + if (*names == NULL) { + log_err("Failed to malloc names."); + goto fail_malloc; + } + + list_for_each(p, ®.names) { + struct reg_name * entry; + entry = list_entry(p, struct reg_name, next); + if (__get_name_info(&((*names)[i]), entry) < 0) { + log_err("Failed to create name list info."); + goto fail; + } + + ++i; + } + + assert(i == (int) reg.n_names); + finish: + pthread_mutex_unlock(®.mtx); + + return i; + + fail: + while (i > 0) + name_info_msg__free_unpacked((*names)[--i], NULL); + + free(*names); + fail_malloc: + pthread_mutex_unlock(®.mtx); + *names = NULL; + return -ENOMEM; +} + +int reg_create_proc(const struct proc_info * info) +{ + struct reg_proc * proc; + + assert(info != NULL); + + pthread_mutex_lock(®.mtx); + + if (__reg_get_proc(info->pid) != NULL) { + log_err("Process %d already exists.", info->pid); + goto fail_proc; + } + + proc = reg_proc_create(info); + if (proc == NULL) { + log_err("Failed to create process %d.", info->pid); + goto fail_proc; + } + + __reg_proc_update_names(proc); + + list_add(&proc->next, __reg_after_proc(info->pid)); + + reg.n_procs++; + + pthread_cond_broadcast(®.cond); + + pthread_mutex_unlock(®.mtx); + + return 0; + + fail_proc: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_destroy_proc(pid_t pid) +{ + struct reg_proc * proc; + struct pid_entry * spawn; + struct reg_ipcp * ipcp; + + pthread_mutex_lock(®.mtx); + + proc = __reg_get_proc(pid); + if (proc != NULL) { + list_del(&proc->next); + reg.n_procs--; + reg_proc_destroy(proc); + __reg_del_proc_from_names(pid); + __reg_cancel_flows_for_proc(pid); + } + + spawn = __reg_get_spawned(pid); + if (spawn != NULL) { + list_del(&spawn->next); + reg.n_spawned--; + free(spawn); + } + + ipcp = __reg_get_ipcp(pid); + if (ipcp != NULL) { + list_del(&ipcp->next); + reg.n_ipcps--; + reg_ipcp_destroy(ipcp); + } + + pthread_mutex_unlock(®.mtx); + + return 0; +} + +bool reg_has_proc(pid_t pid) +{ + bool ret; + + pthread_mutex_lock(®.mtx); + + ret = __reg_get_proc(pid) != NULL; + + pthread_mutex_unlock(®.mtx); + + return ret; +} + +void reg_kill_all_proc(int signal) +{ + pthread_mutex_lock(®.mtx); + + __reg_kill_all_proc(signal); + + pthread_mutex_unlock(®.mtx); +} + +pid_t reg_get_dead_proc(void) +{ + pid_t ret; + + pthread_mutex_lock(®.mtx); + + ret = __reg_get_dead_proc(); + + pthread_mutex_unlock(®.mtx); + + return ret; +} + +int reg_create_spawned(pid_t pid) +{ + struct pid_entry * entry; + + pthread_mutex_lock(®.mtx); + + if (__reg_get_spawned(pid) != NULL) { + log_err("Spawned process %d already exists.", pid); + goto fail_proc; + } + + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + log_err("Failed to create pid_entry %d.", pid); + goto fail_proc; + } + + entry->pid = pid; + + list_add(&entry->next, __reg_after_spawned(pid)); + + reg.n_spawned++; + + pthread_mutex_unlock(®.mtx); + + return 0; + fail_proc: + pthread_mutex_unlock(®.mtx); + return -1; +} + +bool reg_has_spawned(pid_t pid) +{ + bool ret; + + pthread_mutex_lock(®.mtx); + + ret = __reg_get_spawned(pid) != NULL; + + pthread_mutex_unlock(®.mtx); + + return ret; +} + +void reg_kill_all_spawned(int signal) +{ + pthread_mutex_lock(®.mtx); + + __reg_kill_all_spawned(signal); + + pthread_mutex_unlock(®.mtx); +} + +pid_t reg_first_spawned(void) +{ + pid_t pid; + + pthread_mutex_lock(®.mtx); + + pid = __reg_first_spawned(); + + pthread_mutex_unlock(®.mtx); + + return pid; +} + +int reg_bind_proc(const char * name, + pid_t pid) +{ + struct reg_name * n; + struct reg_proc * p; + + assert(name != NULL); + assert(pid > 0); + + pthread_mutex_lock(®.mtx); + + n = __reg_get_name(name); + if (n == NULL) { + log_err("Could not find name %s.", name); + goto fail; + } + + p = __reg_get_proc(pid); + if (p == NULL) { + log_err("Could not find process %d.", pid); + goto fail; + } + + if (reg_name_has_proc(n, pid)) { + log_err("Process %d already bound to name %s.", pid, name); + goto fail; + } + + if (reg_proc_has_name(p, name)) { + log_err("Name %s already bound to process %d.", name, pid); + } + + if (reg_name_add_proc(n, pid) < 0) { + log_err("Failed to add process %d to name %s.", pid, name); + goto fail; + } + + if (reg_proc_add_name(p, name) < 0) { + log_err("Failed to add name %s to process %d.", name, pid); + goto fail_proc; + } + + if (__reg_get_accept_flow(pid) != NULL) { + if (reg_name_add_active(n, pid) < 0) { + log_warn("Failed to update name %s with active %d", + name, pid); + } + } + + pthread_mutex_unlock(®.mtx); + + return 0; + + fail_proc: + reg_name_del_proc(n, pid); + fail: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_unbind_proc(const char * name, + pid_t pid) +{ + struct reg_name * n; + struct reg_proc * p; + + assert(name != NULL); + assert(pid > 0); + + pthread_mutex_lock(®.mtx); + + n = __reg_get_name(name); + if (n == NULL) { + log_err("Could not find name %s.", name); + goto fail; + } + + p = __reg_get_proc(pid); + if (p == NULL) { + log_err("Could not find process %d.", pid); + goto fail; + } + + if (!reg_name_has_proc(n, pid)) { + log_err("Process %d not bound to name %s.", pid, name); + goto fail; + } + + if (!reg_proc_has_name(p, name)) { + log_err("Name %s not bound to process %d.", name, pid); + goto fail; + } + + reg_name_del_proc(n, pid); + + reg_proc_del_name(p, name); + + pthread_mutex_unlock(®.mtx); + + return 0; + + fail: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_create_prog(const struct prog_info * info) +{ + struct reg_prog * prog; + + assert(info != NULL); + + pthread_mutex_lock(®.mtx); + + if (__reg_get_prog(info->name) != NULL) { + log_dbg("Program %s already exists.", info->name); + goto exists; + } + + prog = reg_prog_create(info); + if (prog == NULL) { + log_err("Failed to create program %s.", info->name); + goto fail_prog; + } + + list_add(&prog->next, __reg_after_prog(info->name)); + + reg.n_progs++; + exists: + pthread_mutex_unlock(®.mtx); + + return 0; + + fail_prog: + pthread_mutex_unlock(®.mtx); + return -1; + +} + +int reg_destroy_prog(const char * name) +{ + struct reg_prog * prog; + + pthread_mutex_lock(®.mtx); + + prog = __reg_get_prog(name); + if (prog == NULL) { + log_err("Program %s does not exist.", name); + goto no_prog; + } + + log_err("Removing %s from names.", prog->info.path); + + __reg_del_prog_from_names(prog->info.path); + + list_del(&prog->next); + + reg.n_progs--; + + pthread_mutex_unlock(®.mtx); + + reg_prog_destroy(prog); + + return 0; + + no_prog: + pthread_mutex_unlock(®.mtx); + return -1; +} + +bool reg_has_prog(const char * name) +{ + bool ret; + + assert(name != NULL); + + pthread_mutex_lock(®.mtx); + + ret = __reg_get_prog(name) != NULL; + + pthread_mutex_unlock(®.mtx); + + return ret; +} + +int reg_get_exec(enum hash_algo algo, + const uint8_t * hash, + char *** prog) +{ + char ** exec; + int ret = 0; + + assert(hash != NULL); + assert(prog != NULL); + + pthread_mutex_lock(®.mtx); + + exec = __reg_get_exec(algo, hash); + if (exec == NULL) { + ret = -EPERM; + goto finish; + } + + *prog = argvdup(exec); + if (*prog == NULL) { + log_err("Failed to argvdup exec."); + ret = -ENOMEM; + goto finish; + } + + pthread_mutex_unlock(®.mtx); + + return 0; + + finish: + pthread_mutex_unlock(®.mtx); + return ret; +} + +int reg_bind_prog(const char * name, + char ** exec, + uint8_t flags) +{ + struct reg_name * n; + struct reg_prog * p; + + assert(name != NULL); + assert(exec != NULL); + assert(exec[0] != NULL); + + pthread_mutex_lock(®.mtx); + + n = __reg_get_name(name); + if (n == NULL) { + log_err("Could not find name %s.", name); + goto fail; + } + + p = __reg_get_prog(path_strip(exec[0])); + if (p == NULL) { + log_err("Could not find program %s.", exec[0]); + goto fail; + } + + if (reg_name_has_prog(n, exec[0])) { + log_err("Program %s already bound to %s.", exec[0], name); + goto fail; + } + + if (reg_prog_has_name(p, name)) { + log_err("Name %s already bound to program %s.", name, exec[0]); + goto fail; + } + + + if (flags & BIND_AUTO && reg_name_add_prog(n, exec) < 0) { + log_err("Failed to set autostart %s for %s.", exec[0], name); + goto fail; + } + + if (reg_prog_add_name(p, name) < 0) { + log_err("Failed to add %s to program %s.", name, exec[0]); + goto fail_prog; + } + + pthread_mutex_unlock(®.mtx); + + return 0; + + fail_prog: + reg_name_del_prog(n, exec[0]); + fail: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_unbind_prog(const char * name, + const char * prog) +{ + struct reg_name * n; + struct reg_prog * p; + + assert(name != NULL); + assert(prog != NULL); + + pthread_mutex_lock(®.mtx); + + n = __reg_get_name(name); + if (n == NULL) { + log_err("Could not find name %s.", name); + goto fail; + } + + p = __reg_get_prog(prog); + if (p == NULL) { + log_err("Could not find program %s.", prog); + goto fail; + } + + if (!reg_prog_has_name(p, name)) { + log_err("Name %s not bound to program %s.", name, prog); + goto fail; + } + + reg_name_del_prog(n, prog); + + reg_prog_del_name(p, name); + + pthread_mutex_unlock(®.mtx); + + return 0; + + fail: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_set_layer_for_ipcp(struct ipcp_info * info, + const struct layer_info * layer) +{ + struct reg_ipcp * ipcp; + + assert(info != NULL); + assert(info->state > IPCP_BOOT); + assert(info->state < IPCP_SHUTDOWN); + + pthread_mutex_lock(®.mtx); + + ipcp = __reg_get_ipcp(info->pid); + if (ipcp == NULL) { + log_err("IPCP %d not found.", info->pid); + goto fail_ipcp; + } + + reg_ipcp_set_layer(ipcp, layer); + + ipcp->info.state = info->state; + + pthread_mutex_unlock(®.mtx); + + return 0; + fail_ipcp: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_get_ipcp(struct ipcp_info * info, + struct layer_info * layer) +{ + struct reg_ipcp * ipcp; + + assert(info != NULL); + + pthread_mutex_lock(®.mtx); + + ipcp = __reg_get_ipcp(info->pid); + if (ipcp == NULL) { + log_err("IPCP %d not found.", info->pid); + goto fail_ipcp; + } + + *info = ipcp->info; + if (layer != NULL) + *layer = ipcp->layer; + + pthread_mutex_unlock(®.mtx); + + return 0; + fail_ipcp: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_get_ipcp_by_layer(struct ipcp_info * info, + struct layer_info * layer) +{ + struct reg_ipcp * ipcp; + + assert(info != NULL); + assert(layer != NULL); + + pthread_mutex_lock(®.mtx); + + ipcp = __reg_get_ipcp_by_layer(layer->name); + if (ipcp == NULL) { + log_err("No IPCP for %s not found.", layer->name); + goto fail_ipcp; + } + + *info = ipcp->info; + *layer = ipcp->layer; + + pthread_mutex_unlock(®.mtx); + + return 0; + fail_ipcp: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_prepare_flow_alloc(struct flow_info * info) +{ + struct reg_flow * flow; + int ret; + + assert(info != NULL); + + pthread_mutex_lock(®.mtx); + + flow = __reg_get_flow(info->id); + + assert(flow != NULL); + assert(flow->info.state == FLOW_INIT); + + info->state = FLOW_ALLOC_PENDING; + + ret = reg_flow_update(flow, info); + + pthread_mutex_unlock(®.mtx); + + return ret; +} + +int reg_wait_flow_allocated(struct flow_info * info, + buffer_t * pbuf, + const struct timespec * abstime) +{ + struct reg_flow * flow; + int ret = -1; + bool stop = false; + + assert(info != NULL); + assert(info->id >= ID_OFFT); + + pthread_mutex_lock(®.mtx); + + flow = __reg_get_flow(info->id); + + assert(flow != NULL); + assert(info->id == flow->info.id); + assert(info->n_pid == flow->info.n_pid); + + assert(info->state == FLOW_ALLOC_PENDING); + + pthread_cleanup_push(__cleanup_mutex_unlock, ®.mtx); + + while (!stop) { + switch(flow->info.state) { + case FLOW_ALLOC_PENDING: + ret = -__timedwait(®.cond, ®.mtx, abstime); + break; + case FLOW_ALLOCATED: + ret = 0; + stop = true; + break; + case FLOW_DEALLOCATED: + ret = -1; + stop = true; + break; + default: + assert(false); + } + + flow = __reg_get_flow(flow->info.id); + if (flow == NULL) { + info->state = FLOW_DEALLOCATED; + ret = -1; + break; + } + + if (ret == -ETIMEDOUT) { + info->state = FLOW_DEALLOCATED; + reg_flow_update(flow, info); + break; + } + } + + if (flow != NULL) { + reg_flow_get_data(flow, pbuf); + *info = flow->info; + } + + pthread_cleanup_pop(true); /* __cleanup_mutex_unlock */ + + return ret; +} + +int reg_respond_alloc(struct flow_info * info, + buffer_t * pbuf) +{ + struct reg_flow * flow; + + assert(info != NULL); + assert(info->state == FLOW_ALLOCATED || + info->state == FLOW_DEALLOCATED); + assert(pbuf != NULL); + assert(!(info->state == FLOW_DEALLOCATED && pbuf->data != NULL)); + + pthread_mutex_lock(®.mtx); + + flow = __reg_get_flow(info->id); + if (flow == NULL) { + log_warn("Flow %d already destroyed.", info->id); + goto fail_flow; + } + + if (flow->info.state == FLOW_DEALLOCATED) { + log_warn("Flow %d already deallocated.", info->id); + goto fail_flow; + } + + assert(flow->info.state == FLOW_ALLOC_PENDING); + assert(flow->data.len == 0); + assert(flow->data.data == NULL); + + info->n_pid = flow->info.n_pid; + info->n_1_pid = flow->info.n_pid; + + if (reg_flow_update(flow, info) < 0) { + log_err("Failed to create flow structs."); + goto fail_flow; + }; + + if (info->state == FLOW_ALLOCATED) + reg_flow_set_data(flow, pbuf); + + pthread_cond_broadcast(®.cond); + + pthread_mutex_unlock(®.mtx); + + return 0; + + fail_flow: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_prepare_flow_accept(struct flow_info * info, + buffer_t * pbuf) +{ + struct reg_flow * flow; + int ret; + + assert(info != NULL); + + pthread_mutex_lock(®.mtx); + + flow = __reg_get_flow(info->id); + + assert(flow != NULL); + assert(info->n_pid != 0); + + info->state = FLOW_ACCEPT_PENDING; + + ret = reg_flow_update(flow, info); + + reg_flow_set_data(flow, pbuf); + + pthread_mutex_unlock(®.mtx); + + return ret; +} + +void __cleanup_wait_accept(void * o) +{ + struct reg_flow * flow; + + flow = (struct reg_flow *) o; + + __reg_del_active_proc(flow->info.n_pid); +} + +int reg_wait_flow_accepted(struct flow_info * info, + buffer_t * pbuf, + const struct timespec * abstime) +{ + struct reg_flow * flow; + int ret = -1; + bool stop = false; + + assert(info != NULL); + assert(info->id >= ID_OFFT); + + pthread_mutex_lock(®.mtx); + + flow = __reg_get_flow(info->id); + + assert(flow != NULL); + assert(info->id == flow->info.id); + assert(info->n_pid == flow->info.n_pid); + assert(info->state == flow->info.state); + assert(flow->info.state == FLOW_ACCEPT_PENDING); + + if (__reg_add_active_proc(info->n_pid) < 0) { + log_err("Failed to mark pid %d active.", info->n_pid); + goto fail; + } + + pthread_cond_broadcast(®.cond); + + pthread_cleanup_push(__cleanup_mutex_unlock, ®.mtx); + pthread_cleanup_push(__cleanup_wait_accept, flow); + + while (!stop) { + switch(flow->info.state) { + case FLOW_ACCEPT_PENDING: + ret = -__timedwait(®.cond, ®.mtx, abstime); + break; + case FLOW_ALLOCATED: + ret = 0; + stop = true; + break; + case FLOW_DEALLOCATED: + ret = -1; + stop = true; + break; + default: + assert(false); + } + + flow = __reg_get_flow(flow->info.id); + if (flow == NULL) { + info->state = FLOW_DEALLOCATED; + ret = -1; + break; + } + + if (ret == -ETIMEDOUT) { + info->state = FLOW_DEALLOCATED; + reg_flow_update(flow, info); + break; + } + } + + pthread_cleanup_pop(true); /* __cleanup_wait_accept */ + + if (flow != NULL) { + reg_flow_get_data(flow, pbuf); + *info = flow->info; + } + + pthread_cleanup_pop(true); /* __cleanup_mutex_unlock */ + + return ret; + fail: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_wait_flow_accepting(enum hash_algo algo, + const uint8_t * hash, + const struct timespec * abstime) +{ + int ret; + + assert(hash != NULL); + assert(abstime != NULL); + + pthread_mutex_lock(®.mtx); + + pthread_cleanup_push(__cleanup_mutex_unlock, ®.mtx); + + while (true) { + ret = __reg_get_pending_flow_id_for_hash(algo, hash); + if (ret != -EAGAIN) + break; + + ret = -__timedwait(®.cond, ®.mtx, abstime); + if (ret == -ETIMEDOUT) + break; + } + + pthread_cleanup_pop(true); + + return ret; +} + +int reg_respond_accept(struct flow_info * info, + buffer_t * pbuf) +{ + struct reg_flow * flow; + buffer_t temp; + + assert(info != NULL); + assert(info->state == FLOW_ALLOCATED); + assert(pbuf != NULL); + + pthread_mutex_lock(®.mtx); + + flow = __reg_get_flow(info->id); + if (flow == NULL) { + log_err("Flow not found for request: %d", info->id); + goto fail_flow; + } + + assert(flow->info.state == FLOW_ACCEPT_PENDING); + + info->n_pid = flow->info.n_pid; + + if (info->qs.cypher_s > 0) { + reg_flow_get_data(flow, &temp); + reg_flow_set_data(flow, pbuf); + *pbuf = temp; + } + + if (reg_flow_update(flow, info) < 0) { + log_err("Failed to create flow structs."); + goto fail_flow; + } + + pthread_cond_broadcast(®.cond); + + pthread_mutex_unlock(®.mtx); + + return 0; + + fail_flow: + pthread_mutex_unlock(®.mtx); + return -1; +} + +void reg_dealloc_flow(struct flow_info * info) +{ + struct reg_flow * flow; + + assert(info != NULL); + assert(info->id != 0); + assert(info->n_pid != 0); + + pthread_mutex_lock(®.mtx); + + flow = __reg_get_flow(info->id); + + assert(flow != NULL); + assert(flow->data.data == NULL); + assert(flow->data.len == 0); + + assert(flow->info.state == FLOW_ALLOCATED); + flow->info.state = FLOW_DEALLOC_PENDING; + info->state = FLOW_DEALLOC_PENDING; + info->n_1_pid = flow->info.n_1_pid; + + reg_flow_update(flow, info); + + pthread_mutex_unlock(®.mtx); +} + +void reg_dealloc_flow_resp(struct flow_info * info) +{ + struct reg_flow * flow; + + assert(info != NULL); + assert(info->id != 0); + assert(info->n_1_pid != 0); + + pthread_mutex_lock(®.mtx); + + flow = __reg_get_flow(info->id); + + assert(flow != NULL); + assert(flow->data.data == NULL); + assert(flow->data.len == 0); + + assert(flow->info.state == FLOW_DEALLOC_PENDING); + flow->info.state = FLOW_DEALLOCATED; + info->state = FLOW_DEALLOCATED; + + reg_flow_update(flow, info); + + pthread_mutex_unlock(®.mtx); +} + +int reg_wait_proc(pid_t pid, + const struct timespec * abstime) +{ + struct reg_proc * proc = NULL; + int ret; + + assert(pid > 0); + assert(abstime != NULL); + + pthread_mutex_lock(®.mtx); + + pthread_cleanup_push(__cleanup_mutex_unlock, ®.mtx); + + while (true) { + proc = __reg_get_proc(pid); + if (proc != NULL) { + ret = 0; + break; + } + + ret = -__timedwait(®.cond, ®.mtx, abstime); + if (ret == -ETIMEDOUT) + break; + } + + pthread_cleanup_pop(true); + + return ret; +} + +int reg_wait_ipcp_boot(struct ipcp_info * info, + const struct timespec * abstime) +{ + struct reg_ipcp * ipcp; + int ret; + bool stop = false; + + assert(info->state == IPCP_BOOT); + + pthread_mutex_lock(®.mtx); + + ipcp = __reg_get_ipcp(info->pid); + + if (ipcp->info.state == IPCP_INIT) + reg_ipcp_update(ipcp, info); + + pthread_cleanup_push(__cleanup_mutex_unlock, ®.mtx); + + while (!stop) { + if (ipcp == NULL) + break; + + switch(ipcp->info.state) { + case IPCP_NULL: + ret = -1; + stop = true; + break; + case IPCP_OPERATIONAL: + ret = 0; + stop = true; + break; + case IPCP_BOOT: + ret = -__timedwait(®.cond, ®.mtx, abstime); + break; + default: + assert(false); + continue; /* Shut up static analyzer. */ + } + + ipcp = __reg_get_ipcp(info->pid); + + if (ret == -ETIMEDOUT) + break; + } + + if (ipcp != NULL) + *info = ipcp->info; + + pthread_cleanup_pop(true); + + return ipcp == NULL? -EIPCP : ret; +} + +int reg_respond_ipcp(const struct ipcp_info * info) +{ + struct reg_ipcp * ipcp; + + assert(info != NULL); + + pthread_mutex_lock(®.mtx); + + ipcp = __reg_get_ipcp(info->pid); + if (ipcp == NULL) { + log_err("IPCP %d not found for response.", info->pid); + goto fail_ipcp; + } + + assert(strcmp(info->name, ipcp->info.name) == 0); + assert(info->type == ipcp->info.type); + + reg_ipcp_update(ipcp, info); + + pthread_cond_broadcast(®.cond); + + pthread_mutex_unlock(®.mtx); + + return 0; + + fail_ipcp: + pthread_mutex_unlock(®.mtx); + return -EIPCP; +} diff --git a/src/irmd/reg/reg.h b/src/irmd/reg/reg.h new file mode 100644 index 00000000..17dfcc32 --- /dev/null +++ b/src/irmd/reg/reg.h @@ -0,0 +1,148 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#ifndef OUROBOROS_IRMD_REG_H +#define OUROBOROS_IRMD_REG_H + +#include <ouroboros/flow.h> +#include <ouroboros/ipcp.h> +#include <ouroboros/name.h> +#include <ouroboros/proc.h> +#include <ouroboros/protobuf.h> +#include <ouroboros/time.h> +#include <ouroboros/utils.h> + +int reg_init(void); + +void reg_clear(void); + +void reg_fini(void); + +int reg_create_flow(struct flow_info * info); + +int reg_destroy_flow(int flow_id); + +bool reg_has_flow(int flow_id); + +int reg_create_proc(const struct proc_info * info); + +/* Use this for all processes, including ipcps */ +int reg_destroy_proc(pid_t pid); + +bool reg_has_proc(pid_t pid); + +void reg_kill_all_proc(int signal); + +pid_t reg_get_dead_proc(void); + +int reg_create_spawned(pid_t pid); + +bool reg_has_spawned(pid_t pid); + +void reg_kill_all_spawned(int signal); + +int reg_first_spawned(void); + +int reg_bind_proc(const char * name, + pid_t proc); + +int reg_unbind_proc(const char * name, + pid_t proc); + +int reg_create_ipcp(const struct ipcp_info * info); + +bool reg_has_ipcp(pid_t pid); + +int reg_set_layer_for_ipcp(struct ipcp_info * info, + const struct layer_info * layer); + +int reg_get_ipcp(struct ipcp_info * info, + struct layer_info * layer); + +int reg_get_ipcp_by_layer(struct ipcp_info * info, + struct layer_info * layer); + +/* TODO don't rely on protobuf here */ +int reg_list_ipcps(ipcp_list_msg_t *** msg); + +int reg_create_name(const struct name_info * info); + +int reg_destroy_name(const char * name); + +bool reg_has_name(const char * name); + +/* TODO don't rely on protobuf here */ +int reg_list_names(name_info_msg_t *** names); + +int reg_create_prog(const struct prog_info * info); + +int reg_destroy_prog(const char * name); + +bool reg_has_prog(const char * name); + +int reg_get_exec(enum hash_algo algo, + const uint8_t * hash, + char *** exec); + +int reg_bind_prog(const char * name, + char ** exec, + uint8_t flags); + +int reg_unbind_prog(const char * name, + const char * prog); + +int reg_prepare_flow_alloc(struct flow_info * info); + +int reg_wait_flow_allocated(struct flow_info * info, + buffer_t * pbuf, + const struct timespec * abstime); + +int reg_respond_alloc(struct flow_info * info, + buffer_t * pbuf); + +int reg_prepare_flow_accept(struct flow_info * info, + buffer_t * pbuf); + +int reg_wait_flow_accepted(struct flow_info * info, + buffer_t * pbuf, + const struct timespec * abstime); + +int reg_wait_flow_accepting(enum hash_algo algo, + const uint8_t * hash, + const struct timespec * abstime); + +int reg_respond_accept(struct flow_info * info, + buffer_t * pbuf); + +void reg_dealloc_flow(struct flow_info * info); + +void reg_dealloc_flow_resp(struct flow_info * info); + +int reg_wait_proc(pid_t pid, + const struct timespec * abstime); + +int reg_wait_ipcp_boot(struct ipcp_info * ipcp, + const struct timespec * abstime); + +int reg_respond_ipcp(const struct ipcp_info * info); + +#endif /* OUROBOROS_IRMD_REG_H */ diff --git a/src/irmd/reg/tests/CMakeLists.txt b/src/irmd/reg/tests/CMakeLists.txt new file mode 100644 index 00000000..7bc98571 --- /dev/null +++ b/src/irmd/reg/tests/CMakeLists.txt @@ -0,0 +1,33 @@ +get_filename_component(tmp ".." ABSOLUTE) +get_filename_component(src_folder "${tmp}" NAME) + +create_test_sourcelist(${src_folder}_tests test_suite.c + # Add new tests here + flow_test.c + ipcp_test.c + name_test.c + proc_test.c + prog_test.c + reg_test.c +) + +add_executable(${src_folder}_test EXCLUDE_FROM_ALL ${${src_folder}_tests}) +target_link_libraries(${src_folder}_test ouroboros-common) + +if (CMAKE_BUILD_TYPE MATCHES "Debug*") + add_compile_flags(${src_folder}_test -DCONFIG_OUROBOROS_DEBUG) +endif () + +add_dependencies(check ${src_folder}_test) + +set(tests_to_run ${${src_folder}_tests}) +if(CMAKE_VERSION VERSION_LESS "3.29.0") + remove(tests_to_run test_suite.c) +else () + list(POP_FRONT tests_to_run) +endif() + +foreach(test ${tests_to_run}) + get_filename_component(test_name ${test} NAME_WE) + add_test(irmd/reg/${test_name} ${C_TEST_PATH}/${src_folder}_test ${test_name}) +endforeach(test) diff --git a/src/irmd/reg/tests/flow_test.c b/src/irmd/reg/tests/flow_test.c new file mode 100644 index 00000000..f9d23fd1 --- /dev/null +++ b/src/irmd/reg/tests/flow_test.c @@ -0,0 +1,294 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Flows - Unit Tests + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#include "../flow.c" + +#include <ouroboros/test.h> + +#include <string.h> + +#define TEST_DATA "testpiggybackdata" + +static int test_reg_flow_create(void) +{ + struct reg_flow * f; + + struct flow_info info = { + .id = 1, + .n_pid = 1, + .qs = qos_raw, + .state = FLOW_INIT + }; + + TEST_START(); + + f = reg_flow_create(&info); + if (f == NULL) { + printf("Failed to create flow.\n"); + goto fail; + } + + reg_flow_destroy(f); + + TEST_SUCCESS(); + + return 0; + fail: + TEST_FAIL(); + return -1; +} + +static int test_reg_flow_create_no_id(void) { + struct flow_info info = { + .id = 0, + .n_pid = 1, + .qs = qos_raw, + .state = FLOW_INIT + }; + + reg_flow_create(&info); /* assert fail */ + + return 0; +} + +static int test_reg_flow_create_no_pid(void) { + struct flow_info info = { + .id = 1, + .n_pid = 0, + .qs = qos_raw, + .state = FLOW_INIT + }; + + reg_flow_create(&info); /* assert fail */ + + return 0; +} + +static int test_reg_flow_create_has_n_1_pid(void) { + struct flow_info info = { + .id = 1, + .n_pid = 0, + .n_1_pid = 1, + .qs = qos_raw, + .state = FLOW_INIT + }; + + reg_flow_create(&info); /* assert fail */ + + return 0; +} + +static int test_reg_flow_create_wrong_state(void) { + struct flow_info info = { + .id = 1, + .n_pid = 0, + .n_1_pid = 1, + .qs = qos_raw, + .state = FLOW_ALLOC_PENDING + }; + + reg_flow_create(&info); /* assert fail */ + + return 0; +} + +static int test_reg_flow_create_has_mpl(void) { + struct flow_info info = { + .id = 1, + .n_pid = 1, + .n_1_pid = 0, + .mpl = 10, + .qs = qos_raw, + .state = FLOW_ALLOC_PENDING + }; + + reg_flow_create(&info); /* assert fail */ + + return 0; +} + +static int test_reg_flow_update(void) +{ + struct reg_flow * f; + + struct flow_info info = { + .id = 1, + .n_pid = 1, + .qs = qos_raw, + .state = FLOW_INIT + }; + + struct flow_info upd = { + .id = 1, + .n_pid = 1, + .qs = qos_data, + .state = FLOW_DEALLOCATED + }; + + TEST_START(); + + f = reg_flow_create(&info); + if (f == NULL) { + printf("Failed to create flow.\n"); + goto fail; + } + + reg_flow_update(f, &upd); + + if (memcmp(&f->info, &upd, sizeof(upd)) != 0) { + printf("Flow info not updated.\n"); + goto fail; + } + + reg_flow_destroy(f); + + TEST_SUCCESS(); + + return 0; + fail: + TEST_FAIL(); + return -1; +} + +static int test_reg_flow_update_wrong_id(void) +{ + struct reg_flow * f; + + struct flow_info info = { + .id = 1, + .n_pid = 1, + .qs = qos_raw, + .state = FLOW_INIT + }; + + struct flow_info upd = { + .id = 2, + .n_pid = 1, + .qs = qos_data, + .state = FLOW_DEALLOCATED + }; + + TEST_START(); + + f = reg_flow_create(&info); + if (f == NULL) { + printf("Failed to create flow.\n"); + goto fail; + } + + reg_flow_update(f, &upd); /* assert fail */ + + TEST_SUCCESS(); + + return 0; + fail: + TEST_FAIL(); + return -1; +} + +static int test_reg_flow_assert_fails(void) +{ + int ret = 0; + + ret |= test_assert_fail(test_reg_flow_create_no_id); + + ret |= test_assert_fail(test_reg_flow_create_no_pid); + + ret |= test_assert_fail(test_reg_flow_create_has_n_1_pid); + + ret |= test_assert_fail(test_reg_flow_create_wrong_state); + + ret |= test_assert_fail(test_reg_flow_create_has_mpl); + + ret |= test_assert_fail(test_reg_flow_update_wrong_id); + + return ret; +} + +static int test_flow_data(void) +{ + struct reg_flow * f; + + struct flow_info info = { + .id = 1, + .n_pid = 1, + .qs = qos_raw, + .state = FLOW_INIT + }; + + char * data; + buffer_t buf; + buffer_t rcv = {NULL, 0}; + + TEST_START(); + + data = strdup(TEST_DATA); + if (data == NULL) { + printf("Failed to strdup data.\n"); + goto fail; + } + + buf.data = (uint8_t *) data; + buf.len = strlen(data); + + f = reg_flow_create(&info); + if (f == NULL) { + printf("Failed to create flow.\n"); + goto fail; + } + + reg_flow_set_data(f, &buf); + + reg_flow_get_data(f, &rcv); + + freebuf(buf); + clrbuf(rcv); + + reg_flow_destroy(f); + + TEST_SUCCESS(); + + return 0; + fail: + free(data); + TEST_FAIL(); + return -1; +} + +int flow_test(int argc, + char ** argv) +{ + int ret = 0; + + (void) argc; + (void) argv; + + ret |= test_reg_flow_create(); + + ret |= test_reg_flow_update(); + + ret |= test_reg_flow_assert_fails(); + + ret |= test_flow_data(); + + return ret; +} diff --git a/src/irmd/reg/tests/ipcp_test.c b/src/irmd/reg/tests/ipcp_test.c new file mode 100644 index 00000000..fb8ba71b --- /dev/null +++ b/src/irmd/reg/tests/ipcp_test.c @@ -0,0 +1,89 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - IPCPs - Unit Tests + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#include <ouroboros/test.h> + +#include "../ipcp.c" + +#define TEST_PID 65535 + +static int test_reg_ipcp_create(void) +{ + struct reg_ipcp * ipcp; + struct ipcp_info info = { + .pid = TEST_PID, + .state = IPCP_BOOT + }; + struct layer_info layer = { + .name = "testlayer", + .dir_hash_algo = DIR_HASH_SHA3_224 + }; + + TEST_START(); + + ipcp = reg_ipcp_create(&info); + if (ipcp == NULL) { + printf("Failed to create ipcp.\n"); + goto fail; + } + + if (strcmp(ipcp->layer.name, "Not enrolled.") != 0) { + printf("Layer name was not set.\n"); + goto fail; + } + + ipcp->info.state = IPCP_OPERATIONAL; + + reg_ipcp_set_layer(ipcp, &layer); + + if (strcmp(ipcp->layer.name, layer.name) != 0) { + printf("Layer name was not set.\n"); + goto fail; + } + + if (ipcp->info.state != IPCP_OPERATIONAL) { + printf("IPCP state was not set.\n"); + goto fail; + } + + reg_ipcp_destroy(ipcp); + + TEST_SUCCESS(); + + return 0; + fail: + TEST_FAIL(); + return -1; +} + +int ipcp_test(int argc, + char ** argv) +{ + int res = 0; + + (void) argc; + (void) argv; + + res |= test_reg_ipcp_create(); + + return res; +} diff --git a/src/irmd/reg/tests/name_test.c b/src/irmd/reg/tests/name_test.c new file mode 100644 index 00000000..48f132e9 --- /dev/null +++ b/src/irmd/reg/tests/name_test.c @@ -0,0 +1,283 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Names - Unit Tests + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#include "../name.c" + +#define TEST_PID 65534 +#define TEST_PROG "/usr/bin/testprog" +#define TEST_NAME "testservicename" + +static int test_reg_name_create(void) +{ + struct reg_name * n; + struct name_info info = { + .name = TEST_NAME, + .pol_lb = LB_RR, + }; + + n = reg_name_create(&info); + if (n == NULL) { + printf("Failed to create name %s.\n", info.name); + goto fail; + } + + reg_name_destroy(n); + + return 0; + fail: + return -1; +} + +static int test_reg_name_add_proc(void) +{ + struct reg_name * n; + struct name_info info = { + .name = TEST_NAME, + .pol_lb = LB_RR, + }; + + n = reg_name_create(&info); + if (n == NULL) { + printf("Failed to create name %s.\n", info.name); + goto fail; + } + + if (reg_name_add_proc(n, TEST_PID) < 0) { + printf("Failed to add proc.\n"); + goto fail; + } + + if (n->n_procs != 1) { + printf("n_procs not updated.\n"); + goto fail; + } + + if (!reg_name_has_proc(n, TEST_PID)) { + printf("Proc not found.\n"); + goto fail; + } + + reg_name_del_proc(n, TEST_PID); + + if (n->n_procs != 0) { + printf("n_procs not updated.\n"); + goto fail; + } + + reg_name_destroy(n); + + return 0; + fail: + return -1; +} + +static int test_reg_name_add_prog(void) +{ + struct reg_name * n; + struct name_info info = { + .name = TEST_NAME, + .pol_lb = LB_RR, + }; + + char * exec[] = { TEST_PROG, "--argswitch", "argvalue", NULL}; + + n = reg_name_create(&info); + if (n == NULL) { + printf("Failed to create name %s.\n", info.name); + goto fail; + } + + if (reg_name_add_prog(n, exec) < 0) { + printf("Failed to add prog.\n"); + goto fail; + } + + if (n->n_progs != 1) { + printf("n_progs not updated.\n"); + goto fail; + } + + if (!reg_name_has_prog(n, TEST_PROG)) { + printf("Prog not found.\n"); + goto fail; + } + + reg_name_del_prog(n, TEST_PROG); + + if (n->n_progs != 0) { + printf("n_progs not updated.\n"); + goto fail; + } + + reg_name_destroy(n); + + return 0; + fail: + return -1; +} + +static int test_reg_name_add_active(enum pol_balance lb) +{ + struct reg_name * n; + pid_t pid; + struct name_info info = { + .name = TEST_NAME, + .pol_lb = lb, + }; + + n = reg_name_create(&info); + if (n == NULL) { + printf("Failed to create name %s.\n", info.name); + goto fail; + } + + if (reg_name_get_active(n) != -1) { + printf("Got active from empty actives.\n"); + goto fail; + } + + if (reg_name_add_proc(n, TEST_PID) < 0) { + printf("Failed to add proc 0.\n"); + goto fail; + } + + if (reg_name_add_proc(n, TEST_PID + 1) < 0) { + printf("Failed to add proc 1.\n"); + goto fail; + } + + if (reg_name_add_proc(n, TEST_PID + 2) < 0) { + printf("Failed to add proc 2.\n"); + goto fail; + } + + if (reg_name_add_active(n, TEST_PID) < 0) { + printf("Failed to add active.\n"); + goto fail; + } + + if (n->n_active != 1) { + printf("n_active not updated.\n"); + goto fail; + } + + if (reg_name_get_active(n) != TEST_PID) { + printf("Failed to get active.\n"); + goto fail; + } + + if (reg_name_get_active(n) != TEST_PID) { + printf("Failed to get active.\n"); + goto fail; + } + + if (reg_name_add_active(n, TEST_PID + 1) < 0) { + printf("Failed to add active 3.\n"); + goto fail; + } + + if (reg_name_add_active(n, TEST_PID + 1) < 0) { + printf("Failed to add active 3.\n"); + goto fail; + } + + + if (reg_name_add_active(n, TEST_PID + 2) < 0) { + printf("Failed to add active 4.\n"); + goto fail; + } + + if (n->n_procs != 3) { + printf("n_procs not updated.\n"); + goto fail; + } + + if (n->n_active != 4) { + printf("n_active not updated.\n"); + goto fail; + } + + pid = info.pol_lb == LB_RR ? TEST_PID : TEST_PID + 2; + + if (reg_name_get_active(n) != pid) { + printf("Got wrong active pid 1.\n"); + goto fail; + } + + reg_name_del_active(n, pid); + + if (reg_name_add_active(n, pid) < 0) { + printf("Failed to add active 4.\n"); + goto fail; + } + + pid = info.pol_lb == LB_RR ? TEST_PID + 1 : TEST_PID + 2; + + if (reg_name_get_active(n) != pid) { + printf("Got wrong active pid 2 %d.\n", pid); + goto fail; + } + + reg_name_del_proc(n, TEST_PID + 2); + + reg_name_del_proc(n, TEST_PID + 1); + + reg_name_del_proc(n, TEST_PID); + + if (n->n_procs != 0) { + printf("n_procs not updated.\n"); + goto fail; + } + + if (n->n_active != 0) { + printf("n_active not updated.\n"); + goto fail; + } + + reg_name_destroy(n); + + return 0; + fail: + return -1; +} + + +int name_test(int argc, + char ** argv) +{ + int res = 0; + + (void) argc; + (void) argv; + + res |= test_reg_name_create(); + + res |= test_reg_name_add_proc(); + + res |= test_reg_name_add_prog(); + + res |= test_reg_name_add_active(LB_RR); + + res |= test_reg_name_add_active(LB_SPILL); + + return res; +} diff --git a/src/irmd/reg/tests/proc_test.c b/src/irmd/reg/tests/proc_test.c new file mode 100644 index 00000000..5c9dd865 --- /dev/null +++ b/src/irmd/reg/tests/proc_test.c @@ -0,0 +1,107 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Processes - Unit Tests + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#include "../proc.c" + +#define TEST_PID 65534 +#define TEST_PROG "usr/bin/testprog" + +static int test_reg_proc_create(void) +{ + struct reg_proc * proc; + struct proc_info info = { + .pid = TEST_PID, + .prog = TEST_PROG + }; + + proc = reg_proc_create(&info); + if (proc == NULL) { + printf("Failed to create proc.\n"); + goto fail; + } + + reg_proc_destroy(proc); + + return 0; + fail: + return -1; +} + +static int test_reg_proc_add_name(void) +{ + struct reg_proc * proc; + struct proc_info info = { + .pid = TEST_PID, + .prog = TEST_PROG + }; + + char * name = "testname"; + + proc = reg_proc_create(&info); + if (proc == NULL) { + printf("Failed to create proc.\n"); + goto fail; + } + + if (reg_proc_add_name(proc, name) < 0) { + printf("Failed to add name."); + goto fail; + } + + if (proc->n_names != 1) { + printf("n_names not updated.\n"); + goto fail; + } + + if (!reg_proc_has_name(proc, name)) { + printf("Name not found.\n"); + goto fail; + } + + reg_proc_del_name(proc, name); + + if (proc->n_names != 0) { + printf("n_names not updated.\n"); + goto fail; + } + + reg_proc_destroy(proc); + + return 0; + fail: + return -1; +} + +int proc_test(int argc, + char ** argv) +{ + int res = 0; + + (void) argc; + (void) argv; + + res |= test_reg_proc_create(); + + res |= test_reg_proc_add_name(); + + return res; +} diff --git a/src/irmd/reg/tests/prog_test.c b/src/irmd/reg/tests/prog_test.c new file mode 100644 index 00000000..5e6931d8 --- /dev/null +++ b/src/irmd/reg/tests/prog_test.c @@ -0,0 +1,105 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Programs - Unit Tests + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#include "../prog.c" + +#define TEST_PROG "usr/bin/testprog" + + +static int test_reg_prog_create(void) +{ + struct reg_prog * prog; + struct prog_info info = { + .name = TEST_PROG + }; + + prog = reg_prog_create(&info); + if (prog == NULL) { + printf("Failed to create prog.\n"); + goto fail; + } + + reg_prog_destroy(prog); + + return 0; + fail: + return -1; +} + +static int test_reg_prog_add_name(void) +{ + struct reg_prog * prog; + struct prog_info info = { + .name = TEST_PROG + }; + + char * name = "testname"; + + prog = reg_prog_create(&info); + if (prog == NULL) { + printf("Failed to create prog.\n"); + goto fail; + } + + if (reg_prog_add_name(prog, name) < 0) { + printf("Failed to add name."); + goto fail; + } + + if (prog->n_names != 1) { + printf("n_names not updated.\n"); + goto fail; + } + + if (!reg_prog_has_name(prog, name)) { + printf("Name not found.\n"); + goto fail; + } + + reg_prog_del_name(prog, name); + + if (prog->n_names != 0) { + printf("n_names not updated.\n"); + goto fail; + } + + reg_prog_destroy(prog); + + return 0; + fail: + return -1; +} + +int prog_test(int argc, + char ** argv) +{ + int ret = 0; + + (void) argc; + (void) argv; + + ret |= test_reg_prog_create(); + + ret |= test_reg_prog_add_name(); + + return ret; +} diff --git a/src/irmd/reg/tests/reg_test.c b/src/irmd/reg/tests/reg_test.c new file mode 100644 index 00000000..c341c297 --- /dev/null +++ b/src/irmd/reg/tests/reg_test.c @@ -0,0 +1,1583 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Unit Tests + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + + +#include "../reg.c" + +#include <ouroboros/test.h> + +#define TEST_PID 3666 +#define TEST_N_1_PID 3999 +#define TEST_FAKE_ID 9128349 +#define TEST_MPL 5 +#define TEST_PROG "reg_test" /* own binary for binary check */ +#define TEST_IPCP "testipcp" +#define TEST_NAME "testname" +#define TEST_DATA "testpbufdata" +#define TEST_DATA2 "testpbufdata2" +#define TEST_LAYER "testlayer" +#define REG_TEST_FAIL() \ + do { TEST_FAIL(); memset(®, 0, sizeof(reg)); } while(0) + +static int test_reg_init(void) +{ + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_reg_create_flow(void) +{ + struct flow_info info = { + .n_pid = TEST_PID, + .qs = qos_raw, + }; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_flow(&info) < 0) { + printf("Failed to create flow.\n"); + goto fail; + } + + if (info.id == 0) { + printf("Failed to update id.'n"); + goto fail; + } + + if (reg.n_flows != 1) { + printf("n_flows was not updated.\n"); + goto fail; + } + + if (!reg_has_flow(info.id)) { + printf("Failed to find flow.\n"); + goto fail; + } + + if (reg_destroy_flow(info.id) < 0) { + printf("Failed to destroy flow.\n"); + goto fail; + } + + if (reg.n_flows != 0) { + printf("n_flows was not updated.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_reg_allocate_flow_timeout(void) +{ + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_MS(1); + buffer_t pbuf; + buffer_t rbuf = {NULL, 0}; + + struct flow_info info = { + .n_pid = TEST_PID, + .qs = qos_raw + }; + + TEST_START(); + + pbuf.data = (uint8_t *) strdup(TEST_DATA);; + if (pbuf.data == NULL) { + printf("Failed to strdup data.\n"); + goto fail; + } + + pbuf.len = strlen((char *) pbuf.data) + 1; + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + + ts_add(&abstime, &timeo, &abstime); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_flow(&info) < 0) { + printf("Failed to add flow.\n"); + goto fail; + } + + if (reg_prepare_flow_accept(&info, &pbuf) < 0) { + printf("Failed to prepare flow for accept.\n"); + goto fail; + } + + if (reg_wait_flow_accepted(&info, &rbuf, &abstime) != -ETIMEDOUT) { + printf("Wait allocated did not timeout.\n"); + goto fail; + } + + if (info.state != FLOW_DEALLOCATED) { + printf("Flow did not timeout in deallocated state.\n"); + goto fail; + } + + if (pbuf.data == NULL) { + printf("Flow data was updated on timeout."); + goto fail; + } + + freebuf(pbuf); + reg_destroy_flow(info.id); + + if (reg.n_flows != 0) { + printf("Flow did not destroy.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static void * test_flow_respond_alloc(void * o) +{ + struct flow_info * info = (struct flow_info *) o; + buffer_t pbuf = {NULL, 0}; + + if (info->state == FLOW_ALLOCATED) { + pbuf.data = (uint8_t *) strdup(TEST_DATA2); + if (pbuf.data == NULL) { + printf("Failed to strdup data2.\n"); + goto fail; + } + pbuf.len = strlen((char *) pbuf.data) + 1; + } + + reg_respond_alloc(info, &pbuf); + + return (void *) 0; + fail: + return (void *) -1; +} + +static void * test_flow_respond_accept(void * o) +{ + struct flow_info * info = (struct flow_info *) o; + buffer_t pbuf; + + pbuf.data = (uint8_t *) strdup(TEST_DATA2); + if (pbuf.data == NULL) { + printf("Failed to strdup data2.\n"); + goto fail; + } + pbuf.len = strlen((char *) pbuf.data) + 1; + + reg_respond_accept(info, &pbuf); + + if (info->qs.cypher_s == 0) { + freebuf(pbuf); + } else if (strcmp((char *) pbuf.data, TEST_DATA) != 0) { + printf("Data was not passed correctly.\n"); + goto fail; + } + + return (void *) 0; + fail: + return (void *) -1; +} + +static int test_reg_accept_flow_success(void) +{ + pthread_t thr; + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_S(1); + buffer_t pbuf = {(uint8_t *) TEST_DATA, strlen(TEST_DATA)}; + buffer_t rbuf = {NULL, 0}; + + struct flow_info info = { + .n_pid = TEST_PID, + .qs = qos_raw + }; + + struct flow_info n_1_info = { + .n_1_pid = TEST_N_1_PID, + .qs = qos_data_crypt, + .state = FLOW_ALLOCATED /* RESPONSE SUCCESS */ + }; + + TEST_START(); + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + + ts_add(&abstime, &timeo, &abstime); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_flow(&info) < 0) { + printf("Failed to add flow.\n"); + goto fail; + } + + if (reg_prepare_flow_accept(&info, &pbuf) < 0) { + printf("Failed to prepare flow for accept.\n"); + goto fail; + } + + n_1_info.id = info.id; + n_1_info.mpl = 1; + + pthread_create(&thr, NULL, test_flow_respond_accept, &n_1_info); + + if (reg_wait_flow_accepted(&info, &rbuf, &abstime) < 0 ) { + printf("Flow allocation failed.\n"); + goto fail; + } + + pthread_join(thr, NULL); + + if (info.state != FLOW_ALLOCATED) { + printf("Flow succeeded but not in allocated state.\n"); + goto fail; + } + + if (rbuf.data == NULL) { + printf("rbuf data not returned.\n"); + goto fail; + } + + if (strcmp((char *) rbuf.data, TEST_DATA2) != 0) { + printf("Data2 was not passed correctly.\n"); + goto fail; + } + + freebuf(rbuf); + + reg_dealloc_flow(&info); + + if (info.state != FLOW_DEALLOC_PENDING) { + printf("Flow dealloc requested but not in pending state.\n"); + goto fail; + } + + reg_dealloc_flow_resp(&info); + + if (info.state != FLOW_DEALLOCATED) { + printf("Flow deallocated but not in deallocated state.\n"); + goto fail; + } + + reg_destroy_flow(n_1_info.id); + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_reg_accept_flow_success_no_crypt(void) +{ + pthread_t thr; + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_S(1); + buffer_t pbuf = {(uint8_t *) TEST_DATA, strlen(TEST_DATA)}; + buffer_t rbuf = {NULL, 0}; + + struct flow_info info = { + .n_pid = TEST_PID, + .qs = qos_raw + }; + + struct flow_info n_1_info = { + .n_1_pid = TEST_N_1_PID, + .qs = qos_data, + .state = FLOW_ALLOCATED /* RESPONSE SUCCESS */ + }; + + TEST_START(); + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + + ts_add(&abstime, &timeo, &abstime); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_flow(&info) < 0) { + printf("Failed to add flow.\n"); + goto fail; + } + + if (reg_prepare_flow_accept(&info, &pbuf) < 0) { + printf("Failed to prepare flow for accept.\n"); + goto fail; + } + + n_1_info.id = info.id; + n_1_info.mpl = 1; + + pthread_create(&thr, NULL, test_flow_respond_accept, &n_1_info); + + if (reg_wait_flow_accepted(&info, &rbuf, &abstime) < 0 ) { + printf("Flow allocation failed.\n"); + goto fail; + } + + pthread_join(thr, NULL); + + if (info.state != FLOW_ALLOCATED) { + printf("Flow succeeded but not in allocated state.\n"); + goto fail; + } + + if (rbuf.data == NULL) { + printf("rbuf data was not returned.\n"); + goto fail; + } + + if (strcmp((char *) rbuf.data, TEST_DATA) != 0) { + printf("Data was updated.\n"); + goto fail; + } + + n_1_info.state = FLOW_DEALLOCATED; + + reg_dealloc_flow(&info); + + if (info.state != FLOW_DEALLOC_PENDING) { + printf("Flow dealloc requested but not in pending state.\n"); + goto fail; + } + + reg_dealloc_flow_resp(&info); + + if (info.state != FLOW_DEALLOCATED) { + printf("Flow deallocated but not in deallocated state.\n"); + goto fail; + } + + reg_destroy_flow(n_1_info.id); + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + + +static int test_reg_allocate_flow_fail(void) +{ + buffer_t buf = {NULL, 0}; + pthread_t thr; + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_S(1); + + struct flow_info info = { + .n_pid = TEST_PID, + .qs = qos_raw + }; + + struct flow_info n_1_info = { + .n_1_pid = TEST_N_1_PID, + .qs = qos_data, + .state = FLOW_DEALLOCATED /* RESPONSE FAIL */ + }; + + TEST_START(); + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + + ts_add(&abstime, &timeo, &abstime); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_flow(&info) < 0) { + printf("Failed to add flow.\n"); + goto fail; + } + + info.n_1_pid = TEST_N_1_PID; + + if (reg_prepare_flow_alloc(&info) < 0) { + printf("Failed to prepare flow for alloc.\n"); + goto fail; + } + + n_1_info.id = info.id; + + pthread_create(&thr, NULL, test_flow_respond_alloc, &n_1_info); + + if (reg_wait_flow_allocated(&info, &buf, &abstime) == 0 ) { + printf("Flow allocation succeeded.\n"); + goto fail; + } + + pthread_join(thr, NULL); + + if (info.state != FLOW_DEALLOCATED) { + printf("Flow failed but not in deallocated state.\n"); + goto fail; + } + + reg_destroy_flow(n_1_info.id); + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_reg_flow(void) { + int ret = 0; + + ret |= test_reg_create_flow(); + + ret |= test_reg_allocate_flow_timeout(); + + ret |= test_reg_accept_flow_success(); + + ret |= test_reg_accept_flow_success_no_crypt(); + + ret |= test_reg_allocate_flow_fail(); + + return ret; +} + +static int test_reg_create_ipcp(void) +{ + struct ipcp_info info = { + .name = TEST_IPCP, + .pid = TEST_PID, + .state = IPCP_BOOT /* set by spawn_ipcp */ + }; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_ipcp(&info) < 0) { + printf("Failed to create ipcp.\n"); + goto fail; + } + + if (reg.n_ipcps != 1) { + printf("n_ipcps was not updated.\n"); + goto fail; + } + + if (!reg_has_ipcp(info.pid)) { + printf("Failed to find ipcp.\n"); + goto fail; + } + + if (reg_destroy_proc(info.pid) < 0) { + printf("Failed to destroy ipcp.\n"); + goto fail; + } + + if (reg.n_ipcps != 0) { + printf("n_ipcps was not updated.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_set_layer(void) +{ + struct reg_ipcp * ipcp; + struct ipcp_info info = { + .name = TEST_IPCP, + .pid = TEST_PID, + .state = IPCP_BOOT /* set by spawn_ipcp */ + }; + struct layer_info layer = { + .name = TEST_LAYER, + }; + + struct ipcp_info get_info = { + .pid = TEST_PID + }; + struct layer_info get_layer; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_ipcp(&info) < 0) { + printf("Failed to create ipcp.\n"); + goto fail; + } + + ipcp = __reg_get_ipcp(info.pid); + ipcp->info.state = IPCP_OPERATIONAL; + info.state = IPCP_ENROLLED; + + reg_set_layer_for_ipcp(&info, &layer); + + reg_get_ipcp(&get_info, &get_layer); + + if (memcmp(&get_info, &info, sizeof(ipcp)) != 0) { + printf("Failed to set ipcp info.\n"); + goto fail; + } + + if (memcmp(&get_layer, &layer, sizeof(layer)) != 0) { + printf("Failed to set layer info.\n"); + goto fail; + } + + if (reg_destroy_proc(info.pid) < 0) { + printf("Failed to destroy ipcp.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_reg_ipcp(void) +{ + int ret = 0; + + ret |= test_reg_create_ipcp(); + + ret |= test_set_layer(); + + return ret; +} + +static int test_reg_create_name(void) +{ + struct name_info info = { + .name = TEST_NAME, + .pol_lb = LB_RR + }; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_name(&info) < 0) { + printf("Failed to create name.\n"); + goto fail; + } + + if (reg.n_names != 1) { + printf("n_names was not updated.\n"); + goto fail; + } + + if (!reg_has_name(info.name)) { + printf("Failed to find name.\n"); + goto fail; + } + + if (reg_destroy_name(info.name) < 0) { + printf("Failed to destroy name.\n"); + goto fail; + } + + if (reg.n_names != 0) { + printf("n_names was not updated.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_reg_name(void) +{ + int ret = 0; + + ret |= test_reg_create_name(); + + return ret; +} + +static int test_reg_create_proc(void) +{ + struct proc_info info = { + .pid = TEST_PID, + .prog = TEST_PROG + }; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_proc(&info) < 0) { + printf("Failed to create process.\n"); + goto fail; + } + + if (reg.n_procs != 1) { + printf("n_procs was not updated.\n"); + goto fail; + } + + if (!reg_has_proc(info.pid)) { + printf("Failed to find process.\n"); + goto fail; + } + + if (reg_destroy_proc(info.pid) < 0) { + printf("Failed to destroy process.\n"); + goto fail; + } + + if (reg.n_procs != 0) { + printf("n_procs was not updated.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_reg_proc(void) +{ + int ret = 0; + + ret |= test_reg_create_proc(); + + return ret; +} + +static int test_reg_spawned(void) +{ + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_spawned(TEST_PID) < 0) { + printf("Failed to create process.\n"); + goto fail; + } + + if (reg.n_spawned != 1) { + printf("n_spawned was not updated.\n"); + goto fail; + } + + if (!reg_has_spawned(TEST_PID)) { + printf("Failed to find spawned.\n"); + goto fail; + } + + if (reg_destroy_proc(TEST_PID) < 0) { + printf("Failed to destroy spawned.\n"); + goto fail; + } + + if (reg.n_spawned != 0) { + printf("n_spawned was not updated.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_reg_create_prog(void) +{ + struct prog_info info = { + .name = TEST_PROG + }; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_prog(&info) < 0) { + printf("Failed to create program.\n"); + goto fail; + } + + if (reg.n_progs != 1) { + printf("n_progs was not updated.\n"); + goto fail; + } + + if (!reg_has_prog(info.name)) { + printf("Failed to find program.\n"); + goto fail; + } + + if (reg_destroy_prog(info.name) < 0) { + printf("Failed to destroy program.\n"); + goto fail; + } + + if (reg.n_progs != 0) { + printf("n_progs was not updated.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_reg_prog(void) +{ + int ret = 0; + + ret |= test_reg_create_prog(); + + return ret; +} + +static int test_bind_proc(void) +{ + struct proc_info pinfo = { + .pid = TEST_PID, + .prog = TEST_PROG + }; + + struct name_info ninfo = { + .name = TEST_NAME, + .pol_lb = LB_RR + }; + + TEST_START(); + + if (reg_init()) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_name(&ninfo) < 0) { + printf("Failed to create name.\n"); + goto fail; + } + + if (reg_create_proc(&pinfo) < 0) { + printf("Failed to create proc.\n"); + goto fail; + } + + if (reg_bind_proc(TEST_NAME, TEST_PID) < 0) { + printf("Failed to bind proc.\n"); + goto fail; + } + + if (reg_unbind_proc(TEST_NAME, TEST_PID) < 0) { + printf("Failed to unbind proc.\n"); + goto fail; + } + + reg_destroy_proc(TEST_PID); + + if (reg_name_has_proc( __reg_get_name(TEST_NAME), TEST_PID)) { + printf("Proc still in name after destroy.\n"); + goto fail; + } + + reg_destroy_name(TEST_NAME); + + reg_fini(); + + TEST_SUCCESS(); + + return 0; +fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_bind_prog(void) +{ + struct prog_info pinfo = { + .name = TEST_PROG + }; + + struct name_info ninfo = { + .name = TEST_NAME, + .pol_lb = LB_RR + }; + + char * exec[] = { TEST_PROG, "--argswitch", "argvalue", NULL}; + + TEST_START(); + + if (reg_init()) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_name(&ninfo) < 0) { + printf("Failed to create name.\n"); + goto fail; + } + + if (reg_create_prog(&pinfo) < 0) { + printf("Failed to create prog.\n"); + goto fail; + } + + if (reg_bind_prog(TEST_NAME, exec, BIND_AUTO) < 0) { + printf("Failed to bind prog.\n"); + goto fail; + } + + if (!reg_name_has_prog( __reg_get_name(TEST_NAME), TEST_PROG)) { + printf("Prog not found in name.\n"); + goto fail; + } + + if (!reg_prog_has_name( __reg_get_prog(TEST_PROG), TEST_NAME)) { + printf("Name not found in prog.\n"); + goto fail; + } + + if (reg_unbind_prog(TEST_NAME, TEST_PROG) < 0) { + printf("Failed to unbind prog.\n"); + goto fail; + } + + if (reg_name_has_prog( __reg_get_name(TEST_NAME), TEST_PROG)) { + printf("Prog still in name after unbind.\n"); + goto fail; + } + + if (reg_prog_has_name( __reg_get_prog(TEST_PROG), TEST_NAME)) { + printf("Name still in prog after unbind.\n"); + goto fail; + } + + if (reg_bind_prog(TEST_NAME, exec, 0) < 0) { + printf("Failed to bind prog.\n"); + goto fail; + } + + if (reg_name_has_prog( __reg_get_name(TEST_NAME), TEST_PROG)) { + printf("Non-auto prog found in name.\n"); + goto fail; + } + + if (reg_unbind_prog(TEST_NAME, TEST_PROG) < 0) { + printf("Failed to unbind prog.\n"); + goto fail; + } + + reg_destroy_prog(TEST_PROG); + + reg_destroy_name(TEST_NAME); + + reg_fini(); + + TEST_SUCCESS(); + + return 0; +fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_inherit_prog(void) +{ + struct name_info nameinfo = { + .name = TEST_NAME, + .pol_lb = LB_RR + }; + + struct prog_info proginfo = { + .name = TEST_PROG + }; + + struct proc_info procinfo = { + .pid = TEST_PID, + .prog = TEST_PROG + }; + + char * exec[] = { TEST_PROG, NULL}; + + TEST_START(); + + if (reg_init()) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_name(&nameinfo) < 0) { + printf("Failed to create name.\n"); + goto fail; + } + + if (reg_create_prog(&proginfo) < 0) { + printf("Failed to create prog.\n"); + goto fail; + } + + if (reg_bind_prog(TEST_NAME, exec, 0) < 0) { + printf("Failed to bind prog.\n"); + goto fail; + } + + if (reg_create_proc(&procinfo) < 0) { + printf("Failed to create proc.\n"); + goto fail; + } + + if (!reg_name_has_proc(__reg_get_name(TEST_NAME), TEST_PID)) { + printf("Failed to update name from prog.\n"); + goto fail; + } + + if (!reg_proc_has_name(__reg_get_proc(TEST_PID), TEST_NAME)) { + printf("Failed to update proc from prog.\n"); + goto fail; + } + + reg_destroy_proc(TEST_PID); + + reg_destroy_prog(TEST_PROG); + + reg_destroy_name(TEST_NAME); + + reg_fini(); + + TEST_SUCCESS(); + + return 0; +fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_wait_accepting_timeout(void) +{ + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_MS(1); + int flow_id; + uint8_t hash[64]; + struct name_info ninfo = { + .name = TEST_NAME, + .pol_lb = LB_RR + }; + + TEST_START(); + + if (reg_init()) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_name(&ninfo) < 0) { + printf("Failed to create name.\n"); + goto fail; + } + + str_hash(HASH_SHA3_256, hash, ninfo.name); + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); + + flow_id = reg_wait_flow_accepting(HASH_SHA3_256, hash, &abstime); + if (flow_id != -ETIMEDOUT) { + printf("Wait accept did not time out: %d.\n", flow_id); + goto fail; + } + + reg_destroy_name(TEST_NAME); + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_wait_accepting_fail_name(void) +{ + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_S(1); + int flow_id; + uint8_t hash[64]; + + TEST_START(); + + if (reg_init()) { + printf("Failed to init registry.\n"); + goto fail; + } + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); + str_hash(HASH_SHA3_256, hash, "C0FF33"); + + flow_id = reg_wait_flow_accepting(HASH_SHA3_256, hash, &abstime); + if (flow_id != -ENAME) { + printf("Wait accept did not fail on name: %d.\n", flow_id); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static void * test_call_flow_accept(void * o) +{ + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_MS(1); + buffer_t pbuf = {NULL, 0}; + + struct proc_info pinfo = { + .pid = TEST_PID, + .prog = TEST_PROG + }; + + struct flow_info info = { + .n_pid = pinfo.pid, + .qs = qos_raw, + }; + + if (reg_create_proc(&pinfo) < 0) { + printf("Failed to create proc.\n"); + goto fail; + } + + if (reg_bind_proc((char *) o, TEST_PID) < 0) { + printf("Failed to bind proc.\n"); + goto fail; + } + + if (reg_create_flow(&info) < 0) { + printf("Failed to create flow.\n"); + goto fail; + } + + info.state = FLOW_ACCEPT_PENDING; + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); + + reg_prepare_flow_accept(&info, &pbuf); + + if (reg_wait_flow_accepted(&info, &pbuf, &abstime) != -ETIMEDOUT) { + printf("Wait allocated did not timeout.\n"); + goto fail; + } + + reg_destroy_flow(info.id); + reg_destroy_proc(pinfo.pid); + + return (void *) 0; + fail: + return (void *) -1; +} + +static int test_wait_accepting_success(void) +{ + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_S(1); + int flow_id; + pthread_t thr; + uint8_t hash[64]; + struct name_info ninfo = { + .name = TEST_NAME, + .pol_lb = LB_RR + }; + + TEST_START(); + + if (reg_init()) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_name(&ninfo) < 0) { + printf("Failed to create name.\n"); + goto fail; + } + + pthread_create(&thr, NULL, test_call_flow_accept, ninfo.name); + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); + + str_hash(HASH_SHA3_256, hash, ninfo.name); + + flow_id = reg_wait_flow_accepting(HASH_SHA3_256, hash, &abstime); + if (flow_id < 0) { + printf("Wait accept did not return a flow id: %d.", flow_id); + goto fail; + } + + pthread_join(thr, NULL); + + reg_destroy_name(TEST_NAME); + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_wait_accepting(void) +{ + int ret = 0; + + ret |= test_wait_accepting_timeout(); + + ret |= test_wait_accepting_fail_name(); + + ret |= test_wait_accepting_success(); + + return ret; +} + +static int test_wait_ipcp_boot_timeout(void) +{ + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_MS(1); + struct ipcp_info info = { + .name = TEST_IPCP, + .pid = TEST_PID, + .state = IPCP_BOOT /* set by spawn_ipcp */ + }; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_ipcp(&info) < 0) { + printf("Failed to create ipcp.\n"); + goto fail; + } + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); + + if (reg_wait_ipcp_boot(&info, &abstime) != -ETIMEDOUT) { + printf("Wait boot did not timeout.\n"); + goto fail; + } + + if (reg_destroy_proc(info.pid) < 0) { + printf("Failed to destroy ipcp.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static void * test_ipcp_respond(void * o) +{ + (void) o; + + reg_respond_ipcp((struct ipcp_info *) o); + + return (void *) 0; +} + +static int test_wait_ipcp_boot_fail(void) +{ + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_S(1); + pthread_t thr; + struct ipcp_info info = { + .name = TEST_IPCP, + .pid = TEST_PID, + .state = IPCP_BOOT /* set by spawn_ipcp */ + }; + struct ipcp_info resp_info = { + .name = TEST_IPCP, + .pid = TEST_PID, + .state = IPCP_NULL + }; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_ipcp(&info) < 0) { + printf("Failed to create ipcp.\n"); + goto fail; + } + + pthread_create(&thr, NULL, test_ipcp_respond, &resp_info); + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); + + info.state = IPCP_BOOT; + + if (reg_wait_ipcp_boot(&info, &abstime) == 0) { + printf("IPCP boot reported success.\n"); + goto fail; + } + + pthread_join(thr, NULL); + + if (reg_destroy_proc(info.pid) < 0) { + printf("Failed to destroy ipcp.\n"); + goto fail; + } + + if (reg.n_ipcps != 0) { + printf("n_ipcps was not updated.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_wait_ipcp_boot_success(void) +{ + pthread_t thr; + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_S(1); + struct ipcp_info info = { + .name = TEST_IPCP, + .pid = TEST_PID, + .state = IPCP_BOOT /* set by spawn_ipcp */ + }; + struct ipcp_info resp_info = { + .name = TEST_IPCP, + .pid = TEST_PID, + .state = IPCP_OPERATIONAL + }; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_ipcp(&info) < 0) { + printf("Failed to create ipcp.\n"); + goto fail; + } + + pthread_create(&thr, NULL, test_ipcp_respond, &resp_info); + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); + + info.state = IPCP_BOOT; + + if (reg_wait_ipcp_boot(&info, &abstime) < 0) { + printf("IPCP boot failed.\n"); + goto fail; + } + + pthread_join(thr, NULL); + + if (info.state != IPCP_OPERATIONAL) { + printf("IPCP boot succeeded in non-operational state.\n"); + goto fail; + } + + if (reg_destroy_proc(info.pid) < 0) { + printf("Failed to destroy ipcp.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_wait_ipcp_boot(void) +{ + int ret = 0; + + ret |= test_wait_ipcp_boot_timeout(); + + ret |= test_wait_ipcp_boot_fail(); + + ret |= test_wait_ipcp_boot_success(); + + return ret; +} + +static int test_wait_proc_timeout(void) +{ + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_MS(1); + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); + + if (reg_wait_proc(TEST_PID, &abstime) != -ETIMEDOUT) { + printf("Wait proc did not timeout.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static void * test_proc(void * o) +{ + (void) o; + + reg_create_proc((struct proc_info *) o); + + return (void *) 0; +} + +static int test_wait_proc_success(void) +{ + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_S(1); + pthread_t thr; + struct proc_info info = { + .pid = TEST_PID, + .prog = TEST_PROG + }; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + pthread_create(&thr, NULL, test_proc, &info); + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); + + if (reg_wait_proc(info.pid, &abstime) < 0) { + printf("Waiting for proc failed.\n"); + goto fail; + } + + pthread_join(thr, NULL); + + reg_destroy_proc(info.pid); + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_wait_proc(void) +{ + int ret = 0; + + ret |= test_wait_proc_timeout(); + + ret |= test_wait_proc_success(); + + return ret; +} + + +int reg_test(int argc, + char ** argv) +{ + int ret = 0; + + (void) argc; + (void) argv; + + ret |= test_reg_init(); + + ret |= test_reg_flow(); + + ret |= test_reg_ipcp(); + + ret |= test_reg_name(); + + ret |= test_reg_proc(); + + ret |= test_reg_prog(); + + ret |= test_reg_spawned(); + + ret |= test_bind_proc(); + + ret |= test_bind_prog(); + + ret |= test_inherit_prog(); + + ret |= test_wait_accepting(); + + ret |= test_wait_ipcp_boot(); + + ret |= test_wait_proc(); + + return ret; +} diff --git a/src/irmd/registry.c b/src/irmd/registry.c deleted file mode 100644 index 69e3ae97..00000000 --- a/src/irmd/registry.c +++ /dev/null @@ -1,615 +0,0 @@ -/* - * Ouroboros - Copyright (C) 2016 - 2021 - * - * The IPC Resource Manager - Registry - * - * Dimitri Staessens <dimitri@ouroboros.rocks> - * Sander Vrijders <sander@ouroboros.rocks> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., http://www.fsf.org/about/contact/. - */ - -#if defined(__linux__) || defined(__CYGWIN__) -#define _DEFAULT_SOURCE -#else -#define _POSIX_C_SOURCE 200809L -#endif - -#include "config.h" - -#define OUROBOROS_PREFIX "registry" - -#include <ouroboros/errno.h> -#include <ouroboros/logs.h> -#include <ouroboros/time_utils.h> -#include <ouroboros/pthread.h> - -#include "registry.h" -#include "utils.h" - -#include <stdlib.h> -#include <stdbool.h> -#include <string.h> -#include <signal.h> -#include <unistd.h> -#include <limits.h> -#include <assert.h> - -static struct reg_entry * reg_entry_create(void) -{ - struct reg_entry * e; - - e = malloc(sizeof(*e)); - if (e == NULL) - return NULL; - - e->name = NULL; - e->state = REG_NAME_NULL; - - return e; -} - -static int reg_entry_init(struct reg_entry * e, - char * name) -{ - pthread_condattr_t cattr; - - assert(e); - assert(name); - - list_head_init(&e->next); - list_head_init(&e->reg_progs); - list_head_init(&e->reg_pids); - - e->name = name; - e->pol_lb = 0; - - if (pthread_condattr_init(&cattr)) - goto fail_cattr; - -#ifndef __APPLE__ - pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK); -#endif - if (pthread_cond_init(&e->state_cond, &cattr)) - goto fail_cond; - - if (pthread_mutex_init(&e->state_lock, NULL)) - goto fail_mutex; - - pthread_condattr_destroy(&cattr); - - e->state = REG_NAME_IDLE; - - return 0; - - fail_mutex: - pthread_cond_destroy(&e->state_cond); - fail_cond: - pthread_condattr_destroy(&cattr); - fail_cattr: - return -1; -} - -static void cancel_reg_entry_destroy(void * o) -{ - struct reg_entry * e; - struct list_head * p; - struct list_head * h; - - e = (struct reg_entry *) o; - - pthread_mutex_unlock(&e->state_lock); - - pthread_cond_destroy(&e->state_cond); - pthread_mutex_destroy(&e->state_lock); - - if (e->name != NULL) - free(e->name); - - list_for_each_safe(p, h, &e->reg_pids) { - struct pid_el * pe = list_entry(p, struct pid_el, next); - list_del(&pe->next); - free(pe); - } - - list_for_each_safe(p, h, &e->reg_progs) { - struct str_el * a = list_entry(p, struct str_el, next); - list_del(&a->next); - free(a->str); - free(a); - } - - free(e); -} - -static void reg_entry_destroy(struct reg_entry * e) -{ - if (e == NULL) - return; - - pthread_mutex_lock(&e->state_lock); - - if (e->state == REG_NAME_DESTROY) { - pthread_mutex_unlock(&e->state_lock); - return; - } - - if (e->state != REG_NAME_FLOW_ACCEPT) - e->state = REG_NAME_NULL; - else - e->state = REG_NAME_DESTROY; - - pthread_cond_broadcast(&e->state_cond); - - pthread_cleanup_push(cancel_reg_entry_destroy, e); - - while (e->state != REG_NAME_NULL) - pthread_cond_wait(&e->state_cond, &e->state_lock); - - pthread_cleanup_pop(true); -} - -static bool reg_entry_has_prog(struct reg_entry * e, - const char * prog) -{ - struct list_head * p; - - list_for_each(p, &e->reg_progs) { - struct str_el * e = list_entry(p, struct str_el, next); - if (!strcmp(e->str, prog)) - return true; - } - - return false; -} - -int reg_entry_add_prog(struct reg_entry * e, - struct prog_entry * a) -{ - struct str_el * n; - - if (reg_entry_has_prog(e, a->prog)) { - log_warn("Program %s already accepting flows for %s.", - a->prog, e->name); - return 0; - } - - if (!(a->flags & BIND_AUTO)) { - log_dbg("Program %s cannot be auto-instantiated.", a->prog); - return 0; - } - - n = malloc(sizeof(*n)); - if (n == NULL) - return -ENOMEM; - - n->str = strdup(a->prog); - if (n->str == NULL) { - free(n); - return -ENOMEM; - } - - list_add(&n->next, &e->reg_progs); - - pthread_mutex_lock(&e->state_lock); - - if (e->state == REG_NAME_IDLE) - e->state = REG_NAME_AUTO_ACCEPT; - - pthread_mutex_unlock(&e->state_lock); - - return 0; -} - -void reg_entry_del_prog(struct reg_entry * e, - const char * prog) -{ - struct list_head * p; - struct list_head * h; - - list_for_each_safe(p, h, &e->reg_progs) { - struct str_el * e = list_entry(p, struct str_el, next); - if (!strcmp(prog, e->str)) { - list_del(&e->next); - free(e->str); - free(e); - } - } - - pthread_mutex_lock(&e->state_lock); - - if (e->state == REG_NAME_AUTO_ACCEPT && list_is_empty(&e->reg_progs)) { - e->state = REG_NAME_IDLE; - pthread_cond_broadcast(&e->state_cond); - } - - pthread_mutex_unlock(&e->state_lock); -} - -char * reg_entry_get_prog(struct reg_entry * e) -{ - if (!list_is_empty(&e->reg_pids) || list_is_empty(&e->reg_progs)) - return NULL; - - return list_first_entry(&e->reg_progs, struct str_el, next)->str; -} - -static bool reg_entry_has_pid(struct reg_entry * e, - pid_t pid) -{ - struct list_head * p; - - list_for_each(p, &e->reg_progs) { - struct pid_el * e = list_entry(p, struct pid_el, next); - if (e->pid == pid) - return true; - } - - return false; -} - -int reg_entry_add_pid(struct reg_entry * e, - pid_t pid) -{ - struct pid_el * i; - - assert(e); - - if (reg_entry_has_pid(e, pid)) { - log_dbg("Process already registered with this name."); - return -EPERM; - } - - pthread_mutex_lock(&e->state_lock); - - if (e->state == REG_NAME_NULL) { - pthread_mutex_unlock(&e->state_lock); - log_dbg("Tried to add instance in NULL state."); - return -EPERM; - } - - i = malloc(sizeof(*i)); - if (i == NULL) { - pthread_mutex_unlock(&e->state_lock); - return -ENOMEM; - } - - i->pid = pid; - - /* load balancing policy assigns queue order for this process. */ - switch(e->pol_lb) { - case LB_RR: /* Round robin policy. */ - list_add_tail(&i->next, &e->reg_pids); - break; - case LB_SPILL: /* Keep accepting flows on the current process */ - list_add(&i->next, &e->reg_pids); - break; - default: - assert(false); - }; - - if (e->state == REG_NAME_IDLE || - e->state == REG_NAME_AUTO_ACCEPT || - e->state == REG_NAME_AUTO_EXEC) { - e->state = REG_NAME_FLOW_ACCEPT; - pthread_cond_broadcast(&e->state_cond); - } - - pthread_mutex_unlock(&e->state_lock); - - return 0; -} - -void reg_entry_set_policy(struct reg_entry * e, - enum pol_balance p) -{ - e->pol_lb = p; -} - - -static void reg_entry_check_state(struct reg_entry * e) -{ - assert(e); - - if (e->state == REG_NAME_DESTROY) { - e->state = REG_NAME_NULL; - pthread_cond_broadcast(&e->state_cond); - return; - } - - if (list_is_empty(&e->reg_pids)) { - if (!list_is_empty(&e->reg_progs)) - e->state = REG_NAME_AUTO_ACCEPT; - else - e->state = REG_NAME_IDLE; - } else { - e->state = REG_NAME_FLOW_ACCEPT; - } - - pthread_cond_broadcast(&e->state_cond); -} - -void reg_entry_del_pid_el(struct reg_entry * e, - struct pid_el * p) -{ - assert(e); - assert(p); - - list_del(&p->next); - free(p); - - reg_entry_check_state(e); -} - -void reg_entry_del_pid(struct reg_entry * e, - pid_t pid) -{ - struct list_head * p; - struct list_head * h; - - assert(e); - - if (e == NULL) - return; - - list_for_each_safe(p, h, &e->reg_pids) { - struct pid_el * a = list_entry(p, struct pid_el, next); - if (a->pid == pid) { - list_del(&a->next); - free(a); - } - } - - reg_entry_check_state(e); -} - -pid_t reg_entry_get_pid(struct reg_entry * e) -{ - if (e == NULL) - return -1; - - if (list_is_empty(&e->reg_pids)) - return -1; - - return list_first_entry(&e->reg_pids, struct pid_el, next)->pid; -} - -enum reg_name_state reg_entry_get_state(struct reg_entry * e) -{ - enum reg_name_state state; - - assert(e); - - pthread_mutex_lock(&e->state_lock); - - state = e->state; - - pthread_mutex_unlock(&e->state_lock); - - return state; -} - -int reg_entry_set_state(struct reg_entry * e, - enum reg_name_state state) -{ - assert(state != REG_NAME_DESTROY); - - pthread_mutex_lock(&e->state_lock); - - e->state = state; - pthread_cond_broadcast(&e->state_cond); - - pthread_mutex_unlock(&e->state_lock); - - return 0; -} - -int reg_entry_leave_state(struct reg_entry * e, - enum reg_name_state state, - struct timespec * timeout) -{ - struct timespec abstime; - int ret = 0; - - assert(e); - assert(state != REG_NAME_DESTROY); - - if (timeout != NULL) { - clock_gettime(PTHREAD_COND_CLOCK, &abstime); - ts_add(&abstime, timeout, &abstime); - } - - pthread_mutex_lock(&e->state_lock); - - pthread_cleanup_push(__cleanup_mutex_unlock, &e->state_lock); - - while (e->state == state && ret != -ETIMEDOUT) - if (timeout) - ret = -pthread_cond_timedwait(&e->state_cond, - &e->state_lock, - &abstime); - else - ret = -pthread_cond_wait(&e->state_cond, - &e->state_lock); - - if (e->state == REG_NAME_DESTROY) { - ret = -1; - e->state = REG_NAME_NULL; - pthread_cond_broadcast(&e->state_cond); - } - - pthread_cleanup_pop(true); - - return ret; -} - -int reg_entry_wait_state(struct reg_entry * e, - enum reg_name_state state, - struct timespec * timeout) -{ - struct timespec abstime; - int ret = 0; - - assert(e); - assert(state != REG_NAME_DESTROY); - - if (timeout != NULL) { - clock_gettime(PTHREAD_COND_CLOCK, &abstime); - ts_add(&abstime, timeout, &abstime); - } - - pthread_mutex_lock(&e->state_lock); - - while (e->state != state && - e->state != REG_NAME_DESTROY && - ret != -ETIMEDOUT) - if (timeout) - ret = -pthread_cond_timedwait(&e->state_cond, - &e->state_lock, - &abstime); - else - ret = -pthread_cond_wait(&e->state_cond, - &e->state_lock); - - if (e->state == REG_NAME_DESTROY) { - ret = -1; - e->state = REG_NAME_NULL; - pthread_cond_broadcast(&e->state_cond); - } - - pthread_mutex_unlock(&e->state_lock); - - return ret; -} - -struct reg_entry * registry_get_entry(struct list_head * registry, - const char * name) -{ - struct list_head * p = NULL; - - assert(registry); - - list_for_each(p, registry) { - struct reg_entry * e = list_entry(p, struct reg_entry, next); - if (!strcmp(name, e->name)) - return e; - } - - return NULL; -} - -struct reg_entry * registry_get_entry_by_hash(struct list_head * registry, - enum hash_algo algo, - const uint8_t * hash, - size_t len) -{ - struct list_head * p = NULL; - uint8_t * thash; - - thash = malloc(len); - if (thash == NULL) - return NULL; - - assert(registry); - - list_for_each(p, registry) { - struct reg_entry * e = list_entry(p, struct reg_entry, next); - str_hash(algo, thash, e->name); - if (memcmp(thash, hash, len) == 0) { - free(thash); - return e; - } - } - - free(thash); - - return NULL; -} - -struct reg_entry * registry_add_name(struct list_head * registry, - const char * name) -{ - struct reg_entry * e = NULL; - - assert(registry); - assert(name); - - if (registry_has_name(registry, name)) { - log_dbg("Name %s already registered.", name); - return NULL; - } - - e = reg_entry_create(); - if (e == NULL) { - log_dbg("Could not create registry entry."); - return NULL; - } - - if (reg_entry_init(e, strdup(name))) { - reg_entry_destroy(e); - log_dbg("Could not initialize registry entry."); - return NULL; - } - - list_add(&e->next, registry); - - return e; -} - -void registry_del_name(struct list_head * registry, - const char * name) -{ - struct reg_entry * e = registry_get_entry(registry, name); - if (e == NULL) - return; - - list_del(&e->next); - reg_entry_destroy(e); - - return; -} - -void registry_del_process(struct list_head * registry, - pid_t pid) -{ - struct list_head * p; - - assert(registry); - assert(pid > 0); - - list_for_each(p, registry) { - struct reg_entry * e = list_entry(p, struct reg_entry, next); - pthread_mutex_lock(&e->state_lock); - assert(e); - reg_entry_del_pid(e, pid); - pthread_mutex_unlock(&e->state_lock); - } - - return; -} - -void registry_destroy(struct list_head * registry) -{ - struct list_head * p = NULL; - struct list_head * h = NULL; - - assert(registry); - - list_for_each_safe(p, h, registry) { - struct reg_entry * e = list_entry(p, struct reg_entry, next); - list_del(&e->next); - reg_entry_set_state(e, REG_NAME_NULL); - reg_entry_destroy(e); - } -} diff --git a/src/irmd/registry.h b/src/irmd/registry.h deleted file mode 100644 index af34cffc..00000000 --- a/src/irmd/registry.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Ouroboros - Copyright (C) 2016 - 2021 - * - * The IPC Resource Manager - Registry - * - * Dimitri Staessens <dimitri@ouroboros.rocks> - * Sander Vrijders <sander@ouroboros.rocks> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., http://www.fsf.org/about/contact/. - */ - -#ifndef OUROBOROS_IRMD_REGISTRY_H -#define OUROBOROS_IRMD_REGISTRY_H - -#include <ouroboros/hash.h> -#include <ouroboros/ipcp.h> -#include <ouroboros/list.h> -#include <ouroboros/irm.h> - -#include "proc_table.h" -#include "prog_table.h" - -#include <stdint.h> -#include <stdbool.h> -#include <pthread.h> -#include <string.h> -#include <sys/types.h> - -#define registry_has_name(r, name) \ - (registry_get_entry(r, name) != NULL) - -enum reg_name_state { - REG_NAME_NULL = 0, - REG_NAME_IDLE, - REG_NAME_AUTO_ACCEPT, - REG_NAME_AUTO_EXEC, - REG_NAME_FLOW_ACCEPT, - REG_NAME_FLOW_ARRIVED, - REG_NAME_DESTROY -}; - -/* An entry in the registry */ -struct reg_entry { - struct list_head next; - char * name; - - /* Policies for this name. */ - enum pol_balance pol_lb; /* Load balance incoming flows. */ - /* Programs that can be instantiated by the irmd. */ - struct list_head reg_progs; - /* Processes that are listening for this name. */ - struct list_head reg_pids; - - enum reg_name_state state; - pthread_cond_t state_cond; - pthread_mutex_t state_lock; -}; - -int reg_entry_add_prog(struct reg_entry * e, - struct prog_entry * a); - -void reg_entry_del_prog(struct reg_entry * e, - const char * prog); - -char * reg_entry_get_prog(struct reg_entry * e); - -int reg_entry_add_pid(struct reg_entry * e, - pid_t pid); - -void reg_entry_del_pid(struct reg_entry * e, - pid_t pid); - -void reg_entry_del_pid_el(struct reg_entry * e, - struct pid_el * a); - -pid_t reg_entry_get_pid(struct reg_entry * e); - -void reg_entry_set_policy(struct reg_entry * e, - enum pol_balance p); - -enum reg_name_state reg_entry_get_state(struct reg_entry * e); - -int reg_entry_set_state(struct reg_entry * e, - enum reg_name_state state); - -int reg_entry_leave_state(struct reg_entry * e, - enum reg_name_state state, - struct timespec * timeout); - -int reg_entry_wait_state(struct reg_entry * e, - enum reg_name_state state, - struct timespec * timeout); - -struct reg_entry * registry_add_name(struct list_head * registry, - const char * name); - -void registry_del_name(struct list_head * registry, - const char * name); - -void registry_del_process(struct list_head * registry, - pid_t pid); - -void registry_sanitize_pids(struct list_head * registry); - -struct reg_entry * registry_get_entry(struct list_head * registry, - const char * name); - -struct reg_entry * registry_get_entry_by_hash(struct list_head * registry, - enum hash_algo algo, - const uint8_t * hash, - size_t len); - -void registry_destroy(struct list_head * registry); - -#endif /* OUROBOROS_IRMD_REGISTRY_H */ diff --git a/src/irmd/tests/CMakeLists.txt b/src/irmd/tests/CMakeLists.txt index 68bd762d..99743052 100644 --- a/src/irmd/tests/CMakeLists.txt +++ b/src/irmd/tests/CMakeLists.txt @@ -2,18 +2,22 @@ get_filename_component(tmp ".." ABSOLUTE) get_filename_component(src_folder "${tmp}" NAME) create_test_sourcelist(${src_folder}_tests test_suite.c - # Add new tests here + # Add new tests here ) add_executable(${src_folder}_test EXCLUDE_FROM_ALL ${${src_folder}_tests}) -target_link_libraries(${src_folder}_test ouroboros) +target_link_libraries(${src_folder}_test ouroboros-common) add_dependencies(check ${src_folder}_test) set(tests_to_run ${${src_folder}_tests}) -remove(tests_to_run test_suite.c) +if(CMAKE_VERSION VERSION_LESS "3.29.0") + remove(tests_to_run test_suite.c) +else () + list(POP_FRONT tests_to_run) +endif() foreach(test ${tests_to_run}) get_filename_component(test_name ${test} NAME_WE) - add_test(${test_name} ${C_TEST_PATH}/${src_folder}_test ${test_name}) + add_test(irmd/${test_name} ${C_TEST_PATH}/${src_folder}_test ${test_name}) endforeach(test) diff --git a/src/irmd/utils.c b/src/irmd/utils.c deleted file mode 100644 index 976dcfa2..00000000 --- a/src/irmd/utils.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Ouroboros - Copyright (C) 2016 - 2021 - * - * The IPC Resource Manager - Utilities - * - * Dimitri Staessens <dimitri@ouroboros.rocks> - * Sander Vrijders <sander@ouroboros.rocks> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., http://www.fsf.org/about/contact/. - */ - -#define _POSIX_C_SOURCE 200809L - -#include <stdlib.h> -#include <string.h> - -void argvfree(char ** argv) -{ - char ** argv_dup = argv; - if (argv == NULL) - return; - - while (*argv_dup != NULL) - free(*(argv_dup++)); - - free(argv); -} - -char ** argvdup(char ** argv) -{ - int argc = 0; - char ** argv_dup = argv; - int i; - - if (argv == NULL) - return NULL; - - while (*(argv_dup++) != NULL) - argc++; - - if (argc != 0) { - argv_dup = malloc((argc + 1) * sizeof(*argv_dup)); - for (i = 0; i < argc; ++i) { - argv_dup[i] = strdup(argv[i]); - if (argv_dup[i] == NULL) { - argvfree(argv_dup); - return NULL; - } - } - } - argv_dup[argc] = NULL; - return argv_dup; -} diff --git a/src/lib/.gitignore b/src/lib/.gitignore index 8704469b..3ecaf66d 100644 --- a/src/lib/.gitignore +++ b/src/lib/.gitignore @@ -1 +1 @@ -*.pb-c.[ch]
\ No newline at end of file +*.pb-c.[ch] diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 022c5cca..a6d7ac98 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -4,13 +4,18 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_SOURCE_DIR}/include) include_directories(${CMAKE_BINARY_DIR}/include) -protobuf_generate_c(IRM_PROTO_SRCS IRM_PROTO_HDRS irmd_messages.proto) -protobuf_generate_c(IPCP_PROTO_SRCS IPCP_PROTO_HDRS ipcpd_messages.proto) -protobuf_generate_c(QOSSPEC_PROTO_SRCS QOSSPEC_PROTO_HDRS - qosspec.proto) -protobuf_generate_c(LAYER_CONFIG_PROTO_SRCS LAYER_CONFIG_PROTO_HDRS - ipcp_config.proto) -protobuf_generate_c(CACEP_PROTO_SRCS CACEP_PROTO_HDRS cacep.proto) +protobuf_generate_c(MODEL_PROTO_SRCS MODEL_PROTO_HDRS + pb/model.proto) +protobuf_generate_c(IPCP_CONFIG_PROTO_SRCS IPCP_CONFIG_PROTO_HDRS + pb/ipcp_config.proto) +protobuf_generate_c(ENROLL_PROTO_SRCS ENROLL_PROTO_HDRS + pb/enroll.proto) +protobuf_generate_c(CEP_PROTO_SRCS CEP_PROTO_HDRS + pb/cep.proto) +protobuf_generate_c(IRM_PROTO_SRCS IRM_PROTO_HDRS + pb/irm.proto) +protobuf_generate_c(IPCP_PROTO_SRCS IPCP_PROTO_HDRS + pb/ipcp.proto) if (NOT APPLE) find_library(LIBRT_LIBRARIES rt) @@ -118,6 +123,7 @@ endif () if (NOT HAVE_OPENSSL_RNG) set(OPENSSL_INCLUDE_DIR "") set(OPENSSL_LIBRARIES "") + set(OPENSSL_CRYPTO_LIBRARY "") endif () if (APPLE OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") @@ -140,10 +146,10 @@ if (NOT ((CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") OR APPLE OR endif () mark_as_advanced(LIBRT_LIBRARIES LIBPTHREAD_LIBRARIES - LIBGCRYPT_LIBRARIES OPENSSL_LIBRARIES SYS_RND_INCLUDE_DIR - LIBGCRYPT_INCLUDE_DIR SYS_RND_HDR) + LIBGCRYPT_LIBRARIES OPENSSL_LIBRARIES OPENSSL_CRYPTO_LIBRARY + SYS_RND_INCLUDE_DIR LIBGCRYPT_INCLUDE_DIR SYS_RND_HDR) -set(SHM_BUFFER_SIZE 4096 CACHE STRING +set(SHM_BUFFER_SIZE 16384 CACHE STRING "Number of blocks in packet buffer, must be a power of 2") set(SHM_RBUFF_SIZE 1024 CACHE STRING "Number of blocks in rbuff buffer, must be a power of 2") @@ -166,7 +172,7 @@ else () set(PTHREAD_COND_CLOCK "CLOCK_REALTIME" CACHE INTERNAL "Clock to use for condition variable timing") endif () -set(SOCKET_TIMEOUT 1000 CACHE STRING +set(SOCKET_TIMEOUT 500 CACHE STRING "Default timeout for responses from IPCPs (ms)") set(SHM_PREFIX "ouroboros" CACHE STRING "String to prepend to POSIX shared memory filenames") @@ -192,12 +198,16 @@ set(DELTA_T_ACK 10 CACHE STRING "Maximum time to acknowledge a packet (s)") set(DELTA_T_RTX 120 CACHE STRING "Maximum time to retransmit a packet (s)") -set(DELTA_T_ACK_DELAY 10 CACHE STRING - "Maximum time to wait before acknowledging a packet (ms)") set(FRCT_REORDER_QUEUE_SIZE 256 CACHE STRING "Size of the reordering queue, must be a power of 2") set(FRCT_START_WINDOW 64 CACHE STRING "Start window, must be a power of 2") +set(FRCT_LINUX_RTT_ESTIMATOR TRUE CACHE BOOL + "Use Linux RTT estimator formula instead of the TCP RFC formula") +set(FRCT_RTO_MDEV_MULTIPLIER 2 CACHE STRING + "Multiplier for deviation term in the RTO: RTO = sRTT + (mdev << X)") +set(FRCT_RTO_INC_FACTOR 0 CACHE STRING + "Divisor for RTO increase after timeout: RTO += RTX >> X, 0: Karn/Partridge") set(FRCT_RTO_MIN 250 CACHE STRING "Minimum Retransmission Timeout (RTO) for FRCT (us)") set(FRCT_TICK_TIME 5000 CACHE STRING @@ -214,9 +224,9 @@ set(RXM_WHEEL_LEVELS 3 CACHE STRING "Number of levels in the retransmission wheel") set(RXM_WHEEL_SLOTS_PER_LEVEL 256 CACHE STRING "Number of slots per level in the retransmission wheel, must be a power of 2") -set(ACK_WHEEL_SLOTS 128 CACHE STRING +set(ACK_WHEEL_SLOTS 256 CACHE STRING "Number of slots in the acknowledgment wheel, must be a power of 2") -set(ACK_WHEEL_RESOLUTION 20 CACHE STRING +set(ACK_WHEEL_RESOLUTION 18 CACHE STRING "Minimum acknowledgment delay (ns), as a power to 2") if (HAVE_FUSE) @@ -231,7 +241,7 @@ endif () set(SOURCE_FILES_DEV # Add source files here - cacep.c + cep.c dev.c ) @@ -243,15 +253,19 @@ set(SOURCE_FILES_COMMON bitmap.c btree.c crc32.c + crypt.c hash.c list.c lockfile.c logs.c md5.c notifier.c + protobuf.c qoscube.c random.c rib.c + serdes-irm.c + serdes-oep.c sha3.c shm_flow_set.c shm_rbuff.c @@ -265,12 +279,23 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/config.h" @ONLY) add_library(ouroboros-common SHARED ${SOURCE_FILES_COMMON} ${IRM_PROTO_SRCS} - ${IPCP_PROTO_SRCS} ${LAYER_CONFIG_PROTO_SRCS} ${QOSSPEC_PROTO_SRCS}) + ${IPCP_PROTO_SRCS} ${IPCP_CONFIG_PROTO_SRCS} ${MODEL_PROTO_SRCS} + ${ENROLL_PROTO_SRCS}) -add_library(ouroboros-dev SHARED ${SOURCE_FILES_DEV} ${CACEP_PROTO_SRCS}) +add_library(ouroboros-dev SHARED ${SOURCE_FILES_DEV} ${CEP_PROTO_SRCS}) add_library(ouroboros-irm SHARED ${SOURCE_FILES_IRM}) +set_target_properties(ouroboros-common PROPERTIES + VERSION ${PACKAGE_VERSION} + SOVERSION ${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}) +set_target_properties(ouroboros-dev PROPERTIES + VERSION ${PACKAGE_VERSION} + SOVERSION ${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}) +set_target_properties(ouroboros-irm PROPERTIES + VERSION ${PACKAGE_VERSION} + SOVERSION ${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}) + include(AddCompileFlags) if (CMAKE_BUILD_TYPE MATCHES "Debug*") add_compile_flags(ouroboros-common -DCONFIG_OUROBOROS_DEBUG) @@ -279,7 +304,7 @@ if (CMAKE_BUILD_TYPE MATCHES "Debug*") endif () target_link_libraries(ouroboros-common ${LIBRT_LIBRARIES} - ${LIBPTHREAD_LIBRARIES} ${PROTOBUF_C_LIBRARY} ${OPENSSL_LIBRARIES} + ${LIBPTHREAD_LIBRARIES} ${PROTOBUF_C_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY} ${LIBGCRYPT_LIBRARIES} ${FUSE_LIBRARIES}) target_link_libraries(ouroboros-dev ouroboros-common) diff --git a/src/lib/bitmap.c b/src/lib/bitmap.c index 0c551960..b0840c44 100644 --- a/src/lib/bitmap.c +++ b/src/lib/bitmap.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Bitmap implementation * diff --git a/src/lib/btree.c b/src/lib/btree.c index 64b8689e..1af94b73 100644 --- a/src/lib/btree.c +++ b/src/lib/btree.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * B-trees * diff --git a/src/lib/cacep.c b/src/lib/cep.c index e8d21d8e..ba238023 100644 --- a/src/lib/cacep.c +++ b/src/lib/cep.c @@ -1,7 +1,7 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * - * The Common Application Connection Establishment Protocol + * The Ouroboros Connection Establishment Protocol * * Dimitri Staessens <dimitri@ouroboros.rocks> * Sander Vrijders <sander@ouroboros.rocks> @@ -22,35 +22,35 @@ #define _POSIX_C_SOURCE 199309L -#include <ouroboros/cacep.h> +#include <ouroboros/cep.h> #include <ouroboros/dev.h> #include <ouroboros/errno.h> #include <stdlib.h> #include <string.h> -#include "cacep.pb-c.h" -typedef CacepMsg cacep_msg_t; +#include "cep.pb-c.h" +typedef CepMsg cep_msg_t; #define BUF_SIZE 128 static int read_msg(int fd, struct conn_info * info) { - uint8_t buf[BUF_SIZE]; - cacep_msg_t * msg; - ssize_t len; + uint8_t buf[BUF_SIZE]; + cep_msg_t * msg; + ssize_t len; len = flow_read(fd, buf, BUF_SIZE); if (len < 0) - return -1; + return (int) len; - msg = cacep_msg__unpack(NULL, len, buf); + msg = cep_msg__unpack(NULL, len, buf); if (msg == NULL) return -1; - if (strlen(msg->comp_name) > CACEP_BUF_STRLEN) { - cacep_msg__free_unpacked(msg, NULL); + if (strlen(msg->comp_name) > OCEP_BUF_STRLEN) { + cep_msg__free_unpacked(msg, NULL); return -1; } @@ -61,7 +61,7 @@ static int read_msg(int fd, info->pref_syntax = msg->pref_syntax; info->addr = msg->address; - cacep_msg__free_unpacked(msg, NULL); + cep_msg__free_unpacked(msg, NULL); return 0; } @@ -69,9 +69,9 @@ static int read_msg(int fd, static int send_msg(int fd, const struct conn_info * info) { - cacep_msg_t msg = CACEP_MSG__INIT; - uint8_t * data = NULL; - size_t len = 0; + cep_msg_t msg = CEP_MSG__INIT; + uint8_t * data = NULL; + size_t len = 0; msg.comp_name = (char *) info->comp_name; msg.protocol = (char *) info->protocol; @@ -81,7 +81,7 @@ static int send_msg(int fd, if (msg.pref_syntax < 0) return -1; - len = cacep_msg__get_packed_size(&msg); + len = cep_msg__get_packed_size(&msg); if (len == 0) return -1; @@ -89,7 +89,7 @@ static int send_msg(int fd, if (data == NULL) return -ENOMEM; - cacep_msg__pack(&msg, data); + cep_msg__pack(&msg, data); if (flow_write(fd, data, len) < 0) { free(data); @@ -101,26 +101,20 @@ static int send_msg(int fd, return 0; } -int cacep_snd(int fd, - const struct conn_info * in) +int cep_snd(int fd, + const struct conn_info * in) { if (in == NULL) return -EINVAL; - if (send_msg(fd, in)) - return -1; - - return 0; + return send_msg(fd, in); } -int cacep_rcv(int fd, - struct conn_info * out) +int cep_rcv(int fd, + struct conn_info * out) { if (out == NULL) return -EINVAL; - if (read_msg(fd, out)) - return -1; - - return 0; + return read_msg(fd, out); } diff --git a/src/lib/config.h.in b/src/lib/config.h.in index 5c5b6caf..604038b4 100644 --- a/src/lib/config.h.in +++ b/src/lib/config.h.in @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Ouroboros library configuration * @@ -42,6 +42,7 @@ #define SHM_RDRB_BLOCK_SIZE @SHM_RDRB_BLOCK_SIZE@ #define SHM_BUFFER_SIZE @SHM_BUFFER_SIZE@ #define SHM_RBUFF_SIZE @SHM_RBUFF_SIZE@ +#define FLOW_ALLOC_TIMEOUT @FLOW_ALLOC_TIMEOUT@ #if defined(__linux__) || (defined(__MACH__) && !defined(__APPLE__)) /* Avoid a bug in robust mutex implementation of glibc 2.25 */ @@ -69,15 +70,15 @@ #define DU_BUFF_TAILSPACE @DU_BUFF_TAILSPACE@ /* Default Delta-t parameters */ -#define DELT_MPL (@DELTA_T_MPL@ * BILLION) /* ns */ -#define DELT_A (@DELTA_T_ACK@ * BILLION) /* ns */ -#define DELT_R (@DELTA_T_RTX@ * BILLION) /* ns */ - -#define DELT_ACK (@DELTA_T_ACK_DELAY@ * MILLION) /* ns */ +#cmakedefine FRCT_LINUX_RTT_ESTIMATOR +#define DELT_A (@DELTA_T_ACK@) /* ns */ +#define DELT_R (@DELTA_T_RTX@) /* ns */ #define RQ_SIZE (@FRCT_REORDER_QUEUE_SIZE@) #define START_WINDOW (@FRCT_START_WINDOW@) #define RTO_MIN (@FRCT_RTO_MIN@ * 1000) +#define RTO_DIV (@FRCT_RTO_INC_FACTOR@) +#define MDEV_MUL (@FRCT_RTO_MDEV_MULTIPLIER@) #define TICTIME (@FRCT_TICK_TIME@ * 1000) /* ns */ diff --git a/src/lib/crc32.c b/src/lib/crc32.c index cd267faf..f369ad20 100644 --- a/src/lib/crc32.c +++ b/src/lib/crc32.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * 32-bit Cyclic Redundancy Check * diff --git a/src/lib/crypt.c b/src/lib/crypt.c index 070f5113..ad679501 100644 --- a/src/lib/crypt.c +++ b/src/lib/crypt.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Elliptic curve Diffie-Hellman key exchange and * AES encryption for flows using OpenSSL @@ -20,9 +20,19 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., http://www.fsf.org/about/contact/. */ +#include <config.h> + +#include <ouroboros/crypt.h> +#include <ouroboros/errno.h> + +#include <assert.h> +#include <string.h> #ifdef HAVE_OPENSSL +#include <ouroboros/hash.h> +#include <ouroboros/random.h> + #include <openssl/evp.h> #include <openssl/ec.h> #include <openssl/pem.h> @@ -144,8 +154,8 @@ static int __openssl_ecdh_gen_key(void ** kp) static ssize_t openssl_ecdh_pkp_create(void ** pkp, uint8_t * pk) { - uint8_t * pos; - ssize_t len; + uint8_t * pos; + ssize_t len; assert(pkp != NULL); assert(*pkp == NULL); @@ -172,15 +182,14 @@ static void openssl_ecdh_pkp_destroy(void * pkp) } static int openssl_ecdh_derive(void * pkp, - uint8_t * pk, - size_t len, + buffer_t pk, uint8_t * s) { uint8_t * pos; EVP_PKEY * pub; - pos = pk; /* d2i_PUBKEY increments the pointer, don't use key ptr! */ - pub = d2i_PUBKEY(NULL, (const uint8_t **) &pos, (long) len); + pos = pk.data; /* d2i_PUBKEY increments the pointer, don't use key ptr! */ + pub = d2i_PUBKEY(NULL, (const uint8_t **) &pos, (long) pk.len); if (pub == NULL) return -ECRYPT; @@ -202,7 +211,8 @@ static int openssl_ecdh_derive(void * pkp, * encryption context for each packet. */ -static int openssl_encrypt(struct flow * f, +static int openssl_encrypt(void * ctx, + uint8_t * key, struct shm_du_buff * sdb) { uint8_t * out; @@ -217,6 +227,8 @@ static int openssl_encrypt(struct flow * f, in = shm_du_buff_head(sdb); in_sz = shm_du_buff_tail(sdb) - in; + assert(in_sz > 0); + if (random_buffer(iv, IVSZ) < 0) goto fail_iv; @@ -224,28 +236,24 @@ static int openssl_encrypt(struct flow * f, if (out == NULL) goto fail_iv; - EVP_CIPHER_CTX_reset(f->ctx); + EVP_CIPHER_CTX_reset(ctx); - ret = EVP_EncryptInit_ex(f->ctx, - EVP_aes_256_cbc(), - NULL, - f->key, - iv); + ret = EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv); if (ret != 1) goto fail_encrypt_init; - ret = EVP_EncryptUpdate(f->ctx, out, &tmp_sz, in, in_sz); + ret = EVP_EncryptUpdate(ctx, out, &tmp_sz, in, in_sz); if (ret != 1) goto fail_encrypt; out_sz = tmp_sz; - ret = EVP_EncryptFinal_ex(f->ctx, out + tmp_sz, &tmp_sz); + ret = EVP_EncryptFinal_ex(ctx, out + tmp_sz, &tmp_sz); if (ret != 1) goto fail_encrypt; out_sz += tmp_sz; - EVP_CIPHER_CTX_cleanup(f->ctx); + EVP_CIPHER_CTX_cleanup(ctx); assert(out_sz >= in_sz); @@ -266,14 +274,15 @@ static int openssl_encrypt(struct flow * f, fail_tail_alloc: shm_du_buff_head_release(sdb, IVSZ); fail_encrypt: - EVP_CIPHER_CTX_cleanup(f->ctx); + EVP_CIPHER_CTX_cleanup(ctx); fail_encrypt_init: free(out); fail_iv: return -ECRYPT; } -static int openssl_decrypt(struct flow * f, +static int openssl_decrypt(void * ctx, + uint8_t * key, struct shm_du_buff * sdb) { uint8_t * in; @@ -284,35 +293,34 @@ static int openssl_decrypt(struct flow * f, int in_sz; int tmp_sz; + in_sz = shm_du_buff_len(sdb); + if (in_sz < IVSZ) + return -ECRYPT; + in = shm_du_buff_head_release(sdb, IVSZ); memcpy(iv, in, IVSZ); in = shm_du_buff_head(sdb); - - in_sz = shm_du_buff_tail(sdb) - shm_du_buff_head(sdb); + in_sz = shm_du_buff_tail(sdb) - in; out = malloc(in_sz); if (out == NULL) goto fail_malloc; - EVP_CIPHER_CTX_reset(f->ctx); + EVP_CIPHER_CTX_reset(ctx); - ret = EVP_DecryptInit_ex(f->ctx, - EVP_aes_256_cbc(), - NULL, - f->key, - iv); + ret = EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv); if (ret != 1) goto fail_decrypt_init; - ret = EVP_DecryptUpdate(f->ctx, out, &tmp_sz, in, in_sz); + ret = EVP_DecryptUpdate(ctx, out, &tmp_sz, in, in_sz); if (ret != 1) goto fail_decrypt; out_sz = tmp_sz; - ret = EVP_DecryptFinal_ex(f->ctx, out + tmp_sz, &tmp_sz); + ret = EVP_DecryptFinal_ex(ctx, out + tmp_sz, &tmp_sz); if (ret != 1) goto fail_decrypt; @@ -329,7 +337,7 @@ static int openssl_decrypt(struct flow * f, return 0; fail_decrypt: - EVP_CIPHER_CTX_cleanup(f->ctx); + EVP_CIPHER_CTX_cleanup(ctx); fail_decrypt_init: free(out); fail_malloc: @@ -353,8 +361,8 @@ static void openssl_crypt_fini(void * ctx) #endif /* HAVE_OPENSSL */ -static int crypt_dh_pkp_create(void ** pkp, - uint8_t * pk) +int crypt_dh_pkp_create(void ** pkp, + uint8_t * pk) { #ifdef HAVE_OPENSSL assert(pkp != NULL); @@ -364,14 +372,13 @@ static int crypt_dh_pkp_create(void ** pkp, (void) pkp; (void) pk; - memset(pk, 0, MSGBUFSZ); *pkp = NULL; return 0; #endif } -static void crypt_dh_pkp_destroy(void * pkp) +void crypt_dh_pkp_destroy(void * pkp) { #ifdef HAVE_OPENSSL openssl_ecdh_pkp_destroy(pkp); @@ -381,17 +388,15 @@ static void crypt_dh_pkp_destroy(void * pkp) #endif } -static int crypt_dh_derive(void * pkp, - uint8_t * pk, - size_t len, - uint8_t * s) +int crypt_dh_derive(void * pkp, + buffer_t pk, + uint8_t * s) { #ifdef HAVE_OPENSSL - return openssl_ecdh_derive(pkp, pk, len, s); + return openssl_ecdh_derive(pkp, pk, s); #else (void) pkp; (void) pk; - (void) len; memset(s, 0, SYMMKEYSZ); @@ -399,50 +404,52 @@ static int crypt_dh_derive(void * pkp, #endif } -static int crypt_encrypt(struct flow * f, - struct shm_du_buff * sdb) +int crypt_encrypt(struct crypt_info * info, + struct shm_du_buff * sdb) { + if (info->flags == 0) + return 0; + #ifdef HAVE_OPENSSL - return openssl_encrypt(f, sdb); + return openssl_encrypt(info->ctx, info->key, sdb); #else - (void) f; (void) sdb; return 0; #endif } -static int crypt_decrypt(struct flow * f, - struct shm_du_buff * sdb) +int crypt_decrypt(struct crypt_info * info, + struct shm_du_buff * sdb) { + if (info->flags == 0) + return 0; + #ifdef HAVE_OPENSSL - return openssl_decrypt(f, sdb); + return openssl_decrypt(info->ctx, info->key, sdb); #else - (void) f; (void) sdb; return -ECRYPT; #endif } -static int crypt_init(void ** ctx) +int crypt_init(struct crypt_info * info) { #ifdef HAVE_OPENSSL - return openssl_crypt_init(ctx); + return openssl_crypt_init(&info->ctx); #else - assert(ctx != NULL); - *ctx = NULL; - + info->ctx = NULL; return 0; #endif } -static void crypt_fini(void * ctx) +void crypt_fini(struct crypt_info * info) { #ifdef HAVE_OPENSSL - openssl_crypt_fini(ctx); + openssl_crypt_fini(info->ctx); #else - assert(ctx == NULL); - (void) ctx; + (void) info; + assert(info->ctx == NULL); #endif } diff --git a/src/lib/dev.c b/src/lib/dev.c index c8b7d93b..92310b9e 100644 --- a/src/lib/dev.c +++ b/src/lib/dev.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * API for applications * @@ -28,26 +28,36 @@ #include "config.h" -#include <ouroboros/hash.h> -#include <ouroboros/cacep.h> -#include <ouroboros/errno.h> +#include <ouroboros/bitmap.h> +#include <ouroboros/cep.h> +#include <ouroboros/crypt.h> #include <ouroboros/dev.h> +#include <ouroboros/errno.h> +#include <ouroboros/fccntl.h> +#include <ouroboros/flow.h> +#include <ouroboros/fqueue.h> +#include <ouroboros/hash.h> +#include <ouroboros/ipcp.h> #include <ouroboros/ipcp-dev.h> +#include <ouroboros/list.h> #include <ouroboros/local-dev.h> -#include <ouroboros/sockets.h> -#include <ouroboros/fccntl.h> -#include <ouroboros/bitmap.h> +#include <ouroboros/np1_flow.h> #include <ouroboros/pthread.h> #include <ouroboros/random.h> +#include <ouroboros/serdes-irm.h> #include <ouroboros/shm_flow_set.h> #include <ouroboros/shm_rdrbuff.h> #include <ouroboros/shm_rbuff.h> +#include <ouroboros/sockets.h> #include <ouroboros/utils.h> -#include <ouroboros/fqueue.h> #ifdef PROC_FLOW_STATS #include <ouroboros/rib.h> #endif +#ifdef HAVE_LIBGCRYPT +#include <gcrypt.h> +#endif +#include <assert.h> #include <stdlib.h> #include <string.h> #include <stdio.h> @@ -65,51 +75,34 @@ #define CRCLEN (sizeof(uint32_t)) #define SECMEMSZ 16384 -#define SYMMKEYSZ 32 #define MSGBUFSZ 2048 -struct flow_set { - size_t idx; -}; - -struct fqueue { - int fqueue[2 * SHM_BUFFER_SIZE]; /* Safe copy from shm. */ - size_t fqsize; - size_t next; -}; - -enum port_state { - PORT_NULL = 0, - PORT_INIT, - PORT_ID_PENDING, - PORT_ID_ASSIGNED, - PORT_DESTROY -}; - -struct port { +/* map flow_ids to flow descriptors; track state of the flow */ +struct fmap { int fd; - - enum port_state state; - pthread_mutex_t state_lock; - pthread_cond_t state_cond; + /* TODO: use actual flow state */ + enum flow_state state; }; #define frcti_to_flow(frcti) \ ((struct flow *)((uint8_t *) frcti - offsetof(struct flow, frcti))) struct flow { + struct list_head next; + + struct flow_info info; + struct shm_rbuff * rx_rb; struct shm_rbuff * tx_rb; struct shm_flow_set * set; - int flow_id; - int oflags; - qosspec_t qs; + + uint16_t oflags; ssize_t part_idx; - void * ctx; - uint8_t key[SYMMKEYSZ]; + struct crypt_info crypt; - pid_t pid; + struct timespec snd_act; + struct timespec rcv_act; bool snd_timesout; bool rcv_timesout; @@ -119,148 +112,299 @@ struct flow { struct frcti * frcti; }; -struct { - char * prog; - pid_t pid; +struct flow_set { + size_t idx; + pthread_rwlock_t lock; +}; +struct fqueue { + struct flowevent fqueue[SHM_BUFFER_SIZE]; /* Safe copy from shm. */ + size_t fqsize; + size_t next; +}; + +struct { struct shm_rdrbuff * rdrb; struct shm_flow_set * fqset; - struct timerwheel * tw; - struct bmp * fds; struct bmp * fqueues; struct flow * flows; - struct port * ports; + struct fmap * id_to_fd; + struct list_head flow_list; + + pthread_mutex_t mtx; + pthread_cond_t cond; + + pthread_t tx; + pthread_t rx; + size_t n_frcti; + fset_t * frct_set; pthread_rwlock_t lock; } ai; -#include "frct.c" - -static void port_destroy(struct port * p) +static void flow_destroy(struct fmap * p) { - pthread_mutex_lock(&p->state_lock); + pthread_mutex_lock(&ai.mtx); - if (p->state == PORT_DESTROY) { - pthread_mutex_unlock(&p->state_lock); + if (p->state == FLOW_DESTROY) { + pthread_mutex_unlock(&ai.mtx); return; } - if (p->state == PORT_ID_PENDING) - p->state = PORT_DESTROY; + if (p->state == FLOW_ALLOC_PENDING) + p->state = FLOW_DESTROY; else - p->state = PORT_NULL; + p->state = FLOW_NULL; + + pthread_cond_signal(&ai.cond); - pthread_cond_signal(&p->state_cond); + pthread_cleanup_push(__cleanup_mutex_unlock, &ai.mtx); - while (p->state != PORT_NULL) - pthread_cond_wait(&p->state_cond, &p->state_lock); + while (p->state != FLOW_NULL) + pthread_cond_wait(&ai.cond, &ai.mtx); - p->fd = -1; - p->state = PORT_INIT; + p->fd = -1; + p->state = FLOW_INIT; - pthread_mutex_unlock(&p->state_lock); + pthread_cleanup_pop(true); } -static void port_set_state(struct port * p, - enum port_state state) +static void flow_set_state(struct fmap * p, + enum flow_state state) { - pthread_mutex_lock(&p->state_lock); + pthread_mutex_lock(&ai.mtx); - if (p->state == PORT_DESTROY) { - pthread_mutex_unlock(&p->state_lock); + if (p->state == FLOW_DESTROY) { + pthread_mutex_unlock(&ai.mtx); return; } p->state = state; - pthread_cond_broadcast(&p->state_cond); + pthread_cond_broadcast(&ai.cond); - pthread_mutex_unlock(&p->state_lock); + pthread_mutex_unlock(&ai.mtx); } -static enum port_state port_wait_assign(int flow_id) +static enum flow_state flow_wait_assign(int flow_id) { - enum port_state state; - struct port * p; + enum flow_state state; + struct fmap * p; - p = &ai.ports[flow_id]; + p = &ai.id_to_fd[flow_id]; - pthread_mutex_lock(&p->state_lock); + pthread_mutex_lock(&ai.mtx); - if (p->state == PORT_ID_ASSIGNED) { - pthread_mutex_unlock(&p->state_lock); - return PORT_ID_ASSIGNED; + if (p->state == FLOW_ALLOCATED) { + pthread_mutex_unlock(&ai.mtx); + return FLOW_ALLOCATED; } - if (p->state == PORT_INIT) - p->state = PORT_ID_PENDING; + if (p->state == FLOW_INIT) + p->state = FLOW_ALLOC_PENDING; + + pthread_cleanup_push(__cleanup_mutex_unlock, &ai.mtx); - while (p->state == PORT_ID_PENDING) - pthread_cond_wait(&p->state_cond, &p->state_lock); + while (p->state == FLOW_ALLOC_PENDING) + pthread_cond_wait(&ai.cond, &ai.mtx); - if (p->state == PORT_DESTROY) { - p->state = PORT_NULL; - pthread_cond_broadcast(&p->state_cond); + if (p->state == FLOW_DESTROY) { + p->state = FLOW_NULL; + pthread_cond_broadcast(&ai.cond); } state = p->state; - assert(state != PORT_INIT); + pthread_cleanup_pop(true); - pthread_mutex_unlock(&p->state_lock); + assert(state != FLOW_INIT); return state; } -static int proc_announce(char * prog) +static int proc_announce(const char * prog) +{ + uint8_t buf[SOCK_BUF_SIZE]; + buffer_t msg = {buf, SOCK_BUF_SIZE}; + int err; + + if (proc_announce__irm_req_ser(&msg, prog) < 0) + return -ENOMEM; + + err = send_recv_msg(&msg); + if (err < 0) + return err; + + return irm__irm_result_des(&msg); +} + +/* IRMd will clean up the mess if this fails */ +static void proc_exit(void) { - irm_msg_t msg = IRM_MSG__INIT; - irm_msg_t * recv_msg; - int ret = -1; - - msg.code = IRM_MSG_CODE__IRM_PROC_ANNOUNCE; - msg.has_pid = true; - msg.pid = ai.pid; - msg.prog = prog; - - recv_msg = send_recv_irm_msg(&msg); - if (recv_msg == NULL) { - return -EIRMD; + uint8_t buf[SOCK_BUF_SIZE]; + buffer_t msg = {buf, SOCK_BUF_SIZE}; + + if (proc_exit__irm_req_ser(&msg) < 0) + return; + + send_recv_msg(&msg); +} + +#include "frct.c" + +void * flow_tx(void * o) +{ + struct timespec tic = TIMESPEC_INIT_NS(TICTIME); + + (void) o; + + while (true) { + timerwheel_move(); + + nanosleep(&tic, NULL); } - if (!recv_msg->has_result || (ret = recv_msg->result)) { - irm_msg__free_unpacked(recv_msg, NULL); - return ret; + return (void *) 0; +} + +static void flow_send_keepalive(struct flow * flow, + struct timespec now) +{ + struct shm_du_buff * sdb; + ssize_t idx; + uint8_t * ptr; + + idx = shm_rdrbuff_alloc(ai.rdrb, 0, &ptr, &sdb); + if (idx < 0) + return; + + pthread_rwlock_wrlock(&ai.lock); + + flow->snd_act = now; + + if (shm_rbuff_write(flow->tx_rb, idx)) + shm_rdrbuff_remove(ai.rdrb, idx); + else + shm_flow_set_notify(flow->set, flow->info.id, FLOW_PKT); + + pthread_rwlock_unlock(&ai.lock); +} + +/* Needs rdlock on ai. */ +static void _flow_keepalive(struct flow * flow) +{ + struct timespec now; + struct timespec s_act; + struct timespec r_act; + int flow_id; + time_t timeo; + uint32_t acl; + + s_act = flow->snd_act; + r_act = flow->rcv_act; + + flow_id = flow->info.id; + timeo = flow->info.qs.timeout; + + acl = shm_rbuff_get_acl(flow->rx_rb); + if (timeo == 0 || acl & (ACL_FLOWPEER | ACL_FLOWDOWN)) + return; + + clock_gettime(PTHREAD_COND_CLOCK, &now); + + if (ts_diff_ns(&r_act, &now) > (int64_t) timeo * MILLION) { + shm_rbuff_set_acl(flow->rx_rb, ACL_FLOWPEER); + shm_flow_set_notify(ai.fqset, flow_id, FLOW_PEER); + return; } - irm_msg__free_unpacked(recv_msg, NULL); + if (ts_diff_ns(&s_act, &now) > (int64_t) timeo * (MILLION >> 2)) { + pthread_rwlock_unlock(&ai.lock); - return ret; + flow_send_keepalive(flow, now); + + pthread_rwlock_rdlock(&ai.lock); + } +} + +static void handle_keepalives(void) +{ + struct list_head * p; + struct list_head * h; + + pthread_rwlock_rdlock(&ai.lock); + + list_for_each_safe(p, h, &ai.flow_list) { + struct flow * flow; + flow = list_entry(p, struct flow, next); + _flow_keepalive(flow); + } + + pthread_rwlock_unlock(&ai.lock); +} + +static void __cleanup_fqueue_destroy(void * fq) +{ + fqueue_destroy((fqueue_t *) fq); +} + +void * flow_rx(void * o) +{ + struct timespec tic = TIMESPEC_INIT_NS(TICTIME); + int ret; + struct fqueue * fq; + + (void) o; + + fq = fqueue_create(); + + pthread_cleanup_push(__cleanup_fqueue_destroy, fq); + + /* fevent will filter all FRCT packets for us */ + while ((ret = fevent(ai.frct_set, fq, &tic)) != 0) { + if (ret == -ETIMEDOUT) { + handle_keepalives(); + continue; + } + + while (fqueue_next(fq) >= 0) + ; /* no need to act */ + } + + pthread_cleanup_pop(true); + + return (void *) 0; } static void flow_clear(int fd) { memset(&ai.flows[fd], 0, sizeof(ai.flows[fd])); - ai.flows[fd].flow_id = -1; - ai.flows[fd].pid = -1; + ai.flows[fd].info.id = -1; } -#include "crypt.c" - -static void flow_fini(int fd) +static void __flow_fini(int fd) { assert(fd >= 0 && fd < SYS_MAX_FLOWS); - if (ai.flows[fd].flow_id != -1) { - port_destroy(&ai.ports[ai.flows[fd].flow_id]); - bmp_release(ai.fds, fd); - } + if (ai.flows[fd].frcti != NULL) { + ai.n_frcti--; + if (ai.n_frcti == 0) { + pthread_cancel(ai.tx); + pthread_join(ai.tx, NULL); + } + + shm_flow_set_del(ai.fqset, 0, ai.flows[fd].info.id); - if (ai.flows[fd].frcti != NULL) frcti_destroy(ai.flows[fd].frcti); + } + + if (ai.flows[fd].info.id != -1) { + flow_destroy(&ai.id_to_fd[ai.flows[fd].info.id]); + bmp_release(ai.fds, fd); + } if (ai.flows[fd].rx_rb != NULL) { shm_rbuff_set_acl(ai.flows[fd].rx_rb, ACL_FLOWDOWN); @@ -274,24 +418,36 @@ static void flow_fini(int fd) if (ai.flows[fd].set != NULL) { shm_flow_set_notify(ai.flows[fd].set, - ai.flows[fd].flow_id, + ai.flows[fd].info.id, FLOW_DEALLOC); shm_flow_set_close(ai.flows[fd].set); } - if (ai.flows[fd].ctx != NULL) - crypt_fini(ai.flows[fd].ctx); + crypt_fini(&ai.flows[fd].crypt); + + list_del(&ai.flows[fd].next); flow_clear(fd); } -static int flow_init(int flow_id, - pid_t pid, - qosspec_t qs, - uint8_t * s) +static void flow_fini(int fd) { - int fd; - int err = -ENOMEM; + pthread_rwlock_wrlock(&ai.lock); + + __flow_fini(fd); + + pthread_rwlock_unlock(&ai.lock); +} + +static int flow_init(struct flow_info * info, + buffer_t * sk) +{ + struct timespec now; + struct flow * flow; + int fd; + int err = -ENOMEM; + + clock_gettime(PTHREAD_COND_CLOCK, &now); pthread_rwlock_wrlock(&ai.lock); @@ -301,46 +457,75 @@ static int flow_init(int flow_id, goto fail_fds; } - ai.flows[fd].rx_rb = shm_rbuff_open(ai.pid, flow_id); - if (ai.flows[fd].rx_rb == NULL) + flow = &ai.flows[fd]; + + flow->info = *info; + + flow->rx_rb = shm_rbuff_open(info->n_pid, info->id); + if (flow->rx_rb == NULL) goto fail_rx_rb; - ai.flows[fd].tx_rb = shm_rbuff_open(pid, flow_id); - if (ai.flows[fd].tx_rb == NULL) + flow->tx_rb = shm_rbuff_open(info->n_1_pid, info->id); + if (flow->tx_rb == NULL) goto fail_tx_rb; - ai.flows[fd].set = shm_flow_set_open(pid); - if (ai.flows[fd].set == NULL) + flow->set = shm_flow_set_open(info->n_1_pid); + if (flow->set == NULL) goto fail_set; - ai.flows[fd].flow_id = flow_id; - ai.flows[fd].oflags = FLOWFDEFAULT; - ai.flows[fd].pid = pid; - ai.flows[fd].part_idx = NO_PART; - ai.flows[fd].qs = qs; + flow->oflags = FLOWFDEFAULT; + flow->part_idx = NO_PART; + flow->snd_act = now; + flow->rcv_act = now; + + flow->crypt.flags = info->qs.cypher_s; /* TODO: move cypher_s */ + + memset(flow->crypt.key, 0, SYMMKEYSZ); - if (qs.cypher_s > 0) { - assert(s != NULL); - if (crypt_init(&ai.flows[fd].ctx) < 0) - goto fail_ctx; + if (flow->crypt.flags > 0 && sk!= NULL && sk->data != NULL) + memcpy(flow->crypt.key, sk->data , sk->len); + + if (crypt_init(&flow->crypt) < 0) + goto fail_crypt; + + assert(flow->frcti == NULL); + + if (info->qs.in_order != 0) { + flow->frcti = frcti_create(fd, DELT_A, DELT_R, info->mpl); + if (flow->frcti == NULL) + goto fail_frcti; - memcpy(ai.flows[fd].key, s, SYMMKEYSZ); + if (shm_flow_set_add(ai.fqset, 0, info->id)) + goto fail_flow_set_add; + + ++ai.n_frcti; + if (ai.n_frcti == 1 && + pthread_create(&ai.tx, NULL, flow_tx, NULL) < 0) + goto fail_tx_thread; } - ai.ports[flow_id].fd = fd; + list_add_tail(&flow->next, &ai.flow_list); + + ai.id_to_fd[info->id].fd = fd; - port_set_state(&ai.ports[flow_id], PORT_ID_ASSIGNED); + flow_set_state(&ai.id_to_fd[info->id], FLOW_ALLOCATED); pthread_rwlock_unlock(&ai.lock); return fd; - fail_ctx: - shm_flow_set_close(ai.flows[fd].set); + fail_tx_thread: + shm_flow_set_del(ai.fqset, 0, info->id); + fail_flow_set_add: + frcti_destroy(flow->frcti); + fail_frcti: + crypt_fini(&flow->crypt); + fail_crypt: + shm_flow_set_close(flow->set); fail_set: - shm_rbuff_close(ai.flows[fd].tx_rb); + shm_rbuff_close(flow->tx_rb); fail_tx_rb: - shm_rbuff_close(ai.flows[fd].rx_rb); + shm_rbuff_close(flow->rx_rb); fail_rx_rb: bmp_release(ai.fds, fd); fail_fds: @@ -362,110 +547,143 @@ static void init(int argc, char ** argv, char ** envp) { - const char * prog = argv[0]; - int i; + char * prog = argv[0]; + int i; #ifdef PROC_FLOW_STATS - char procstr[32]; + char procstr[32]; #endif (void) argc; (void) envp; - assert(ai.prog == NULL); - if (check_python(argv[0])) prog = argv[1]; - ai.pid = getpid(); + prog = path_strip(prog); + if (prog == NULL) { + fprintf(stderr, "FATAL: Could not determine program name.\n"); + goto fail_prog; + } + + if (proc_announce(prog)) { + fprintf(stderr, "FATAL: Could not announce to IRMd.\n"); + goto fail_prog; + } + #ifdef HAVE_LIBGCRYPT if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) { - if (!gcry_check_version(GCRYPT_VERSION)) - goto fail_fds; + if (!gcry_check_version(GCRYPT_VERSION)) { + fprintf(stderr, "FATAL: Could not get gcry version.\n"); + goto fail_prog; + } gcry_control(GCRYCTL_DISABLE_SECMEM, 0); gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); } #endif ai.fds = bmp_create(PROG_MAX_FLOWS - PROG_RES_FDS, PROG_RES_FDS); - if (ai.fds == NULL) + if (ai.fds == NULL) { + fprintf(stderr, "FATAL: Could not create fd bitmap.\n"); goto fail_fds; + } ai.fqueues = bmp_create(PROG_MAX_FQUEUES, 0); - if (ai.fqueues == NULL) + if (ai.fqueues == NULL) { + fprintf(stderr, "FATAL: Could not create fqueue bitmap.\n"); goto fail_fqueues; + } ai.rdrb = shm_rdrbuff_open(); - if (ai.rdrb == NULL) + if (ai.rdrb == NULL) { + fprintf(stderr, "FATAL: Could not open packet buffer.\n"); goto fail_rdrb; + } ai.flows = malloc(sizeof(*ai.flows) * PROG_MAX_FLOWS); - if (ai.flows == NULL) + if (ai.flows == NULL) { + fprintf(stderr, "FATAL: Could not malloc flows.\n"); goto fail_flows; + } for (i = 0; i < PROG_MAX_FLOWS; ++i) flow_clear(i); - ai.ports = malloc(sizeof(*ai.ports) * SYS_MAX_FLOWS); - if (ai.ports == NULL) - goto fail_ports; + ai.id_to_fd = malloc(sizeof(*ai.id_to_fd) * SYS_MAX_FLOWS); + if (ai.id_to_fd == NULL) { + fprintf(stderr, "FATAL: Could not malloc id_to_fd.\n"); + goto fail_id_to_fd; + } - if (prog != NULL) { - ai.prog = strdup(path_strip((char *) prog)); - if (ai.prog == NULL) - goto fail_prog; + for (i = 0; i < SYS_MAX_FLOWS; ++i) + ai.id_to_fd[i].state = FLOW_INIT; - if (proc_announce((char *) ai.prog)) - goto fail_announce; + if (pthread_mutex_init(&ai.mtx, NULL)) { + fprintf(stderr, "FATAL: Could not init mutex.\n"); + goto fail_mtx; } - for (i = 0; i < SYS_MAX_FLOWS; ++i) { - ai.ports[i].state = PORT_INIT; - if (pthread_mutex_init(&ai.ports[i].state_lock, NULL)) { - int j; - for (j = 0; j < i; ++j) - pthread_mutex_destroy(&ai.ports[j].state_lock); - goto fail_announce; - } - if (pthread_cond_init(&ai.ports[i].state_cond, NULL)) { - int j; - for (j = 0; j < i; ++j) - pthread_cond_destroy(&ai.ports[j].state_cond); - goto fail_state_cond; - } + if (pthread_cond_init(&ai.cond, NULL) < 0) { + fprintf(stderr, "FATAL: Could not init condvar.\n"); + goto fail_cond; } - if (pthread_rwlock_init(&ai.lock, NULL)) - goto fail_lock; + if (pthread_rwlock_init(&ai.lock, NULL) < 0) { + fprintf(stderr, "FATAL: Could not initialize flow lock.\n"); + goto fail_flow_lock; + } ai.fqset = shm_flow_set_open(getpid()); - if (ai.fqset == NULL) + if (ai.fqset == NULL) { + fprintf(stderr, "FATAL: Could not open flow set.\n"); goto fail_fqset; + } - if (timerwheel_init() < 0) + ai.frct_set = fset_create(); + if (ai.frct_set == NULL || ai.frct_set->idx != 0) { + fprintf(stderr, "FATAL: Could not create FRCT set.\n"); + goto fail_frct_set; + } + + if (timerwheel_init() < 0) { + fprintf(stderr, "FATAL: Could not initialize timerwheel.\n"); goto fail_timerwheel; + } #if defined PROC_FLOW_STATS if (strstr(argv[0], "ipcpd") == NULL) { sprintf(procstr, "proc.%d", getpid()); - /* Don't bail on fail, it just won't show metrics */ - rib_init(procstr); + if (rib_init(procstr) < 0) { + fprintf(stderr, "FATAL: Could not initialize RIB.\n"); + goto fail_rib_init; + } } #endif + if (pthread_create(&ai.rx, NULL, flow_rx, NULL) < 0) { + fprintf(stderr, "FATAL: Could not start monitor thread.\n"); + goto fail_monitor; + } + + list_head_init(&ai.flow_list); + return; + fail_monitor: +#if defined PROC_FLOW_STATS + rib_fini(); + fail_rib_init: +#endif + timerwheel_fini(); fail_timerwheel: + fset_destroy(ai.frct_set); + fail_frct_set: shm_flow_set_close(ai.fqset); fail_fqset: pthread_rwlock_destroy(&ai.lock); - fail_lock: - for (i = 0; i < SYS_MAX_FLOWS; ++i) - pthread_cond_destroy(&ai.ports[i].state_cond); - fail_state_cond: - for (i = 0; i < SYS_MAX_FLOWS; ++i) - pthread_mutex_destroy(&ai.ports[i].state_lock); - fail_announce: - free(ai.prog); - fail_prog: - free(ai.ports); - fail_ports: + fail_flow_lock: + pthread_cond_destroy(&ai.cond); + fail_cond: + pthread_mutex_destroy(&ai.mtx); + fail_mtx: + free(ai.id_to_fd); + fail_id_to_fd: free(ai.flows); fail_flows: shm_rdrbuff_close(ai.rdrb); @@ -474,57 +692,60 @@ static void init(int argc, fail_fqueues: bmp_destroy(ai.fds); fail_fds: - fprintf(stderr, "FATAL: ouroboros-dev init failed. " - "Make sure an IRMd is running.\n\n"); memset(&ai, 0, sizeof(ai)); + fail_prog: exit(EXIT_FAILURE); } static void fini(void) { - int i = 0; -#ifdef PROC_FLOW_STATS - rib_fini(); -#endif + int i; if (ai.fds == NULL) return; - if (ai.prog != NULL) - free(ai.prog); + pthread_cancel(ai.rx); + pthread_join(ai.rx, NULL); pthread_rwlock_wrlock(&ai.lock); for (i = 0; i < PROG_MAX_FLOWS; ++i) { - if (ai.flows[i].flow_id != -1) { + if (ai.flows[i].info.id != -1) { ssize_t idx; shm_rbuff_set_acl(ai.flows[i].rx_rb, ACL_FLOWDOWN); while ((idx = shm_rbuff_read(ai.flows[i].rx_rb)) >= 0) shm_rdrbuff_remove(ai.rdrb, idx); - flow_fini(i); + __flow_fini(i); } } - shm_flow_set_close(ai.fqset); + pthread_cond_destroy(&ai.cond); + pthread_mutex_destroy(&ai.mtx); - for (i = 0; i < SYS_MAX_FLOWS; ++i) { - pthread_mutex_destroy(&ai.ports[i].state_lock); - pthread_cond_destroy(&ai.ports[i].state_cond); - } + pthread_rwlock_unlock(&ai.lock); +#ifdef PROC_FLOW_STATS + rib_fini(); +#endif timerwheel_fini(); - shm_rdrbuff_close(ai.rdrb); + fset_destroy(ai.frct_set); + + shm_flow_set_close(ai.fqset); + + pthread_rwlock_destroy(&ai.lock); free(ai.flows); - free(ai.ports); + free(ai.id_to_fd); + + shm_rdrbuff_close(ai.rdrb); bmp_destroy(ai.fds); bmp_destroy(ai.fqueues); - pthread_rwlock_unlock(&ai.lock); + proc_exit(); - pthread_rwlock_destroy(&ai.lock); + memset(&ai, 0, sizeof(ai)); } #if defined(__MACH__) && defined(__APPLE__) @@ -541,310 +762,241 @@ __attribute__((section(FINI_SECTION))) __typeof__(fini) * __fini = fini; int flow_accept(qosspec_t * qs, const struct timespec * timeo) { - irm_msg_t msg = IRM_MSG__INIT; - irm_msg_t * recv_msg; - int fd; - void * pkp; /* public key pair */ - uint8_t s[SYMMKEYSZ]; /* secret key for flow */ - uint8_t buf[MSGBUFSZ]; - int err = -EIRMD; - ssize_t key_len; - - memset(s, 0, SYMMKEYSZ); - - msg.code = IRM_MSG_CODE__IRM_FLOW_ACCEPT; - msg.has_pid = true; - msg.pid = ai.pid; - - if (timeo != NULL) { - msg.has_timeo_sec = true; - msg.has_timeo_nsec = true; - msg.timeo_sec = timeo->tv_sec; - msg.timeo_nsec = timeo->tv_nsec; - } - - key_len = crypt_dh_pkp_create(&pkp, buf); - if (key_len < 0) { - err = -ECRYPT; - goto fail_crypt_pkp; - } - if (key_len > 0) { - msg.has_pk = true; - msg.pk.data = buf; - msg.pk.len = (uint32_t) key_len; - } - - pthread_cleanup_push(crypt_dh_pkp_destroy, pkp); - - recv_msg = send_recv_irm_msg(&msg); - - pthread_cleanup_pop(false); - - if (recv_msg == NULL) - goto fail_recv; - - if (!recv_msg->has_result) - goto fail_result; - - if (recv_msg->result != 0) { - err = recv_msg->result; - goto fail_result; - } - - if (!recv_msg->has_pid || !recv_msg->has_flow_id || - recv_msg->qosspec == NULL) - goto fail_result; - - if (recv_msg->pk.len != 0 && - crypt_dh_derive(pkp, recv_msg->pk.data, - recv_msg->pk.len, s) < 0) { - err = -ECRYPT; - goto fail_result; - } + struct flow_info flow; + uint8_t buf[SOCK_BUF_SIZE]; + buffer_t msg = {buf, SOCK_BUF_SIZE}; + buffer_t sk; + int fd; + int err; - crypt_dh_pkp_destroy(pkp); +#ifdef QOS_DISABLE_CRC + if (qs != NULL) + qs->ber = 1; +#endif + memset(&flow, 0, sizeof(flow)); - fd = flow_init(recv_msg->flow_id, recv_msg->pid, - msg_to_spec(recv_msg->qosspec), s); + flow.n_pid = getpid(); + flow.qs = qs == NULL ? qos_raw : *qs; - irm_msg__free_unpacked(recv_msg, NULL); + if (flow_accept__irm_req_ser(&msg, &flow, timeo)) + return -ENOMEM; - if (fd < 0) - return fd; + err = send_recv_msg(&msg); + if (err < 0) + return err; - pthread_rwlock_wrlock(&ai.lock); + err = flow__irm_result_des(&msg, &flow, &sk); + if (err < 0) + return err; - assert(ai.flows[fd].frcti == NULL); + fd = flow_init(&flow, &sk); - if (ai.flows[fd].qs.in_order != 0) { - ai.flows[fd].frcti = frcti_create(fd); - if (ai.flows[fd].frcti == NULL) { - pthread_rwlock_unlock(&ai.lock); - flow_dealloc(fd); - return -ENOMEM; - } - } + freebuf(sk); if (qs != NULL) - *qs = ai.flows[fd].qs; - - pthread_rwlock_unlock(&ai.lock); + *qs = flow.qs; return fd; - - fail_result: - irm_msg__free_unpacked(recv_msg, NULL); - fail_recv: - crypt_dh_pkp_destroy(pkp); - fail_crypt_pkp: - return err; } -static int __flow_alloc(const char * dst, - qosspec_t * qs, - const struct timespec * timeo, - bool join) +int flow_alloc(const char * dst, + qosspec_t * qs, + const struct timespec * timeo) { - irm_msg_t msg = IRM_MSG__INIT; - qosspec_msg_t qs_msg = QOSSPEC_MSG__INIT; - irm_msg_t * recv_msg; - int fd; - void * pkp = NULL; /* public key pair */ - uint8_t s[SYMMKEYSZ]; /* secret key for flow */ - uint8_t buf[MSGBUFSZ]; - int err = -EIRMD; - - memset(s, 0, SYMMKEYSZ); + struct flow_info flow; + uint8_t buf[SOCK_BUF_SIZE]; + buffer_t msg = {buf, SOCK_BUF_SIZE}; + buffer_t sk; /* symmetric key */ + int fd; + int err; #ifdef QOS_DISABLE_CRC if (qs != NULL) qs->ber = 1; #endif - msg.code = join ? IRM_MSG_CODE__IRM_FLOW_JOIN - : IRM_MSG_CODE__IRM_FLOW_ALLOC; - msg.dst = (char *) dst; - msg.has_pid = true; - msg.pid = ai.pid; - qs_msg = spec_to_msg(qs); - msg.qosspec = &qs_msg; - if (timeo != NULL) { - msg.has_timeo_sec = true; - msg.has_timeo_nsec = true; - msg.timeo_sec = timeo->tv_sec; - msg.timeo_nsec = timeo->tv_nsec; - } + memset(&flow, 0, sizeof(flow)); - if (!join && qs != NULL && qs->cypher_s != 0) { - ssize_t key_len; + flow.n_pid = getpid(); + flow.qs = qs == NULL ? qos_raw : *qs; - key_len = crypt_dh_pkp_create(&pkp, buf); - if (key_len < 0) { - err = -ECRYPT; - goto fail_crypt_pkp; - } + if (flow_alloc__irm_req_ser(&msg, &flow, dst, timeo)) + return -ENOMEM; - msg.has_pk = true; - msg.pk.data = buf; - msg.pk.len = (uint32_t) key_len; - } + err = send_recv_msg(&msg); + if (err < 0) + return err; - recv_msg = send_recv_irm_msg(&msg); - if (recv_msg == NULL) - goto fail_send; + err = flow__irm_result_des(&msg, &flow, &sk); + if (err < 0) + return err; - if (!recv_msg->has_result) - goto fail_result; + fd = flow_init(&flow, &sk); - if (recv_msg->result != 0) { - err = recv_msg->result; - goto fail_result; - } + freebuf(sk); - if (!recv_msg->has_pid || !recv_msg->has_flow_id) - goto fail_result; + if (qs != NULL) + *qs = flow.qs; - if (!join && qs != NULL && qs->cypher_s != 0) { - if (!recv_msg->has_pk || recv_msg->pk.len == 0) { - err = -ECRYPT; - goto fail_result; - } + return fd; +} - if (crypt_dh_derive(pkp, recv_msg->pk.data, - recv_msg->pk.len, s) < 0) { - err = -ECRYPT; - goto fail_result; - } +int flow_join(const char * dst, + qosspec_t * qs, + const struct timespec * timeo) +{ + struct flow_info flow; + uint8_t buf[SOCK_BUF_SIZE]; + buffer_t msg = {buf, SOCK_BUF_SIZE}; + int fd; + int err; - crypt_dh_pkp_destroy(pkp); - } +#ifdef QOS_DISABLE_CRC + if (qs != NULL) + qs->ber = 1; +#endif + if (qs != NULL && qs->cypher_s > 0) + return -ENOTSUP; /* TODO: Encrypted broadcast */ - fd = flow_init(recv_msg->flow_id, recv_msg->pid, - qs == NULL ? qos_raw : *qs, s); + memset(&flow, 0, sizeof(flow)); - irm_msg__free_unpacked(recv_msg, NULL); + flow.n_pid = getpid(); + flow.qs = qs == NULL ? qos_raw : *qs; - if (fd < 0) - return fd; + if (flow_alloc__irm_req_ser(&msg, &flow, dst, timeo)) + return -ENOMEM; - pthread_rwlock_wrlock(&ai.lock); + err = send_recv_msg(&msg); + if (err < 0) + return err; - assert(ai.flows[fd].frcti == NULL); + err = flow__irm_result_des(&msg, &flow, NULL); + if (err < 0) + return err; - if (ai.flows[fd].qs.in_order != 0) { - ai.flows[fd].frcti = frcti_create(fd); - if (ai.flows[fd].frcti == NULL) { - pthread_rwlock_unlock(&ai.lock); - flow_dealloc(fd); - return -ENOMEM; - } - } + fd = flow_init(&flow, NULL); - pthread_rwlock_unlock(&ai.lock); + if (qs != NULL) + *qs = flow.qs; return fd; - - fail_result: - irm_msg__free_unpacked(recv_msg, NULL); - fail_send: - crypt_dh_pkp_destroy(pkp); - fail_crypt_pkp: - return err; -} - -int flow_alloc(const char * dst, - qosspec_t * qs, - const struct timespec * timeo) -{ - return __flow_alloc(dst, qs, timeo, false); -} - -int flow_join(const char * dst, - qosspec_t * qs, - const struct timespec * timeo) -{ - if (qs != NULL && qs->cypher_s != 0) - return -ECRYPT; - - return __flow_alloc(dst, qs, timeo, true); } +#define PKT_BUF_LEN 2048 int flow_dealloc(int fd) { - irm_msg_t msg = IRM_MSG__INIT; - irm_msg_t * recv_msg; - struct flow * f; - time_t timeo; + struct flow_info info; + uint8_t pkt[PKT_BUF_LEN]; + uint8_t buf[SOCK_BUF_SIZE]; + buffer_t msg = {buf, SOCK_BUF_SIZE}; + struct timespec tic = TIMESPEC_INIT_NS(TICTIME); + struct timespec timeo = TIMESPEC_INIT_S(0); + struct flow * flow; + int err; if (fd < 0 || fd >= SYS_MAX_FLOWS ) return -EINVAL; - msg.code = IRM_MSG_CODE__IRM_FLOW_DEALLOC; - msg.has_flow_id = true; - msg.has_pid = true; - msg.pid = ai.pid; - msg.has_timeo_sec = true; - msg.has_timeo_nsec = true; - msg.timeo_nsec = 0; + memset(&info, 0, sizeof(flow)); - f = &ai.flows[fd]; + flow = &ai.flows[fd]; pthread_rwlock_rdlock(&ai.lock); - if (f->flow_id < 0) { + if (flow->info.id < 0) { pthread_rwlock_unlock(&ai.lock); return -ENOTALLOC; } - msg.flow_id = f->flow_id; + flow->oflags = FLOWFDEFAULT | FLOWFRNOPART; - timeo = frcti_dealloc(f->frcti); - while (timeo < 0) { /* keep the flow active for rtx */ - ssize_t ret; - uint8_t buf[128]; - struct timespec tic = {0, TICTIME}; + flow->rcv_timesout = true; + flow->rcv_timeo = tic; + + pthread_rwlock_unlock(&ai.lock); + + flow_read(fd, buf, SOCK_BUF_SIZE); - f->oflags = FLOWFDEFAULT | FLOWFRNOPART; + pthread_rwlock_rdlock(&ai.lock); - f->rcv_timesout = true; - f->rcv_timeo = tic; + timeo.tv_sec = frcti_dealloc(flow->frcti); + while (timeo.tv_sec < 0) { /* keep the flow active for rtx */ + ssize_t ret; pthread_rwlock_unlock(&ai.lock); - ret = flow_read(fd, buf, 128); + ret = flow_read(fd, pkt, PKT_BUF_LEN); pthread_rwlock_rdlock(&ai.lock); - timeo = frcti_dealloc(f->frcti); + timeo.tv_sec = frcti_dealloc(flow->frcti); - if (ret == -EFLOWDOWN && timeo < 0) - timeo = -timeo; + if (ret == -EFLOWDOWN && timeo.tv_sec < 0) + timeo.tv_sec = -timeo.tv_sec; } - msg.timeo_sec = timeo; + pthread_cleanup_push(__cleanup_rwlock_unlock, &ai.lock); - shm_rbuff_fini(ai.flows[fd].tx_rb); + shm_rbuff_fini(flow->tx_rb); - pthread_rwlock_unlock(&ai.lock); + pthread_cleanup_pop(true); - recv_msg = send_recv_irm_msg(&msg); - if (recv_msg == NULL) - return -EIRMD; + info.id = flow->info.id; + info.n_pid = getpid(); - if (!recv_msg->has_result) { - irm_msg__free_unpacked(recv_msg, NULL); - return -EIRMD; - } + if (flow_dealloc__irm_req_ser(&msg, &info, &timeo) < 0) + return -ENOMEM; - irm_msg__free_unpacked(recv_msg, NULL); + err = send_recv_msg(&msg); + if (err < 0) + return err; - pthread_rwlock_wrlock(&ai.lock); + err = irm__irm_result_des(&msg); flow_fini(fd); + return err; +} + +int ipcp_flow_dealloc(int fd) +{ + struct flow_info info; + uint8_t buf[SOCK_BUF_SIZE]; + buffer_t msg = {buf, SOCK_BUF_SIZE}; + struct flow * flow; + int err; + + if (fd < 0 || fd >= SYS_MAX_FLOWS ) + return -EINVAL; + + flow = &ai.flows[fd]; + + memset(&info, 0, sizeof(flow)); + + pthread_rwlock_rdlock(&ai.lock); + + if (flow->info.id < 0) { + pthread_rwlock_unlock(&ai.lock); + return -ENOTALLOC; + } + + info.id = flow->info.id; + info.n_1_pid = flow->info.n_1_pid; + pthread_rwlock_unlock(&ai.lock); - return 0; + if (ipcp_flow_dealloc__irm_req_ser(&msg, &info) < 0) + return -ENOMEM; + + err = send_recv_msg(&msg); + if (err < 0) + return err; + + err = irm__irm_result_des(&msg); + + flow_fini(fd); + + return err; } int fccntl(int fd, @@ -853,6 +1005,7 @@ int fccntl(int fd, { uint32_t * fflags; uint16_t * cflags; + uint16_t csflags; va_list l; struct timespec * timeo; qosspec_t * qs; @@ -870,7 +1023,7 @@ int fccntl(int fd, pthread_rwlock_wrlock(&ai.lock); - if (flow->flow_id < 0) { + if (flow->info.id < 0) { pthread_rwlock_unlock(&ai.lock); va_end(l); return -ENOTALLOC; @@ -909,13 +1062,13 @@ int fccntl(int fd, goto einval; if (!flow->rcv_timesout) goto eperm; - *timeo = flow->snd_timeo; + *timeo = flow->rcv_timeo; break; case FLOWGQOSSPEC: qs = va_arg(l, qosspec_t *); if (qs == NULL) goto einval; - *qs = flow->qs; + *qs = flow->info.qs; break; case FLOWGRXQLEN: qlen = va_arg(l, size_t *); @@ -942,13 +1095,13 @@ int fccntl(int fd, rx_acl |= ACL_FLOWDOWN; tx_acl |= ACL_FLOWDOWN; shm_flow_set_notify(flow->set, - flow->flow_id, + flow->info.id, FLOW_DOWN); } else { rx_acl &= ~ACL_FLOWDOWN; tx_acl &= ~ACL_FLOWDOWN; shm_flow_set_notify(flow->set, - flow->flow_id, + flow->info.id, FLOW_UP); } @@ -963,15 +1116,13 @@ int fccntl(int fd, *fflags = flow->oflags; break; case FRCTSFLAGS: - cflags = va_arg(l, uint16_t *); - if (cflags == NULL) - goto einval; + csflags = (uint16_t) va_arg(l, uint32_t); if (flow->frcti == NULL) goto eperm; - frcti_setflags(flow->frcti, *cflags); + frcti_setflags(flow->frcti, csflags); break; case FRCTGFLAGS: - cflags = (uint16_t *) va_arg(l, int *); + cflags = (uint16_t *) va_arg(l, uint32_t *); if (cflags == NULL) goto einval; if (flow->frcti == NULL) @@ -1024,6 +1175,60 @@ static int add_crc(struct shm_du_buff * sdb) return 0; } +static int flow_tx_sdb(struct flow * flow, + struct shm_du_buff * sdb, + bool block, + struct timespec * abstime) +{ + struct timespec now; + ssize_t idx; + int ret; + + clock_gettime(PTHREAD_COND_CLOCK, &now); + + pthread_rwlock_wrlock(&ai.lock); + + flow->snd_act = now; + + pthread_rwlock_unlock(&ai.lock); + + idx = shm_du_buff_get_idx(sdb); + + pthread_rwlock_rdlock(&ai.lock); + + if (shm_du_buff_len(sdb) > 0) { + if (frcti_snd(flow->frcti, sdb) < 0) + goto enomem; + + if (crypt_encrypt(&flow->crypt, sdb) < 0) + goto enomem; + + if (flow->info.qs.ber == 0 && add_crc(sdb) != 0) + goto enomem; + } + + pthread_cleanup_push(__cleanup_rwlock_unlock, &ai.lock); + + if (!block) + ret = shm_rbuff_write(flow->tx_rb, idx); + else + ret = shm_rbuff_write_b(flow->tx_rb, idx, abstime); + + if (ret < 0) + shm_rdrbuff_remove(ai.rdrb, idx); + else + shm_flow_set_notify(flow->set, flow->info.id, FLOW_PKT); + + pthread_cleanup_pop(true); + + return 0; + +enomem: + pthread_rwlock_unlock(&ai.lock); + shm_rdrbuff_remove(ai.rdrb, idx); + return -ENOMEM; +} + ssize_t flow_write(int fd, const void * buf, size_t count) @@ -1034,31 +1239,27 @@ ssize_t flow_write(int fd, int flags; struct timespec abs; struct timespec * abstime = NULL; - struct timespec tic = {0, TICTIME}; - struct timespec tictime; struct shm_du_buff * sdb; uint8_t * ptr; - if (buf == NULL) - return 0; + if (buf == NULL && count != 0) + return -EINVAL; - if (fd < 0 || fd > PROG_MAX_FLOWS) + if (fd < 0 || fd >= PROG_MAX_FLOWS) return -EBADF; flow = &ai.flows[fd]; clock_gettime(PTHREAD_COND_CLOCK, &abs); - pthread_rwlock_rdlock(&ai.lock); + pthread_rwlock_wrlock(&ai.lock); - if (flow->flow_id < 0) { + if (flow->info.id < 0) { pthread_rwlock_unlock(&ai.lock); return -ENOTALLOC; } - ts_add(&tic, &abs, &tictime); - - if (ai.flows[fd].snd_timesout) { + if (flow->snd_timesout) { ts_add(&abs, &flow->snd_timeo, &abs); abstime = &abs; } @@ -1075,54 +1276,66 @@ ssize_t flow_write(int fd, return -EAGAIN; idx = shm_rdrbuff_alloc(ai.rdrb, count, &ptr, &sdb); } else { - while ((ret = frcti_window_wait(flow->frcti, &tictime)) < 0) { - if (ret != -ETIMEDOUT) - return ret; - - if (abstime != NULL && ts_diff_ns(&tictime, &abs) <= 0) - return -ETIMEDOUT; - - frcti_tick(flow->frcti); - - ts_add(&tictime, &tic, &tictime); - } + ret = frcti_window_wait(flow->frcti, abstime); + if (ret < 0) + return ret; idx = shm_rdrbuff_alloc_b(ai.rdrb, count, &ptr, &sdb, abstime); } if (idx < 0) return idx; - memcpy(ptr, buf, count); + if (count > 0) + memcpy(ptr, buf, count); - pthread_rwlock_rdlock(&ai.lock); + ret = flow_tx_sdb(flow, sdb, !(flags & FLOWFWNOBLOCK), abstime); + + return ret < 0 ? (ssize_t) ret : (ssize_t) count; +} + +static bool invalid_pkt(struct flow * flow, + struct shm_du_buff * sdb) +{ + if (shm_du_buff_len(sdb) == 0) + return true; - if (frcti_snd(flow->frcti, sdb) < 0) - goto enomem; + if (flow->info.qs.ber == 0 && chk_crc(sdb) != 0) + return true; - if (flow->qs.cypher_s > 0 && crypt_encrypt(flow, sdb) < 0) - goto enomem; + if (crypt_decrypt(&flow->crypt, sdb) < 0) + return true; - if (flow->qs.ber == 0 && add_crc(sdb) != 0) - goto enomem; + return false; +} - if (flags & FLOWFWNOBLOCK) - ret = shm_rbuff_write(flow->tx_rb, idx); - else - ret = shm_rbuff_write_b(flow->tx_rb, idx, abstime); +static ssize_t flow_rx_sdb(struct flow * flow, + struct shm_du_buff ** sdb, + bool block, + struct timespec * abstime) +{ + ssize_t idx; + struct timespec now; - if (ret < 0) - shm_rdrbuff_remove(ai.rdrb, idx); - else - shm_flow_set_notify(flow->set, flow->flow_id, FLOW_PKT); + idx = block ? shm_rbuff_read_b(flow->rx_rb, abstime) : + shm_rbuff_read(flow->rx_rb); + if (idx < 0) + return idx; - pthread_rwlock_unlock(&ai.lock); + clock_gettime(PTHREAD_COND_CLOCK, &now); - return ret < 0 ? (ssize_t) ret : (ssize_t) count; + pthread_rwlock_wrlock(&ai.lock); + + flow->rcv_act = now; - enomem: pthread_rwlock_unlock(&ai.lock); - shm_rdrbuff_remove(ai.rdrb, idx); - return -ENOMEM; + + *sdb = shm_rdrbuff_get(ai.rdrb, idx); + if (invalid_pkt(flow, *sdb)) { + shm_rdrbuff_remove(ai.rdrb, idx); + return -EAGAIN; + } + + return idx; } ssize_t flow_read(int fd, @@ -1132,120 +1345,100 @@ ssize_t flow_read(int fd, ssize_t idx; ssize_t n; uint8_t * packet; - struct shm_rbuff * rb; struct shm_du_buff * sdb; struct timespec abs; - struct timespec tic = {0, TICTIME}; - struct timespec tictime; + struct timespec now; struct timespec * abstime = NULL; struct flow * flow; - bool noblock; + bool block; bool partrd; - if (fd < 0 || fd > PROG_MAX_FLOWS) + if (fd < 0 || fd >= PROG_MAX_FLOWS) return -EBADF; flow = &ai.flows[fd]; - clock_gettime(PTHREAD_COND_CLOCK, &abs); + clock_gettime(PTHREAD_COND_CLOCK, &now); pthread_rwlock_rdlock(&ai.lock); - if (flow->part_idx == DONE_PART) { + if (flow->info.id < 0) { pthread_rwlock_unlock(&ai.lock); - flow->part_idx = NO_PART; - return 0; + return -ENOTALLOC; } - if (flow->flow_id < 0) { + if (flow->part_idx == DONE_PART) { pthread_rwlock_unlock(&ai.lock); - return -ENOTALLOC; + flow->part_idx = NO_PART; + return 0; } - rb = flow->rx_rb; - noblock = flow->oflags & FLOWFRNOBLOCK; + block = !(flow->oflags & FLOWFRNOBLOCK); partrd = !(flow->oflags & FLOWFRNOPART); - ts_add(&tic, &abs, &tictime); - - if (ai.flows[fd].rcv_timesout) { - ts_add(&abs, &flow->rcv_timeo, &abs); + if (flow->rcv_timesout) { + ts_add(&now, &flow->rcv_timeo, &abs); abstime = &abs; } idx = flow->part_idx; - if (idx < 0) { while ((idx = frcti_queued_pdu(flow->frcti)) < 0) { pthread_rwlock_unlock(&ai.lock); - idx = noblock ? shm_rbuff_read(rb) : - shm_rbuff_read_b(rb, &tictime); + idx = flow_rx_sdb(flow, &sdb, block, abstime); if (idx < 0) { - frcti_tick(flow->frcti); - - if (idx != -ETIMEDOUT) + if (block && idx != -EAGAIN) + return idx; + if (!block) return idx; - if (abstime != NULL - && ts_diff_ns(&tictime, &abs) <= 0) - return -ETIMEDOUT; - - ts_add(&tictime, &tic, &tictime); - pthread_rwlock_rdlock(&ai.lock); - continue; - } - - sdb = shm_rdrbuff_get(ai.rdrb, idx); - if (flow->qs.ber == 0 && chk_crc(sdb) != 0) { pthread_rwlock_rdlock(&ai.lock); - shm_rdrbuff_remove(ai.rdrb, idx); continue; } pthread_rwlock_rdlock(&ai.lock); - if (flow->qs.cypher_s > 0 - && crypt_decrypt(flow, sdb) < 0) { - pthread_rwlock_unlock(&ai.lock); - shm_rdrbuff_remove(ai.rdrb, idx); - return -ENOMEM; - } - frcti_rcv(flow->frcti, sdb); } } - frcti_tick(flow->frcti); + sdb = shm_rdrbuff_get(ai.rdrb, idx); pthread_rwlock_unlock(&ai.lock); - n = shm_rdrbuff_read(&packet, ai.rdrb, idx); + packet = shm_du_buff_head(sdb); + + n = shm_du_buff_len(sdb); assert(n >= 0); if (n <= (ssize_t) count) { memcpy(buf, packet, n); - shm_rdrbuff_remove(ai.rdrb, idx); + ipcp_sdb_release(sdb); pthread_rwlock_wrlock(&ai.lock); flow->part_idx = (partrd && n == (ssize_t) count) ? DONE_PART : NO_PART; + flow->rcv_act = now; + pthread_rwlock_unlock(&ai.lock); return n; } else { if (partrd) { memcpy(buf, packet, count); - sdb = shm_rdrbuff_get(ai.rdrb, idx); shm_du_buff_head_release(sdb, n); pthread_rwlock_wrlock(&ai.lock); flow->part_idx = idx; + + flow->rcv_act = now; + pthread_rwlock_unlock(&ai.lock); return count; } else { - shm_rdrbuff_remove(ai.rdrb, idx); + ipcp_sdb_release(sdb); return -EMSGSIZE; } } @@ -1253,26 +1446,31 @@ ssize_t flow_read(int fd, /* fqueue functions. */ -struct flow_set * fset_create() +struct flow_set * fset_create(void) { - struct flow_set * set = malloc(sizeof(*set)); + struct flow_set * set; + + set = malloc(sizeof(*set)); if (set == NULL) - return NULL; + goto fail_malloc; assert(ai.fqueues); pthread_rwlock_wrlock(&ai.lock); set->idx = bmp_allocate(ai.fqueues); - if (!bmp_is_id_valid(ai.fqueues, set->idx)) { - pthread_rwlock_unlock(&ai.lock); - free(set); - return NULL; - } + if (!bmp_is_id_valid(ai.fqueues, set->idx)) + goto fail_bmp_alloc; pthread_rwlock_unlock(&ai.lock); return set; + + fail_bmp_alloc: + pthread_rwlock_unlock(&ai.lock); + free(set); + fail_malloc: + return NULL; } void fset_destroy(struct flow_set * set) @@ -1291,13 +1489,13 @@ void fset_destroy(struct flow_set * set) free(set); } -struct fqueue * fqueue_create() +struct fqueue * fqueue_create(void) { struct fqueue * fq = malloc(sizeof(*fq)); if (fq == NULL) return NULL; - memset(fq->fqueue, -1, (SHM_BUFFER_SIZE) * sizeof(*fq->fqueue)); + memset(fq->fqueue, -1, SHM_BUFFER_SIZE * sizeof(*fq->fqueue)); fq->fqsize = 0; fq->next = 0; @@ -1320,41 +1518,57 @@ void fset_zero(struct flow_set * set) int fset_add(struct flow_set * set, int fd) { - int ret; - size_t packets; - size_t i; + struct flow * flow; + int ret; - if (set == NULL || fd < 0 || fd > SYS_MAX_FLOWS) + if (set == NULL || fd < 0 || fd >= SYS_MAX_FLOWS) return -EINVAL; - pthread_rwlock_wrlock(&ai.lock); + flow = &ai.flows[fd]; - if (ai.flows[fd].flow_id < 0) { - pthread_rwlock_unlock(&ai.lock); - return -EINVAL; + pthread_rwlock_rdlock(&ai.lock); + + if (flow->info.id < 0) { + ret = -EINVAL; + goto fail; } - ret = shm_flow_set_add(ai.fqset, set->idx, ai.flows[fd].flow_id); + if (flow->frcti != NULL) + shm_flow_set_del(ai.fqset, 0, ai.flows[fd].info.id); + + ret = shm_flow_set_add(ai.fqset, set->idx, ai.flows[fd].info.id); + if (ret < 0) + goto fail; - packets = shm_rbuff_queued(ai.flows[fd].rx_rb); - for (i = 0; i < packets; i++) - shm_flow_set_notify(ai.fqset, ai.flows[fd].flow_id, FLOW_PKT); + if (shm_rbuff_queued(ai.flows[fd].rx_rb)) + shm_flow_set_notify(ai.fqset, ai.flows[fd].info.id, FLOW_PKT); pthread_rwlock_unlock(&ai.lock); return ret; + + fail: + pthread_rwlock_unlock(&ai.lock); + return ret; } void fset_del(struct flow_set * set, int fd) { - if (set == NULL || fd < 0 || fd > SYS_MAX_FLOWS) + struct flow * flow; + + if (set == NULL || fd < 0 || fd >= SYS_MAX_FLOWS) return; + flow = &ai.flows[fd]; + pthread_rwlock_rdlock(&ai.lock); - if (ai.flows[fd].flow_id >= 0) - shm_flow_set_del(ai.fqset, set->idx, ai.flows[fd].flow_id); + if (flow->info.id >= 0) + shm_flow_set_del(ai.fqset, set->idx, flow->info.id); + + if (flow->frcti != NULL) + shm_flow_set_add(ai.fqset, 0, ai.flows[fd].info.id); pthread_rwlock_unlock(&ai.lock); } @@ -1362,28 +1576,86 @@ void fset_del(struct flow_set * set, bool fset_has(const struct flow_set * set, int fd) { - bool ret = false; + bool ret; - if (set == NULL || fd < 0 || fd > SYS_MAX_FLOWS) + if (set == NULL || fd < 0 || fd >= SYS_MAX_FLOWS) return false; pthread_rwlock_rdlock(&ai.lock); - if (ai.flows[fd].flow_id < 0) { + if (ai.flows[fd].info.id < 0) { pthread_rwlock_unlock(&ai.lock); return false; } - ret = (shm_flow_set_has(ai.fqset, set->idx, ai.flows[fd].flow_id) == 1); + ret = (shm_flow_set_has(ai.fqset, set->idx, ai.flows[fd].info.id) == 1); pthread_rwlock_unlock(&ai.lock); return ret; } +/* Filter fqueue events for non-data packets */ +static int fqueue_filter(struct fqueue * fq) +{ + struct shm_du_buff * sdb; + int fd; + ssize_t idx; + struct frcti * frcti; + + while (fq->next < fq->fqsize) { + if (fq->fqueue[fq->next].event != FLOW_PKT) + return 1; + + pthread_rwlock_rdlock(&ai.lock); + + fd = ai.id_to_fd[fq->fqueue[fq->next].flow_id].fd; + if (fd < 0) { + ++fq->next; + pthread_rwlock_unlock(&ai.lock); + continue; + } + + frcti = ai.flows[fd].frcti; + if (frcti == NULL) { + pthread_rwlock_unlock(&ai.lock); + return 1; + } + + if (__frcti_pdu_ready(frcti) >= 0) { + pthread_rwlock_unlock(&ai.lock); + return 1; + } + + pthread_rwlock_unlock(&ai.lock); + + idx = flow_rx_sdb(&ai.flows[fd], &sdb, false, NULL); + if (idx < 0) + return 0; + + pthread_rwlock_rdlock(&ai.lock); + + sdb = shm_rdrbuff_get(ai.rdrb, idx); + + __frcti_rcv(frcti, sdb); + + if (__frcti_pdu_ready(frcti) >= 0) { + pthread_rwlock_unlock(&ai.lock); + return 1; + } + + pthread_rwlock_unlock(&ai.lock); + + ++fq->next; + } + + return 0; +} + int fqueue_next(struct fqueue * fq) { - int fd; + int fd; + struct flowevent * e; if (fq == NULL) return -EINVAL; @@ -1391,16 +1663,16 @@ int fqueue_next(struct fqueue * fq) if (fq->fqsize == 0 || fq->next == fq->fqsize) return -EPERM; + if (fq->next != 0 && fqueue_filter(fq) == 0) + return -EPERM; + pthread_rwlock_rdlock(&ai.lock); - if (fq->next != 0 && frcti_filter(fq) == 0) { - pthread_rwlock_unlock(&ai.lock); - return -EPERM; - } + e = fq->fqueue + fq->next; - fd = ai.ports[fq->fqueue[fq->next]].fd; + fd = ai.id_to_fd[e->flow_id].fd; - fq->next += 2; + ++fq->next; pthread_rwlock_unlock(&ai.lock); @@ -1415,7 +1687,7 @@ enum fqtype fqueue_type(struct fqueue * fq) if (fq->fqsize == 0 || fq->next == 0) return -EPERM; - return fq->fqueue[fq->next - 1]; + return fq->fqueue[(fq->next - 1)].event; } ssize_t fevent(struct flow_set * set, @@ -1423,8 +1695,6 @@ ssize_t fevent(struct flow_set * set, const struct timespec * timeo) { ssize_t ret = 0; - struct timespec tic = {0, TICTIME}; - struct timespec tictime; struct timespec abs; struct timespec * t = NULL; @@ -1436,46 +1706,43 @@ ssize_t fevent(struct flow_set * set, clock_gettime(PTHREAD_COND_CLOCK, &abs); - ts_add(&tic, &abs, &tictime); - t = &tictime; - - if (timeo != NULL) + if (timeo != NULL) { ts_add(&abs, timeo, &abs); + t = &abs; + } while (ret == 0) { ret = shm_flow_set_wait(ai.fqset, set->idx, fq->fqueue, t); - if (ret == -ETIMEDOUT) { - if (timeo != NULL && ts_diff_ns(t, &abs) < 0) { - fq->fqsize = 0; - return -ETIMEDOUT; - } - ret = 0; - ts_add(t, &tic, t); - pthread_rwlock_rdlock(&ai.lock); - timerwheel_move(); - pthread_rwlock_unlock(&ai.lock); - continue; - } + if (ret == -ETIMEDOUT) + return -ETIMEDOUT; - fq->fqsize = ret << 1; + fq->fqsize = ret; fq->next = 0; - ret = frcti_filter(fq); + ret = fqueue_filter(fq); } - assert(ret); + assert(ret != 0); return 1; } /* ipcp-dev functions. */ -int np1_flow_alloc(pid_t n_pid, - int flow_id, - qosspec_t qs) +int np1_flow_alloc(pid_t n_pid, + int flow_id) { - qs.cypher_s = 0; /* No encryption ctx for np1 */ - return flow_init(flow_id, n_pid, qs, NULL); + struct flow_info flow; + + memset(&flow, 0, sizeof(flow)); + + flow.id = flow_id; + flow.n_pid = getpid(); + flow.qs = qos_np1; + flow.mpl = 0; + flow.n_1_pid = n_pid; /* This "flow" is upside-down! */ + + return flow_init(&flow, NULL); } int np1_flow_dealloc(int flow_id, @@ -1493,7 +1760,7 @@ int np1_flow_dealloc(int flow_id, pthread_rwlock_rdlock(&ai.lock); - fd = ai.ports[flow_id].fd; + fd = ai.id_to_fd[flow_id].fd; pthread_rwlock_unlock(&ai.lock); @@ -1504,140 +1771,106 @@ int np1_flow_resp(int flow_id) { int fd; - if (port_wait_assign(flow_id) != PORT_ID_ASSIGNED) + if (flow_wait_assign(flow_id) != FLOW_ALLOCATED) return -1; pthread_rwlock_rdlock(&ai.lock); - fd = ai.ports[flow_id].fd; + fd = ai.id_to_fd[flow_id].fd; pthread_rwlock_unlock(&ai.lock); return fd; } -int ipcp_create_r(int result) +int ipcp_create_r(const struct ipcp_info * info) { - irm_msg_t msg = IRM_MSG__INIT; - irm_msg_t * recv_msg; - int ret; - - msg.code = IRM_MSG_CODE__IPCP_CREATE_R; - msg.has_pid = true; - msg.pid = getpid(); - msg.has_result = true; - msg.result = result; - - recv_msg = send_recv_irm_msg(&msg); - if (recv_msg == NULL) - return -EIRMD; - - if (!recv_msg->has_result) { - irm_msg__free_unpacked(recv_msg, NULL); - return -1; - } + uint8_t buf[SOCK_BUF_SIZE]; + buffer_t msg = {buf, SOCK_BUF_SIZE}; + int err; - ret = recv_msg->result; - irm_msg__free_unpacked(recv_msg, NULL); + if (ipcp_create_r__irm_req_ser(&msg,info) < 0) + return -ENOMEM; - return ret; + err = send_recv_msg(&msg); + if (err < 0) + return err; + + return irm__irm_result_des(&msg); } -int ipcp_flow_req_arr(const uint8_t * dst, - size_t len, - qosspec_t qs, - const void * data, - size_t dlen) +int ipcp_flow_req_arr(const buffer_t * dst, + qosspec_t qs, + time_t mpl, + const buffer_t * data) { - irm_msg_t msg = IRM_MSG__INIT; - irm_msg_t * recv_msg; - qosspec_msg_t qs_msg; - int fd; - - assert(dst != NULL); - - msg.code = IRM_MSG_CODE__IPCP_FLOW_REQ_ARR; - msg.has_pid = true; - msg.pid = getpid(); - msg.has_hash = true; - msg.hash.len = len; - msg.hash.data = (uint8_t *) dst; - qs_msg = spec_to_msg(&qs); - msg.qosspec = &qs_msg; - msg.has_pk = true; - msg.pk.data = (uint8_t *) data; - msg.pk.len = dlen; - - recv_msg = send_recv_irm_msg(&msg); - if (recv_msg == NULL) - return -EIRMD; - - if (!recv_msg->has_flow_id || !recv_msg->has_pid) { - irm_msg__free_unpacked(recv_msg, NULL); - return -1; - } + struct flow_info flow; + uint8_t buf[SOCK_BUF_SIZE]; + buffer_t msg = {buf, SOCK_BUF_SIZE}; + int err; - if (recv_msg->has_result && recv_msg->result) { - irm_msg__free_unpacked(recv_msg, NULL); - return -1; - } + memset(&flow, 0, sizeof(flow)); - qs.cypher_s = 0; /* No encryption ctx for np1 */ - fd = flow_init(recv_msg->flow_id, recv_msg->pid, qs, NULL); + assert(dst != NULL && dst->len != 0 && dst->data != NULL); - irm_msg__free_unpacked(recv_msg, NULL); + flow.n_1_pid = getpid(); + flow.qs = qs; + flow.mpl = mpl; - return fd; + if (ipcp_flow_req_arr__irm_req_ser(&msg, dst, &flow, data) < 0) + return -ENOMEM; + + err = send_recv_msg(&msg); + if (err < 0) + return err; + + err = flow__irm_result_des(&msg, &flow, NULL); + if (err < 0) + return err; + + /* inverted for np1_flow */ + flow.n_1_pid = flow.n_pid; + flow.n_pid = getpid(); + flow.mpl = 0; + + return flow_init(&flow, NULL); } -int ipcp_flow_alloc_reply(int fd, - int response, - const void * data, - size_t len) +int ipcp_flow_alloc_reply(int fd, + int response, + time_t mpl, + const buffer_t * data) { - irm_msg_t msg = IRM_MSG__INIT; - irm_msg_t * recv_msg; - int ret; + struct flow_info flow; + uint8_t buf[SOCK_BUF_SIZE]; + buffer_t msg = {buf, SOCK_BUF_SIZE}; + int err; assert(fd >= 0 && fd < SYS_MAX_FLOWS); - msg.code = IRM_MSG_CODE__IPCP_FLOW_ALLOC_REPLY; - msg.has_flow_id = true; - msg.has_pk = true; - msg.pk.data = (uint8_t *) data; - msg.pk.len = (uint32_t) len; - pthread_rwlock_rdlock(&ai.lock); - msg.flow_id = ai.flows[fd].flow_id; + flow.id = ai.flows[fd].info.id; pthread_rwlock_unlock(&ai.lock); - msg.has_response = true; - msg.response = response; + flow.mpl = mpl; - recv_msg = send_recv_irm_msg(&msg); - if (recv_msg == NULL) - return -EIRMD; - - if (!recv_msg->has_result) { - irm_msg__free_unpacked(recv_msg, NULL); - return -1; - } - - ret = recv_msg->result; + if (ipcp_flow_alloc_reply__irm_msg_ser(&msg, &flow, response, data) < 0) + return -ENOMEM; - irm_msg__free_unpacked(recv_msg, NULL); + err = send_recv_msg(&msg); + if (err < 0) + return err; - return ret; + return irm__irm_result_des(&msg); } int ipcp_flow_read(int fd, struct shm_du_buff ** sdb) { - struct flow * flow; - struct shm_rbuff * rb; - ssize_t idx = -1; + struct flow * flow; + ssize_t idx = -1; assert(fd >= 0 && fd < SYS_MAX_FLOWS); assert(sdb); @@ -1646,50 +1879,39 @@ int ipcp_flow_read(int fd, pthread_rwlock_rdlock(&ai.lock); - assert(flow->flow_id >= 0); - - rb = flow->rx_rb; + assert(flow->info.id >= 0); - while ((idx = frcti_queued_pdu(flow->frcti)) < 0) { + while (frcti_queued_pdu(flow->frcti) < 0) { pthread_rwlock_unlock(&ai.lock); - idx = shm_rbuff_read(rb); + idx = flow_rx_sdb(flow, sdb, false, NULL); if (idx < 0) return idx; pthread_rwlock_rdlock(&ai.lock); - *sdb = shm_rdrbuff_get(ai.rdrb, idx); - if (flow->qs.ber == 0 && chk_crc(*sdb) != 0) - continue; - frcti_rcv(flow->frcti, *sdb); } - frcti_tick(flow->frcti); - pthread_rwlock_unlock(&ai.lock); - *sdb = shm_rdrbuff_get(ai.rdrb, idx); - return 0; } int ipcp_flow_write(int fd, struct shm_du_buff * sdb) { - struct flow * flow; - int ret; - ssize_t idx; + struct flow * flow; + int ret; assert(fd >= 0 && fd < SYS_MAX_FLOWS); assert(sdb); flow = &ai.flows[fd]; - pthread_rwlock_rdlock(&ai.lock); + pthread_rwlock_wrlock(&ai.lock); - if (flow->flow_id < 0) { + if (flow->info.id < 0) { pthread_rwlock_unlock(&ai.lock); return -ENOTALLOC; } @@ -1699,30 +1921,74 @@ int ipcp_flow_write(int fd, return -EPERM; } - assert(flow->tx_rb); + pthread_rwlock_unlock(&ai.lock); - idx = shm_du_buff_get_idx(sdb); + ret = flow_tx_sdb(flow, sdb, true, NULL); + + return ret; +} + +int np1_flow_read(int fd, + struct shm_du_buff ** sdb) +{ + struct flow * flow; + ssize_t idx = -1; + + assert(fd >= 0 && fd < SYS_MAX_FLOWS); + assert(sdb); - if (frcti_snd(flow->frcti, sdb) < 0) { + flow = &ai.flows[fd]; + + assert(flow->info.id >= 0); + + pthread_rwlock_rdlock(&ai.lock); + + idx = shm_rbuff_read(flow->rx_rb);; + if (idx < 0) { pthread_rwlock_unlock(&ai.lock); - return -ENOMEM; + return idx; } - if (flow->qs.ber == 0 && add_crc(sdb) != 0) { + pthread_rwlock_unlock(&ai.lock); + + *sdb = shm_rdrbuff_get(ai.rdrb, idx); + + return 0; +} + +int np1_flow_write(int fd, + struct shm_du_buff * sdb) +{ + struct flow * flow; + int ret; + ssize_t idx; + + assert(fd >= 0 && fd < SYS_MAX_FLOWS); + assert(sdb); + + flow = &ai.flows[fd]; + + pthread_rwlock_rdlock(&ai.lock); + + if (flow->info.id < 0) { pthread_rwlock_unlock(&ai.lock); - shm_rdrbuff_remove(ai.rdrb, idx); - return -ENOMEM; + return -ENOTALLOC; } - ret = shm_rbuff_write_b(flow->tx_rb, idx, NULL); - if (ret == 0) - shm_flow_set_notify(flow->set, flow->flow_id, FLOW_PKT); - else - shm_rdrbuff_remove(ai.rdrb, idx); + if ((flow->oflags & FLOWFACCMODE) == FLOWFRDONLY) { + pthread_rwlock_unlock(&ai.lock); + return -EPERM; + } pthread_rwlock_unlock(&ai.lock); - assert(ret <= 0); + idx = shm_du_buff_get_idx(sdb); + + ret = shm_rbuff_write_b(flow->tx_rb, idx, NULL); + if (ret < 0) + shm_rdrbuff_remove(ai.rdrb, idx); + else + shm_flow_set_notify(flow->set, flow->info.id, FLOW_PKT); return ret; } @@ -1746,7 +2012,7 @@ int ipcp_flow_fini(int fd) pthread_rwlock_rdlock(&ai.lock); - if (ai.flows[fd].flow_id < 0) { + if (ai.flows[fd].info.id < 0) { pthread_rwlock_unlock(&ai.lock); return -1; } @@ -1755,7 +2021,7 @@ int ipcp_flow_fini(int fd) shm_rbuff_set_acl(ai.flows[fd].tx_rb, ACL_FLOWDOWN); shm_flow_set_notify(ai.flows[fd].set, - ai.flows[fd].flow_id, + ai.flows[fd].info.id, FLOW_DEALLOC); rx_rb = ai.flows[fd].rx_rb; @@ -1776,9 +2042,9 @@ int ipcp_flow_get_qoscube(int fd, pthread_rwlock_rdlock(&ai.lock); - assert(ai.flows[fd].flow_id >= 0); + assert(ai.flows[fd].info.id >= 0); - *cube = qos_spec_to_cube(ai.flows[fd].qs); + *cube = qos_spec_to_cube(ai.flows[fd].info.qs); pthread_rwlock_unlock(&ai.lock); @@ -1791,7 +2057,7 @@ size_t ipcp_flow_queued(int fd) pthread_rwlock_rdlock(&ai.lock); - assert(ai.flows[fd].flow_id >= 0); + assert(ai.flows[fd].info.id >= 0); q = shm_rbuff_queued(ai.flows[fd].tx_rb); @@ -1827,13 +2093,14 @@ int local_flow_write(int fd, pthread_rwlock_rdlock(&ai.lock); - if (flow->flow_id < 0) { + if (flow->info.id < 0) { pthread_rwlock_unlock(&ai.lock); return -ENOTALLOC; } + ret = shm_rbuff_write_b(flow->tx_rb, idx, NULL); if (ret == 0) - shm_flow_set_notify(flow->set, flow->flow_id, FLOW_PKT); + shm_flow_set_notify(flow->set, flow->info.id, FLOW_PKT); else shm_rdrbuff_remove(ai.rdrb, idx); diff --git a/src/lib/frct.c b/src/lib/frct.c index a1e792af..c6fef35c 100644 --- a/src/lib/frct.c +++ b/src/lib/frct.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Flow and Retransmission Control * @@ -20,6 +20,8 @@ * Foundation, Inc., http://www.fsf.org/about/contact/. */ +#include <ouroboros/endian.h> + #define DELT_RDV (100 * MILLION) /* ns */ #define MAX_RDV (1 * BILLION) /* ns */ @@ -52,10 +54,20 @@ struct frcti { uint32_t rttseq; struct timespec t_probe; /* Probe time */ bool probe; /* Probe active */ - +#ifdef PROC_FLOW_STATS + size_t n_rtx; /* Number of rxm packets */ + size_t n_prb; /* Number of rtt probes */ + size_t n_rtt; /* Number of estimates */ + size_t n_dup; /* Duplicates received */ + size_t n_dak; /* Delayed ACKs received */ + size_t n_rdv; /* Number of rdv packets */ + size_t n_out; /* Packets out of window */ + size_t n_rqo; /* Packets out of rqueue */ +#endif struct frct_cr snd_cr; struct frct_cr rcv_cr; + ssize_t rq[RQ_SIZE]; pthread_rwlock_t lock; @@ -130,7 +142,15 @@ static int frct_rib_read(const char * path, "Receiver left window edge: %20u\n" "Receiver right window edge: %20u\n" "Receiver inactive (ns): %20ld\n" - "Receiver last ack: %20u\n", + "Receiver last ack: %20u\n" + "Number of pkt retransmissions: %20zu\n" + "Number of rtt probes: %20zu\n" + "Number of rtt estimates: %20zu\n" + "Number of duplicates received: %20zu\n" + "Number of delayed acks received: %20zu\n" + "Number of rendez-vous sent: %20zu\n" + "Number of packets out of window: %20zu\n" + "Number of packets out of rqueue: %20zu\n", frcti->mpl, frcti->a, frcti->r, @@ -144,7 +164,15 @@ static int frct_rib_read(const char * path, frcti->rcv_cr.lwe, frcti->rcv_cr.rwe, ts_diff_ns(&frcti->rcv_cr.act, &now), - frcti->rcv_cr.seqno); + frcti->rcv_cr.seqno, + frcti->n_rtx, + frcti->n_prb, + frcti->n_rtt, + frcti->n_dup, + frcti->n_dak, + frcti->n_rdv, + frcti->n_out, + frcti->n_rqo); pthread_rwlock_unlock(&flow->frcti->lock); @@ -156,10 +184,19 @@ static int frct_rib_read(const char * path, static int frct_rib_readdir(char *** buf) { *buf = malloc(sizeof(**buf)); + if (*buf == NULL) + goto fail_malloc; (*buf)[0] = strdup("frct"); + if ((*buf)[0] == NULL) + goto fail_strdup; return 1; + + fail_strdup: + free(*buf); + fail_malloc: + return -ENOMEM; } static int frct_rib_getattr(const char * path, @@ -168,7 +205,7 @@ static int frct_rib_getattr(const char * path, (void) path; (void) attr; - attr->size = 1024; + attr->size = 1189; attr->mtime = 0; return 0; @@ -223,16 +260,24 @@ static void __send_frct_pkt(int fd, pci->ackno = hton32(ackno); f = &ai.flows[fd]; + + if (crypt_encrypt(&f->crypt, sdb) < 0) + goto fail; + #ifdef RXM_BLOCKING - if (shm_rbuff_write_b(f->tx_rb, idx, NULL)) { + if (shm_rbuff_write_b(f->tx_rb, idx, NULL)) #else - if (shm_rbuff_write(f->tx_rb, idx)) { + if (shm_rbuff_write(f->tx_rb, idx)) #endif - ipcp_sdb_release(sdb); - return; - } + goto fail; + + shm_flow_set_notify(f->set, f->info.id, FLOW_PKT); + + return; - shm_flow_set_notify(f->set, f->flow_id, FLOW_PKT); + fail: + ipcp_sdb_release(sdb); + return; } static void send_frct_pkt(struct frcti * frcti) @@ -245,9 +290,11 @@ static void send_frct_pkt(struct frcti * frcti) assert(frcti); - pthread_rwlock_rdlock(&frcti->lock); + clock_gettime(PTHREAD_COND_CLOCK, &now); + + pthread_rwlock_wrlock(&frcti->lock); - if (frcti->rcv_cr.lwe == frcti->rcv_cr.seqno) { + if (!after(frcti->rcv_cr.lwe, frcti->rcv_cr.seqno)) { pthread_rwlock_unlock(&frcti->lock); return; } @@ -256,65 +303,46 @@ static void send_frct_pkt(struct frcti * frcti) ackno = frcti->rcv_cr.lwe; rwe = frcti->rcv_cr.rwe; - clock_gettime(PTHREAD_COND_CLOCK, &now); - diff = ts_diff_ns(&frcti->rcv_cr.act, &now); - - pthread_rwlock_unlock(&frcti->lock); - - if (diff > frcti->a || diff < DELT_ACK) + if (diff > frcti->a) { + pthread_rwlock_unlock(&frcti->lock); return; + } - __send_frct_pkt(fd, FRCT_ACK | FRCT_FC, ackno, rwe); - - pthread_rwlock_wrlock(&frcti->lock); + diff = ts_diff_ns(&frcti->snd_cr.act, &now); + if (diff < TICTIME) { + pthread_rwlock_unlock(&frcti->lock); + return; + } - if (after(frcti->rcv_cr.lwe, frcti->rcv_cr.seqno)) - frcti->rcv_cr.seqno = frcti->rcv_cr.lwe; + frcti->rcv_cr.seqno = frcti->rcv_cr.lwe; pthread_rwlock_unlock(&frcti->lock); + __send_frct_pkt(fd, FRCT_ACK | FRCT_FC, ackno, rwe); } static void __send_rdv(int fd) { - struct shm_du_buff * sdb; - struct frct_pci * pci; - ssize_t idx; - struct flow * f; - - /* Raw calls needed to bypass frcti. */ - idx = shm_rdrbuff_alloc_b(ai.rdrb, sizeof(*pci), NULL, &sdb, NULL); - if (idx < 0) - return; - - pci = (struct frct_pci *) shm_du_buff_head(sdb); - memset(pci, 0, sizeof(*pci)); - - pci->flags = FRCT_RDVS; - - f = &ai.flows[fd]; - - if (shm_rbuff_write_b(f->tx_rb, idx, NULL)) { - ipcp_sdb_release(sdb); - return; - } - - shm_flow_set_notify(f->set, f->flow_id, FLOW_PKT); + __send_frct_pkt(fd, FRCT_RDVS, 0, 0); } -static struct frcti * frcti_create(int fd) +static struct frcti * frcti_create(int fd, + time_t a, + time_t r, + time_t mpl) { struct frcti * frcti; ssize_t idx; struct timespec now; - time_t mpl; - time_t a; - time_t r; pthread_condattr_t cattr; #ifdef PROC_FLOW_STATS char frctstr[FRCT_NAME_STRLEN + 1]; #endif + mpl *= BILLION; + a *= BILLION; + r *= BILLION; + frcti = malloc(sizeof(*frcti)); if (frcti == NULL) goto fail_malloc; @@ -347,9 +375,9 @@ static struct frcti * frcti_create(int fd) clock_gettime(PTHREAD_COND_CLOCK, &now); - frcti->mpl = mpl = DELT_MPL; - frcti->a = a = DELT_A; - frcti->r = r = DELT_R; + frcti->mpl = mpl; + frcti->a = a; + frcti->r = r; frcti->rdv = DELT_RDV; frcti->fd = fd; @@ -358,10 +386,19 @@ static struct frcti * frcti_create(int fd) frcti->probe = false; frcti->srtt = 0; /* Updated on first ACK */ - frcti->mdev = 10 * MILLION; /* Initial rxm will be after 20 ms */ - frcti->rto = 20 * MILLION; /* Initial rxm will be after 20 ms */ - - if (ai.flows[fd].qs.loss == 0) { + frcti->mdev = 10 * MILLION; /* Updated on first ACK */ + frcti->rto = BILLION; /* Initial rxm will be after 1 s */ +#ifdef PROC_FLOW_STATS + frcti->n_rtx = 0; + frcti->n_prb = 0; + frcti->n_rtt = 0; + frcti->n_dup = 0; + frcti->n_dak = 0; + frcti->n_rdv = 0; + frcti->n_out = 0; + frcti->n_rqo = 0; +#endif + if (ai.flows[fd].info.qs.loss == 0) { frcti->snd_cr.cflags |= FRCTFRTX | FRCTFLINGER; frcti->rcv_cr.cflags |= FRCTFRTX; } @@ -448,9 +485,6 @@ static void frcti_setflags(struct frcti * frcti, #define frcti_rcv(frcti, sdb) \ (frcti == NULL ? 0 : __frcti_rcv(frcti, sdb)) -#define frcti_tick(frcti) \ - (frcti == NULL ? 0 : __frcti_tick()) - #define frcti_dealloc(frcti) \ (frcti == NULL ? 0 : __frcti_dealloc(frcti)) @@ -494,6 +528,10 @@ static bool __frcti_is_window_open(struct frcti * frcti) if (diff > frcti->rdv) { frcti->t_rdvs = now; __send_rdv(frcti->fd); +#ifdef PROC_FLOW_STATS + frcti->n_rdv++; +#endif + } } @@ -520,7 +558,6 @@ static int __frcti_window_wait(struct frcti * frcti, while (snd_cr->seqno == snd_cr->rwe && ret != -ETIMEDOUT) { struct timespec now; - pthread_rwlock_unlock(&frcti->lock); pthread_mutex_lock(&frcti->mtx); @@ -534,9 +571,7 @@ static int __frcti_window_wait(struct frcti * frcti, pthread_cleanup_push(__cleanup_mutex_unlock, &frcti->mtx); - ret = -pthread_cond_timedwait(&frcti->cond, - &frcti->mtx, - abstime); + ret = -__timedwait(&frcti->cond, &frcti->mtx, abstime); pthread_cleanup_pop(false); @@ -658,6 +693,7 @@ static int __frcti_snd(struct frcti * frcti, bool rtx; assert(frcti); + assert(shm_du_buff_len(sdb) != 0); snd_cr = &frcti->snd_cr; rcv_cr = &frcti->rcv_cr; @@ -706,9 +742,11 @@ static int __frcti_snd(struct frcti * frcti, frcti->rttseq = snd_cr->seqno; frcti->t_probe = now; frcti->probe = true; +#ifdef PROC_FLOW_STATS + frcti->n_prb++; +#endif } - - if (now.tv_sec - rcv_cr->act.tv_sec <= frcti->a) { + if ((now.tv_sec - rcv_cr->act.tv_sec) * BILLION <= frcti->a) { pci->flags |= FRCT_ACK; pci->ackno = hton32(rcv_cr->lwe); rcv_cr->seqno = rcv_cr->lwe; @@ -738,17 +776,19 @@ static void rtt_estimator(struct frcti * frcti, } else { time_t delta = mrtt - srtt; srtt += (delta >> 3); - rttvar += (ABS(delta) - rttvar) >> 2; + delta = (ABS(delta) - rttvar) >> 2; +#ifdef FRCT_LINUX_RTT_ESTIMATOR + if (delta < 0) + delta >>= 3; +#endif + rttvar += delta; } - - frcti->srtt = MAX(1000U, srtt); - frcti->mdev = MAX(100U, rttvar); - frcti->rto = MAX(RTO_MIN, frcti->srtt + (frcti->mdev << 1)); -} - -static void __frcti_tick(void) -{ - timerwheel_move(); +#ifdef PROC_FLOW_STATS + frcti->n_rtt++; +#endif + frcti->srtt = MAX(1000L, srtt); + frcti->mdev = MAX(100L, rttvar); + frcti->rto = MAX(RTO_MIN, frcti->srtt + (frcti->mdev << MDEV_MUL)); } /* Always queues the next application packet on the RQ. */ @@ -785,11 +825,14 @@ static void __frcti_rcv(struct frcti * frcti, if (pci->flags & FRCT_DRF) { /* New run. */ rcv_cr->lwe = seqno; rcv_cr->rwe = seqno + RQ_SIZE; - } else { + rcv_cr->seqno = seqno; + } else if (pci->flags & FRCT_DATA) { goto drop_packet; } } + rcv_cr->act = now; + /* For now, just send an immediate window update. */ if (pci->flags & FRCT_RDVS) { fd = frcti->fd; @@ -808,6 +851,10 @@ static void __frcti_rcv(struct frcti * frcti, frcti->snd_cr.lwe = ackno; if (frcti->probe && after(ackno, frcti->rttseq)) { +#ifdef PROC_FLOW_STATS + if (!(pci->flags & FRCT_DATA)) + frcti->n_dak++; +#endif rtt_estimator(frcti, ts_diff_ns(&frcti->t_probe, &now)); frcti->probe = false; } @@ -838,20 +885,33 @@ static void __frcti_rcv(struct frcti * frcti, if (before(seqno, rcv_cr->lwe)) { rcv_cr->seqno = seqno; /* Ensures we send a new ACK. */ +#ifdef PROC_FLOW_STATS + frcti->n_dup++; +#endif goto drop_packet; } if (rcv_cr->cflags & FRCTFRTX) { - if (!before(seqno, rcv_cr->rwe)) /* Out of window. */ + if (!before(seqno, rcv_cr->rwe)) { /* Out of window. */ +#ifdef PROC_FLOW_STATS + frcti->n_out++; +#endif goto drop_packet; + } - if (!before(seqno, rcv_cr->lwe + RQ_SIZE)) + if (!before(seqno, rcv_cr->lwe + RQ_SIZE)) { +#ifdef PROC_FLOW_STATS + frcti->n_rqo++; +#endif goto drop_packet; /* Out of rq. */ - - if (frcti->rq[pos] != -1) + } + if (frcti->rq[pos] != -1) { +#ifdef PROC_FLOW_STATS + frcti->n_dup++; +#endif goto drop_packet; /* Duplicate in rq. */ - + } fd = frcti->fd; } else { rcv_cr->lwe = seqno; @@ -859,72 +919,16 @@ static void __frcti_rcv(struct frcti * frcti, frcti->rq[pos] = idx; - rcv_cr->act = now; - pthread_rwlock_unlock(&frcti->lock); if (fd != -1) - timerwheel_ack(fd, frcti); + timerwheel_delayed_ack(fd, frcti); return; drop_packet: pthread_rwlock_unlock(&frcti->lock); - - send_frct_pkt(frcti); - shm_rdrbuff_remove(ai.rdrb, idx); + send_frct_pkt(frcti); return; } - -/* Filter fqueue events for non-data packets */ -int frcti_filter(struct fqueue * fq) -{ - struct shm_du_buff * sdb; - int fd; - ssize_t idx; - struct frcti * frcti; - struct shm_rbuff * rb; - - while (fq->next < fq->fqsize) { - if (fq->fqueue[fq->next + 1] != FLOW_PKT) - return 1; - - pthread_rwlock_rdlock(&ai.lock); - - fd = ai.ports[fq->fqueue[fq->next]].fd; - rb = ai.flows[fd].rx_rb; - frcti = ai.flows[fd].frcti; - - if (frcti == NULL) { - pthread_rwlock_unlock(&ai.lock); - return 1; - } - - if (__frcti_pdu_ready(frcti) >= 0) { - pthread_rwlock_unlock(&ai.lock); - return 1; - } - - idx = shm_rbuff_read(rb); - if (idx < 0) { - pthread_rwlock_unlock(&ai.lock); - return 0; - } - - sdb = shm_rdrbuff_get(ai.rdrb, idx); - - __frcti_rcv(frcti, sdb); - - if (__frcti_pdu_ready(frcti) >= 0) { - pthread_rwlock_unlock(&ai.lock); - return 1; - } - - pthread_rwlock_unlock(&ai.lock); - - fq->next += 2; - } - - return fq->next < fq->fqsize; -} diff --git a/src/lib/hash.c b/src/lib/hash.c index 4ce3f3b4..b465f894 100644 --- a/src/lib/hash.c +++ b/src/lib/hash.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Hashing * @@ -29,43 +29,50 @@ #include "config.h" +#include <ouroboros/endian.h> #include <ouroboros/hash.h> -#ifndef HAVE_LIBGCRYPT +#ifdef HAVE_LIBGCRYPT +#include <gcrypt.h> +#else #include <ouroboros/crc32.h> #include <ouroboros/md5.h> #include <ouroboros/sha3.h> -#else -#include <gcrypt.h> #endif #include <string.h> #include <assert.h> #include <stdbool.h> +#ifdef HAVE_LIBGCRYPT +int gcry_algo_tbl [] = { + /* DIR_HASH policies first */ + GCRY_MD_SHA3_224, + GCRY_MD_SHA3_256, + GCRY_MD_SHA3_384, + GCRY_MD_SHA3_512, + /* Below for internal use only */ + GCRY_MD_CRC32, + GCRY_MD_MD5, +}; +#else +int hash_len_tbl [] = { + /* DIR_HASH policies first */ + SHA3_224_HASH_LEN, + SHA3_256_HASH_LEN, + SHA3_384_HASH_LEN, + SHA3_512_HASH_LEN, + /* Below for internal use only */ + CRC32_HASH_LEN, + MD5_HASH_LEN +}; +#endif + uint16_t hash_len(enum hash_algo algo) { #ifdef HAVE_LIBGCRYPT - return (uint16_t) gcry_md_get_algo_dlen(algo); + return (uint16_t) gcry_md_get_algo_dlen(gcry_algo_tbl[algo]); #else - switch (algo) { - case HASH_CRC32: - return CRC32_HASH_LEN; - case HASH_MD5: - return MD5_HASH_LEN; - case HASH_SHA3_224: - return SHA3_224_HASH_LEN; - case HASH_SHA3_256: - return SHA3_256_HASH_LEN; - case HASH_SHA3_384: - return SHA3_384_HASH_LEN; - case HASH_SHA3_512: - return SHA3_512_HASH_LEN; - default: - assert(false); - break; - } - - return 0; + return hash_len_tbl[algo]; #endif } @@ -75,7 +82,7 @@ void mem_hash(enum hash_algo algo, size_t len) { #ifdef HAVE_LIBGCRYPT - gcry_md_hash_buffer(algo, dst, buf, len); + gcry_md_hash_buffer(gcry_algo_tbl[algo], dst, buf, len); #else struct sha3_ctx sha3_ctx; struct md5_ctx md5_ctx; @@ -84,6 +91,7 @@ void mem_hash(enum hash_algo algo, case HASH_CRC32: memset(dst, 0, CRC32_HASH_LEN); crc32((uint32_t *) dst, buf, len); + *(uint32_t *) dst = htobe32(*(uint32_t *) dst); break; case HASH_MD5: rhash_md5_init(&md5_ctx); diff --git a/src/lib/ipcp_config.proto b/src/lib/ipcp_config.proto deleted file mode 100644 index 185d9af7..00000000 --- a/src/lib/ipcp_config.proto +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Ouroboros - Copyright (C) 2016 - 2021 - * - * Layer configuration message - * - * Dimitri Staessens <dimitri@ouroboros.rocks> - * Sander Vrijders <sander@ouroboros.rocks> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * version 2.1 as published by the Free Software Foundation. - * - * This library 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 this library; if not, write to the Free Software - * Foundation, Inc., http://www.fsf.org/about/contact/. - */ - -syntax = "proto2"; - -message layer_info_msg { - required string layer_name = 1; - required uint32 dir_hash_algo = 2; -} - -message ipcp_config_msg { - required layer_info_msg layer_info = 1; - required int32 ipcp_type = 2; - // Config for unicast IPCP - optional uint32 addr_size = 3; - optional uint32 eid_size = 4; - optional uint32 max_ttl = 5; - optional uint32 addr_auth_type = 6; - optional uint32 routing_type = 7; - optional uint32 cong_avoid = 8; - // Config for UDP - optional uint32 ip_addr = 9; - optional uint32 dns_addr = 10; - optional uint32 port = 11; - // Config for the Ethernet - optional string dev = 12; - // Config for DIX Ethernet - optional uint32 ethertype = 13; -} - -enum enroll_code { - ENROLL_REQ = 1; - ENROLL_BOOT = 2; - ENROLL_DONE = 4; -}; - -message enroll_msg { - required enroll_code code = 1; - optional ipcp_config_msg conf = 2; - optional int64 t_sec = 3; - optional uint32 t_nsec = 4; - optional int32 result = 5; -};
\ No newline at end of file diff --git a/src/lib/irm.c b/src/lib/irm.c index 64353f45..d25101f3 100644 --- a/src/lib/irm.c +++ b/src/lib/irm.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * The API to instruct the IRM * @@ -31,28 +31,37 @@ #include <ouroboros/irm.h> #include <ouroboros/utils.h> #include <ouroboros/sockets.h> +#include <ouroboros/protobuf.h> #include <stdbool.h> #include <string.h> #include <stdlib.h> #include <sys/stat.h> -pid_t irm_create_ipcp(const char * name, - enum ipcp_type type) +int irm_create_ipcp(const char * name, + enum ipcp_type type) { - irm_msg_t msg = IRM_MSG__INIT; - irm_msg_t * recv_msg = NULL; - int ret = -1; + irm_msg_t msg = IRM_MSG__INIT; + irm_msg_t * recv_msg; + int ret; + struct ipcp_info info; - if (name == NULL) + if (name == NULL || strlen(name) > IPCP_NAME_SIZE) return -EINVAL; - msg.code = IRM_MSG_CODE__IRM_CREATE_IPCP; - msg.name = (char *) name; - msg.has_ipcp_type = true; - msg.ipcp_type = type; + memset(&info, 0, sizeof(info)); + + info.type = type; + strcpy(info.name, name); + + msg.code = IRM_MSG_CODE__IRM_CREATE_IPCP; + msg.ipcp_info = ipcp_info_s_to_msg(&info); + if (msg.ipcp_info == NULL) + return -ENOMEM; recv_msg = send_recv_irm_msg(&msg); + ipcp_info_msg__free_unpacked(msg.ipcp_info, NULL); + if (recv_msg == NULL) return -EIRMD; @@ -98,11 +107,9 @@ int irm_destroy_ipcp(pid_t pid) int irm_bootstrap_ipcp(pid_t pid, const struct ipcp_config * conf) { - irm_msg_t msg = IRM_MSG__INIT; - ipcp_config_msg_t config = IPCP_CONFIG_MSG__INIT; - layer_info_msg_t layer_info = LAYER_INFO_MSG__INIT; - irm_msg_t * recv_msg = NULL; - int ret = -1; + irm_msg_t msg = IRM_MSG__INIT; + irm_msg_t * recv_msg; + int ret; if (pid == -1 || conf == NULL) return -EINVAL; @@ -110,56 +117,10 @@ int irm_bootstrap_ipcp(pid_t pid, msg.code = IRM_MSG_CODE__IRM_BOOTSTRAP_IPCP; msg.has_pid = true; msg.pid = pid; - - config.layer_info = &layer_info; - msg.conf = &config; - layer_info.layer_name = (char *) conf->layer_info.layer_name; - - config.ipcp_type = conf->type; - - if (conf->type != IPCP_UDP) - layer_info.dir_hash_algo = conf->layer_info.dir_hash_algo; - - switch (conf->type) { - case IPCP_UNICAST: - config.has_addr_size = true; - config.addr_size = conf->addr_size; - config.has_eid_size = true; - config.eid_size = conf->eid_size; - config.has_max_ttl = true; - config.max_ttl = conf->max_ttl; - config.has_addr_auth_type = true; - config.addr_auth_type = conf->addr_auth_type; - config.has_routing_type = true; - config.routing_type = conf->routing_type; - config.has_cong_avoid = true; - config.cong_avoid = conf->cong_avoid; - break; - case IPCP_UDP: - config.has_ip_addr = true; - config.ip_addr = conf->ip_addr; - config.has_dns_addr = true; - config.dns_addr = conf->dns_addr; - config.has_port = true; - config.port = conf->port; - break; - case IPCP_LOCAL: - /* FALLTHRU */ - case IPCP_BROADCAST: - break; - case IPCP_ETH_LLC: - config.dev = conf->dev; - break; - case IPCP_ETH_DIX: - config.dev = conf->dev; - config.has_ethertype = true; - config.ethertype = conf->ethertype; - break; - default: - return -EIPCPTYPE; - } + msg.conf = ipcp_config_s_to_msg(conf); recv_msg = send_recv_irm_msg(&msg); + ipcp_config_msg__free_unpacked(msg.conf, NULL); if (recv_msg == NULL) return -EIRMD; @@ -179,8 +140,7 @@ int irm_connect_ipcp(pid_t pid, const char * component, qosspec_t qs) { - irm_msg_t msg = IRM_MSG__INIT; - qosspec_msg_t qs_msg = QOSSPEC_MSG__INIT; + irm_msg_t msg = IRM_MSG__INIT; irm_msg_t * recv_msg; int ret; @@ -190,10 +150,11 @@ int irm_connect_ipcp(pid_t pid, msg.comp = (char *) component; msg.has_pid = true; msg.pid = pid; - qs_msg = spec_to_msg(&qs); - msg.qosspec = &qs_msg; + msg.qosspec = qos_spec_s_to_msg(&qs); recv_msg = send_recv_irm_msg(&msg); + qosspec_msg__free_unpacked(msg.qosspec, NULL); + if (recv_msg == NULL) return -EIRMD; @@ -237,7 +198,7 @@ int irm_disconnect_ipcp(pid_t pid, return ret; } -ssize_t irm_list_ipcps(struct ipcp_info ** ipcps) +ssize_t irm_list_ipcps(struct ipcp_list_info ** ipcps) { irm_msg_t msg = IRM_MSG__INIT; irm_msg_t * recv_msg; @@ -407,50 +368,69 @@ int irm_bind_program(const char * prog, int argc, char ** argv) { - irm_msg_t msg = IRM_MSG__INIT; - irm_msg_t * recv_msg = NULL; - int ret = -1; - char * full_name; + irm_msg_t msg = IRM_MSG__INIT; + irm_msg_t * recv_msg; + char ** exec; + int ret; + int i; if (prog == NULL || name == NULL) return -EINVAL; - full_name = strdup(prog); - if (full_name == NULL) - return -ENOMEM; + exec = malloc((argc + 2) * sizeof(*exec)); + if (exec== NULL) { + ret = -ENOMEM; + goto fail_exec; + } - if ((ret = check_prog_path(&full_name)) < 0) { - free(full_name); - return ret; + exec[0] = strdup(prog); + if (exec[0] == NULL) { + ret = -ENOMEM; + goto fail_exec0; } + ret = check_prog_path(&exec[0]); + if (ret < 0) + goto fail; + + for (i = 0; i < argc; i++) + exec[i + 1] = argv[i]; + + exec[argc + 1] = ""; + msg.code = IRM_MSG_CODE__IRM_BIND_PROGRAM; msg.name = (char *) name; - msg.prog = full_name; - if (argv != NULL) { - msg.n_args = argc; - msg.args = (char **) argv; - } + msg.n_exec = argc + 2; + msg.exec = exec; msg.has_opts = true; msg.opts = opts; recv_msg = send_recv_irm_msg(&msg); - - free(full_name); - - if (recv_msg == NULL) - return -EIRMD; + if (recv_msg == NULL) { + ret = -EIRMD; + goto fail; + } if (!recv_msg->has_result) { irm_msg__free_unpacked(recv_msg, NULL); - return -1; + ret = -EPERM; + goto fail; } ret = recv_msg->result; irm_msg__free_unpacked(recv_msg, NULL); + free(exec[0]); + free(exec); + + return ret; + fail: + free(exec[0]); + fail_exec0: + free(exec); + fail_exec: return ret; } diff --git a/src/lib/irmd_messages.proto b/src/lib/irmd_messages.proto deleted file mode 100644 index 515d486f..00000000 --- a/src/lib/irmd_messages.proto +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Ouroboros - Copyright (C) 2016 - 2021 - * - * IRMd message - * - * Dimitri Staessens <dimitri@ouroboros.rocks> - * Sander Vrijders <sander@ouroboros.rocks> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * version 2.1 as published by the Free Software Foundation. - * - * This library 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 this library; if not, write to the Free Software - * Foundation, Inc., http://www.fsf.org/about/contact/. - */ - -syntax = "proto2"; - -import "ipcp_config.proto"; -import "qosspec.proto"; - -enum irm_msg_code { - IRM_CREATE_IPCP = 1; - IPCP_CREATE_R = 2; - IRM_DESTROY_IPCP = 3; - IRM_LIST_IPCPS = 4; - IRM_BOOTSTRAP_IPCP = 5; - IRM_ENROLL_IPCP = 6; - IRM_CONNECT_IPCP = 7; - IRM_DISCONNECT_IPCP = 8; - IRM_BIND_PROGRAM = 9; - IRM_UNBIND_PROGRAM = 10; - IRM_PROC_ANNOUNCE = 11; - IRM_BIND_PROCESS = 12; - IRM_UNBIND_PROCESS = 13; - IRM_CREATE_NAME = 14; - IRM_DESTROY_NAME = 15; - IRM_LIST_NAMES = 16; - IRM_REG_NAME = 17; - IRM_UNREG_NAME = 18; - IRM_FLOW_ALLOC = 19; - IRM_FLOW_ACCEPT = 20; - IRM_FLOW_JOIN = 21; - IRM_FLOW_DEALLOC = 22; - IPCP_FLOW_REQ_ARR = 23; - IPCP_FLOW_ALLOC_REPLY = 24; - IRM_REPLY = 25; -}; - -message ipcp_info_msg { - required uint32 pid = 1; - required uint32 type = 2; - required string name = 3; - required string layer = 4; -}; - -message name_info_msg { - required string name = 1; - required uint32 pol_lb = 2; -}; - -message irm_msg { - required irm_msg_code code = 1; - optional string prog = 2; - optional sint32 pid = 3; - optional string name = 4; - optional uint32 ipcp_type = 5; - optional string layer = 6; - repeated string args = 7; - optional sint32 response = 8; - optional string dst = 9; - optional bytes hash = 10; - optional sint32 flow_id = 11; - optional qosspec_msg qosspec = 12; - optional ipcp_config_msg conf = 13; - optional uint32 opts = 14; - repeated ipcp_info_msg ipcps = 15; - repeated name_info_msg names = 16; - optional uint32 timeo_sec = 17; - optional uint32 timeo_nsec = 18; - optional string comp = 19; - optional bytes pk = 20; /* piggyback */ - optional sint32 result = 21; -}; diff --git a/src/lib/list.c b/src/lib/list.c index 55c5d04a..62b2eb27 100644 --- a/src/lib/list.c +++ b/src/lib/list.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Simple doubly linked list implementation. * @@ -62,10 +62,10 @@ void list_add_tail(struct list_head * n, void list_del(struct list_head * e) { del_list(e->prv, e->nxt); - e->nxt = e->prv = NULL; + e->nxt = e->prv = e; } -bool list_is_empty(struct list_head * h) +bool list_is_empty(const struct list_head * h) { return h->nxt == h; } diff --git a/src/lib/lockfile.c b/src/lib/lockfile.c index b0e1115f..cf6d3c94 100644 --- a/src/lib/lockfile.c +++ b/src/lib/lockfile.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Lockfile for Ouroboros * @@ -36,81 +36,69 @@ #include <sys/stat.h> #define LF_SIZE (sizeof(pid_t)) +#define LF_PROT (PROT_READ | PROT_WRITE) struct lockfile { pid_t * pid; }; -struct lockfile * lockfile_create() { - int fd; - mode_t mask; - struct lockfile * lf = malloc(sizeof(*lf)); - if (lf == NULL) - return NULL; - - mask = umask(0); +static struct lockfile * __lockfile_open(int oflag) +{ + int fd; + struct lockfile * lf; - fd = shm_open(SHM_LOCKFILE_NAME, O_CREAT | O_EXCL | O_RDWR, 0666); - if (fd == -1) { - free(lf); - return NULL; - } + lf = malloc(sizeof(*lf)); + if (lf == NULL) + goto fail_lockfile; - umask(mask); + fd = shm_open(SHM_LOCKFILE_NAME, oflag, 0666); + if (fd == -1) + goto fail_shm_open; - if (ftruncate(fd, LF_SIZE - 1) < 0) { - free(lf); - return NULL; - } + if ((oflag & O_CREAT) && ftruncate(fd, LF_SIZE) < 0) + goto fail_truncate; - lf->pid = mmap(NULL, - LF_SIZE, PROT_READ | PROT_WRITE, - MAP_SHARED, - fd, - 0); + lf->pid = mmap(NULL, LF_SIZE, LF_PROT, MAP_SHARED, fd, 0); + if (lf->pid == MAP_FAILED) + goto fail_mmap; close (fd); - if (lf->pid == MAP_FAILED) { - shm_unlink(SHM_LOCKFILE_NAME); - free(lf); - return NULL; - } - - *lf->pid = getpid(); - return lf; + + fail_mmap: + shm_unlink(SHM_LOCKFILE_NAME); + fail_truncate: + close(fd); + fail_shm_open: + free(lf); + fail_lockfile: + return NULL; } -struct lockfile * lockfile_open() { - int fd; - struct lockfile * lf = malloc(sizeof(*lf)); - if (lf == NULL) - return NULL; +struct lockfile * lockfile_create(void) +{ + struct lockfile * lf; + mode_t mask; - fd = shm_open(SHM_LOCKFILE_NAME, O_RDWR, 0666); - if (fd < 0) { - free(lf); - return NULL; - } + mask = umask(0); - lf->pid = mmap(NULL, - LF_SIZE, PROT_READ | PROT_WRITE, - MAP_SHARED, - fd, - 0); + lf = __lockfile_open(O_CREAT | O_EXCL | O_RDWR); + if (lf == NULL) + return NULL; - close(fd); + umask(mask); - if (lf->pid == MAP_FAILED) { - shm_unlink(SHM_LOCKFILE_NAME); - free(lf); - return NULL; - } + *lf->pid = getpid(); return lf; } +struct lockfile * lockfile_open(void) +{ + return __lockfile_open(O_RDWR); +} + void lockfile_close(struct lockfile * lf) { assert(lf); @@ -127,11 +115,9 @@ void lockfile_destroy(struct lockfile * lf) if (getpid() != *lf->pid && kill(*lf->pid, 0) == 0) return; - munmap(lf->pid, LF_SIZE); + lockfile_close(lf); shm_unlink(SHM_LOCKFILE_NAME); - - free(lf); } pid_t lockfile_owner(struct lockfile * lf) diff --git a/src/lib/logs.c b/src/lib/logs.c index edfeac15..d90bcd63 100644 --- a/src/lib/logs.c +++ b/src/lib/logs.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Logging facilities * diff --git a/src/lib/md5.c b/src/lib/md5.c index a9152ee2..ad0dd4d7 100644 --- a/src/lib/md5.c +++ b/src/lib/md5.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * MD5 algorithm * diff --git a/src/lib/notifier.c b/src/lib/notifier.c index 1f600765..45745b9a 100644 --- a/src/lib/notifier.c +++ b/src/lib/notifier.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Notifier event system using callbacks * diff --git a/src/lib/cacep.proto b/src/lib/pb/cep.proto index f0ef1e98..d31cf4f7 100644 --- a/src/lib/cacep.proto +++ b/src/lib/pb/cep.proto @@ -1,7 +1,7 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * - * Message for Connection Information in CACEP + * Message for Connection Information in OCEP * * Dimitri Staessens <dimitri@ouroboros.rocks> * Sander Vrijders <sander@ouroboros.rocks> @@ -27,7 +27,7 @@ message fixed_conc_syntax_msg { repeated uint32 lens = 2; } -message cacep_msg { +message cep_msg { required string comp_name = 1; required string protocol = 2; required int32 pref_version = 3; @@ -36,4 +36,4 @@ message cacep_msg { repeated int32 supp_syntax = 6; optional fixed_conc_syntax_msg syntax_spec = 7; required uint64 address = 8; -}
\ No newline at end of file +} diff --git a/src/lib/qosspec.proto b/src/lib/pb/enroll.proto index 8a355363..7fe612a8 100644 --- a/src/lib/qosspec.proto +++ b/src/lib/pb/enroll.proto @@ -1,7 +1,7 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * - * QoS specification message + * Enrollment protocol * * Dimitri Staessens <dimitri@ouroboros.rocks> * Sander Vrijders <sander@ouroboros.rocks> @@ -21,14 +21,22 @@ */ syntax = "proto2"; +import "ipcp_config.proto"; -message qosspec_msg { - required uint32 delay = 1; /* In ms */ - required uint64 bandwidth = 2; /* In bits/s */ - required uint32 availability = 3; /* Class of 9s */ - required uint32 loss = 4; /* Packet loss */ - required uint32 ber = 5; /* Bit error rate, ppb */ - required uint32 in_order = 6; /* In-order delivery */ - required uint32 max_gap = 7; /* In ms */ - required uint32 cypher_s = 8; /* Crypto strength in bits */ -}; +message enroll_req_msg { + /* TODO authentication */ + required bytes id = 1; +} + +message enroll_resp_msg { + required bytes id = 1; + required int64 t_sec = 2; + required int32 t_nsec = 3; + required int32 response = 4; + optional ipcp_config_msg conf = 5; +} + +message enroll_ack_msg { + required bytes id = 1; + required int32 result = 2; +} diff --git a/src/lib/ipcpd_messages.proto b/src/lib/pb/ipcp.proto index a8c3bfb4..c2c7f48b 100644 --- a/src/lib/ipcpd_messages.proto +++ b/src/lib/pb/ipcp.proto @@ -1,7 +1,7 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * - * IPCPd message + * Messages sent to IPCPds * * Dimitri Staessens <dimitri@ouroboros.rocks> * Sander Vrijders <sander@ouroboros.rocks> @@ -23,7 +23,8 @@ syntax = "proto2"; import "ipcp_config.proto"; -import "qosspec.proto"; +import "model.proto"; + enum ipcp_msg_code { IPCP_BOOTSTRAP = 1; @@ -38,7 +39,7 @@ enum ipcp_msg_code { IPCP_CONNECT = 10; IPCP_DISCONNECT = 11; IPCP_REPLY = 12; -}; +} message ipcp_msg { required ipcp_msg_code code = 1; @@ -53,5 +54,6 @@ message ipcp_msg { optional int32 response = 10; optional string comp = 11; optional uint32 timeo_sec = 12; - optional int32 result = 13; -}; + optional sint32 mpl = 13; + optional int32 result = 14; +} diff --git a/src/lib/pb/ipcp_config.proto b/src/lib/pb/ipcp_config.proto new file mode 100644 index 00000000..28528b0c --- /dev/null +++ b/src/lib/pb/ipcp_config.proto @@ -0,0 +1,57 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * Layer configuration message + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +syntax = "proto2"; + +import "model.proto"; + +message dt_config_msg { + required uint32 addr_size = 1; + required uint32 eid_size = 2; + required uint32 max_ttl = 3; + required uint32 routing_type = 4; +} + +message uni_config_msg { + required dt_config_msg dt = 1; + required uint32 addr_auth_type = 2; + required uint32 cong_avoid = 3; +} + +message eth_config_msg { + required string dev = 1; + required uint32 ethertype = 2; +} + +message udp_config_msg { + required uint32 ip_addr = 1; + required uint32 port = 2; + required uint32 dns_addr = 3; /* set to 0 if unused */ +} + +message ipcp_config_msg { + required layer_info_msg layer_info = 1; + required uint32 ipcp_type = 2; + optional uni_config_msg unicast = 3; + optional udp_config_msg udp = 4; + optional eth_config_msg eth = 5; +} diff --git a/src/lib/pb/irm.proto b/src/lib/pb/irm.proto new file mode 100644 index 00000000..da3bd982 --- /dev/null +++ b/src/lib/pb/irm.proto @@ -0,0 +1,97 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * Messages sent to IRMd + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +syntax = "proto2"; + +import "ipcp_config.proto"; +import "model.proto"; + +enum irm_msg_code { + IRM_CREATE_IPCP = 1; + IPCP_CREATE_R = 2; + IRM_DESTROY_IPCP = 3; + IRM_LIST_IPCPS = 4; + IRM_BOOTSTRAP_IPCP = 5; + IRM_ENROLL_IPCP = 6; + IRM_CONNECT_IPCP = 7; + IRM_DISCONNECT_IPCP = 8; + IRM_BIND_PROGRAM = 9; + IRM_UNBIND_PROGRAM = 10; + IRM_PROC_ANNOUNCE = 11; + IRM_PROC_EXIT = 12; + IRM_BIND_PROCESS = 13; + IRM_UNBIND_PROCESS = 14; + IRM_CREATE_NAME = 15; + IRM_DESTROY_NAME = 16; + IRM_LIST_NAMES = 17; + IRM_REG_NAME = 18; + IRM_UNREG_NAME = 19; + IRM_FLOW_ALLOC = 20; + IRM_FLOW_ACCEPT = 21; + IRM_FLOW_JOIN = 22; + IRM_FLOW_DEALLOC = 23; + IPCP_FLOW_DEALLOC = 24; + IPCP_FLOW_REQ_ARR = 25; + IPCP_FLOW_ALLOC_REPLY = 26; + IRM_REPLY = 27; +} + +message timespec_msg { + required uint64 tv_sec = 1; + required uint32 tv_nsec = 2; +} + +message ipcp_list_msg { + required uint32 pid = 1; + required uint32 type = 2; + required string name = 3; + required string layer = 4; + required uint32 hash_algo = 5; +} + +message irm_msg { + required irm_msg_code code = 1; + optional string prog = 2; + optional sint32 pid = 3; + optional string name = 4; + optional flow_info_msg flow_info = 5; + optional ipcp_info_msg ipcp_info = 6; + optional string layer = 7; + repeated string exec = 8; + optional sint32 response = 9; + optional string dst = 10; + optional bytes hash = 11; + optional sint32 flow_id = 12; + optional qosspec_msg qosspec = 13; + optional ipcp_config_msg conf = 14; + optional uint32 opts = 15; + repeated ipcp_list_msg ipcps = 16; + repeated name_info_msg names = 17; + optional timespec_msg timeo = 18; + optional sint32 mpl = 20; + optional string comp = 21; + optional bytes pk = 22; /* piggyback */ + optional bytes symmkey = 23; + optional uint32 timeo_sec = 24; + optional uint32 timeo_nsec = 25; + optional sint32 result = 26; +} diff --git a/src/lib/pb/model.proto b/src/lib/pb/model.proto new file mode 100644 index 00000000..f1e401f9 --- /dev/null +++ b/src/lib/pb/model.proto @@ -0,0 +1,61 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * Model description messages + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +syntax = "proto2"; + +message qosspec_msg { + required uint32 delay = 1; /* In ms. */ + required uint64 bandwidth = 2; /* In bits/s. */ + required uint32 availability = 3; /* Class of 9s. */ + required uint32 loss = 4; /* Packet loss. */ + required uint32 ber = 5; /* Bit error rate, ppb. */ + required uint32 in_order = 6; /* In-order delivery. */ + required uint32 max_gap = 7; /* In ms. */ + required uint32 cypher_s = 8; /* Crypto strength in bits. */ + required uint32 timeout = 9; /* Timeout in ms. */ +} + +message flow_info_msg { + required uint32 id = 1; + required uint32 n_pid = 2; + required uint32 n_1_pid = 3; + required uint32 mpl = 4; + required uint32 state = 5; + required qosspec_msg qos = 6; +} + +message name_info_msg { + required string name = 1; + required uint32 pol_lb = 2; +} + +message layer_info_msg { + required string name = 1; + required uint32 dir_hash_algo = 2; +} + +message ipcp_info_msg { + required uint32 type = 1; + required string name = 2; + required uint32 pid = 3; + required uint32 state = 4; +} diff --git a/src/lib/protobuf.c b/src/lib/protobuf.c new file mode 100644 index 00000000..b586168c --- /dev/null +++ b/src/lib/protobuf.c @@ -0,0 +1,605 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * Protobuf syntax conversion + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#define _DEFAULT_SOURCE + +#include <ouroboros/protobuf.h> + +#include <stdlib.h> +#include <string.h> +#include <time.h> + +timespec_msg_t * timespec_s_to_msg(const struct timespec * s) +{ + timespec_msg_t * msg; + + assert(s != NULL); + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto fail_malloc; + + timespec_msg__init(msg); + + msg->tv_sec = s->tv_sec; + msg->tv_nsec = s->tv_nsec; + + return msg; + + fail_malloc: + return NULL; +} + +struct timespec timespec_msg_to_s(timespec_msg_t * msg) +{ + struct timespec s; + + assert(msg != NULL); + + s.tv_sec = msg->tv_sec; + s.tv_nsec = msg->tv_nsec; + + return s; +} + +flow_info_msg_t * flow_info_s_to_msg(const struct flow_info * s) +{ + flow_info_msg_t * msg; + + assert(s != NULL); + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto fail_malloc; + + flow_info_msg__init(msg); + + msg->id = s->id; + msg->n_pid = s->n_pid; + msg->n_1_pid = s->n_1_pid; + msg->mpl = s->mpl; + msg->state = s->state; + msg->qos = qos_spec_s_to_msg(&s->qs); + if (msg->qos == NULL) + goto fail_msg; + + return msg; + + fail_msg: + flow_info_msg__free_unpacked(msg, NULL); + fail_malloc: + return NULL; +} + +struct flow_info flow_info_msg_to_s(const flow_info_msg_t * msg) +{ + struct flow_info s; + + assert(msg != NULL); + + s.id = msg->id; + s.n_pid = msg->n_pid; + s.n_1_pid = msg->n_1_pid; + s.mpl = msg->mpl; + s.state = msg->state; + s.qs = qos_spec_msg_to_s(msg->qos); + + return s; +} + +layer_info_msg_t * layer_info_s_to_msg(const struct layer_info * s) +{ + layer_info_msg_t * msg; + + assert(s != NULL); + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto fail_malloc; + + layer_info_msg__init(msg); + + msg->name = strdup(s->name); + if (msg->name == NULL) + goto fail_msg; + + msg->dir_hash_algo = s->dir_hash_algo; + + return msg; + + fail_msg: + layer_info_msg__free_unpacked(msg, NULL); + fail_malloc: + return NULL; +} + +struct layer_info layer_info_msg_to_s(const layer_info_msg_t * msg) +{ + struct layer_info s; + + assert(msg != NULL); + assert(strlen(msg->name) <= LAYER_NAME_SIZE); + + s.dir_hash_algo = msg->dir_hash_algo; + strcpy(s.name, msg->name); + + return s; +} + +ipcp_info_msg_t * ipcp_info_s_to_msg(const struct ipcp_info * s) +{ + ipcp_info_msg_t * msg; + + assert(s != NULL); + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto fail_malloc; + + ipcp_info_msg__init(msg); + + msg->name = strdup(s->name); + if (msg->name == NULL) + goto fail_msg; + + msg->type = s->type; + msg->pid = s->pid; + msg->state = s->state; + + return msg; + fail_msg: + ipcp_info_msg__free_unpacked(msg, NULL); + fail_malloc: + return NULL; +} + +struct ipcp_info ipcp_info_msg_to_s(const ipcp_info_msg_t * msg) +{ + struct ipcp_info s; + + assert(msg != NULL); + assert(msg->name != NULL); + assert(strlen(msg->name) <= NAME_SIZE); + + strcpy(s.name, msg->name); + s.type = msg->type; + s.pid = msg->pid; + s.state = msg->state; + + return s; +} + +dt_config_msg_t * dt_config_s_to_msg(const struct dt_config * s) +{ + dt_config_msg_t * msg; + + assert(s != NULL); + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + return NULL; + + dt_config_msg__init(msg); + + msg->addr_size = s->addr_size; + msg->eid_size = s->eid_size; + msg->max_ttl = s->max_ttl; + msg->routing_type = s->routing_type; + + return msg; +} + +struct dt_config dt_config_msg_to_s(const dt_config_msg_t * msg) +{ + struct dt_config s; + + assert(msg != NULL); + + s.addr_size = msg->addr_size; + s.eid_size = msg->eid_size; + s.max_ttl = msg->max_ttl; + s.routing_type = msg->routing_type; + + return s; +} + +uni_config_msg_t * uni_config_s_to_msg(const struct uni_config * s) +{ + uni_config_msg_t * msg; + + assert(s != NULL); + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto fail_malloc; + + uni_config_msg__init(msg); + + msg->dt = dt_config_s_to_msg(&s->dt); + if (msg->dt == NULL) + goto fail_msg; + + msg->addr_auth_type = s->addr_auth_type; + msg->cong_avoid = s->cong_avoid; + + return msg; + + fail_msg: + uni_config_msg__free_unpacked(msg, NULL); + fail_malloc: + return NULL; +} + +struct uni_config uni_config_msg_to_s(const uni_config_msg_t * msg) +{ + struct uni_config s; + + s.dt = dt_config_msg_to_s(msg->dt); + + s.addr_auth_type = msg->addr_auth_type; + s.cong_avoid = msg->cong_avoid; + + return s; +} + +udp_config_msg_t * udp_config_s_to_msg(const struct udp_config * s) +{ + udp_config_msg_t * msg; + + assert(s != NULL); + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + return NULL; + + udp_config_msg__init(msg); + + msg->ip_addr = s->ip_addr; + msg->dns_addr = s->dns_addr; + msg->port = s->port; + + return msg; +} + +struct udp_config udp_config_msg_to_s(const udp_config_msg_t * msg) +{ + struct udp_config s; + + assert(msg != NULL); + + s.ip_addr = msg->ip_addr; + s.dns_addr = msg->dns_addr; + s.port = msg->port; + + return s; +} + +eth_config_msg_t * eth_config_s_to_msg(const struct eth_config * s) +{ + eth_config_msg_t * msg; + + assert(s != NULL); + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto fail_malloc; + + eth_config_msg__init(msg); + + msg->dev = strdup(s->dev); + if (msg->dev == NULL) + goto fail_msg; + + msg->ethertype = s->ethertype; + + return msg; + + fail_msg: + eth_config_msg__free_unpacked(msg, NULL); + fail_malloc: + return NULL; +} + +struct eth_config eth_config_msg_to_s(const eth_config_msg_t * msg) +{ + struct eth_config s; + + assert(msg != NULL); + assert(strlen(msg->dev) <= DEV_NAME_SIZE); + + strcpy(s.dev, msg->dev); + s.ethertype = msg->ethertype; + + return s; +} + + +ipcp_config_msg_t * ipcp_config_s_to_msg(const struct ipcp_config * s) +{ + ipcp_config_msg_t * msg; + + assert(s != NULL); + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto fail_malloc; + + ipcp_config_msg__init(msg); + + switch (s->type) { + case IPCP_LOCAL: + break; + case IPCP_UNICAST: + msg->unicast = uni_config_s_to_msg(&s->unicast); + if (msg->unicast == NULL) + goto fail_msg; + break; + case IPCP_BROADCAST: + break; + case IPCP_ETH_LLC: + /* FALLTHRU */ + case IPCP_ETH_DIX: + msg->eth = eth_config_s_to_msg(&s->eth); + if (msg->eth == NULL) + goto fail_msg; + break; + case IPCP_UDP: + msg->udp = udp_config_s_to_msg(&s->udp); + if (msg->udp == NULL) + goto fail_msg; + break; + default: + /* No checks here */ + break; + } + + msg->ipcp_type = s->type; + + msg->layer_info = layer_info_s_to_msg(&s->layer_info); + if (msg->layer_info == NULL) + goto fail_msg; + + return msg; + + fail_msg: + ipcp_config_msg__free_unpacked(msg, NULL); + fail_malloc: + return NULL; +} + +struct ipcp_config ipcp_config_msg_to_s(const ipcp_config_msg_t * msg) +{ + struct ipcp_config s; + + assert(msg != NULL); + + s.type = msg->ipcp_type; + + s.layer_info = layer_info_msg_to_s(msg->layer_info); + + switch(msg->ipcp_type) { + case IPCP_LOCAL: + break; + case IPCP_UNICAST: + s.unicast = uni_config_msg_to_s(msg->unicast); + break; + case IPCP_ETH_LLC: + /* FALLTHRU */ + case IPCP_ETH_DIX: + s.eth = eth_config_msg_to_s(msg->eth); + break; + case IPCP_UDP: + s.udp = udp_config_msg_to_s(msg->udp); + break; + case IPCP_BROADCAST: + break; + default: + /* No checks here */ + break; + } + + return s; +} + +qosspec_msg_t * qos_spec_s_to_msg(const struct qos_spec * s) +{ + qosspec_msg_t * msg; + + assert(s != NULL); + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + return NULL; + + qosspec_msg__init(msg); + + msg->delay = s->delay; + msg->bandwidth = s->bandwidth; + msg->availability = s->availability; + msg->loss = s->loss; + msg->ber = s->ber; + msg->in_order = s->in_order; + msg->max_gap = s->max_gap; + msg->cypher_s = s->cypher_s; + msg->timeout = s->timeout; + + return msg; +} + +struct qos_spec qos_spec_msg_to_s(const qosspec_msg_t * msg) +{ + struct qos_spec s; + + assert(msg != NULL); + + s.delay = msg->delay; + s.bandwidth = msg->bandwidth; + s.availability = msg->availability; + s.loss = msg->loss; + s.ber = msg->ber; + s.in_order = msg->in_order; + s.max_gap = msg->max_gap; + s.cypher_s = msg->cypher_s; + s.timeout = msg->timeout; + + return s; +} + +enroll_req_msg_t * enroll_req_s_to_msg(const struct enroll_req * s) +{ + enroll_req_msg_t * msg; + uint8_t * id; + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto fail_msg; + + id = malloc(ENROLL_ID_LEN); + if (id == NULL) + goto fail_id; + + memcpy(id, s->id, ENROLL_ID_LEN); + + enroll_req_msg__init(msg); + + msg->id.len = ENROLL_ID_LEN; + msg->id.data = id; + + return msg; + + fail_id: + free(msg); + fail_msg: + return NULL; +} + +struct enroll_req enroll_req_msg_to_s(const enroll_req_msg_t * msg) +{ + struct enroll_req s; + + assert(msg != NULL); + + memcpy(s.id, msg->id.data, ENROLL_ID_LEN); + + return s; +} + +enroll_resp_msg_t * enroll_resp_s_to_msg(const struct enroll_resp * s) +{ + enroll_resp_msg_t * msg; + uint8_t * id; + + assert(s != NULL); + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto fail_msg; + + id = malloc(ENROLL_ID_LEN); + if (id == NULL) + goto fail_id; + + memcpy(id, s->id, ENROLL_ID_LEN); + + enroll_resp_msg__init(msg); + + msg->id.len = ENROLL_ID_LEN; + msg->id.data = id; + + msg->t_sec = s->t.tv_sec; + msg->t_nsec = s->t.tv_nsec; + msg->response = s->response; + if (msg->response < 0) + return msg; + + msg->conf = ipcp_config_s_to_msg(&s->conf); + if (msg->conf == NULL) + goto fail_id; + + return msg; + + fail_id: + enroll_resp_msg__free_unpacked(msg, NULL); + fail_msg: + return NULL; +} + +struct enroll_resp enroll_resp_msg_to_s(const enroll_resp_msg_t * msg) +{ + struct enroll_resp s; + + assert (msg != NULL); + + s.response = msg->response; + if (s.response >= 0) + s.conf = ipcp_config_msg_to_s(msg->conf); + + s.t.tv_sec = msg->t_sec; + s.t.tv_nsec = msg->t_nsec; + + memcpy(s.id, msg->id.data, ENROLL_ID_LEN); + + return s; +} + +enroll_ack_msg_t * enroll_ack_s_to_msg(const struct enroll_ack * s) +{ + enroll_ack_msg_t * msg; + uint8_t * id; + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto fail_msg; + + id = malloc(ENROLL_ID_LEN); + if (id == NULL) + goto fail_id; + + memcpy(id, s->id, ENROLL_ID_LEN); + + enroll_ack_msg__init(msg); + + msg->id.len = ENROLL_ID_LEN; + msg->id.data = id; + + msg->result = s->result; + + return msg; + + fail_id: + enroll_ack_msg__free_unpacked(msg, NULL); + fail_msg: + return NULL; +} + +struct enroll_ack enroll_ack_msg_to_s(const enroll_ack_msg_t * msg) +{ + struct enroll_ack s; + + assert(msg != NULL); + + memcpy(s.id, msg->id.data, ENROLL_ID_LEN); + + s.result = msg->result; + + return s; +} diff --git a/src/lib/qoscube.c b/src/lib/qoscube.c index b9482263..267b3a87 100644 --- a/src/lib/qoscube.c +++ b/src/lib/qoscube.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Quality of Service cube * diff --git a/src/lib/random.c b/src/lib/random.c index ee992fda..09e0b844 100644 --- a/src/lib/random.c +++ b/src/lib/random.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Pseudo random generator * diff --git a/src/lib/rib.c b/src/lib/rib.c index 27c66f2f..97a20f47 100644 --- a/src/lib/rib.c +++ b/src/lib/rib.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * RIB export using FUSE * @@ -139,6 +139,10 @@ static int rib_readdir(const char * path, (void) offset; (void) info; + /* Fix ls calling readdir in an infinite loop on raspbian. */ + if (info != NULL && info->nonseekable != 0) + return -ENOENT; + filler(buf, ".", NULL, 0); filler(buf, "..", NULL, 0); @@ -203,12 +207,15 @@ static size_t __getattr(const char * path, if (strcmp(comp, r->path) == 0) { size_t ret = r->ops->getattr(path + 1, &attr); pthread_rwlock_unlock(&rib.lock); - st->st_mode = S_IFREG | 0755; - st->st_nlink = 1; - st->st_uid = getuid(); - st->st_gid = getgid(); - st->st_size = attr.size; - st->st_mtime = attr.mtime; + st->st_mode = S_IFREG | 0644; + st->st_blocks = 1; + st->st_nlink = 1; + st->st_uid = getuid(); + st->st_gid = getgid(); + st->st_size = attr.size; + st->st_atime = attr.mtime; + st->st_mtime = attr.mtime; + st->st_ctime = attr.mtime; return ret; } } @@ -252,6 +259,8 @@ static int rib_getattr(const char * path, st->st_uid = getuid(); st->st_gid = getgid(); st->st_mtime = now.tv_sec; + st->st_atime = now.tv_sec; + st->st_ctime = now.tv_sec; return 0; } @@ -338,7 +347,7 @@ int rib_init(const char * mountpt) fuse_opt_free_args(&args); rmdir(rib.mnt); fail_mnt: - memset(rib.mnt, 0, RIB_PATH_LEN + 1); + memset(rib.mnt, 0, sizeof(rib.mnt)); fail: return -1; #else @@ -377,6 +386,18 @@ void rib_fini(void) pthread_rwlock_unlock(&rib.lock); pthread_rwlock_destroy(&rib.lock); + + memset(rib.mnt, 0, sizeof(rib.mnt)); +#endif +} + +void rib_cleanup(const char * mnt) +{ +#ifdef HAVE_FUSE + fuse_unmount(mnt, NULL); + rmdir(mnt); +#else + (void) mnt; #endif } diff --git a/src/lib/serdes-irm.c b/src/lib/serdes-irm.c new file mode 100644 index 00000000..c4ba3053 --- /dev/null +++ b/src/lib/serdes-irm.c @@ -0,0 +1,478 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * Ouroboros IRM Protocol - serialization/deserialization + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#define _POSIX_C_SOURCE 200809L + +#include "config.h" + +#include <ouroboros/errno.h> +#include <ouroboros/serdes-irm.h> +#include <ouroboros/protobuf.h> + +#include <stdlib.h> +#include <string.h> + +int flow_accept__irm_req_ser(buffer_t * buf, + const struct flow_info * flow, + const struct timespec * timeo) +{ + irm_msg_t * msg; + size_t len; + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto fail_malloc; + + irm_msg__init(msg); + + msg->code = IRM_MSG_CODE__IRM_FLOW_ACCEPT; + msg->flow_info = flow_info_s_to_msg(flow); + if (msg->flow_info == NULL) + goto fail_msg; + + msg->timeo = timeo == NULL ? NULL : timespec_s_to_msg(timeo); + if (timeo != NULL && msg->timeo == NULL) + goto fail_msg; + + len = irm_msg__get_packed_size(msg); + if (len == 0 || len > buf->len) + goto fail_msg; + + buf->len = len; + + irm_msg__pack(msg, buf->data); + irm_msg__free_unpacked(msg, NULL); + + return 0; + + fail_msg: + irm_msg__free_unpacked(msg, NULL); + fail_malloc: + return -ENOMEM; +} + +static int __flow_alloc_ser(buffer_t * buf, + const struct flow_info * flow, + const char * dst, + const struct timespec * timeo, + int msg_code) +{ + irm_msg_t * msg; + size_t len; + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto fail_malloc; + + irm_msg__init(msg); + + msg->code = msg_code; + msg->flow_info = flow_info_s_to_msg(flow); + if (msg->flow_info == NULL) + goto fail_msg; + + msg->dst = strdup(dst); + if (msg->dst == NULL) + goto fail_msg; + + msg->timeo = timeo == NULL ? NULL : timespec_s_to_msg(timeo); + if (timeo != NULL && msg->timeo == NULL) + goto fail_msg; + + len = irm_msg__get_packed_size(msg); + if (len == 0 || len > buf->len) + goto fail_msg; + + buf->len = len; + + irm_msg__pack(msg, buf->data); + irm_msg__free_unpacked(msg, NULL); + + return 0; + + fail_msg: + irm_msg__free_unpacked(msg, NULL); + fail_malloc: + return -ENOMEM; +} + +int flow_alloc__irm_req_ser(buffer_t * buf, + const struct flow_info * flow, + const char * dst, + const struct timespec * timeo) +{ + return __flow_alloc_ser(buf, flow, dst, timeo, + IRM_MSG_CODE__IRM_FLOW_ALLOC); +} + +int flow_join__irm_req_ser(buffer_t * buf, + const struct flow_info * flow, + const char * dst, + const struct timespec * timeo) +{ + return __flow_alloc_ser(buf, flow, dst, timeo, + IRM_MSG_CODE__IRM_FLOW_JOIN); +} + +int flow__irm_result_des(buffer_t * buf, + struct flow_info * flow, + buffer_t * sk) +{ + irm_msg_t * msg; + int err; + + if (sk != NULL) + sk->data = NULL; + + msg = irm_msg__unpack(NULL, buf->len, buf->data); + if (msg == NULL) { + err = -EIRMD; + goto fail_msg; + } + + if (!msg->has_result) { + err = -EIRMD; + goto fail; + } + + if (msg->result < 0) { + err = msg->result; + goto fail; + } + + if (msg->flow_info == NULL) { + err = -EBADF; + goto fail; + } + + *flow = flow_info_msg_to_s(msg->flow_info); + + if (flow->qs.cypher_s > 0 && sk != NULL) { + if (msg->symmkey.data == NULL || msg->symmkey.len == 0) { + err = -ECRYPT; + goto fail; + } + + sk->len = msg->symmkey.len; + sk->data = msg->symmkey.data; + + msg->symmkey.data = NULL; + msg->symmkey.len = 0; + } + + irm_msg__free_unpacked(msg, NULL); + + return 0; + fail: + irm_msg__free_unpacked(msg, NULL); + fail_msg: + return err; +} + +int flow_dealloc__irm_req_ser(buffer_t * buf, + const struct flow_info * flow, + const struct timespec * timeo) +{ + irm_msg_t * msg; + size_t len; + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto fail_malloc; + + irm_msg__init(msg); + + msg->code = IRM_MSG_CODE__IRM_FLOW_DEALLOC; + msg->flow_info = flow_info_s_to_msg(flow); + if (msg->flow_info == NULL) + goto fail_msg; + + msg->timeo = timespec_s_to_msg(timeo); + if (msg->timeo == NULL) + goto fail_msg; + + len = irm_msg__get_packed_size(msg); + if (len == 0 || len > buf->len) + goto fail_msg; + + buf->len = len; + + irm_msg__pack(msg, buf->data); + irm_msg__free_unpacked(msg, NULL); + + return 0; + + fail_msg: + irm_msg__free_unpacked(msg, NULL); + fail_malloc: + return -ENOMEM; +} + +int ipcp_flow_dealloc__irm_req_ser(buffer_t * buf, + const struct flow_info * flow) +{ + irm_msg_t * msg; + size_t len; + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto fail_malloc; + + irm_msg__init(msg); + + msg->code = IRM_MSG_CODE__IPCP_FLOW_DEALLOC; + msg->flow_info = flow_info_s_to_msg(flow); + if (msg->flow_info == NULL) + goto fail_msg; + + len = irm_msg__get_packed_size(msg); + if (len == 0 || len > buf->len) + goto fail_msg; + + buf->len = len; + + irm_msg__pack(msg, buf->data); + irm_msg__free_unpacked(msg, NULL); + + return 0; + fail_msg: + irm_msg__free_unpacked(msg, NULL); + fail_malloc: + return -ENOMEM; +} + + +int ipcp_create_r__irm_req_ser(buffer_t * buf, + const struct ipcp_info * ipcp) +{ + irm_msg_t * msg; + size_t len; + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto fail_malloc; + + irm_msg__init(msg); + + msg->code = IRM_MSG_CODE__IPCP_CREATE_R; + msg->ipcp_info = ipcp_info_s_to_msg(ipcp); + if (msg->ipcp_info == NULL) + goto fail_msg; + + len = irm_msg__get_packed_size(msg); + if (len == 0 || len > buf->len) + goto fail_msg; + + buf->len = len; + + irm_msg__pack(msg, buf->data); + irm_msg__free_unpacked(msg, NULL); + + return 0; + fail_msg: + irm_msg__free_unpacked(msg, NULL); + fail_malloc: + return -ENOMEM; +} + +int proc_announce__irm_req_ser(buffer_t * buf, + const char * prog) +{ + irm_msg_t * msg; + size_t len; + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto fail_malloc; + + irm_msg__init(msg); + + msg->code = IRM_MSG_CODE__IRM_PROC_ANNOUNCE; + msg->has_pid = true; + msg->pid = getpid(); + msg->prog = strdup(prog); + if (msg->prog == NULL) + goto fail_msg; + + len = irm_msg__get_packed_size(msg); + if (len == 0 || len > buf->len) + goto fail_msg; + + buf->len = len; + + irm_msg__pack(msg, buf->data); + irm_msg__free_unpacked(msg, NULL); + + return 0; + fail_msg: + irm_msg__free_unpacked(msg, NULL); + fail_malloc: + return -ENOMEM; +} + +int proc_exit__irm_req_ser(buffer_t * buf) +{ + irm_msg_t * msg; + size_t len; + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto fail_malloc; + + irm_msg__init(msg); + + msg->code = IRM_MSG_CODE__IRM_PROC_EXIT; + msg->has_pid = true; + msg->pid = getpid(); + + len = irm_msg__get_packed_size(msg); + if (len == 0 || len > buf->len) + goto fail_msg; + + buf->len = len; + + irm_msg__pack(msg, buf->data); + irm_msg__free_unpacked(msg, NULL); + + return 0; + fail_msg: + irm_msg__free_unpacked(msg, NULL); + fail_malloc: + return -ENOMEM; +} + +int ipcp_flow_req_arr__irm_req_ser(buffer_t * buf, + const buffer_t * dst, + const struct flow_info * flow, + const buffer_t * data) +{ + irm_msg_t * msg; + size_t len; + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto fail_malloc; + + irm_msg__init(msg); + + msg->code = IRM_MSG_CODE__IPCP_FLOW_REQ_ARR; + msg->flow_info = flow_info_s_to_msg(flow); + if (msg->flow_info == NULL) + goto fail_msg; + + msg->has_hash = true; + msg->hash.len = dst->len; + msg->hash.data = dst->data; + msg->has_pk = true; + msg->pk.len = data->len; + msg->pk.data = data->data; + + len = irm_msg__get_packed_size(msg); + if (len == 0 || len > buf->len) + goto fail_msg; + + buf->len = len; + + irm_msg__pack(msg, buf->data); + + /* Don't free * dst or data! */ + msg->hash.len = 0; + msg->hash.data = NULL; + msg->pk.len = 0; + msg->pk.data = NULL; + irm_msg__free_unpacked(msg, NULL); + + return 0; + fail_msg: + irm_msg__free_unpacked(msg, NULL); + fail_malloc: + return -ENOMEM; +} + +int ipcp_flow_alloc_reply__irm_msg_ser(buffer_t * buf, + const struct flow_info * flow, + int response, + const buffer_t * data) +{ + irm_msg_t * msg; + size_t len; + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto fail_malloc; + + irm_msg__init(msg); + + msg->code = IRM_MSG_CODE__IPCP_FLOW_ALLOC_REPLY; + msg->flow_info = flow_info_s_to_msg(flow); + if (msg->flow_info == NULL) + goto fail_msg; + + msg->has_pk = true; + msg->pk.len = data->len; + msg->pk.data = data->data; + msg->has_response = true; + msg->response = response; + + len = irm_msg__get_packed_size(msg); + if (len == 0 || len > buf->len) + goto fail_msg; + + buf->len = len; + + irm_msg__pack(msg, buf->data); + + /* Don't free * data! */ + msg->pk.len = 0; + msg->pk.data = NULL; + + irm_msg__free_unpacked(msg, NULL); + + return 0; + fail_msg: + irm_msg__free_unpacked(msg, NULL); + fail_malloc: + return -ENOMEM; +} + +int irm__irm_result_des(buffer_t * buf) +{ + irm_msg_t * msg; + int err; + + msg = irm_msg__unpack(NULL, buf->len, buf->data); + if (msg == NULL) { + err = -EIRMD; + goto fail_msg; + } + + if (!msg->has_result) { + err = -EIRMD; + goto fail; + } + + err = msg->result; + fail: + irm_msg__free_unpacked(msg, NULL); + fail_msg: + return err; +} diff --git a/src/lib/serdes-oep.c b/src/lib/serdes-oep.c new file mode 100644 index 00000000..8a836b3b --- /dev/null +++ b/src/lib/serdes-oep.c @@ -0,0 +1,161 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * Ouroboros Enrollment Protocol - serialization/deserialization + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#define _POSIX_C_SOURCE 200112L + +#include <ouroboros/protobuf.h> +#include <ouroboros/serdes-oep.h> + +ssize_t enroll_req_ser(const struct enroll_req * req, + buffer_t buf) +{ + enroll_req_msg_t * msg; + ssize_t sz; + + msg = enroll_req_s_to_msg(req); + if (msg == NULL) + goto fail_msg; + + sz = enroll_req_msg__get_packed_size(msg); + if (sz < 0 || (size_t) sz > buf.len) + goto fail_pack; + + enroll_req_msg__pack(msg, buf.data); + + enroll_req_msg__free_unpacked(msg, NULL); + + return sz; + + fail_pack: + enroll_req_msg__free_unpacked(msg, NULL); + fail_msg: + return -1; +} + +int enroll_req_des(struct enroll_req * req, + const buffer_t buf) +{ + enroll_req_msg_t * msg; + + msg = enroll_req_msg__unpack(NULL, buf.len, buf.data); + if (msg == NULL) + goto fail_unpack; + + if (msg->id.len != ENROLL_ID_LEN) + goto fail_id; + + *req = enroll_req_msg_to_s(msg); + + enroll_req_msg__free_unpacked(msg, NULL); + + return 0; + + fail_id: + enroll_req_msg__free_unpacked(msg, NULL); + fail_unpack: + return -1; +} + +ssize_t enroll_resp_ser(const struct enroll_resp * resp, + buffer_t buf) +{ + enroll_resp_msg_t * msg; + ssize_t sz; + + msg = enroll_resp_s_to_msg(resp); + if (msg == NULL) + goto fail_msg; + + sz = enroll_resp_msg__get_packed_size(msg); + if (sz < 0 || (size_t) sz > buf.len) + goto fail_pack; + + enroll_resp_msg__pack(msg, buf.data); + + enroll_resp_msg__free_unpacked(msg, NULL); + + return sz; + + fail_pack: + enroll_resp_msg__free_unpacked(msg, NULL); + fail_msg: + return -1; +} + +int enroll_resp_des(struct enroll_resp * resp, + const buffer_t buf) +{ + enroll_resp_msg_t * msg; + + msg = enroll_resp_msg__unpack(NULL, buf.len, buf.data); + if (msg == NULL) + return -1; + + *resp = enroll_resp_msg_to_s(msg); + + enroll_resp_msg__free_unpacked(msg, NULL); + + return 0; +} + +ssize_t enroll_ack_ser(const struct enroll_ack * ack, + buffer_t buf) +{ + enroll_ack_msg_t * msg; + ssize_t sz; + + msg = enroll_ack_s_to_msg(ack); + if (msg == NULL) + goto fail_msg; + + sz = enroll_ack_msg__get_packed_size(msg); + if (sz < 0 || (size_t) sz > buf.len) + goto fail_pack; + + enroll_ack_msg__pack(msg, buf.data); + + enroll_ack_msg__free_unpacked(msg, NULL); + + return sz; + + fail_pack: + enroll_ack_msg__free_unpacked(msg, NULL); + fail_msg: + return -1; + +} + +int enroll_ack_des(struct enroll_ack * ack, + const buffer_t buf) +{ + enroll_ack_msg_t * msg; + + msg = enroll_ack_msg__unpack(NULL, buf.len, buf.data); + if (msg == NULL) + return -1; + + *ack = enroll_ack_msg_to_s(msg); + + enroll_ack_msg__free_unpacked(msg, NULL); + + return 0; +} diff --git a/src/lib/sha3.c b/src/lib/sha3.c index 76ddbae6..b9d6b07f 100644 --- a/src/lib/sha3.c +++ b/src/lib/sha3.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * SHA3 algorithm * diff --git a/src/lib/shm_flow_set.c b/src/lib/shm_flow_set.c index 5a9bee6c..39913fd1 100644 --- a/src/lib/shm_flow_set.c +++ b/src/lib/shm_flow_set.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Management of flow_sets for fqueue * @@ -24,21 +24,21 @@ #include "config.h" -#include <ouroboros/lockfile.h> -#include <ouroboros/time_utils.h> -#include <ouroboros/shm_flow_set.h> #include <ouroboros/errno.h> +#include <ouroboros/lockfile.h> #include <ouroboros/pthread.h> +#include <ouroboros/shm_flow_set.h> +#include <ouroboros/time.h> -#include <sys/mman.h> +#include <assert.h> #include <fcntl.h> +#include <signal.h> #include <stdlib.h> #include <stdio.h> +#include <string.h> #include <unistd.h> -#include <signal.h> +#include <sys/mman.h> #include <sys/stat.h> -#include <string.h> -#include <assert.h> /* * pthread_cond_timedwait has a WONTFIX bug as of glibc 2.25 where it @@ -52,39 +52,35 @@ #endif #define FN_MAX_CHARS 255 +#define FS_PROT (PROT_READ | PROT_WRITE) -#define QUEUESIZE ((SHM_BUFFER_SIZE) * sizeof(struct portevent)) +#define QUEUESIZE ((SHM_BUFFER_SIZE) * sizeof(struct flowevent)) -#define SHM_FLOW_SET_FILE_SIZE (SYS_MAX_FLOWS * sizeof(ssize_t) \ - + PROG_MAX_FQUEUES * sizeof(size_t) \ - + PROG_MAX_FQUEUES * sizeof(pthread_cond_t) \ - + PROG_MAX_FQUEUES * QUEUESIZE \ - + sizeof(pthread_mutex_t)) +#define SHM_FSET_FILE_SIZE (SYS_MAX_FLOWS * sizeof(ssize_t) \ + + PROG_MAX_FQUEUES * sizeof(size_t) \ + + PROG_MAX_FQUEUES * sizeof(pthread_cond_t) \ + + PROG_MAX_FQUEUES * QUEUESIZE \ + + sizeof(pthread_mutex_t)) #define fqueue_ptr(fs, idx) (fs->fqueues + (SHM_BUFFER_SIZE) * idx) -struct portevent { - int flow_id; - int event; -}; - struct shm_flow_set { ssize_t * mtable; size_t * heads; pthread_cond_t * conds; - struct portevent * fqueues; + struct flowevent * fqueues; pthread_mutex_t * lock; pid_t pid; }; static struct shm_flow_set * flow_set_create(pid_t pid, - int flags) + int oflags) { struct shm_flow_set * set; ssize_t * shm_base; char fn[FN_MAX_CHARS]; - int shm_fd; + int fd; sprintf(fn, SHM_FLOW_SET_PREFIX "%d", pid); @@ -92,39 +88,33 @@ static struct shm_flow_set * flow_set_create(pid_t pid, if (set == NULL) goto fail_malloc; - shm_fd = shm_open(fn, flags, 0666); - if (shm_fd == -1) + fd = shm_open(fn, oflags, 0666); + if (fd == -1) goto fail_shm_open; - if (ftruncate(shm_fd, SHM_FLOW_SET_FILE_SIZE - 1) < 0) { - close(shm_fd); - goto fail_shm_open; - } - - shm_base = mmap(NULL, - SHM_FLOW_SET_FILE_SIZE, - PROT_READ | PROT_WRITE, - MAP_SHARED, - shm_fd, - 0); - - close(shm_fd); + if ((oflags & O_CREAT) && ftruncate(fd, SHM_FSET_FILE_SIZE) < 0) + goto fail_truncate; + shm_base = mmap(NULL, SHM_FSET_FILE_SIZE, FS_PROT, MAP_SHARED, fd, 0); if (shm_base == MAP_FAILED) goto fail_mmap; + close(fd); + set->mtable = shm_base; set->heads = (size_t *) (set->mtable + SYS_MAX_FLOWS); set->conds = (pthread_cond_t *)(set->heads + PROG_MAX_FQUEUES); - set->fqueues = (struct portevent *) (set->conds + PROG_MAX_FQUEUES); + set->fqueues = (struct flowevent *) (set->conds + PROG_MAX_FQUEUES); set->lock = (pthread_mutex_t *) (set->fqueues + PROG_MAX_FQUEUES * (SHM_BUFFER_SIZE)); return set; fail_mmap: - if (flags & O_CREAT) + if (oflags & O_CREAT) shm_unlink(fn); + fail_truncate: + close(fd); fail_shm_open: free(set); fail_malloc: @@ -206,7 +196,7 @@ struct shm_flow_set * shm_flow_set_open(pid_t pid) void shm_flow_set_destroy(struct shm_flow_set * set) { - char fn[25]; + char fn[FN_MAX_CHARS]; assert(set); @@ -221,7 +211,7 @@ void shm_flow_set_close(struct shm_flow_set * set) { assert(set); - munmap(set->mtable, SHM_FLOW_SET_FILE_SIZE); + munmap(set->mtable, SHM_FSET_FILE_SIZE); free(set); } @@ -307,6 +297,8 @@ void shm_flow_set_notify(struct shm_flow_set * set, int flow_id, int event) { + struct flowevent * e; + assert(set); assert(!(flow_id < 0) && flow_id < SYS_MAX_FLOWS); @@ -317,10 +309,13 @@ void shm_flow_set_notify(struct shm_flow_set * set, return; } - (fqueue_ptr(set, set->mtable[flow_id]) + - (set->heads[set->mtable[flow_id]]))->flow_id = flow_id; - (fqueue_ptr(set, set->mtable[flow_id]) + - (set->heads[set->mtable[flow_id]])++)->event = event; + e = fqueue_ptr(set, set->mtable[flow_id]) + + set->heads[set->mtable[flow_id]]; + + e->flow_id = flow_id; + e->event = event; + + ++set->heads[set->mtable[flow_id]]; pthread_cond_signal(&set->conds[set->mtable[flow_id]]); @@ -330,7 +325,7 @@ void shm_flow_set_notify(struct shm_flow_set * set, ssize_t shm_flow_set_wait(const struct shm_flow_set * set, size_t idx, - int * fqueue, + struct flowevent * fqueue, const struct timespec * abstime) { ssize_t ret = 0; @@ -349,18 +344,11 @@ ssize_t shm_flow_set_wait(const struct shm_flow_set * set, pthread_cleanup_push(__cleanup_mutex_unlock, set->lock); while (set->heads[idx] == 0 && ret != -ETIMEDOUT) { - if (abstime != NULL) { - ret = -pthread_cond_timedwait(set->conds + idx, - set->lock, - abstime); + ret = -__timedwait(set->conds + idx, set->lock, abstime); #ifdef HAVE_CANCEL_BUG - if (ret == -ETIMEDOUT) - pthread_testcancel(); + if (ret == -ETIMEDOUT) + pthread_testcancel(); #endif - } else { - ret = -pthread_cond_wait(set->conds + idx, - set->lock); - } #ifdef HAVE_ROBUST_MUTEX if (ret == -EOWNERDEAD) pthread_mutex_consistent(set->lock); @@ -370,7 +358,7 @@ ssize_t shm_flow_set_wait(const struct shm_flow_set * set, if (ret != -ETIMEDOUT) { memcpy(fqueue, fqueue_ptr(set, idx), - set->heads[idx] * sizeof(struct portevent)); + set->heads[idx] * sizeof(*fqueue)); ret = set->heads[idx]; set->heads[idx] = 0; } diff --git a/src/lib/shm_rbuff.c b/src/lib/shm_rbuff.c index 361d5bb0..22cff41c 100644 --- a/src/lib/shm_rbuff.c +++ b/src/lib/shm_rbuff.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Ring buffer implementations for incoming packets * @@ -26,22 +26,22 @@ #include <ouroboros/shm_rbuff.h> #include <ouroboros/lockfile.h> -#include <ouroboros/time_utils.h> #include <ouroboros/errno.h> #include <ouroboros/fccntl.h> #include <ouroboros/pthread.h> +#include <ouroboros/time.h> -#include <sys/mman.h> +#include <assert.h> #include <fcntl.h> +#include <signal.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> -#include <stdio.h> -#include <stdint.h> #include <unistd.h> -#include <signal.h> +#include <sys/mman.h> #include <sys/stat.h> -#include <assert.h> -#include <stdbool.h> #define FN_MAX_CHARS 255 @@ -69,20 +69,11 @@ struct shm_rbuff { int flow_id; /* flow_id of the flow */ }; -void shm_rbuff_close(struct shm_rbuff * rb) -{ - assert(rb); - - munmap(rb->shm_base, SHM_RB_FILE_SIZE); - - free(rb); -} - #define MM_FLAGS (PROT_READ | PROT_WRITE) -struct shm_rbuff * rbuff_create(pid_t pid, - int flow_id, - int flags) +static struct shm_rbuff * rbuff_create(pid_t pid, + int flow_id, + int flags) { struct shm_rbuff * rb; int fd; @@ -99,7 +90,7 @@ struct shm_rbuff * rbuff_create(pid_t pid, if (fd == -1) goto fail_open; - if ((flags & O_CREAT) && ftruncate(fd, SHM_RB_FILE_SIZE - 1) < 0) + if ((flags & O_CREAT) && ftruncate(fd, SHM_RB_FILE_SIZE) < 0) goto fail_truncate; shm_base = mmap(NULL, SHM_RB_FILE_SIZE, MM_FLAGS, MAP_SHARED, fd, 0); @@ -130,6 +121,13 @@ struct shm_rbuff * rbuff_create(pid_t pid, return NULL; } +static void rbuff_destroy(struct shm_rbuff * rb) +{ + munmap(rb->shm_base, SHM_RB_FILE_SIZE); + + free(rb); +} + struct shm_rbuff * shm_rbuff_create(pid_t pid, int flow_id) { @@ -174,7 +172,7 @@ struct shm_rbuff * shm_rbuff_create(pid_t pid, *rb->head = 0; *rb->tail = 0; - rb->pid = pid; + rb->pid = pid; rb->flow_id = flow_id; pthread_mutexattr_destroy(&mattr); @@ -202,6 +200,13 @@ struct shm_rbuff * shm_rbuff_open(pid_t pid, return rbuff_create(pid, flow_id, O_RDWR); } +void shm_rbuff_close(struct shm_rbuff * rb) +{ + assert(rb); + + rbuff_destroy(rb); +} + #if (defined(SHM_RBUFF_LOCKLESS) && \ (defined(__GNUC__) || defined (__clang__))) #include "shm_rbuff_ll.c" diff --git a/src/lib/shm_rbuff_ll.c b/src/lib/shm_rbuff_ll.c index eef8a2fb..46a5314e 100644 --- a/src/lib/shm_rbuff_ll.c +++ b/src/lib/shm_rbuff_ll.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Lockless ring buffer for incoming packets * @@ -31,6 +31,11 @@ void shm_rbuff_destroy(struct shm_rbuff * rb) sprintf(fn, SHM_RBUFF_PREFIX "%d.%d", rb->pid, rb->flow_id); + __sync_bool_compare_and_swap(rb->acl, *rb->acl, ACL_FLOWDOWN); + + pthread_cond_broadcast(rb->del); + pthread_cond_broadcast(rb->add); + shm_rbuff_close(rb); shm_unlink(fn); @@ -103,12 +108,7 @@ int shm_rbuff_write_b(struct shm_rbuff * rb, pthread_cleanup_push(__cleanup_mutex_unlock, rb->lock); while (!shm_rbuff_free(rb) && ret != -ETIMEDOUT) { - if (abstime != NULL) - ret = -pthread_cond_timedwait(rb->add, - rb->lock, - abstime); - else - ret = -pthread_cond_wait(rb->add, rb->lock); + ret = -__timedwait(rb->add, rb->lock, abstime); #ifdef HAVE_ROBUST_MUTEX if (ret == -EOWNERDEAD) pthread_mutex_consistent(rb->lock); @@ -138,9 +138,15 @@ ssize_t shm_rbuff_read(struct shm_rbuff * rb) assert(rb); - if (shm_rbuff_empty(rb)) - return __sync_fetch_and_add(rb->acl, 0) & ACL_FLOWDOWN ? - -EFLOWDOWN : -EAGAIN; + if (shm_rbuff_empty(rb)) { + if (_sync_fetch_and_add(rb->acl, 0) & ACL_FLOWDOWN) + return -EFLOWDOWN; + + if (_sync_fetch_and_add(rb->acl, 0) & ACL_FLOWPEER) + return -EFLOWPEER; + + return -EAGAIN; + } ntail = RB_TAIL; @@ -176,12 +182,7 @@ ssize_t shm_rbuff_read_b(struct shm_rbuff * rb, pthread_cleanup_push(__cleanup_mutex_unlock, rb->lock); while (shm_rbuff_empty(rb) && (idx != -ETIMEDOUT)) { - if (abstime != NULL) - idx = -pthread_cond_timedwait(rb->add, - rb->lock, - abstime); - else - idx = -pthread_cond_wait(rb->add, rb->lock); + idx = -__timedwait(rb->add, rb->lock, abstime); #ifdef HAVE_ROBUST_MUTEX if (idx == -EOWNERDEAD) pthread_mutex_consistent(rb->lock); diff --git a/src/lib/shm_rbuff_pthr.c b/src/lib/shm_rbuff_pthr.c index cedbc7b1..b543fb07 100644 --- a/src/lib/shm_rbuff_pthr.c +++ b/src/lib/shm_rbuff_pthr.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Ring buffer for incoming packets * @@ -24,12 +24,15 @@ void shm_rbuff_destroy(struct shm_rbuff * rb) { char fn[FN_MAX_CHARS]; - assert(rb); + assert(rb != NULL); #ifdef CONFIG_OUROBOROS_DEBUG pthread_mutex_lock(rb->lock); - assert(shm_rbuff_empty(rb)); + *rb->acl = *rb->acl & ACL_FLOWDOWN; + + pthread_cond_broadcast(rb->del); + pthread_cond_broadcast(rb->add); pthread_mutex_unlock(rb->lock); #endif @@ -45,7 +48,7 @@ int shm_rbuff_write(struct shm_rbuff * rb, { int ret = 0; - assert(rb); + assert(rb != NULL); assert(idx < SHM_BUFFER_SIZE); #ifndef HAVE_ROBUST_MUTEX @@ -88,7 +91,7 @@ int shm_rbuff_write_b(struct shm_rbuff * rb, { int ret = 0; - assert(rb); + assert(rb != NULL); assert(idx < SHM_BUFFER_SIZE); #ifndef HAVE_ROBUST_MUTEX @@ -111,12 +114,7 @@ int shm_rbuff_write_b(struct shm_rbuff * rb, while (!shm_rbuff_free(rb) && ret != -ETIMEDOUT && !(*rb->acl & ACL_FLOWDOWN)) { - if (abstime != NULL) - ret = -pthread_cond_timedwait(rb->del, - rb->lock, - abstime); - else - ret = -pthread_cond_wait(rb->del, rb->lock); + ret = -__timedwait(rb->del, rb->lock, abstime); #ifdef HAVE_ROBUST_MUTEX if (ret == -EOWNERDEAD) pthread_mutex_consistent(rb->lock); @@ -138,11 +136,24 @@ int shm_rbuff_write_b(struct shm_rbuff * rb, return ret; } +static int check_rb_acl(struct shm_rbuff * rb) +{ + assert(rb != NULL); + + if (*rb->acl & ACL_FLOWDOWN) + return -EFLOWDOWN; + + if (*rb->acl & ACL_FLOWPEER) + return -EFLOWPEER; + + return -EAGAIN; +} + ssize_t shm_rbuff_read(struct shm_rbuff * rb) { ssize_t ret = 0; - assert(rb); + assert(rb != NULL); #ifndef HAVE_ROBUST_MUTEX pthread_mutex_lock(rb->lock); @@ -152,7 +163,7 @@ ssize_t shm_rbuff_read(struct shm_rbuff * rb) #endif if (shm_rbuff_empty(rb)) { - ret = *rb->acl & ACL_FLOWDOWN ? -EFLOWDOWN : -EAGAIN; + ret = check_rb_acl(rb); pthread_mutex_unlock(rb->lock); return ret; } @@ -171,7 +182,7 @@ ssize_t shm_rbuff_read_b(struct shm_rbuff * rb, { ssize_t idx = -1; - assert(rb); + assert(rb != NULL); #ifndef HAVE_ROBUST_MUTEX pthread_mutex_lock(rb->lock); @@ -187,36 +198,35 @@ ssize_t shm_rbuff_read_b(struct shm_rbuff * rb, pthread_cleanup_push(__cleanup_mutex_unlock, rb->lock); - while (shm_rbuff_empty(rb) - && (idx != -ETIMEDOUT) - && !(*rb->acl & ACL_FLOWDOWN)) { - if (abstime != NULL) - idx = -pthread_cond_timedwait(rb->add, - rb->lock, - abstime); - else - idx = -pthread_cond_wait(rb->add, rb->lock); + while (shm_rbuff_empty(rb) && + idx != -ETIMEDOUT && + check_rb_acl(rb) == -EAGAIN) { + idx = -__timedwait(rb->add, rb->lock, abstime); #ifdef HAVE_ROBUST_MUTEX if (idx == -EOWNERDEAD) pthread_mutex_consistent(rb->lock); #endif } - if (idx != -ETIMEDOUT) { + if (!shm_rbuff_empty(rb)) { idx = *tail_el_ptr(rb); *rb->tail = (*rb->tail + 1) & ((SHM_RBUFF_SIZE) - 1); pthread_cond_broadcast(rb->del); + } else if (idx != -ETIMEDOUT) { + idx = check_rb_acl(rb); } pthread_cleanup_pop(true); + assert(idx != -EAGAIN); + return idx; } void shm_rbuff_set_acl(struct shm_rbuff * rb, uint32_t flags) { - assert(rb); + assert(rb != NULL); #ifndef HAVE_ROBUST_MUTEX pthread_mutex_lock(rb->lock); @@ -236,7 +246,7 @@ uint32_t shm_rbuff_get_acl(struct shm_rbuff * rb) { uint32_t flags; - assert(rb); + assert(rb != NULL); #ifndef HAVE_ROBUST_MUTEX pthread_mutex_lock(rb->lock); @@ -253,7 +263,7 @@ uint32_t shm_rbuff_get_acl(struct shm_rbuff * rb) void shm_rbuff_fini(struct shm_rbuff * rb) { - assert(rb); + assert(rb != NULL); #ifndef HAVE_ROBUST_MUTEX pthread_mutex_lock(rb->lock); @@ -277,7 +287,7 @@ size_t shm_rbuff_queued(struct shm_rbuff * rb) { size_t ret; - assert(rb); + assert(rb != NULL); #ifndef HAVE_ROBUST_MUTEX pthread_mutex_lock(rb->lock); diff --git a/src/lib/shm_rdrbuff.c b/src/lib/shm_rdrbuff.c index e3552100..7ad1bd2e 100644 --- a/src/lib/shm_rdrbuff.c +++ b/src/lib/shm_rdrbuff.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Random Deletion Ring Buffer for Data Units * @@ -25,21 +25,19 @@ #include "config.h" #include <ouroboros/errno.h> -#include <ouroboros/shm_rdrbuff.h> -#include <ouroboros/shm_du_buff.h> -#include <ouroboros/time_utils.h> #include <ouroboros/pthread.h> +#include <ouroboros/shm_rdrbuff.h> -#include <sys/mman.h> +#include <assert.h> #include <fcntl.h> -#include <unistd.h> +#include <signal.h> +#include <stdbool.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> -#include <stdio.h> -#include <signal.h> +#include <unistd.h> +#include <sys/mman.h> #include <sys/stat.h> -#include <stdbool.h> -#include <assert.h> #define SHM_BLOCKS_SIZE ((SHM_BUFFER_SIZE) * SHM_RDRB_BLOCK_SIZE) #define SHM_FILE_SIZE (SHM_BLOCKS_SIZE + 2 * sizeof(size_t) \ @@ -173,7 +171,7 @@ static struct shm_rdrbuff * rdrb_create(int flags) if (fd == -1) goto fail_open; - if ((flags & O_CREAT) && ftruncate(fd, SHM_FILE_SIZE - 1) < 0) + if ((flags & O_CREAT) && ftruncate(fd, SHM_FILE_SIZE) < 0) goto fail_truncate; shm_base = mmap(NULL, SHM_FILE_SIZE, MM_FLAGS, MAP_SHARED, fd, 0); @@ -205,7 +203,7 @@ static struct shm_rdrbuff * rdrb_create(int flags) return NULL; } -struct shm_rdrbuff * shm_rdrbuff_create() +struct shm_rdrbuff * shm_rdrbuff_create(void) { struct shm_rdrbuff * rdrb; mode_t mask; @@ -263,7 +261,7 @@ struct shm_rdrbuff * shm_rdrbuff_create() return NULL; } -struct shm_rdrbuff * shm_rdrbuff_open() +struct shm_rdrbuff * shm_rdrbuff_open(void) { return rdrb_create(O_RDWR); } @@ -402,13 +400,7 @@ ssize_t shm_rdrbuff_alloc_b(struct shm_rdrbuff * rdrb, #else while (!shm_rdrb_free(rdrb, 1) && ret != ETIMEDOUT) { #endif - if (abstime != NULL) - ret = pthread_cond_timedwait(rdrb->healthy, - rdrb->lock, - abstime); - else - ret = pthread_cond_wait(rdrb->healthy, rdrb->lock); - + ret = __timedwait(rdrb->healthy, rdrb->lock, abstime); #ifdef SHM_RDRB_MULTI_BLOCK if (blocks + *rdrb->head > (SHM_BUFFER_SIZE)) padblocks = (SHM_BUFFER_SIZE) - *rdrb->head; @@ -532,6 +524,13 @@ uint8_t * shm_du_buff_tail(struct shm_du_buff * sdb) return (uint8_t *) (sdb + 1) + sdb->du_tail; } +size_t shm_du_buff_len(struct shm_du_buff * sdb) +{ + assert(sdb); + + return sdb->du_tail - sdb->du_head; +} + uint8_t * shm_du_buff_head_alloc(struct shm_du_buff * sdb, size_t size) { @@ -578,7 +577,7 @@ uint8_t * shm_du_buff_head_release(struct shm_du_buff * sdb, } uint8_t * shm_du_buff_tail_release(struct shm_du_buff * sdb, - size_t size) + size_t size) { assert(sdb); assert(!(size > sdb->du_tail - sdb->du_head)); diff --git a/src/lib/sockets.c b/src/lib/sockets.c index 8179d2b3..13219db0 100644 --- a/src/lib/sockets.c +++ b/src/lib/sockets.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * The sockets layer to communicate between daemons * @@ -21,6 +21,7 @@ */ #include <ouroboros/errno.h> +#include <ouroboros/pthread.h> #include <ouroboros/sockets.h> #include <ouroboros/utils.h> @@ -29,9 +30,7 @@ #include <string.h> #include <stdio.h> #include <stdlib.h> -#include <pthread.h> #include <stdbool.h> -#include <sys/time.h> /* Apple doesn't support SEQPACKET. */ #ifdef __APPLE__ @@ -57,8 +56,7 @@ int client_socket_open(char * file_name) serv_addr.sun_family = AF_UNIX; sprintf(serv_addr.sun_path, "%s", file_name); - if (connect(sockfd, - (struct sockaddr *) &serv_addr, + if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr))) { close(sockfd); return -1; @@ -102,10 +100,9 @@ int server_socket_open(char * file_name) irm_msg_t * send_recv_irm_msg(irm_msg_t * msg) { - int sockfd; - uint8_t buf[SOCK_BUF_SIZE]; - ssize_t len; - irm_msg_t * recv_msg = NULL; + int sockfd; + uint8_t buf[SOCK_BUF_SIZE]; + ssize_t len; sockfd = client_socket_open(IRM_SOCK_PATH); if (sockfd < 0) @@ -117,19 +114,44 @@ irm_msg_t * send_recv_irm_msg(irm_msg_t * msg) return NULL; } - pthread_cleanup_push(__cleanup_close_ptr, &sockfd); - irm_msg__pack(msg, buf); - if (write(sockfd, buf, len) != -1) + pthread_cleanup_push(__cleanup_close_ptr, &sockfd); + + len = write(sockfd, buf, len); + if (len >= 0) len = read(sockfd, buf, SOCK_BUF_SIZE); - if (len > 0) - recv_msg = irm_msg__unpack(NULL, len, buf); + pthread_cleanup_pop(true); + + if (len < 0) + goto fail; + + return irm_msg__unpack(NULL, len, buf); + fail: + return NULL; +} + +int send_recv_msg(buffer_t * msg) +{ + int sockfd; + ssize_t len = 0; + + sockfd = client_socket_open(IRM_SOCK_PATH); + if (sockfd < 0) + return -1; + + pthread_cleanup_push(__cleanup_close_ptr, &sockfd); + + len = write(sockfd, msg->data, msg->len); + if (len >= 0) + len = read(sockfd, msg->data, SOCK_BUF_SIZE); pthread_cleanup_pop(true); - return recv_msg; + msg->len = (size_t) len; + + return len < 0 ? -1 : 0; } char * ipcp_sock_path(pid_t pid) @@ -165,40 +187,3 @@ char * ipcp_sock_path(pid_t pid) return full_name; } - -qosspec_msg_t spec_to_msg(const qosspec_t * qs) -{ - qosspec_t spec; - qosspec_msg_t msg = QOSSPEC_MSG__INIT; - - spec = (qs == NULL ? qos_raw : *qs); - - msg.delay = spec.delay; - msg.bandwidth = spec.bandwidth; - msg.availability = spec.availability; - msg.loss = spec.loss; - msg.ber = spec.ber; - msg.in_order = spec.in_order; - msg.max_gap = spec.max_gap; - msg.cypher_s = spec.cypher_s; - - return msg; -} - -qosspec_t msg_to_spec(const qosspec_msg_t * msg) -{ - qosspec_t spec; - - assert(msg); - - spec.delay = msg->delay; - spec.bandwidth = msg->bandwidth; - spec.availability = msg->availability; - spec.loss = msg->loss; - spec.ber = msg->ber; - spec.in_order = msg->in_order; - spec.max_gap = msg->max_gap; - spec.cypher_s = msg->cypher_s; - - return spec; -} diff --git a/src/lib/tests/CMakeLists.txt b/src/lib/tests/CMakeLists.txt index 9e23b0ee..dc90671b 100644 --- a/src/lib/tests/CMakeLists.txt +++ b/src/lib/tests/CMakeLists.txt @@ -6,10 +6,11 @@ create_test_sourcelist(${PARENT_DIR}_tests test_suite.c bitmap_test.c btree_test.c crc32_test.c + hash_test.c md5_test.c sha3_test.c shm_rbuff_test.c - time_utils_test.c + time_test.c ) add_executable(${PARENT_DIR}_test EXCLUDE_FROM_ALL ${${PARENT_DIR}_tests}) @@ -19,7 +20,11 @@ target_link_libraries(${PARENT_DIR}_test ouroboros-common) add_dependencies(check ${PARENT_DIR}_test) set(tests_to_run ${${PARENT_DIR}_tests}) -remove(tests_to_run test_suite.c) +if(CMAKE_VERSION VERSION_LESS "3.29.0") + remove(tests_to_run test_suite.c) +else () + list(POP_FRONT tests_to_run) +endif() foreach (test ${tests_to_run}) get_filename_component(test_name ${test} NAME_WE) diff --git a/src/lib/tests/bitmap_test.c b/src/lib/tests/bitmap_test.c index 0815ecff..4dbd6653 100644 --- a/src/lib/tests/bitmap_test.c +++ b/src/lib/tests/bitmap_test.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Test of the bitmap * @@ -27,7 +27,8 @@ #define BITMAP_SIZE 200 -int bitmap_test(int argc, char ** argv) +int bitmap_test(int argc, + char ** argv) { struct bmp * bmp; ssize_t bits = BITMAP_SIZE; @@ -60,27 +61,23 @@ int bitmap_test(int argc, char ** argv) if (!bmp_is_id_valid(bmp, id)) { if (i < BITMAP_SIZE + offset) { printf("Failed valid ID %d (%zd).\n", i, id); - bmp_destroy(bmp); - return -1; + goto fail; } if (id >= offset && id < bits + offset) { printf("Valid ID %zd returned invalid.\n", id); - bmp_destroy(bmp); - return -1; + goto fail; } continue; } if (!bmp_is_id_used(bmp, id)) { printf("ID not marked in use.\n"); - bmp_destroy(bmp); - return -1; + goto fail; } if (id != i) { printf("Wrong ID returned.\n"); - bmp_destroy(bmp); - return -1; + goto fail; } } @@ -89,20 +86,24 @@ int bitmap_test(int argc, char ** argv) if (bmp_release(bmp, r)) { printf("Failed to release ID.\n"); - return -1; + goto fail; } id = bmp_allocate(bmp); if (!bmp_is_id_valid(bmp, id)) continue; + if (id != r) { printf("Wrong prev ID returned.\n"); - bmp_destroy(bmp); - return -1; + goto fail; } } bmp_destroy(bmp); return 0; + + fail: + bmp_destroy(bmp); + return -1; } diff --git a/src/lib/tests/btree_test.c b/src/lib/tests/btree_test.c index 9dc59d32..8bd30370 100644 --- a/src/lib/tests/btree_test.c +++ b/src/lib/tests/btree_test.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Test of the B-tree implementation * diff --git a/src/lib/tests/crc32_test.c b/src/lib/tests/crc32_test.c index a0f70423..a26c8220 100644 --- a/src/lib/tests/crc32_test.c +++ b/src/lib/tests/crc32_test.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Test of the CRC32 function * diff --git a/src/lib/tests/hash_test.c b/src/lib/tests/hash_test.c new file mode 100644 index 00000000..970d9185 --- /dev/null +++ b/src/lib/tests/hash_test.c @@ -0,0 +1,202 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * Test of the hashing functions + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#include <ouroboros/hash.h> +#include <ouroboros/test.h> + +#include <stdlib.h> +#include <stdint.h> +#include <assert.h> +#include <string.h> +#include <stdio.h> + +/* + * Test vectors calculated at + * https://www.lammertbies.nl/comm/info/crc-calculation.html + */ + +struct vec_entry { + char * in; + char * out; +}; + +static int test_crc32(void) +{ + int ret = 0; + + struct vec_entry vec [] = { + { "0", "f4dbdf21" }, + { "123456789", "cbf43926" }, + { "987654321", "015f0201" }, + { NULL, NULL } + }; + + struct vec_entry * cur = vec; + + TEST_START(); + + while (cur->in != NULL) { + uint8_t crc[4]; + char res[9]; + + str_hash(HASH_CRC32, crc, cur->in); + + sprintf(res, HASH_FMT32, HASH_VAL32(crc)); + if (strcmp(res, cur->out) != 0) { + printf("Hash failed %s != %s.\n", res, cur->out); + ret |= -1; + } + + ++cur; + } + + TEST_END(ret); + + return ret; +} + +static int test_md5(void) +{ + int ret = 0; + + struct vec_entry vec [] = {{ + "abc", + "900150983cd24fb0d6963f7d28e17f72" + }, { + "The quick brown fox jumps over the lazy dog", + "9e107d9d372bb6826bd81d3542a419d6" + }, { + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "8215ef0796a20bcaaae116d3876c664a" + }, { + NULL, + NULL + }}; + + struct vec_entry * cur = vec; + + TEST_START(); + + + while (cur->in != NULL) { + uint8_t md5[16]; + char res[33]; + + str_hash(HASH_MD5, md5, cur->in); + + sprintf(res, HASH_FMT128, HASH_VAL128(md5)); + if (strcmp(res, cur->out) != 0) { + printf("Hash failed %s != %s.\n", res, cur->out); + ret |= -1; + } + + ++cur; + } + + TEST_END(ret); + + return ret; +} + +static int test_sha3(void) +{ + int ret = 0; + + uint8_t sha3[64]; + char res[129]; + + char * in = "abc"; + + char * out = + "e642824c3f8cf24ad09234ee7d3c766f" + "c9a3a5168d0c94ad73b46fdf"; + + TEST_START(); + + str_hash(HASH_SHA3_224, sha3, in); + + sprintf(res, HASH_FMT224, HASH_VAL224(sha3)); + if (strcmp(res, out) != 0) { + printf("SHA3-224 failed %s != %s", res, out); + ret |= -1; + } + + out = + "3a985da74fe225b2045c172d6bd390bd" + "855f086e3e9d525b46bfe24511431532"; + + str_hash(HASH_SHA3_256, sha3, in); + + sprintf(res, HASH_FMT256, HASH_VAL256(sha3)); + if (strcmp(res, out) != 0) { + printf("SHA3-256 failed %s != %s.\n", res, out); + ret |= -1; + } + + out = + "ec01498288516fc926459f58e2c6ad8d" + "f9b473cb0fc08c2596da7cf0e49be4b2" + "98d88cea927ac7f539f1edf228376d25"; + + str_hash(HASH_SHA3_384, sha3, in); + + sprintf(res, HASH_FMT384, HASH_VAL384(sha3)); + if (strcmp(res, out) != 0) { + printf("SHA3-384failed %s != %s.'n", res, out); + ret |= -1; + } + + out = + "b751850b1a57168a5693cd924b6b096e" + "08f621827444f70d884f5d0240d2712e" + "10e116e9192af3c91a7ec57647e39340" + "57340b4cf408d5a56592f8274eec53f0"; + + str_hash(HASH_SHA3_512, sha3, in); + + sprintf(res, HASH_FMT512, HASH_VAL512(sha3)); + if (strcmp(res, out) != 0) { + printf("SHA3-512 failed %s != %s.\n", res, out); + ret |= -1; + } + + TEST_END(ret); + + return ret; +} + +int hash_test(int argc, + char ** argv) +{ + int ret = 0; + + (void) argc; + (void) argv; + + ret |= test_crc32(); + + ret |= test_md5(); + + ret |= test_sha3(); + + return ret; +} diff --git a/src/lib/tests/md5_test.c b/src/lib/tests/md5_test.c index b5ad127f..28e8f42f 100644 --- a/src/lib/tests/md5_test.c +++ b/src/lib/tests/md5_test.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Test of the MD5 function * diff --git a/src/lib/tests/sha3_test.c b/src/lib/tests/sha3_test.c index 4860cd9b..82b4ef0d 100644 --- a/src/lib/tests/sha3_test.c +++ b/src/lib/tests/sha3_test.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Test of the SHA3 function * diff --git a/src/lib/tests/shm_rbuff_test.c b/src/lib/tests/shm_rbuff_test.c index a3ed1449..e36c3229 100644 --- a/src/lib/tests/shm_rbuff_test.c +++ b/src/lib/tests/shm_rbuff_test.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Test of the shm_rbuff * diff --git a/src/lib/tests/time_utils_test.c b/src/lib/tests/time_test.c index fa65c4dc..65f896bb 100644 --- a/src/lib/tests/time_utils_test.c +++ b/src/lib/tests/time_test.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Test of the time utilities * @@ -22,7 +22,7 @@ #define _POSIX_C_SOURCE 200809L -#include <ouroboros/time_utils.h> +#include <ouroboros/time.h> #include <stdio.h> @@ -66,8 +66,8 @@ static int tv_check(struct timeval * v, return v->tv_sec == sec && v->tv_usec == usec; } -int time_utils_test(int argc, - char ** argv) +int time_test(int argc, + char ** argv) { struct timespec s0; struct timespec s1; diff --git a/src/lib/timerwheel.c b/src/lib/timerwheel.c index 487dcd0e..96f4ac47 100644 --- a/src/lib/timerwheel.c +++ b/src/lib/timerwheel.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Timerwheel * @@ -30,16 +30,12 @@ struct rxm { struct list_head next; uint32_t seqno; -#ifdef RXM_BUFFER_ON_HEAP - uint8_t * pkt; - size_t pkt_len; -#else +#ifndef RXM_BUFFER_ON_HEAP struct shm_du_buff * sdb; - uint8_t * head; - uint8_t * tail; #endif + struct frct_pci * pkt; + size_t len; time_t t0; /* Time when original was sent (us). */ - size_t mul; /* RTO multiplier. */ struct frcti * frcti; int fd; int flow_id; /* Prevent rtx when fd reused. */ @@ -62,11 +58,9 @@ struct { struct list_head acks[ACKQ_SLOTS]; bool map[ACKQ_SLOTS][PROG_MAX_FLOWS]; - size_t prv_rxm; /* Last processed rxm slot at lvl 0. */ - size_t prv_ack; /* Last processed ack slot. */ + size_t prv_rxm[RXMQ_LVLS]; /* Last processed rxm slots. */ + size_t prv_ack; /* Last processed ack slot. */ pthread_mutex_t lock; - - bool in_use; } rw; static void timerwheel_fini(void) @@ -119,8 +113,10 @@ static int timerwheel_init(void) clock_gettime(PTHREAD_COND_CLOCK, &now); - rw.prv_rxm = (ts_to_rxm_slot(now) - 1) & (RXMQ_SLOTS - 1); for (i = 0; i < RXMQ_LVLS; ++i) { + rw.prv_rxm[i] = (ts_to_rxm_slot(now) - 1); + rw.prv_rxm[i] >>= (RXMQ_BUMP * i); + rw.prv_rxm[i] &= (RXMQ_SLOTS - 1); for (j = 0; j < RXMQ_SLOTS; ++j) list_head_init(&rw.rxms[i][j]); } @@ -142,38 +138,34 @@ static void timerwheel_move(void) size_t i; size_t j; - if (!__sync_bool_compare_and_swap(&rw.in_use, true, true)) - return; - pthread_mutex_lock(&rw.lock); pthread_cleanup_push(__cleanup_mutex_unlock, &rw.lock); clock_gettime(PTHREAD_COND_CLOCK, &now); - rxm_slot = ts_to_ns(now) >> RXMQ_RES; - j = rw.prv_rxm; - rw.prv_rxm = rxm_slot & (RXMQ_SLOTS - 1); + rxm_slot = ts_to_rxm_slot(now); for (i = 0; i < RXMQ_LVLS; ++i) { size_t j_max_slot = rxm_slot & (RXMQ_SLOTS - 1); + j = rw.prv_rxm[i]; if (j_max_slot < j) j_max_slot += RXMQ_SLOTS; - while (j++ < j_max_slot) { list_for_each_safe(p, h, &rw.rxms[i][j & (RXMQ_SLOTS - 1)]) { struct rxm * r; struct frct_cr * snd_cr; struct frct_cr * rcv_cr; + size_t slot; size_t rslot; ssize_t idx; struct shm_du_buff * sdb; - uint8_t * head; + struct frct_pci * pci; struct flow * f; uint32_t snd_lwe; uint32_t rcv_lwe; - time_t rto; + size_t lvl = 0; r = list_entry(p, struct rxm, next); @@ -186,14 +178,13 @@ static void timerwheel_move(void) shm_du_buff_ack(r->sdb); #endif if (f->frcti == NULL - || f->flow_id != r->flow_id) + || f->info.id != r->flow_id) goto cleanup; pthread_rwlock_rdlock(&r->frcti->lock); snd_lwe = snd_cr->lwe; rcv_lwe = rcv_cr->lwe; - rto = r->frcti->rto; pthread_rwlock_unlock(&r->frcti->lock); @@ -205,65 +196,69 @@ static void timerwheel_move(void) if (ts_to_ns(now) - r->t0 > r->frcti->r) goto flow_down; - if (r->frcti->probe - && (r->frcti->rttseq + 1) == r->seqno) + pthread_rwlock_wrlock(&r->frcti->lock); + + if (r->seqno == r->frcti->rttseq) { + r->frcti->rto += + r->frcti->rto >> RTO_DIV; r->frcti->probe = false; + } +#ifdef PROC_FLOW_STATS + r->frcti->n_rtx++; +#endif + rslot = r->frcti->rto >> RXMQ_RES; + + pthread_rwlock_unlock(&r->frcti->lock); + + /* Schedule at least in the next time slot. */ + slot = ts_to_ns(now) >> RXMQ_RES; + + while (rslot >= RXMQ_SLOTS) { + ++lvl; + rslot >>= RXMQ_BUMP; + slot >>= RXMQ_BUMP; + } + + if (lvl >= RXMQ_LVLS) /* Can't reschedule */ + goto flow_down; + + rslot = (rslot + slot + 1) & (RXMQ_SLOTS - 1); #ifdef RXM_BLOCKING - #ifdef RXM_BUFFER_ON_HEAP - if (ipcp_sdb_reserve(&sdb, r->pkt_len)) - #else - if (ipcp_sdb_reserve(&sdb, r->tail - r->head)) - #endif + if (ipcp_sdb_reserve(&sdb, r->len) < 0) #else - #ifdef RXM_BUFFER_ON_HEAP - if (shm_rdrbuff_alloc(ai.rdrb, r->pkt_len, NULL, - &sdb)) - #else - if (shm_rdrbuff_alloc(ai.rdrb, - r->tail - r->head, NULL, - &sdb)) - #endif + if (shm_rdrbuff_alloc(ai.rdrb, r->len, NULL, + &sdb) < 0) #endif - goto reschedule; /* rbuff full */ - idx = shm_du_buff_get_idx(sdb); + goto reschedule; /* rdrbuff full */ - head = shm_du_buff_head(sdb); -#ifdef RXM_BUFFER_ON_HEAP - memcpy(head, r->pkt, r->pkt_len); -#else - memcpy(head, r->head, r->tail - r->head); + pci = (struct frct_pci *) shm_du_buff_head(sdb); + memcpy(pci, r->pkt, r->len); +#ifndef RXM_BUFFER_ON_HEAP ipcp_sdb_release(r->sdb); - r->sdb = sdb; - r->head = head; - r->tail = shm_du_buff_tail(sdb); + r->sdb = sdb; + r->pkt = pci; shm_du_buff_wait_ack(sdb); #endif + idx = shm_du_buff_get_idx(sdb); + /* Retransmit the copy. */ - ((struct frct_pci *) head)->ackno = - hton32(rcv_lwe); + pci->ackno = hton32(rcv_lwe); #ifdef RXM_BLOCKING - if (shm_rbuff_write_b(f->tx_rb, idx, NULL) == 0) + if (shm_rbuff_write_b(f->tx_rb, idx, NULL) < 0) #else - if (shm_rbuff_write(f->tx_rb, idx) == 0) + if (shm_rbuff_write(f->tx_rb, idx) < 0) #endif - shm_flow_set_notify(f->set, f->flow_id, - FLOW_PKT); - reschedule: - r->mul++; - - /* Schedule at least in the next time slot. */ - rslot = (rxm_slot - + MAX(((rto * r->mul) >> RXMQ_RES), 1)) - & (RXMQ_SLOTS - 1); - - list_add_tail(&r->next, &rw.rxms[i][rslot]); - + goto flow_down; + shm_flow_set_notify(f->set, f->info.id, + FLOW_PKT); + reschedule: + list_add(&r->next, &rw.rxms[lvl][rslot]); continue; - flow_down: + flow_down: shm_rbuff_set_acl(f->tx_rb, ACL_FLOWDOWN); shm_rbuff_set_acl(f->rx_rb, ACL_FLOWDOWN); - cleanup: + cleanup: #ifdef RXM_BUFFER_ON_HEAP free(r->pkt); #else @@ -272,9 +267,9 @@ static void timerwheel_move(void) free(r); } } + rw.prv_rxm[i] = rxm_slot & (RXMQ_SLOTS - 1); /* Move up a level in the wheel. */ rxm_slot >>= RXMQ_BUMP; - j >>= RXMQ_BUMP; } ack_slot = ts_to_ack_slot(now) & (ACKQ_SLOTS - 1) ; @@ -297,11 +292,10 @@ static void timerwheel_move(void) rw.map[j & (ACKQ_SLOTS - 1)][a->fd] = false; - if (f->flow_id == a->flow_id && f->frcti != NULL) + if (f->info.id == a->flow_id && f->frcti != NULL) send_frct_pkt(a->frcti); free(a); - } } @@ -327,21 +321,19 @@ static int timerwheel_rxm(struct frcti * frcti, clock_gettime(PTHREAD_COND_CLOCK, &now); r->t0 = ts_to_ns(now); - r->mul = 0; r->seqno = seqno; r->frcti = frcti; + r->len = shm_du_buff_len(sdb); #ifdef RXM_BUFFER_ON_HEAP - r->pkt_len = shm_du_buff_tail(sdb) - shm_du_buff_head(sdb); - r->pkt = malloc(r->pkt_len); + r->pkt = malloc(r->len); if (r->pkt == NULL) { free(r); return -ENOMEM; } - memcpy(r->pkt, shm_du_buff_head(sdb), r->pkt_len); + memcpy(r->pkt, shm_du_buff_head(sdb), r->len); #else - r->sdb = sdb; - r->head = shm_du_buff_head(sdb); - r->tail = shm_du_buff_tail(sdb); + r->sdb = sdb; + r->pkt = (struct frct_pci *) shm_du_buff_head(sdb); #endif pthread_rwlock_rdlock(&r->frcti->lock); @@ -349,7 +341,7 @@ static int timerwheel_rxm(struct frcti * frcti, slot = r->t0 >> RXMQ_RES; r->fd = frcti->fd; - r->flow_id = ai.flows[r->fd].flow_id; + r->flow_id = ai.flows[r->fd].info.id; pthread_rwlock_unlock(&r->frcti->lock); @@ -367,7 +359,7 @@ static int timerwheel_rxm(struct frcti * frcti, return -EPERM; } - slot = (slot + rto_slot) & (RXMQ_SLOTS - 1); + slot = (slot + rto_slot + 1) & (RXMQ_SLOTS - 1); pthread_mutex_lock(&rw.lock); @@ -377,13 +369,11 @@ static int timerwheel_rxm(struct frcti * frcti, #endif pthread_mutex_unlock(&rw.lock); - __sync_bool_compare_and_swap(&rw.in_use, false, true); - return 0; } -static int timerwheel_ack(int fd, - struct frcti * frcti) +static int timerwheel_delayed_ack(int fd, + struct frcti * frcti) { struct timespec now; struct ack * a; @@ -395,18 +385,16 @@ static int timerwheel_ack(int fd, clock_gettime(PTHREAD_COND_CLOCK, &now); - slot = DELT_ACK >> ACKQ_RES; - if (slot >= ACKQ_SLOTS) { /* Out of timerwheel range. */ - free(a); - return -EPERM; - } + pthread_rwlock_rdlock(&frcti->lock); - slot = (((ts_to_ns(now) + DELT_ACK) >> ACKQ_RES) + 1) + slot = (((ts_to_ns(now) + (TICTIME << 1)) >> ACKQ_RES) + 1) & (ACKQ_SLOTS - 1); + pthread_rwlock_unlock(&frcti->lock); + a->fd = fd; a->frcti = frcti; - a->flow_id = ai.flows[fd].flow_id; + a->flow_id = ai.flows[fd].info.id; pthread_mutex_lock(&rw.lock); @@ -422,7 +410,5 @@ static int timerwheel_ack(int fd, pthread_mutex_unlock(&rw.lock); - __sync_bool_compare_and_swap(&rw.in_use, false, true); - return 0; } diff --git a/src/lib/tpm.c b/src/lib/tpm.c index dfba6492..0ef1fda8 100644 --- a/src/lib/tpm.c +++ b/src/lib/tpm.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Threadpool management * @@ -26,12 +26,12 @@ #include <ouroboros/errno.h> #include <ouroboros/list.h> -#include <ouroboros/time_utils.h> +#include <ouroboros/time.h> #include <ouroboros/tpm.h> +#include <assert.h> #include <pthread.h> #include <stdlib.h> -#include <assert.h> #define TPM_TIMEOUT 1000 @@ -117,8 +117,7 @@ static void tpm_kill(struct tpm * tpm) static void * tpmgr(void * o) { struct timespec dl; - struct timespec to = {(TPM_TIMEOUT / 1000), - (TPM_TIMEOUT % 1000) * MILLION}; + struct timespec to = TIMESPEC_INIT_MS(TPM_TIMEOUT); struct tpm * tpm = (struct tpm *) o; while (true) { @@ -239,12 +238,12 @@ void tpm_stop(struct tpm * tpm) tpm->state = TPM_NULL; pthread_mutex_unlock(&tpm->lock); + + pthread_join(tpm->mgr, NULL); } void tpm_destroy(struct tpm * tpm) { - pthread_join(tpm->mgr, NULL); - pthread_mutex_destroy(&tpm->lock); pthread_cond_destroy(&tpm->cond); diff --git a/src/lib/utils.c b/src/lib/utils.c index 931ee449..fdbcd9d9 100644 --- a/src/lib/utils.c +++ b/src/lib/utils.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Handy utilities * @@ -20,6 +20,10 @@ * Foundation, Inc., http://www.fsf.org/about/contact/. */ +#define _POSIX_C_SOURCE 200809L + +#include <ouroboros/utils.h> + #include <stdlib.h> #include <string.h> @@ -35,14 +39,14 @@ int n_digits(unsigned i) return n; } -char * path_strip(char * src) +char * path_strip(const char * src) { - char * dst = NULL; + char * dst; if (src == NULL) return NULL; - dst = src + strlen(src); + dst = (char *) src + strlen(src); while (dst > src && *dst != '/') --dst; @@ -52,3 +56,58 @@ char * path_strip(char * src) return dst; } + +size_t argvlen(const char ** argv) +{ + size_t argc = 0; + + if (argv == NULL) + return 0; + + while (*argv++ != NULL) + argc++; + + return argc; +} + +void argvfree(char ** argv) +{ + char ** argv_dup; + + if (argv == NULL) + return; + + argv_dup = argv; + while (*argv_dup != NULL) + free(*(argv_dup++)); + + free(argv); +} + +char ** argvdup(char ** argv) +{ + int argc = 0; + char ** argv_dup = argv; + int i; + + if (argv == NULL) + return NULL; + + while (*(argv_dup++) != NULL) + argc++; + + argv_dup = malloc((argc + 1) * sizeof(*argv_dup)); + if (argv_dup == NULL) + return NULL; + + for (i = 0; i < argc; ++i) { + argv_dup[i] = strdup(argv[i]); + if (argv_dup[i] == NULL) { + argvfree(argv_dup); + return NULL; + } + } + + argv_dup[argc] = NULL; + return argv_dup; +} diff --git a/src/tools/irm/irm.c b/src/tools/irm/irm.c index 0078025b..ba0f4713 100644 --- a/src/tools/irm/irm.c +++ b/src/tools/irm/irm.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * A tool to instruct the IRM daemon * @@ -49,7 +49,7 @@ static void usage(void) { printf("Usage: irm [OPERATION]\n\n" - "where OPERATION = {ipcp bind unbind name}\n"); + "where OPERATION in { ipcp bind unbind name }\n"); } static int do_help(int argc, diff --git a/src/tools/irm/irm_bind.c b/src/tools/irm/irm_bind.c index 86294f67..2e8b14ef 100644 --- a/src/tools/irm/irm_bind.c +++ b/src/tools/irm/irm_bind.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Bind names in the processing system * @@ -47,7 +47,7 @@ static void usage(void) { printf("Usage: irm bind [OPERATION]\n" "\n" - "where OPERATION = {program process ipcp help}\n"); + "where OPERATION in {program process ipcp help}\n"); } static int do_help(int argc, diff --git a/src/tools/irm/irm_bind_ipcp.c b/src/tools/irm/irm_bind_ipcp.c index d95a0c97..7d5dd636 100644 --- a/src/tools/irm/irm_bind_ipcp.c +++ b/src/tools/irm/irm_bind_ipcp.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Bind IPCP Instance to a name * @@ -55,11 +55,11 @@ static void usage(void) int do_bind_ipcp(int argc, char ** argv) { - char * ipcp = NULL; - char * name = NULL; - struct ipcp_info * ipcps; - ssize_t len; - ssize_t i; + char * ipcp = NULL; + char * name = NULL; + struct ipcp_list_info * ipcps; + ssize_t len; + ssize_t i; while (argc > 0) { if (matches(*argv, "name") == 0) { diff --git a/src/tools/irm/irm_bind_process.c b/src/tools/irm/irm_bind_process.c index 5fd9e740..fffd5fe9 100644 --- a/src/tools/irm/irm_bind_process.c +++ b/src/tools/irm/irm_bind_process.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Bind a process to a name * diff --git a/src/tools/irm/irm_bind_program.c b/src/tools/irm/irm_bind_program.c index d36521dd..8a0dc33c 100644 --- a/src/tools/irm/irm_bind_program.c +++ b/src/tools/irm/irm_bind_program.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Bind programs to a name * diff --git a/src/tools/irm/irm_ipcp.c b/src/tools/irm/irm_ipcp.c index 9e539ca4..63e617d9 100644 --- a/src/tools/irm/irm_ipcp.c +++ b/src/tools/irm/irm_ipcp.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * A tool to instruct the IRM daemon * @@ -44,10 +44,11 @@ static void usage(void) { printf("Usage: irm ipcp [OPERATION]\n\n" - "where OPERATION = {create destroy\n" - " bootstrap enroll\n" - " connect disconnect\n" - " list help}\n"); + "where OPERATION in {create destroy\n" + " bootstrap enroll\n" + " connect disconnect\n" + " list\n" + " help}\n"); } static int do_help(int argc, char **argv) diff --git a/src/tools/irm/irm_ipcp_bootstrap.c b/src/tools/irm/irm_ipcp_bootstrap.c index a246d661..b8e5c54d 100644 --- a/src/tools/irm/irm_ipcp_bootstrap.c +++ b/src/tools/irm/irm_ipcp_bootstrap.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Bootstrap IPC Processes * @@ -88,7 +88,7 @@ static void usage(void) " name <ipcp name>\n" " layer <layer name>\n" " [type [TYPE]]\n" - "where TYPE = {" UNICAST " " BROADCAST " " LOCAL " " + "where TYPE in {" UNICAST " " BROADCAST " " LOCAL " " UDP " " ETH_LLC " " ETH_DIX "},\n\n" "if TYPE == " UNICAST "\n" " [addr <address size> (default: %d)]\n" @@ -99,11 +99,11 @@ static void usage(void) " [congestion <CONG_POLICY> (default: %s)]\n" " [hash [ALGORITHM] (default: %s)]\n" " [autobind]\n" - "where ADDRESS_POLICY = {" FLAT_RANDOM_ADDR_AUTH "}\n" - " ROUTING_POLICY = {" LINK_STATE_ROUTING " " + "where ADDRESS_POLICY in {" FLAT_RANDOM_ADDR_AUTH "}\n" + " ROUTING_POLICY in {" LINK_STATE_ROUTING " " LINK_STATE_LFA_ROUTING " " LINK_STATE_ECM_ROUTING "}\n" - " CONG_POLICY = {" NONE_CA " " MB_ECN_CA "}\n" - " ALGORITHM = {" SHA3_224 " " SHA3_256 " " + " CONG_POLICY in {" NONE_CA " " MB_ECN_CA "}\n" + " ALGORITHM in {" SHA3_224 " " SHA3_256 " " SHA3_384 " " SHA3_512 "}\n\n" "if TYPE == " UDP "\n" " ip <IP address in dotted notation>\n" @@ -113,17 +113,17 @@ static void usage(void) "if TYPE == " ETH_LLC "\n" " dev <interface name>\n" " [hash [ALGORITHM] (default: %s)]\n" - "where ALGORITHM = {" SHA3_224 " " SHA3_256 " " + "where ALGORITHM in {" SHA3_224 " " SHA3_256 " " SHA3_384 " " SHA3_512 "}\n\n" "if TYPE == " ETH_DIX "\n" " dev <interface name>\n" " [ethertype <ethertype> (default: 0x%4X)]\n" " [hash [ALGORITHM] (default: %s)]\n" - "where ALGORITHM = {" SHA3_224 " " SHA3_256 " " + "where ALGORITHM in {" SHA3_224 " " SHA3_256 " " SHA3_384 " " SHA3_512 "}\n\n" "if TYPE == " LOCAL "\n" " [hash [ALGORITHM] (default: %s)]\n" - "where ALGORITHM = {" SHA3_224 " " SHA3_256 " " + "where ALGORITHM in {" SHA3_224 " " SHA3_256 " " SHA3_384 " " SHA3_512 "}\n\n" "if TYPE == " BROADCAST "\n" " [autobind]\n\n", @@ -136,29 +136,29 @@ static void usage(void) int do_bootstrap_ipcp(int argc, char ** argv) { - char * ipcp = NULL; - pid_t pid = -1; - struct ipcp_config conf; - uint8_t addr_size = DEFAULT_ADDR_SIZE; - uint8_t eid_size = DEFAULT_EID_SIZE; - uint8_t max_ttl = DEFAULT_TTL; - enum pol_addr_auth addr_auth_type = DEFAULT_ADDR_AUTH; - enum pol_routing routing_type = DEFAULT_ROUTING; - enum pol_dir_hash hash_algo = DEFAULT_HASH_ALGO; - enum pol_cong_avoid cong_avoid = DEFAULT_CONG_AVOID; - uint32_t ip_addr = 0; - uint32_t dns_addr = DEFAULT_DDNS; - char * ipcp_type = NULL; - enum ipcp_type type = IPCP_INVALID; - char * layer = NULL; - char * dev = NULL; - uint16_t ethertype = DEFAULT_ETHERTYPE; - struct ipcp_info * ipcps; - ssize_t len = 0; - int i = 0; - bool autobind = false; - int cargs; - int port = DEFAULT_UDP_PORT; + char * ipcp = NULL; + pid_t pid = -1; + struct ipcp_config conf; + uint8_t addr_size = DEFAULT_ADDR_SIZE; + uint8_t eid_size = DEFAULT_EID_SIZE; + uint8_t max_ttl = DEFAULT_TTL; + enum pol_addr_auth addr_auth_type = DEFAULT_ADDR_AUTH; + enum pol_routing routing_type = DEFAULT_ROUTING; + enum pol_dir_hash hash_algo = DEFAULT_HASH_ALGO; + enum pol_cong_avoid cong_avoid = DEFAULT_CONG_AVOID; + uint32_t ip_addr = 0; + uint32_t dns_addr = DEFAULT_DDNS; + char * ipcp_type = NULL; + enum ipcp_type type = IPCP_INVALID; + char * layer = NULL; + char * dev = NULL; + uint16_t ethertype = DEFAULT_ETHERTYPE; + struct ipcp_list_info * ipcps; + ssize_t len = 0; + int i = 0; + bool autobind = false; + int cargs; + int port = DEFAULT_UDP_PORT; while (argc > 0) { cargs = 2; @@ -193,7 +193,7 @@ int do_bootstrap_ipcp(int argc, ethertype = strtol(*(argv + 1), NULL, 0); else ethertype = strtol(*(argv + 1), NULL, 16); - if (ethertype < 0x0600 || ethertype == 0xFFFF) { + if (ethertype < 0x0600 || ethertype >= 0xFFFF) { printf("Invalid Ethertype: \"%s\".\n" "Recommended range: 0xA000-0xEFFF.\n", *(argv + 1)); @@ -292,6 +292,7 @@ int do_bootstrap_ipcp(int argc, printf("Types do not match.\n\n"); goto fail; } + conf.type = ipcps[i].type; if (autobind && (conf.type != IPCP_UNICAST && @@ -302,40 +303,41 @@ int do_bootstrap_ipcp(int argc, } if (strlen(layer) > LAYER_NAME_SIZE) { - printf("Layer name too big.\n\n"); + printf("Layer name too long.\n\n"); goto fail_usage; } - strcpy(conf.layer_info.layer_name, layer); - if (conf.type != IPCP_UDP) - conf.layer_info.dir_hash_algo = hash_algo; + strcpy(conf.layer_info.name, layer); + conf.layer_info.dir_hash_algo = hash_algo; switch (conf.type) { case IPCP_UNICAST: - conf.addr_size = addr_size; - conf.eid_size = eid_size; - conf.max_ttl = max_ttl; - conf.addr_auth_type = addr_auth_type; - conf.routing_type = routing_type; - conf.cong_avoid = cong_avoid; + conf.unicast.dt.addr_size = addr_size; + conf.unicast.dt.eid_size = eid_size; + conf.unicast.dt.max_ttl = max_ttl; + conf.unicast.dt.routing_type = routing_type; + conf.unicast.addr_auth_type = addr_auth_type; + conf.unicast.cong_avoid = cong_avoid; break; case IPCP_UDP: if (ip_addr == 0) goto fail_usage; - conf.ip_addr = ip_addr; - conf.dns_addr = dns_addr; - conf.port = port; + conf.udp.ip_addr = ip_addr; + conf.udp.dns_addr = dns_addr; + conf.udp.port = port; break; + case IPCP_ETH_DIX: + conf.eth.ethertype = ethertype; + /* FALLTHRU */ case IPCP_ETH_LLC: if (dev == NULL) goto fail_usage; - conf.dev = dev; - break; - case IPCP_ETH_DIX: - if (dev == NULL) + if (strlen(dev) > DEV_NAME_SIZE) { + printf("Device name too long.\n\n"); goto fail_usage; - conf.dev = dev; - conf.ethertype = ethertype; + } + + strcpy(conf.eth.dev, dev); break; case IPCP_BROADCAST: /* FALLTHRU */ diff --git a/src/tools/irm/irm_ipcp_connect.c b/src/tools/irm/irm_ipcp_connect.c index 965a3a26..68e13bd0 100644 --- a/src/tools/irm/irm_ipcp_connect.c +++ b/src/tools/irm/irm_ipcp_connect.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Connect components of unicast or broadcast IPC processes * @@ -55,25 +55,25 @@ static void usage(void) " name <ipcp name>\n" " dst <name of destination IPCP>\n" " [component [COMPONENT]]\n" - "where COMPONENT = {" DT " " MGMT "}\n\n" + "where COMPONENT in {" DT " " MGMT "}\n\n" "if COMPONENT == " DT "\n" " [qos [QOS]\n" - "where QOS = {raw, best, voice, video, data}\n\n"); + "where QOS in {raw, best, voice, video, data}\n"); } int do_connect_ipcp(int argc, char ** argv) { - char * ipcp = NULL; - char * dst = NULL; - char * comp = "*"; - char * component = NULL; - char * qos = NULL; - struct ipcp_info * ipcps; - ssize_t len = 0; - pid_t pid = -1; - ssize_t i; - qosspec_t qs = qos_raw; + char * ipcp = NULL; + char * dst = NULL; + char * comp = "*"; + char * component = NULL; + char * qos = NULL; + struct ipcp_list_info * ipcps; + ssize_t len = 0; + pid_t pid = -1; + ssize_t i; + qosspec_t qs = qos_raw; while (argc > 0) { if (matches(*argv, "name") == 0) { diff --git a/src/tools/irm/irm_ipcp_create.c b/src/tools/irm/irm_ipcp_create.c index 586cdf85..35d33782 100644 --- a/src/tools/irm/irm_ipcp_create.c +++ b/src/tools/irm/irm_ipcp_create.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Create IPC Processes * @@ -44,7 +44,7 @@ #include "irm_ops.h" #include "irm_utils.h" -#define UNICAST "unicast" +#define UNICAST "unicast" #define BROADCAST "broadcast" #define UDP "udp" #define ETH_LLC "eth-llc" @@ -56,17 +56,17 @@ static void usage(void) printf("Usage: irm ipcp create\n" " name <ipcp name>\n" " type [TYPE]\n\n" - "where TYPE = {" UNICAST " " BROADCAST " " LOCAL " " - UDP " " ETH_LLC "}\n"); + "where TYPE in {" UNICAST " " BROADCAST " " LOCAL " " + UDP " " ETH_LLC " " ETH_DIX "}\n"); } int do_create_ipcp(int argc, char ** argv) { - char * ipcp_type = NULL; - char * ipcp_name = NULL; - enum ipcp_type type = 0; - pid_t pid; + char * ipcp_type = NULL; + char * ipcp_name = NULL; + enum ipcp_type type = 0; + pid_t pid; while (argc > 0) { if (matches(*argv, "type") == 0) { @@ -101,12 +101,13 @@ int do_create_ipcp(int argc, else if (strcmp(ipcp_type, ETH_DIX) == 0) type = IPCP_ETH_DIX; else { + printf("IPCP type \"%s\" is unknown.\n", ipcp_type); usage(); return -1; } pid = irm_create_ipcp(ipcp_name, type); - if (pid <= 0) + if (pid < 0) return -1; return 0; diff --git a/src/tools/irm/irm_ipcp_destroy.c b/src/tools/irm/irm_ipcp_destroy.c index 6f42d09f..1a5e564e 100644 --- a/src/tools/irm/irm_ipcp_destroy.c +++ b/src/tools/irm/irm_ipcp_destroy.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Destroy IPC Processes * @@ -55,10 +55,10 @@ static void usage(void) int do_destroy_ipcp(int argc, char ** argv) { - char * ipcp = NULL; - struct ipcp_info * ipcps; - ssize_t len; - int i; + char * ipcp = NULL; + struct ipcp_list_info * ipcps; + ssize_t len; + int i; while (argc > 0) { if (matches(*argv, "name") == 0) { diff --git a/src/tools/irm/irm_ipcp_disconnect.c b/src/tools/irm/irm_ipcp_disconnect.c index 87685e25..7ce724e1 100644 --- a/src/tools/irm/irm_ipcp_disconnect.c +++ b/src/tools/irm/irm_ipcp_disconnect.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Connect components of unicast or broadcast IPC processes * @@ -54,20 +54,20 @@ static void usage(void) " name <ipcp name>\n" " dst <name of destination IPCP>\n" " [component [COMPONENT]]\n\n" - "where COMPONENT = {" DT " " MGMT "}\n"); + "where COMPONENT in {" DT " " MGMT "}\n"); } int do_disconnect_ipcp(int argc, char ** argv) { - char * ipcp = NULL; - char * dst = NULL; - char * comp = "*"; - char * component = NULL; - struct ipcp_info * ipcps; - ssize_t len = 0; - pid_t pid = -1; - ssize_t i; + char * ipcp = NULL; + char * dst = NULL; + char * comp = "*"; + char * component = NULL; + struct ipcp_list_info * ipcps; + ssize_t len = 0; + pid_t pid = -1; + ssize_t i; while (argc > 0) { if (matches(*argv, "name") == 0) { diff --git a/src/tools/irm/irm_ipcp_enroll.c b/src/tools/irm/irm_ipcp_enroll.c index 2feffc54..86a22a71 100644 --- a/src/tools/irm/irm_ipcp_enroll.c +++ b/src/tools/irm/irm_ipcp_enroll.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Enroll IPC Processes * @@ -57,15 +57,15 @@ static void usage(void) " [dst <destination to enroll with>]\n" " [type [TYPE], default = " UNICAST "]\n" " [autobind]\n" - "where TYPE = {" UNICAST " " BROADCAST "}\n"); + "where TYPE in {" UNICAST " " BROADCAST "}\n"); } static int get_layer_name(const char * ipcp, char * layer_name) { - struct ipcp_info * ipcps; - size_t len; - size_t i; + struct ipcp_list_info * ipcps; + ssize_t len; + ssize_t i; len = irm_list_ipcps(&ipcps); for (i = 0; i < len; i++) @@ -83,17 +83,17 @@ static int get_layer_name(const char * ipcp, int do_enroll_ipcp(int argc, char ** argv) { - char * ipcp = NULL; - char * layer = NULL; - char * dst = NULL; - struct ipcp_info * ipcps; - pid_t pid = -1; - ssize_t len = 0; - int i = 0; - bool autobind = false; - int cargs; - char * ipcp_type = UNICAST; - enum ipcp_type type = IPCP_INVALID; + char * ipcp = NULL; + char * layer = NULL; + char * dst = NULL; + struct ipcp_list_info * ipcps; + pid_t pid = -1; + ssize_t len = 0; + int i = 0; + bool autobind = false; + int cargs; + char * ipcp_type = UNICAST; + enum ipcp_type type = IPCP_INVALID; while (argc > 0) { cargs = 2; @@ -146,40 +146,39 @@ int do_enroll_ipcp(int argc, } for (i = 0; i < len; i++) { + char enr_layer[LAYER_NAME_SIZE]; if (ipcps[i].type != type) continue; - if (wildcard_match(ipcps[i].name, ipcp) == 0) { - char enr_layer[LAYER_NAME_SIZE]; + if (wildcard_match(ipcps[i].name, ipcp) != 0) + continue; - pid = ipcps[i].pid; + pid = ipcps[i].pid; + + if (irm_enroll_ipcp(pid, dst)) { + printf("Failed to enroll IPCP.\n"); + goto fail; + } + + if (get_layer_name(ipcps[i].name, enr_layer)) { + printf("Could not get layer name.\n"); + goto fail; + } - if (irm_enroll_ipcp(pid, dst)) { - printf("Failed to enroll IPCP.\n"); - goto fail; - } - - if (get_layer_name(ipcps[i].name, enr_layer)) { - printf("Could not get layer name.\n"); - goto fail; - } - - if (layer != NULL && strcmp(enr_layer, layer)) { - printf("Enrollment destination does not " - "match requested layer.\n"); - goto fail; - } - - if (autobind && irm_bind_process(pid, ipcp)) { - printf("Failed to bind %d to %s.\n", pid, ipcp); - goto fail; - } - - if (autobind && irm_bind_process(pid, enr_layer)) { - printf("Failed to bind %d to %s.\n", - pid, enr_layer); - goto fail; - } + if (layer != NULL && strcmp(enr_layer, layer)) { + printf("Enrollment destination does not " + "match requested layer.\n"); + goto fail; + } + + if (autobind && irm_bind_process(pid, ipcp)) { + printf("Failed to bind %d to %s.\n", pid, ipcp); + goto fail; + } + + if (autobind && irm_bind_process(pid, enr_layer)) { + printf("Failed to bind %d to %s.\n", pid, enr_layer); + goto fail; } } diff --git a/src/tools/irm/irm_ipcp_list.c b/src/tools/irm/irm_ipcp_list.c index 512e1645..dfa3099f 100644 --- a/src/tools/irm/irm_ipcp_list.c +++ b/src/tools/irm/irm_ipcp_list.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * List IPC Processes * @@ -86,12 +86,12 @@ static char * str_type(enum ipcp_type type) int do_list_ipcp(int argc, char ** argv) { - char * ipcp_type = NULL; - char * ipcp_name = NULL; - enum ipcp_type type = -1; - struct ipcp_info * ipcps; - ssize_t len; - ssize_t i; + char * ipcp_type = NULL; + char * ipcp_name = NULL; + enum ipcp_type type = -1; + struct ipcp_list_info * ipcps; + ssize_t len; + ssize_t i; while (argc > 0) { if (matches(*argv, "type") == 0) { diff --git a/src/tools/irm/irm_name.c b/src/tools/irm/irm_name.c index a14db4a0..d60b6c78 100644 --- a/src/tools/irm/irm_name.c +++ b/src/tools/irm/irm_name.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * A tool to instruct the IRM daemon * @@ -44,9 +44,10 @@ static void usage(void) { printf("Usage: irm name [OPERATION]\n\n" - "where OPERATION = {create destroy\n" - " register unregister\n" - " list help}\n"); + "where OPERATION in {create destroy\n" + " register unregister\n" + " list\n" + " help}\n"); } static int do_help(int argc, char **argv) diff --git a/src/tools/irm/irm_name_create.c b/src/tools/irm/irm_name_create.c index 61f20ea8..a0079cad 100644 --- a/src/tools/irm/irm_name_create.c +++ b/src/tools/irm/irm_name_create.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Create IPC Processes * @@ -52,7 +52,7 @@ static void usage(void) printf("Usage: irm name create\n" " <name>\n" " lb [LB_POLICY], default: %s\n\n" - "where LB_POLICY = {" RR " " SPILL "}\n", RR); + "where LB_POLICY in {" RR " " SPILL "}\n", RR); } int do_create_name(int argc, diff --git a/src/tools/irm/irm_name_destroy.c b/src/tools/irm/irm_name_destroy.c index f2d833e0..d4bd6c82 100644 --- a/src/tools/irm/irm_name_destroy.c +++ b/src/tools/irm/irm_name_destroy.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Create IPC Processes * diff --git a/src/tools/irm/irm_name_list.c b/src/tools/irm/irm_name_list.c index 0398b1cb..a807008c 100644 --- a/src/tools/irm/irm_name_list.c +++ b/src/tools/irm/irm_name_list.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * List names * diff --git a/src/tools/irm/irm_name_reg.c b/src/tools/irm/irm_name_reg.c index 04ca0b47..061ed8be 100644 --- a/src/tools/irm/irm_name_reg.c +++ b/src/tools/irm/irm_name_reg.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Register names with IPCPs * @@ -64,17 +64,17 @@ static void usage(void) int do_reg_name(int argc, char ** argv) { - char * name = NULL; - char * layers[MAX_LAYERS]; - size_t layers_len = 0; - char * ipcp[MAX_IPCPS]; - size_t ipcp_len = 0; - struct ipcp_info * ipcps; - ssize_t ipcps_len; - struct name_info * names; - ssize_t names_len; - bool name_create = true; - ssize_t i; + char * name = NULL; + char * layers[MAX_LAYERS]; + size_t layers_len = 0; + char * ipcp[MAX_IPCPS]; + size_t ipcp_len = 0; + struct ipcp_list_info * ipcps; + ssize_t ipcps_len; + struct name_info * names; + ssize_t names_len; + bool name_create = true; + ssize_t i; name = *(argv++); --argc; diff --git a/src/tools/irm/irm_name_unreg.c b/src/tools/irm/irm_name_unreg.c index 39d4b5f3..6e579f04 100644 --- a/src/tools/irm/irm_name_unreg.c +++ b/src/tools/irm/irm_name_unreg.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Unregister names from IPCPs * @@ -63,14 +63,14 @@ static void usage(void) int do_unreg_name(int argc, char ** argv) { - char * name = NULL; - char * layers[MAX_LAYERS]; - size_t layers_len = 0; - char * ipcp[MAX_IPCPS]; - size_t ipcp_len = 0; - struct ipcp_info * ipcps; - ssize_t len; - size_t i; + char * name = NULL; + char * layers[MAX_LAYERS]; + size_t layers_len = 0; + char * ipcp[MAX_IPCPS]; + size_t ipcp_len = 0; + struct ipcp_list_info * ipcps; + ssize_t len; + size_t i; name = *(argv++); --argc; diff --git a/src/tools/irm/irm_ops.h b/src/tools/irm/irm_ops.h index 7ce773d0..e04ffc02 100644 --- a/src/tools/irm/irm_ops.h +++ b/src/tools/irm/irm_ops.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Functions of the IRM tool that are one level deep * diff --git a/src/tools/irm/irm_unbind.c b/src/tools/irm/irm_unbind.c index ff9f0d38..d6594d01 100644 --- a/src/tools/irm/irm_unbind.c +++ b/src/tools/irm/irm_unbind.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Unbind names in the processing system * @@ -47,7 +47,7 @@ static void usage(void) { printf("Usage: irm unbind [OPERATION]\n" "\n" - "where OPERATION = {program process ipcp help}\n"); + "where OPERATION in {program process ipcp help}\n"); } static int do_help(int argc, diff --git a/src/tools/irm/irm_unbind_ipcp.c b/src/tools/irm/irm_unbind_ipcp.c index 705af1ab..53a2d16c 100644 --- a/src/tools/irm/irm_unbind_ipcp.c +++ b/src/tools/irm/irm_unbind_ipcp.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Unbind name from IPCP Instance * @@ -57,11 +57,11 @@ static void usage(void) int do_unbind_ipcp(int argc, char ** argv) { - char * ipcp = NULL; - char * name = NULL; - struct ipcp_info * ipcps; - ssize_t len; - ssize_t i; + char * ipcp = NULL; + char * name = NULL; + struct ipcp_list_info * ipcps; + ssize_t len; + ssize_t i; while (argc > 0) { if (matches(*argv, "name") == 0) { diff --git a/src/tools/irm/irm_unbind_process.c b/src/tools/irm/irm_unbind_process.c index 733410a1..264ed538 100644 --- a/src/tools/irm/irm_unbind_process.c +++ b/src/tools/irm/irm_unbind_process.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Unbind process names * diff --git a/src/tools/irm/irm_unbind_program.c b/src/tools/irm/irm_unbind_program.c index 41395c85..0c751e80 100644 --- a/src/tools/irm/irm_unbind_program.c +++ b/src/tools/irm/irm_unbind_program.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Unbind programs * diff --git a/src/tools/irm/irm_utils.c b/src/tools/irm/irm_utils.c index 4a15dfce..9694d647 100644 --- a/src/tools/irm/irm_utils.c +++ b/src/tools/irm/irm_utils.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Handy helper functions for the IRM tool * diff --git a/src/tools/irm/irm_utils.h b/src/tools/irm/irm_utils.h index f32b519a..27a0b941 100644 --- a/src/tools/irm/irm_utils.h +++ b/src/tools/irm/irm_utils.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Handy helper functions for the IRM tool * diff --git a/src/tools/obc/obc.c b/src/tools/obc/obc.c index 63e4eca1..462cbea9 100644 --- a/src/tools/obc/obc.c +++ b/src/tools/obc/obc.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * A simple broadcast application * diff --git a/src/tools/ocbr/ocbr.c b/src/tools/ocbr/ocbr.c index 2643b588..775bcaac 100644 --- a/src/tools/ocbr/ocbr.c +++ b/src/tools/ocbr/ocbr.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * CBR traffic generator * @@ -155,17 +155,22 @@ int main(int argc, char ** argv) if (s_apn == NULL) { printf("No server specified.\n"); usage(); - return 0; + return 1; } if (size > BUF_SIZE) { printf("Maximum size: %ld.\n", BUF_SIZE); - return 0; + return 1; } if (size < 0) { printf("Size overflow.\n"); - return 0; + return 1; + } + + if (rate <= 0) { + printf("Invalid rate.\n"); + return 1; } ret = client_main(s_apn, duration, size, rate, flood, sleep); diff --git a/src/tools/ocbr/ocbr_client.c b/src/tools/ocbr/ocbr_client.c index f2a6e20f..ba7b41f4 100644 --- a/src/tools/ocbr/ocbr_client.c +++ b/src/tools/ocbr/ocbr_client.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * A simple CBR generator * diff --git a/src/tools/ocbr/ocbr_server.c b/src/tools/ocbr/ocbr_server.c index 05659dc4..a4bbadd4 100644 --- a/src/tools/ocbr/ocbr_server.c +++ b/src/tools/ocbr/ocbr_server.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * A simple CBR generator * diff --git a/src/tools/oecho/oecho.c b/src/tools/oecho/oecho.c index 62f3f636..d5d03027 100644 --- a/src/tools/oecho/oecho.c +++ b/src/tools/oecho/oecho.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * A simple echo application * diff --git a/src/tools/operf/operf.c b/src/tools/operf/operf.c index 2afeb71f..10896bd5 100644 --- a/src/tools/operf/operf.c +++ b/src/tools/operf/operf.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Ouroboros perf application * diff --git a/src/tools/operf/operf_client.c b/src/tools/operf/operf_client.c index 58d9aaff..63f98299 100644 --- a/src/tools/operf/operf_client.c +++ b/src/tools/operf/operf_client.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Ouroboros ping application * diff --git a/src/tools/operf/operf_server.c b/src/tools/operf/operf_server.c index aa900919..d11f3486 100644 --- a/src/tools/operf/operf_server.c +++ b/src/tools/operf/operf_server.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Ouroboros perf application * diff --git a/src/tools/oping/oping.c b/src/tools/oping/oping.c index 093f3e84..ed3529e5 100644 --- a/src/tools/oping/oping.c +++ b/src/tools/oping/oping.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Ouroboros ping application * @@ -60,14 +60,31 @@ #include <errno.h> #include <float.h> -#define OPING_BUF_SIZE 1500 - -#define ECHO_REQUEST 0 -#define ECHO_REPLY 1 - +#define OPING_BUF_SIZE 1500 +#define ECHO_REQUEST 0 +#define ECHO_REPLY 1 #define OPING_MAX_FLOWS 256 -struct c { +#define USAGE_STRING \ +"Usage: oping [OPTION]...\n" \ +"\n" \ +"Checks liveness between a client and a server\n" \ +"and reports the Round Trip Time (RTT)\n" \ +"\n" \ +" -l, --listen Run in server mode\n" \ +"\n" \ +" -c, --count Number of packets\n" \ +" -d, --duration Duration of the test (default 1s)\n" \ +" -i, --interval Interval (default 1000ms)\n" \ +" -n, --server-name Name of the oping server\n" \ +" -q, --qos QoS (raw, raw_crypt, best, video, voice, data)\n" \ +" -s, --size Payload size (B, default 64)\n" \ +" -Q, --quiet Only print final statistics\n" \ +" -D, --timeofday Print time of day before each line\n" \ +"\n" \ +" --help Display this help text and exit\n" \ + +struct { char * s_apn; int interval; uint32_t count; @@ -90,12 +107,14 @@ struct c { pthread_t writer_pt; } client; -struct s { +struct { struct timespec times[OPING_MAX_FLOWS]; fset_t * flows; fqueue_t * fq; pthread_mutex_t lock; + bool quiet; + pthread_t cleaner_pt; pthread_t accept_pt; pthread_t server_pt; @@ -114,22 +133,7 @@ struct oping_msg { static void usage(void) { - printf("Usage: oping [OPTION]...\n" - "Checks liveness between a client and a server\n" - "and reports the Round Trip Time (RTT)\n\n" - " -l, --listen Run in server mode\n" - "\n" - " -c, --count Number of packets\n" - " -d, --duration Duration of the test (default 1s)\n" - " -i, --interval Interval (default 1000ms)\n" - " -n, --server-name Name of the oping server\n" - " -q, --qos QoS (raw, best, video, voice, data)" - "\n" - " -s, --size Payload size (B, default 64)\n" - " -Q, --quiet Only print final statistics\n" - " -D, --timeofday Print time of day before each line" - "\n" - " --help Display this help text and exit\n"); + printf(USAGE_STRING); } /* Times are in ms. */ @@ -170,32 +174,39 @@ int main(int argc, client.timestamp = false; client.qs = qos_raw; client.quiet = false; + server.quiet = false; while (argc > 0) { - if (strcmp(*argv, "-i") == 0 || - strcmp(*argv, "--interval") == 0) { + if ((strcmp(*argv, "-i") == 0 || + strcmp(*argv, "--interval") == 0) && + argc > 1) { client.interval = strtol(*(++argv), &rem, 10); client.interval *= time_mul(rem); --argc; - } else if (strcmp(*argv, "-n") == 0 || - strcmp(*argv, "--server-name") == 0) { + } else if ((strcmp(*argv, "-n") == 0 || + strcmp(*argv, "--server-name") == 0) && + argc > 1) { client.s_apn = *(++argv); --argc; - } else if (strcmp(*argv, "-c") == 0 || - strcmp(*argv, "--count") == 0) { + } else if ((strcmp(*argv, "-c") == 0 || + strcmp(*argv, "--count") == 0) && + argc > 1) { client.count = strtol(*(++argv), &rem, 10); --argc; - } else if (strcmp(*argv, "-d") == 0 || - strcmp(*argv, "--duration") == 0) { + } else if ((strcmp(*argv, "-d") == 0 || + strcmp(*argv, "--duration") == 0) && + argc > 1) { duration = strtol(*(++argv), &rem, 10); duration *= time_mul(rem); --argc; - } else if (strcmp(*argv, "-s") == 0 || - strcmp(*argv, "--size") == 0) { + } else if ((strcmp(*argv, "-s") == 0 || + strcmp(*argv, "--size") == 0) && + argc > 1) { client.size = strtol(*(++argv), &rem, 10); --argc; - } else if (strcmp(*argv, "-q") == 0 || - strcmp(*argv, "--qos") == 0) { + } else if ((strcmp(*argv, "-q") == 0 || + strcmp(*argv, "--qos") == 0) && + argc > 1) { qos = *(++argv); --argc; } else if (strcmp(*argv, "-l") == 0 || @@ -207,7 +218,7 @@ int main(int argc, } else if (strcmp(*argv, "-Q") == 0 || strcmp(*argv, "--quiet") == 0) { client.quiet = true; - + server.quiet = true; } else { goto fail; } diff --git a/src/tools/oping/oping_client.c b/src/tools/oping/oping_client.c index 0ab5a4af..7b03c83d 100644 --- a/src/tools/oping/oping_client.c +++ b/src/tools/oping/oping_client.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Ouroboros ping application * @@ -232,7 +232,7 @@ static int client_main(void) fd = flow_alloc(client.s_apn, &client.qs, NULL); if (fd < 0) { - printf("Failed to allocate flow.\n"); + printf("Failed to allocate flow: %d.\n", fd); client_fini(); return -1; } diff --git a/src/tools/oping/oping_server.c b/src/tools/oping/oping_server.c index ec9e85ab..6f76869c 100644 --- a/src/tools/oping/oping_server.c +++ b/src/tools/oping/oping_server.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Ouroboros ping application * @@ -105,7 +105,8 @@ void * server_thread(void *o) continue; } - printf("Received %d bytes on fd %d.\n", msg_len, fd); + if (!server.quiet) + printf("Received %d bytes on fd %d.\n", msg_len, fd); clock_gettime(CLOCK_REALTIME, &now); diff --git a/src/tools/ovpn/ovpn.c b/src/tools/ovpn/ovpn.c index dddbdde0..5333ff40 100644 --- a/src/tools/ovpn/ovpn.c +++ b/src/tools/ovpn/ovpn.c @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Ouroboros VPN * diff --git a/src/tools/time_utils.h b/src/tools/time_utils.h index 17f03d61..c17282dc 100644 --- a/src/tools/time_utils.h +++ b/src/tools/time_utils.h @@ -1,5 +1,5 @@ /* - * Ouroboros - Copyright (C) 2016 - 2021 + * Ouroboros - Copyright (C) 2016 - 2024 * * Time utilities * |