diff options
Diffstat (limited to 'src/irmd')
-rw-r--r-- | src/irmd/CMakeLists.txt | 33 | ||||
-rw-r--r-- | src/irmd/config.h.in | 57 | ||||
-rw-r--r-- | src/irmd/configfile.c | 332 | ||||
-rw-r--r-- | src/irmd/ipcp.c | 39 | ||||
-rw-r--r-- | src/irmd/irmd.h | 2 | ||||
-rw-r--r-- | src/irmd/main.c | 978 | ||||
-rw-r--r-- | src/irmd/oap.c | 288 | ||||
-rw-r--r-- | src/irmd/oap.h | 94 | ||||
-rw-r--r-- | src/irmd/reg/flow.h | 12 | ||||
-rw-r--r-- | src/irmd/reg/ipcp.c | 8 | ||||
-rw-r--r-- | src/irmd/reg/name.c | 74 | ||||
-rw-r--r-- | src/irmd/reg/name.h | 28 | ||||
-rw-r--r-- | src/irmd/reg/proc.h | 4 | ||||
-rw-r--r-- | src/irmd/reg/reg.c | 289 | ||||
-rw-r--r-- | src/irmd/reg/reg.h | 24 | ||||
-rw-r--r-- | src/irmd/reg/tests/CMakeLists.txt | 6 | ||||
-rw-r--r-- | src/irmd/reg/tests/flow_test.c | 40 | ||||
-rw-r--r-- | src/irmd/reg/tests/ipcp_test.c | 9 | ||||
-rw-r--r-- | src/irmd/reg/tests/name_test.c | 94 | ||||
-rw-r--r-- | src/irmd/reg/tests/proc_test.c | 25 | ||||
-rw-r--r-- | src/irmd/reg/tests/prog_test.c | 22 | ||||
-rw-r--r-- | src/irmd/reg/tests/reg_test.c | 495 | ||||
-rw-r--r-- | src/irmd/tests/CMakeLists.txt | 10 | ||||
-rw-r--r-- | src/irmd/tests/irm_test.c | 33 | ||||
-rw-r--r-- | src/irmd/tests/oap_test.c | 285 |
25 files changed, 2518 insertions, 763 deletions
diff --git a/src/irmd/CMakeLists.txt b/src/irmd/CMakeLists.txt index c9c2e553..46d49391 100644 --- a/src/irmd/CMakeLists.txt +++ b/src/irmd/CMakeLists.txt @@ -4,13 +4,14 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_SOURCE_DIR}/include) include_directories(${CMAKE_BINARY_DIR}/include) +set(OUROBOROS_CONFIG_DIR /etc/ouroboros CACHE STRING + "Configuration directory") + 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) @@ -23,11 +24,11 @@ if (LIBTOML_LIBRARIES) install(FILES "${CMAKE_BINARY_DIR}/irmd.conf.example" DESTINATION "${OUROBOROS_CONFIG_DIR}") unset(INSTALL_DIR) - mark_as_advanced(LIBTOML_LIBRARIES) + find_path(LIBTOML_INCLUDE toml.h) + mark_as_advanced(LIBTOML_LIBRARIES LIBTOML_INCLUDE) 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 () @@ -38,16 +39,26 @@ else () unset(HAVE_TOML) endif () +set(OUROBOROS_SECURITY_DIR "${OUROBOROS_CONFIG_DIR}/security" CACHE STRING + "Security directory holding authentication information") +set(OUROBOROS_CA_CRT_DIR "${OUROBOROS_SECURITY_DIR}/cacert" CACHE STRING + "Directory holding trusted CA certificates") +set(OUROBOROS_SRV_CRT_DIR "${OUROBOROS_SECURITY_DIR}/server" CACHE STRING + "Directory holding trusted CA certificates") +set(OUROBOROS_CLI_CRT_DIR "${OUROBOROS_SECURITY_DIR}/client" CACHE STRING + "Directory holding trusted CA certificates") +set(OUROBOROS_UNTRUSTED_DIR "${OUROBOROS_SECURITY_DIR}/untrusted" CACHE STRING + "Directory holding untrusted intermediate certificates") + set(IRMD_REQ_ARR_TIMEOUT 1000 CACHE STRING "Timeout for an application to respond to a new flow (ms)") - set(BOOTSTRAP_TIMEOUT 5000 CACHE STRING "Timeout for an IPCP to bootstrap (ms)") set(ENROLL_TIMEOUT 20000 CACHE STRING "Timeout for an IPCP to enroll (ms)") set(REG_TIMEOUT 20000 CACHE STRING "Timeout for registering a name (ms)") -set(QUERY_TIMEOUT 20000 CACHE STRING +set(QUERY_TIMEOUT 200 CACHE STRING "Timeout to query a name with an IPCP (ms)") set(CONNECT_TIMEOUT 20000 CACHE STRING "Timeout to connect an IPCP to another IPCP (ms)") @@ -61,6 +72,8 @@ 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") +set(DEBUG_PROTO_OAP FALSE CACHE BOOL + "Add Flow allocation protocol message output to IRMd debug logging") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/config.h" @ONLY) @@ -70,6 +83,7 @@ set(SOURCE_FILES ipcp.c configfile.c main.c + oap.c reg/flow.c reg/ipcp.c reg/proc.c @@ -83,6 +97,10 @@ add_executable (irmd ${SOURCE_FILES}) target_link_libraries (irmd LINK_PUBLIC ouroboros-common ${LIBTOML_LIBRARIES}) +if (HAVE_TOML) + target_include_directories(irmd PUBLIC ${LIBTOML_INCLUDE}) +endif () + include(AddCompileFlags) if (CMAKE_BUILD_TYPE MATCHES "Debug*") add_compile_flags(irmd -DCONFIG_OUROBOROS_DEBUG) @@ -90,6 +108,5 @@ endif () install(TARGETS irmd RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}) -# Enable once irmd has tests -#add_subdirectory(tests) add_subdirectory(reg) +add_subdirectory(tests) diff --git a/src/irmd/config.h.in b/src/irmd/config.h.in index fa1156b9..527694c0 100644 --- a/src/irmd/config.h.in +++ b/src/irmd/config.h.in @@ -21,7 +21,8 @@ */ -#define IPCP_UDP_EXEC "@IPCP_UDP_TARGET@" +#define IPCP_UDP4_EXEC "@IPCP_UDP4_TARGET@" +#define IPCP_UDP6_EXEC "@IPCP_UDP6_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@" @@ -50,7 +51,6 @@ #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@" @@ -62,36 +62,33 @@ #define OUROBOROS_CONFIG_FILE "@OUROBOROS_CONFIG_FILE@" #endif +#define OUROBOROS_SECURITY_DIR "@OUROBOROS_SECURITY_DIR@" +#define OUROBOROS_CA_CRT_DIR "@OUROBOROS_CA_CRT_DIR@" +#define OUROBOROS_SRV_CRT_DIR "@OUROBOROS_SRV_CRT_DIR@" +#define OUROBOROS_CLI_CRT_DIR "@OUROBOROS_CLI_CRT_DIR@" +#define OUROBOROS_CHAIN_DIR "@OUROBOROS_UNTRUSTED_DIR@" + #define IRMD_PKILL_TIMEOUT @IRMD_PKILL_TIMEOUT@ #cmakedefine IRMD_KILL_ALL_PROCESSES #cmakedefine HAVE_LIBGCRYPT +#cmakedefine HAVE_OPENSSL +#ifdef CONFIG_OUROBOROS_DEBUG +#cmakedefine DEBUG_PROTO_OAP +#endif + +#define _B "[38;5;4m" +#define _G "[38;5;8m" +#define RST "[0m" -#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" +#define O7S_ASCII_ART \ +RST "\n" \ +_B " ▄▄█████▄▄▄ \n" \ +_B " ▄█▀▀ ▀▀███▄ " _G " █ \n" \ +_B " ██ ▄▄▄ ▄███▄ " _G "▄ ▄ ▄ ▄ ▄▄ █▄▄ ▄▄ ▄ ▄ ▄▄ ▄▄ \n" \ +_B " ██ █ █ █████ " _G "█ █ █▀ ▀ █ █ █ █ █ █ █▀ ▀ █ █ ▀▄ ▀\n" \ +_B " ██ ▀▄▄▄▀ ▀█▀ " _G "█ █ █ █ █ █ █ █ █ █ █ █ ▄ ▀▄\n" \ +_B " █▄ █ " _G " ▀▀ ▀ ▀ ▀▀ ▀▀▀ ▀▀ ▀ ▀▀ ▀▀ \n" \ +_B " ▀█▄▄▄▄▄▄▄▄▀ \n" \ +_B " ▀▀▀▀▀▀ \n" \ +RST "\n" diff --git a/src/irmd/configfile.c b/src/irmd/configfile.c index 688c4ade..ce9fc8fc 100644 --- a/src/irmd/configfile.c +++ b/src/irmd/configfile.c @@ -47,8 +47,12 @@ #include <string.h> #include <toml.h> #include <arpa/inet.h> +#ifdef __FreeBSD__ +#include <sys/socket.h> +#endif #define ERRBUFSZ 200 +#define DATUMSZ 256 static int toml_hash(toml_table_t * table, struct layer_info * info) @@ -157,14 +161,66 @@ static int toml_eth_dix(toml_table_t * table, return 0; } -static int toml_udp(toml_table_t * table, - struct ipcp_config * conf) +static int toml_udp4(toml_table_t * table, + struct ipcp_config * conf) +{ + struct udp4_config * udp4; + toml_datum_t ip; + toml_datum_t port; + toml_datum_t dns; + + *conf = udp4_default_conf; + udp4 = &conf->udp4; + + 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, &udp4->ip_addr.s_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) + udp4->port = port.u.i; + + dns = toml_string_in(table, "dns"); + if (dns.ok) { + if (inet_pton(AF_INET, dns.u.s, &udp4->dns_addr.s_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_udp6(toml_table_t * table, + struct ipcp_config * conf) { + struct in6_addr ip6; + struct in6_addr dns6; toml_datum_t ip; toml_datum_t port; toml_datum_t dns; - *conf = udp_default_conf; + *conf = udp6_default_conf; + ip6 = conf->udp6.ip_addr; + dns6 = conf->udp6.dns_addr; ip = toml_string_in(table, "ip"); if (!ip.ok) { @@ -172,18 +228,18 @@ static int toml_udp(toml_table_t * table, goto fail_ip; } - if (inet_pton (AF_INET, ip.u.s, &conf->udp.ip_addr) != 1) { + if (inet_pton (AF_INET6, ip.u.s, &ip6.s6_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; + conf->udp6.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) { + if (inet_pton(AF_INET6, dns.u.s, &dns6.s6_addr) < 0) { log_err("Failed to parse DNS address %s.", ip.u.s); goto fail_dns; } @@ -214,26 +270,160 @@ static int toml_broadcast(toml_table_t * table, return 0; } +#define BETWEEN(a, b, c) ((a) >= (b) && (a) <= (c)) +#define DHT(conf, x) (conf)->dht.params.x +static int toml_dir(toml_table_t * table, + struct dir_config * conf) +{ + toml_datum_t dir; + toml_datum_t alpha; + toml_datum_t t_expire; + toml_datum_t t_refresh; + toml_datum_t t_replicate; + toml_datum_t k; + + dir = toml_string_in(table, "directory"); + if (dir.ok) { + log_dbg("Found directory type: %s", dir.u.s); + if (strlen(dir.u.s) > DATUMSZ) { + log_err("Directory name too long: %s", dir.u.s); + free(dir.u.s); + return -1; + } + if (strcmp(dir.u.s, "DHT") == 0) + conf->pol = DIR_DHT; + else if (strcmp(dir.u.s, "dht") == 0) + conf->pol = DIR_DHT; + else { + log_err("Unknown directory type: %s", dir.u.s); + free(dir.u.s); + return -EINVAL; + } + free(dir.u.s); + } + + switch(conf->pol) { + case DIR_DHT: + log_info("Using DHT directory policy."); + alpha = toml_int_in(table, "dht_alpha"); + if (alpha.ok) { + if (!BETWEEN(alpha.u.i, + DHT_ALPHA_MIN, DHT_ALPHA_MAX)) { + log_err("Invalid alpha value: %ld", + (long) alpha.u.i); + return -EINVAL; + } + DHT(conf, alpha) = alpha.u.i; + } + t_expire = toml_int_in(table, "dht_t_expire"); + if (t_expire.ok) { + if (!BETWEEN(t_expire.u.i, + DHT_T_EXPIRE_MIN, DHT_T_EXPIRE_MAX)) { + log_err("Invalid expire time: %ld", + (long) t_expire.u.i); + return -EINVAL; + } + DHT(conf, t_expire) = t_expire.u.i; + } + t_refresh = toml_int_in(table, "dht_t_refresh"); + if (t_refresh.ok) { + if (!BETWEEN(t_refresh.u.i, + DHT_T_REFRESH_MIN, DHT_T_REFRESH_MAX)) { + log_err("Invalid refresh time: %ld", + (long) t_refresh.u.i); + return -EINVAL; + } + DHT(conf, t_refresh) = t_refresh.u.i; + } + t_replicate = toml_int_in(table, "dht_t_replicate"); + if (t_replicate.ok) { + if (!BETWEEN(t_replicate.u.i, + DHT_T_REPLICATE_MIN, DHT_T_REPLICATE_MAX)) { + log_err("Invalid replication time: %ld", + (long) t_replicate.u.i); + return -EINVAL; + } + DHT(conf, t_replicate) = t_replicate.u.i; + } + k = toml_int_in(table, "dht_k"); + if (k.ok) { + if (!BETWEEN(k.u.i, DHT_K_MIN, DHT_K_MAX)) { + log_err("Invalid replication factor: %ld", + (long) k.u.i); + return -EINVAL; + } + DHT(conf, k) = k.u.i; + } + break; + default: + assert(false); + break; + } + + return 0; +} + static int toml_routing(toml_table_t * table, struct dt_config * conf) { toml_datum_t routing; + toml_datum_t t_recalc; + toml_datum_t t_update; + toml_datum_t t_timeo; 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; + if (strcmp(routing.u.s, "link-state") == 0) { + conf->routing.pol = ROUTING_LINK_STATE; + conf->routing.ls.pol = LS_SIMPLE; + } else if (strcmp(routing.u.s, "lfa") == 0) { + conf->routing.pol = ROUTING_LINK_STATE; + conf->routing.ls.pol = LS_LFA; + } else if (strcmp(routing.u.s, "ecmp") == 0) { + conf->routing.pol = ROUTING_LINK_STATE; + conf->routing.ls.pol = LS_ECMP; + } else { + conf->routing.pol = ROUTING_INVALID; + return -EINVAL; + } free(routing.u.s); } - if (conf->routing_type == ROUTING_INVALID) - return -1; + switch (conf->routing.pol) { + case ROUTING_LINK_STATE: + log_info("Using Link State routing policy."); + t_recalc = toml_int_in(table, "ls_t_recalc"); + if (t_recalc.ok) { + if (t_recalc.u.i < 1) { + log_err("Invalid ls_t_recalc value: %ld", + (long) t_recalc.u.i); + return -EINVAL; + } + conf->routing.ls.t_recalc = t_recalc.u.i; + } + t_update = toml_int_in(table, "ls_t_update"); + if (t_update.ok) { + if (t_update.u.i < 1) { + log_err("Invalid ls_t_update value: %ld", + (long) t_update.u.i); + return -EINVAL; + } + conf->routing.ls.t_update = t_update.u.i; + } + t_timeo = toml_int_in(table, "ls_t_timeo"); + if (t_timeo.ok) { + if (t_timeo.u.i < 1) { + log_err("Invalid ls_t_timeo value: %ld", + (long) t_timeo.u.i); + return -EINVAL; + } + conf->routing.ls.t_timeo = t_timeo.u.i; + } + break; + default: + log_err("Invalid routing policy: %d", conf->routing.pol); + return -EINVAL; + } return 0; } @@ -311,12 +501,12 @@ static int toml_dt(toml_table_t * table, static int toml_unicast(toml_table_t * table, struct ipcp_config * conf) { - - *conf = uni_default_conf; - if (toml_hash(table, &conf->layer_info) < 0) + if (toml_dir(table, &conf->unicast.dir) < 0) { + log_err("Invalid directory configuration."); return -1; + } if (toml_dt(table, &conf->unicast.dt) < 0) { log_err("Invalid DT configuration."); @@ -333,6 +523,7 @@ static int toml_unicast(toml_table_t * table, return -1; } + return 0; } @@ -504,8 +695,11 @@ static int toml_ipcp(toml_table_t * table, case IPCP_ETH_LLC: ret = toml_eth_llc(table, conf); break; - case IPCP_UDP: - ret = toml_udp(table, conf); + case IPCP_UDP4: + ret = toml_udp4(table, conf); + break; + case IPCP_UDP6: + ret = toml_udp6(table, conf); break; case IPCP_BROADCAST: ret = toml_broadcast(table, conf); @@ -563,7 +757,7 @@ static int toml_ipcp_list(toml_table_t * table, } info.type = type; - strcpy(info.name,key); + strcpy(info.name, key); conf.type = type; ret = toml_ipcp(toml_table_in(table, key), &info, &conf); @@ -691,18 +885,63 @@ static int toml_prog_list(toml_array_t * progs, return ret; } +static int cp_chk_path(char * buf, + char * path) +{ + char * rp; + + assert(path != NULL); + + rp = realpath(path, NULL); + if (rp == NULL) { + log_err("Failed to check path %s: %s.", path, strerror(errno)); + goto fail_rp; + } + + if (strlen(rp) > NAME_PATH_SIZE) { + log_err("File path too long: %s.", rp); + goto fail_len; + } + + strcpy(buf, rp); + free(rp); + free(path); + + return 0; + + fail_len: + free(rp); + fail_rp: + free(path); + return -1; +} + static int toml_name(toml_table_t * table, const char * name) { - toml_array_t * progs; - toml_array_t * args; - toml_datum_t lb; + toml_array_t * progs; + toml_array_t * args; + toml_datum_t lb; + toml_datum_t senc; + toml_datum_t scrt; + toml_datum_t skey; + toml_datum_t cenc; + toml_datum_t ccrt; + toml_datum_t ckey; + struct name_info info = { .pol_lb = LB_SPILL }; log_dbg("Found service name %s in configuration file.", name); + if (strlen(name) > NAME_SIZE) { + log_err("Name too long: %s", name); + return -1; + } + + strcpy(info.name, name); + lb = toml_string_in(table, "lb"); if (lb.ok) { if (strcmp(lb.u.s, "spill") == 0) @@ -718,8 +957,29 @@ static int toml_name(toml_table_t * table, log_err("Invalid load-balancing policy for %s.", name); return -1; } + senc = toml_string_in(table, "server_enc_file"); + if (senc.ok && cp_chk_path(info.s.enc, senc.u.s) < 0) + return -1; - strcpy(info.name, name); + scrt = toml_string_in(table, "server_crt_file"); + if (scrt.ok && cp_chk_path(info.s.crt, scrt.u.s) < 0) + return -1; + + skey = toml_string_in(table, "server_key_file"); + if (skey.ok && cp_chk_path(info.s.key, skey.u.s) < 0) + return -1; + + cenc = toml_string_in(table, "client_enc_file"); + if (cenc.ok && cp_chk_path(info.c.enc, cenc.u.s) < 0) + return -1; + + ccrt = toml_string_in(table, "client_crt_file"); + if (ccrt.ok && cp_chk_path(info.c.crt, ccrt.u.s) < 0) + return -1; + + ckey = toml_string_in(table, "client_key_file"); + if (ckey.ok && cp_chk_path(info.c.key, ckey.u.s) < 0) + return -1; if (name_create(&info) < 0) { log_err("Failed to create name %s.", name); @@ -762,23 +1022,24 @@ static int toml_toplevel(toml_table_t * table, toml_table_t * subtable; subtable = toml_table_in(table, key); - - if (strcmp(key, "local") == 0) + if (strcmp(key, "name") == 0) + return toml_name_list(subtable); + else 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, "udp4") == 0) + return toml_ipcp_list(subtable, IPCP_UDP4); + else if (strcmp(key, "udp6") == 0) + return toml_ipcp_list(subtable, IPCP_UDP6); 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); + else + log_err("Unkown toplevel key: %s.", key); return -1; } @@ -838,7 +1099,8 @@ int irm_configure(const char * path) rp = realpath(path, NULL); if (rp == NULL) { - log_err("Failed to resolve path for %s", path); + log_err("Failed to check path for %s: %s.", + path, strerror(errno)); goto fail_resolve; } diff --git a/src/irmd/ipcp.c b/src/irmd/ipcp.c index 5a9a79d3..6226aeda 100644 --- a/src/irmd/ipcp.c +++ b/src/irmd/ipcp.c @@ -83,28 +83,30 @@ ipcp_msg_t * send_recv_ipcp_msg(pid_t pid, { int sockfd; uint8_t buf[SOCK_BUF_SIZE]; - char * sock_path; + char * spath; ssize_t len; - ipcp_msg_t * recv_msg; struct timeval tv; struct timespec tic; struct timespec toc; - bool dealloc = false; + bool may_fail = false; if (kill(pid, 0) < 0) return NULL; - sock_path = ipcp_sock_path(pid); - if (sock_path == NULL) + spath = sock_path(pid, IPCP_SOCK_PATH_PREFIX); + if (spath == NULL) { + log_err("Failed to get IPCP socket path for pid %d.", pid); return NULL; + } - sockfd = client_socket_open(sock_path); + sockfd = client_socket_open(spath); if (sockfd < 0) { - free(sock_path); + log_err("Failed to open client socket at %s.", spath); + free(spath); return NULL; } - free(sock_path); + free(spath); len = ipcp_msg__get_packed_size(msg); if (len == 0 || len >= SOCK_BUF_SIZE) { @@ -127,6 +129,7 @@ ipcp_msg_t * send_recv_ipcp_msg(pid_t pid, tv.tv_usec = (REG_TIMEOUT % 1000) * 1000; break; case IPCP_MSG_CODE__IPCP_QUERY: + may_fail = true; /* name not always in Layer */ tv.tv_sec = QUERY_TIMEOUT / 1000; tv.tv_usec = (QUERY_TIMEOUT % 1000) * 1000; break; @@ -139,7 +142,7 @@ ipcp_msg_t * send_recv_ipcp_msg(pid_t pid, tv.tv_usec = (FLOW_ALLOC_TIMEOUT % 1000) * 1000; break; case IPCP_MSG_CODE__IPCP_FLOW_DEALLOC: - dealloc = true; + may_fail = true; tv.tv_sec = 0; /* FIX DEALLOC: don't wait for dealloc */ tv.tv_usec = 500; break; @@ -167,17 +170,15 @@ ipcp_msg_t * send_recv_ipcp_msg(pid_t pid, pthread_cleanup_pop(true); /* close socket */ 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 ipcp_msg__unpack(NULL, len, buf); + + if (errno == EAGAIN && !may_fail) { + int diff = ts_diff_ms(&toc, &tic); + log_warn("IPCP %s timed out after %d ms.", + str_ipcp_cmd(msg->code), diff); } - return recv_msg; + return NULL; } int ipcp_bootstrap(pid_t pid, @@ -496,7 +497,7 @@ int ipcp_flow_alloc_resp(const struct flow_info * flow, msg.pid = flow->n_pid; msg.has_response = true; msg.response = response; - msg.has_pk = true; + msg.has_pk = response == 0; msg.pk.data = data.data; msg.pk.len = data.len; diff --git a/src/irmd/irmd.h b/src/irmd/irmd.h index cf8f6953..3e54904a 100644 --- a/src/irmd/irmd.h +++ b/src/irmd/irmd.h @@ -39,7 +39,7 @@ int connect_ipcp(pid_t pid, const char * component, qosspec_t qs); -int name_create(const struct name_info * info); +int name_create(struct name_info * info); int name_reg(const char * name, pid_t pid); diff --git a/src/irmd/main.c b/src/irmd/main.c index bc13fa7c..834a7a8c 100644 --- a/src/irmd/main.c +++ b/src/irmd/main.c @@ -40,6 +40,7 @@ #include <ouroboros/lockfile.h> #include <ouroboros/logs.h> #include <ouroboros/pthread.h> +#include <ouroboros/random.h> #include <ouroboros/rib.h> #include <ouroboros/shm_rdrbuff.h> #include <ouroboros/sockets.h> @@ -50,9 +51,11 @@ #include "irmd.h" #include "ipcp.h" +#include "oap.h" #include "reg/reg.h" #include "configfile.h" +#include <dirent.h> #include <sys/socket.h> #include <sys/un.h> #include <signal.h> @@ -71,15 +74,24 @@ #define SHM_SAN_HOLDOFF 1000 /* ms */ #define IPCP_HASH_LEN(p) hash_len((p)->dir_hash_algo) #define BIND_TIMEOUT 10 /* ms */ +#define TIMESYNC_SLACK 100 /* ms */ +#define OAP_SEEN_TIMER 20 /* s */ #define DEALLOC_TIME 300 /* s */ -#define MSGBUFSZ 2048 enum irm_state { IRMD_NULL = 0, + IRMD_INIT, IRMD_RUNNING, IRMD_SHUTDOWN }; +struct oaph { + struct list_head next; + + uint64_t stamp; + uint8_t id[OAP_ID_SIZE]; +}; + struct cmd { struct list_head next; @@ -93,6 +105,12 @@ struct { #ifdef HAVE_TOML char * cfg_file; /* configuration file path */ #endif + struct { + struct auth_ctx * ctx; /* default authentication ctx */ + struct list_head list; /* OAP headers seen before */ + pthread_mutex_t mtx; /* mutex for OAP headers */ + } auth; + struct lockfile * lf; /* single irmd per system */ struct shm_rdrbuff * rdrb; /* rdrbuff for packets */ @@ -173,8 +191,11 @@ static pid_t spawn_ipcp(struct ipcp_info * info) case IPCP_BROADCAST: exec_name = IPCP_BROADCAST_EXEC; break; - case IPCP_UDP: - exec_name = IPCP_UDP_EXEC; + case IPCP_UDP4: + exec_name = IPCP_UDP4_EXEC; + break; + case IPCP_UDP6: + exec_name = IPCP_UDP6_EXEC; break; case IPCP_ETH_LLC: exec_name = IPCP_ETH_LLC_EXEC; @@ -217,7 +238,7 @@ static pid_t spawn_ipcp(struct ipcp_info * info) } info->pid = pid; - info->state = IPCP_BOOT; + info->state = IPCP_INIT; return 0; } @@ -312,7 +333,7 @@ int bootstrap_ipcp(pid_t pid, goto fail; } - if (conf->type == IPCP_UDP) + if (conf->type == IPCP_UDP4 || conf->type == IPCP_UDP6) conf->layer_info.dir_hash_algo = (enum pol_dir_hash) HASH_MD5; if (ipcp_bootstrap(pid, conf, &layer)) { @@ -320,7 +341,7 @@ int bootstrap_ipcp(pid_t pid, goto fail; } - info.state = IPCP_BOOTSTRAPPED; + info.state = IPCP_BOOT; if (reg_set_layer_for_ipcp(&info, &layer) < 0) { log_err("Failed to set layer info for IPCP."); @@ -352,6 +373,8 @@ int enroll_ipcp(pid_t pid, goto fail; } + info.state = IPCP_BOOT; + if (reg_set_layer_for_ipcp(&info, &layer) < 0) { log_err("Failed to set layer info for IPCP."); goto fail; @@ -425,6 +448,71 @@ static int disconnect_ipcp(pid_t pid, return 0; } +static void name_update_sec_paths(struct name_info * info) +{ + char * srv_dir = OUROBOROS_SRV_CRT_DIR; + char * cli_dir = OUROBOROS_CLI_CRT_DIR; + + assert(info != NULL); + + if (strlen(info->s.enc) == 0) + sprintf(info->s.enc, "%s/%s/enc.cfg", srv_dir, info->name); + + if (strlen(info->s.crt) == 0) + sprintf(info->s.crt, "%s/%s/crt.pem", srv_dir, info->name); + + if (strlen(info->s.key) == 0) + sprintf(info->s.key, "%s/%s/key.pem", srv_dir, info->name); + + if (strlen(info->c.enc) == 0) + sprintf(info->c.enc, "%s/%s/enc.cfg", cli_dir, info->name); + + if (strlen(info->c.crt) == 0) + sprintf(info->c.crt, "%s/%s/crt.pem", cli_dir, info->name); + + if (strlen(info->c.key) == 0) + sprintf(info->c.key, "%s/%s/key.pem", cli_dir, info->name); +} + +int name_create(struct name_info * info) +{ + int ret; + + assert(info != NULL); + + name_update_sec_paths(info); + + ret = reg_create_name(info); + if (ret == -EEXIST) { + log_info("Name %s already exists.", info->name); + return 0; + } + + if (ret < 0) { + log_err("Failed to create name %s.", info->name); + return -1; + } + + log_info("Created new name: %s.", info->name); + + return 0; +} + +static int name_destroy(const char * name) +{ + + assert(name != NULL); + + if (reg_destroy_name(name) < 0) { + log_err("Failed to destroy name %s.", name); + return -1; + } + + log_info("Destroyed name: %s.", name); + + return 0; +} + int bind_program(char ** exec, const char * name, uint8_t flags) @@ -448,10 +536,8 @@ int bind_program(char ** exec, 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); + if (name_create(&ni) < 0) goto fail_name; - } } if (reg_bind_prog(name, exec, flags) < 0) { @@ -497,10 +583,8 @@ int bind_process(pid_t pid, 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); + if (name_create(&ni) < 0) goto fail; - } } if (reg_bind_proc(name, pid) < 0) { @@ -581,43 +665,6 @@ static int list_ipcps(ipcp_list_msg_t *** ipcps, return -1; } -int name_create(const struct name_info * info) -{ - int ret; - - assert(info != NULL); - - ret = reg_create_name(info); - if (ret == -EEXIST) { - log_info("Name %s already exists.", info->name); - return 0; - } - - if (ret < 0) { - log_err("Failed to create name %s.", info->name); - return -1; - } - - log_info("Created new name: %s.", info->name); - - return 0; -} - -static int name_destroy(const char * name) -{ - - assert(name != NULL); - - if (reg_destroy_name(name) < 0) { - log_err("Failed to destroy name %s.", name); - return -1; - } - - log_info("Destroyed name: %s.", name); - - return 0; -} - static int list_names(name_info_msg_t *** names, size_t * n_names) { @@ -760,67 +807,279 @@ static void __cleanup_flow(void * flow) reg_destroy_flow(((struct flow_info *) flow)->id); } +static bool file_exists(const char * path) +{ + struct stat s; + + if (stat(path, &s) < 0 && errno == ENOENT) { + log_dbg("File %s does not exist.", path); + return false; + } + + return true; +} + +static int load_credentials(const char * name, + const struct name_sec_paths * paths, + void ** pkp, + void ** crt, + bool * crypt) +{ + assert(paths != NULL); + assert(pkp != NULL); + assert(crt != NULL); + + *pkp = NULL; + *crt = NULL; + + /* TODO: Allow configuration. For now, encrypt if path exists */ + *crypt = file_exists(paths->enc); + if (*crypt) + log_info("Encryption enabled for %s.", name); + + if (!file_exists(paths->crt) || !file_exists(paths->key)) { + log_info("No security info for %s.", name); + return 0; + } + + if (crypt_load_crt_file(paths->crt, crt) < 0) { + log_err("Failed to load %s for %s.", paths->crt, name); + goto fail_crt; + } + + if (crypt_load_privkey_file(paths->key, pkp) < 0) { + log_err("Failed to load %s for %s.", paths->key, name); + goto fail_key; + } + + log_info("Loaded security keys for %s.", name); + + return 0; + + fail_key: + crypt_free_crt(*crt); + *crt = NULL; + fail_crt: + return -EAUTH; +} + +static int load_srv_credentials(const char * name, + void ** pkp, + void ** crt, + bool * crypt) +{ + struct name_info info; + + assert(name != NULL); + assert(pkp != NULL); + assert(crt != NULL); + + if (reg_get_name_info(name, &info) < 0) { + log_err("Failed to get name info for %s.", name); + return -ENAME; + } + + return load_credentials(name, &info.s, pkp, crt, crypt); +} + +static int load_cli_credentials(const char * name, + void ** pkp, + void ** crt, + bool * crypt) +{ + struct name_info info; + + assert(name != NULL); + assert(pkp != NULL); + assert(crt != NULL); + + if (reg_get_name_info(name, &info) < 0) { + log_err("Failed to get name info for %s.", name); + return -ENAME; + } + + return load_credentials(name, &info.c, pkp, crt, crypt); +} + +#define ID_IS_EQUAL(id1, id2) (memcmp(id1, id2, OAP_ID_SIZE) == 0) +static int irm_check_oap_hdr(const struct oap_hdr * oap_hdr, + time_t mpl) +{ + struct list_head * p; + struct list_head * h; + struct timespec now; + struct oaph * new; + uint64_t stamp; + uint64_t cur; + uint8_t * id; + ssize_t delta; + + assert(oap_hdr != NULL); + + stamp = oap_hdr->timestamp; + id = oap_hdr->id.data; + + clock_gettime(CLOCK_REALTIME, &now); + + cur = TS_TO_UINT64(now); + + delta = (ssize_t)(cur - stamp) / MILLION; + if (delta > mpl) + log_warn("Transit time exceeds MPL by %zd ms.", delta); + if (delta < -TIMESYNC_SLACK) + log_warn("OAP header sent %zd ms from the future.", -delta); + + new = malloc(sizeof(*new)); + if (new == NULL) { + log_err("Failed to allocate memory for OAP element."); + return -ENOMEM; + } + + pthread_mutex_lock(&irmd.auth.mtx); + + list_for_each_safe(p, h, &irmd.auth.list) { + struct oaph * oaph = list_entry(p, struct oaph, next); + if (cur > oaph->stamp + OAP_SEEN_TIMER * BILLION) { + list_del(&oaph->next); + free(oaph); + continue; + } + + if (oaph->stamp == stamp && ID_IS_EQUAL(oaph->id, id)) { + log_warn("OAP header already known: " HASH_FMT64 ".", + HASH_VAL64(id)); + goto fail_replay; + } + } + + memcpy(new->id, id, OAP_ID_SIZE); + new->stamp = stamp; + + list_add_tail(&new->next, &irmd.auth.list); + + pthread_mutex_unlock(&irmd.auth.mtx); + + return 0; + + fail_replay: + pthread_mutex_unlock(&irmd.auth.mtx); + free(new); + return -EAUTH; +} + +static int irm_auth_peer(const char * name, + const struct oap_hdr * oap_hdr, + const struct oap_hdr * r_oap_hdr) +{ + void * crt; + void * pk; + buffer_t sign; + const char * n = name == NULL ? "<client>" : name; + + if (memcmp(r_oap_hdr->id.data, oap_hdr->id.data, OAP_ID_SIZE) != 0) { + log_err("OAP ID mismatch in flow allocation."); + goto fail_check; + } + + if (r_oap_hdr->crt.len == 0) { + log_info("No certificate provided by %s.", n); + return 0; + } + + if (crypt_load_crt_der(r_oap_hdr->crt, &crt) < 0) { + log_err("Failed to load certificate from %s.", n); + goto fail_check; + } + + log_dbg("Loaded peer certificate for %s.", n); + + if (name != NULL) { + if (crypt_check_crt_name(crt, n) < 0) { + log_err("Certificate does not match %s.", n); + goto fail_crt; + } + log_dbg("Certificate matches name %s.", n); + } + + if (crypt_get_pubkey_crt(crt, &pk) < 0) { + log_err("Failed to get pubkey from certificate for %s.", n); + goto fail_crt; + } + + log_dbg("Got public key from certificate for %s.", n); + + if (auth_verify_crt(irmd.auth.ctx, crt) < 0) { + log_err("Failed to verify peer %s with CA store.", n); + goto fail_crt; + } + + log_info("Successfully verified peer certificate for %s.", n); + + sign = r_oap_hdr->hdr; + sign.len -= (r_oap_hdr->sig.len + sizeof(uint16_t)); + + if (auth_verify_sig(pk, sign, r_oap_hdr->sig) < 0) { + log_err("Failed to verify signature for peer %s.", n); + goto fail_check_sig; + } + + crypt_free_key(pk); + crypt_free_crt(crt); + + log_info("Successfully authenticated %s.", n); + + return 0; + + fail_check_sig: + crypt_free_key(pk); + fail_crt: + crypt_free_crt(crt); + fail_check: + return -1; +} + static int flow_accept(struct flow_info * flow, + buffer_t * symmkey, 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; + struct oap_hdr oap_hdr; /* incoming request */ + struct oap_hdr r_oap_hdr; /* outgoing response */ + uint8_t buf[MSGBUFSZ]; /* buffer for local ephkey */ + buffer_t lpk = BUF_INIT; /* local ephemeral pubkey */ + char name[NAME_SIZE + 1]; /* name for flow */ + void * pkp = NULL; /* signing private key */ + void * crt = NULL; /* signing certificate */ + int err; + bool crypt; /* piggyback of user data not yet implemented */ - assert(data != NULL && data->len == 0 && data->data == NULL); + assert(data != NULL && BUF_IS_EMPTY(data)); + assert(symmkey != NULL && BUF_IS_EMPTY(symmkey)); if (!reg_has_proc(flow->n_pid)) { log_err("Unknown process %d calling accept.", flow->n_pid); err = -EINVAL; - goto fail; - } - - s = malloc(SYMMKEYSZ); - if (s == NULL) { - log_err("Failed to malloc symmkey."); - err = -ENOMEM; - goto fail; - } - - key_len = crypt_dh_pkp_create(&pkp, buf); - if (key_len < 0) { - log_err("Failed to generate key pair."); - err = -ECRYPT; - goto fail_pkp; + goto fail_flow; } - lpk.data = buf; - lpk.len = (size_t) key_len; - - log_dbg("Generated ephemeral keys for %d.", flow->n_pid); - if (reg_create_flow(flow) < 0) { log_err("Failed to create flow."); err = -EBADF; goto fail_flow; } - if (reg_prepare_flow_accept(flow, &lpk) < 0) { + if (reg_prepare_flow_accept(flow) < 0) { log_err("Failed to prepare accept."); err = -EBADF; goto fail_wait; } pthread_cleanup_push(__cleanup_flow, flow); - pthread_cleanup_push(__cleanup_pkp, pkp); - pthread_cleanup_push(free, s); - err = reg_wait_flow_accepted(flow, &rpk, abstime); + err = reg_wait_flow_accepted(flow, &oap_hdr.hdr, abstime); pthread_cleanup_pop(false); - pthread_cleanup_pop(false); - pthread_cleanup_pop(false); if (err == -ETIMEDOUT) { log_err("Flow accept timed out."); @@ -835,45 +1094,135 @@ static int flow_accept(struct flow_info * flow, assert(err == 0); - if (flow->qs.cypher_s != 0) { /* crypto requested */ - if (crypt_dh_derive(pkp, rpk, s) < 0) { + if (reg_get_name_for_flow_id(name, flow->id) < 0) { + log_err("Failed to get name for flow %d.", flow->id); + err = -EIPCP; + goto fail_cred; + } + + log_dbg("IPCP %d accepting flow %d for %s.", + flow->n_pid, flow->id, name); + + if (load_srv_credentials(name, &pkp, &crt, &crypt) < 0) { + log_err("Failed to load security keys for %s.", name); + err = -EAUTH; + goto fail_cred; + } + + if (oap_hdr_decode(oap_hdr.hdr, &oap_hdr) < 0) { + log_err("Failed to decode OAP header from %s.", name); + err = -EIPCP; + goto fail_oap_hdr; + } +#ifdef DEBUG_PROTO_OAP + debug_oap_hdr_rcv(&oap_hdr); +#endif + if (irm_check_oap_hdr(&oap_hdr, flow->mpl) < 0) { + log_err("OAP header failed replay check."); + goto fail_oap_hdr; + } + + if (crypt && oap_hdr.eph.len == 0) { + log_warn("Encryption required but no key provided."); + err = -ECRYPT; + goto fail_oap_hdr; + } + + if (oap_hdr.eph.len > 0) { /* crypto requested */ + uint8_t * s; /* symmetric encryption key */ + ssize_t key_len; /* length of local pubkey */ + void * pkp = NULL; /* ephemeral private key pair */ + + s = malloc(SYMMKEYSZ); + if (s == NULL) { + log_err("Failed to malloc symmkey."); + err = -ENOMEM; + goto fail_keys; + } + + key_len = crypt_dh_pkp_create(&pkp, buf); + if (key_len < 0) { + free(s); + log_err("Failed to generate key pair."); + err = -ECRYPT; + goto fail_keys; + } + + lpk.data = buf; + lpk.len = (size_t) key_len; + + log_dbg("Generated ephemeral keys for %d.", flow->n_pid); + + if (crypt_dh_derive(pkp, oap_hdr.eph, s) < 0) { log_err("Failed to derive secret for %d.", flow->id); + crypt_dh_pkp_destroy(pkp); + free(s); err = -ECRYPT; goto fail_derive; } - freebuf(rpk); - data->data = s; - data->len = SYMMKEYSZ; - s= NULL; - } else { - clrbuf(lpk); + + symmkey->data = s; + symmkey->len = SYMMKEYSZ; + + crypt_dh_pkp_destroy(pkp); } - if (ipcp_flow_alloc_resp(flow, 0, lpk) < 0) { + if (oap_hdr_init(oap_hdr.id, pkp, crt, lpk, *data, &r_oap_hdr) < 0) { + log_err("Failed to create OAP header."); + err = -ENOMEM; + goto fail_r_oap_hdr; + } + + if (irm_auth_peer(NULL, &r_oap_hdr, &oap_hdr) < 0) { + log_err("Failed to auth %s client, flow %d.", name, flow->id); + err = -EAUTH; + goto fail_r_oap_hdr; + } + + crypt_free_crt(crt); + crypt_free_key(pkp); + +#ifdef DEBUG_PROTO_OAP + debug_oap_hdr_snd(&r_oap_hdr); +#endif + if (ipcp_flow_alloc_resp(flow, 0, r_oap_hdr.hdr) < 0) { log_err("Failed to respond to flow allocation."); - err = -EIPCP; - goto fail_alloc_resp; + goto fail_resp; } - crypt_dh_pkp_destroy(pkp); - free(s); + log_info("Flow %d accepted by %d for %s.", + flow->id, flow->n_pid, name); + + oap_hdr_fini(&oap_hdr); + oap_hdr_fini(&r_oap_hdr); return 0; + fail_r_oap_hdr: + freebuf(*symmkey); fail_derive: - freebuf(rpk); clrbuf(lpk); + fail_keys: + oap_hdr_fini(&oap_hdr); + fail_oap_hdr: + crypt_free_crt(crt); + crypt_free_key(pkp); + fail_cred: + assert(lpk.data == NULL && lpk.len == 0); 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; + + fail_resp: + flow->state = FLOW_NULL; + oap_hdr_fini(&r_oap_hdr); + freebuf(*symmkey); + clrbuf(lpk); + oap_hdr_fini(&oap_hdr); + reg_destroy_flow(flow->id); + return -EIPCP; } static int flow_join(struct flow_info * flow, @@ -883,7 +1232,7 @@ static int flow_join(struct flow_info * flow, struct ipcp_info ipcp; struct layer_info layer; buffer_t hash; - buffer_t pbuf = {NULL, 0}; /* nothing to piggyback */ + buffer_t pbuf = BUF_INIT; /* nothing to piggyback */ int err; log_info("Allocating flow for %d to %s.", flow->n_pid, dst); @@ -936,6 +1285,7 @@ static int flow_join(struct flow_info * flow, goto fail_alloc; } + assert(pbuf.data == NULL && pbuf.len == 0); assert(err == 0); freebuf(hash); @@ -1009,23 +1359,46 @@ static int get_ipcp_by_dst(const char * dst, static int flow_alloc(struct flow_info * flow, const char * dst, + buffer_t * symmkey, 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; + struct oap_hdr oap_hdr; /* outgoing request */ + struct oap_hdr r_oap_hdr; /* incoming response */ + uint8_t buf[MSGBUFSZ]; /* buffer for local ephkey */ + buffer_t lpk = BUF_INIT; /* local ephemeral pubkey */ + void * pkp = NULL; /* ephemeral private key pair */ + uint8_t * s = NULL; /* symmetric key */ + void * cpkp = NULL; /* signing private key */ + void * ccrt = NULL; /* signing certificate */ + buffer_t hash; + uint8_t idbuf[OAP_ID_SIZE]; + buffer_t id; + int err; + bool crypt; + /* piggyback of user data not yet implemented */ - assert(data != NULL && data->len == 0 && data->data == NULL); + assert(data != NULL && BUF_IS_EMPTY(data)); + assert(symmkey != NULL && BUF_IS_EMPTY(symmkey)); log_info("Allocating flow for %d to %s.", flow->n_pid, dst); + if (random_buffer(idbuf, OAP_ID_SIZE) < 0) { + log_err("Failed to generate ID."); + err = -EIRMD; + goto fail_id; + } + + id.data = idbuf; + id.len = OAP_ID_SIZE; - if (flow->qs.cypher_s > 0) { + if (load_cli_credentials(dst, &cpkp, &ccrt, &crypt) < 0) { + log_err("Failed to load security keys for %s.", dst); + err = -EAUTH; + goto fail_cred; + } + + if (crypt > 0) { ssize_t key_len; s = malloc(SYMMKEYSZ); @@ -1048,6 +1421,14 @@ static int flow_alloc(struct flow_info * flow, log_dbg("Generated ephemeral keys for %d.", flow->n_pid); } + if (oap_hdr_init(id, cpkp, ccrt, lpk, *data, &oap_hdr) < 0) { + log_err("Failed to create OAP header."); + err = -ENOMEM; + goto fail_oap_hdr; + } +#ifdef DEBUG_PROTO_OAP + debug_oap_hdr_snd(&oap_hdr); +#endif if (reg_create_flow(flow) < 0) { log_err("Failed to create flow."); err = -EBADF; @@ -1062,7 +1443,7 @@ static int flow_alloc(struct flow_info * flow, reg_prepare_flow_alloc(flow); - if (ipcp_flow_alloc(flow, hash, lpk)) { + if (ipcp_flow_alloc(flow, hash, oap_hdr.hdr)) { log_err("Flow allocation %d failed.", flow->id); err = -ENOTALLOC; goto fail_alloc; @@ -1073,7 +1454,7 @@ static int flow_alloc(struct flow_info * flow, pthread_cleanup_push(free, hash.data); pthread_cleanup_push(free, s); - err = reg_wait_flow_allocated(flow, &rpk, abstime); + err = reg_wait_flow_allocated(flow, &r_oap_hdr.hdr, abstime); pthread_cleanup_pop(false); pthread_cleanup_pop(false); @@ -1087,48 +1468,86 @@ static int flow_alloc(struct flow_info * flow, if (err == -1) { log_dbg("Flow allocation terminated."); - err = -EPIPE; + err = -EIPCP; goto fail_alloc; } - assert(err == 0); + log_dbg("Response received for flow %d to %s.", flow->id, dst); + + if (err < 0) { + log_warn("Flow allocation rejected for %s: %d.", dst, err); + goto fail_alloc; + } + + if (oap_hdr_decode(r_oap_hdr.hdr, &r_oap_hdr) < 0) { + log_err("Failed to decode OAP header."); + err = -EIPCP; + goto fail_r_oap_hdr; + } +#ifdef DEBUG_PROTO_OAP + debug_oap_hdr_rcv(&r_oap_hdr); +#endif + if (irm_check_oap_hdr(&r_oap_hdr, flow->mpl) < 0) { + log_err("OAP header failed replay check."); + err = -EAUTH; + goto fail_r_oap_hdr; + } - if (flow->qs.cypher_s != 0) { /* crypto requested */ - if (crypt_dh_derive(pkp, rpk, s) < 0) { + if (irm_auth_peer(dst, &oap_hdr, &r_oap_hdr) < 0) { + log_err("Failed to authenticate %s (flow %d).", dst, flow->id); + err = -EAUTH; + goto fail_r_oap_hdr; + } + + if (lpk.len > 0) { /* crypto requested */ + if (crypt_dh_derive(pkp, r_oap_hdr.eph, s) < 0) { log_err("Failed to derive secret for %d.", flow->id); err = -ECRYPT; - goto fail_derive; + goto fail_r_oap_hdr; } crypt_dh_pkp_destroy(pkp); - freebuf(rpk); - data->data = s; - data->len = SYMMKEYSZ; + + symmkey->data = s; + symmkey->len = SYMMKEYSZ; s = NULL; } + oap_hdr_fini(&r_oap_hdr); + oap_hdr_fini(&oap_hdr); + + crypt_free_crt(ccrt); + crypt_free_key(cpkp); + + /* TODO: piggyback user data if needed */ + freebuf(hash); free(s); return 0; - fail_derive: - freebuf(rpk); + fail_r_oap_hdr: flow->state = FLOW_DEALLOCATED; + oap_hdr_fini(&r_oap_hdr); fail_alloc: freebuf(hash); fail_ipcp: reg_destroy_flow(flow->id); fail_flow: - if (flow->qs.cypher_s > 0) - crypt_dh_pkp_destroy(pkp); + oap_hdr_fini(&oap_hdr); + fail_oap_hdr: + crypt_dh_pkp_destroy(pkp); fail_pkp: free(s); fail_malloc: + crypt_free_crt(ccrt); + crypt_free_key(cpkp); + fail_cred: + clrbuf(id); + fail_id: return err; } -static int wait_for_accept(enum hash_algo algo, - const uint8_t * hash) +static int wait_for_accept(const char * name) { struct timespec timeo = TIMESPEC_INIT_MS(IRMD_REQ_ARR_TIMEOUT); struct timespec abstime; @@ -1138,25 +1557,23 @@ static int wait_for_accept(enum hash_algo algo, clock_gettime(PTHREAD_COND_CLOCK, &abstime); ts_add(&abstime, &timeo, &abstime); - ret = reg_wait_flow_accepting(algo, hash, &abstime); + ret = reg_wait_flow_accepting(name, &abstime); if (ret == -ETIMEDOUT) { - if (reg_get_exec(algo, hash, &exec) < 0) { - log_dbg("No program bound to " HASH_FMT32 ".", - HASH_VAL32(hash)); + if (reg_get_exec(name, &exec) < 0) { + log_dbg("No program bound for %s.", name); goto fail; } - log_info("Autostarting %s.", exec[0]); - if (spawn_program(exec) < 0) { - log_dbg("Failed to autostart " HASH_FMT32 ".", - HASH_VAL32(hash)); + log_err("Failed to start %s for %s.", exec[0], name); goto fail_spawn; } + log_info("Starting %s for %s.", exec[0], name); + ts_add(&abstime, &timeo, &abstime); - ret = reg_wait_flow_accepting(algo, hash, &abstime); + ret = reg_wait_flow_accepting(name, &abstime); if (ret == -ETIMEDOUT) goto fail_spawn; @@ -1179,10 +1596,11 @@ static int flow_req_arr(struct flow_info * flow, struct layer_info layer; enum hash_algo algo; int ret; + char name[NAME_SIZE + 1]; info.pid = flow->n_1_pid; - log_info("Flow req arrived from IPCP %d for " HASH_FMT32 ".", + log_dbg("Flow req arrived from IPCP %d for " HASH_FMT32 ".", info.pid, HASH_VAL32(hash)); if (reg_get_ipcp(&info, &layer) < 0) { @@ -1193,10 +1611,17 @@ static int flow_req_arr(struct flow_info * flow, algo = (enum hash_algo) layer.dir_hash_algo; - ret = wait_for_accept(algo, hash); + if (reg_get_name_for_hash(name, algo, hash) < 0) { + log_warn("No name for " HASH_FMT32 ".", HASH_VAL32(hash)); + ret = -ENAME; + goto fail; + } + + log_info("Flow request arrived for %s.", name); + + ret = wait_for_accept(name); if (ret < 0) { - log_err("No activeprocess for " HASH_FMT32 ".", - HASH_VAL32(hash)); + log_err("No active process for %s.", name); goto fail; } @@ -1218,9 +1643,9 @@ static int flow_alloc_reply(struct flow_info * flow, int response, buffer_t * data) { - flow->state = response ? FLOW_DEALLOCATED : FLOW_ALLOCATED; + flow->state = response != 0 ? FLOW_DEALLOCATED : FLOW_ALLOCATED; - if (reg_respond_alloc(flow, data) < 0) { + if (reg_respond_alloc(flow, data, response) < 0) { log_err("Failed to reply to flow %d.", flow->id); flow->state = FLOW_DEALLOCATED; return -EBADF; @@ -1232,7 +1657,7 @@ static int flow_alloc_reply(struct flow_info * flow, static int flow_dealloc(struct flow_info * flow, struct timespec * ts) { - log_info("Deallocating flow %d for process %d (timeout: %zd s).", + log_info("Deallocating flow %d for process %d (timeout: %ld s).", flow->id, flow->n_pid, ts->tv_sec); reg_dealloc_flow(flow); @@ -1308,7 +1733,7 @@ static void * acceptloop(void * o) return (void *) 0; } -static void free_msg(void * o) +static void __cleanup_irm_msg(void * o) { irm_msg__free_unpacked((irm_msg_t *) o, NULL); } @@ -1327,6 +1752,7 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg) int res; irm_msg_t * ret_msg; buffer_t data; + buffer_t symmkey = BUF_INIT;; memset(&flow, 0, sizeof(flow)); @@ -1351,7 +1777,7 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg) ret_msg->code = IRM_MSG_CODE__IRM_REPLY; - pthread_cleanup_push(free_msg, ret_msg); + pthread_cleanup_push(__cleanup_irm_msg, ret_msg); switch (msg->code) { case IRM_MSG_CODE__IRM_CREATE_IPCP: @@ -1380,7 +1806,7 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg) 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 */ + /* Terminate with NULL instead of "" */ free(msg->exec[msg->n_exec - 1]); msg->exec[msg->n_exec - 1] = NULL; res = bind_program(msg->exec, msg->name, msg->opts); @@ -1406,8 +1832,7 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg) 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; + name = name_info_msg_to_s(msg->name_info); res = name_create(&name); break; case IRM_MSG_CODE__IRM_DESTROY_NAME: @@ -1423,17 +1848,21 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg) res = name_unreg(msg->name, msg->pid); break; case IRM_MSG_CODE__IRM_FLOW_ACCEPT: + tpm_wait_work(irmd.tpm); 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); + res = flow_accept(&flow, &symmkey, &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; + ret_msg->has_symmkey = symmkey.len != 0; + ret_msg->symmkey.data = symmkey.data; + ret_msg->symmkey.len = symmkey.len; + ret_msg->has_pk = data.len != 0; + ret_msg->pk.data = data.data; + ret_msg->pk.len = data.len; } break; case IRM_MSG_CODE__IRM_FLOW_ALLOC: @@ -1443,12 +1872,15 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg) 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); + res = flow_alloc(&flow, msg->dst, &symmkey, &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; + ret_msg->has_symmkey = symmkey.len != 0; + ret_msg->symmkey.data = symmkey.data; + ret_msg->symmkey.len = symmkey.len; + ret_msg->has_pk = data.len != 0; + ret_msg->pk.data = data.data; + ret_msg->pk.len = data.len; } break; case IRM_MSG_CODE__IRM_FLOW_JOIN: @@ -1540,10 +1972,10 @@ static void * mainloop(void * o) continue; } - tpm_dec(irmd.tpm); + tpm_begin_work(irmd.tpm); pthread_cleanup_push(__cleanup_close_ptr, &sfd); - pthread_cleanup_push(free_msg, msg); + pthread_cleanup_push(__cleanup_irm_msg, msg); ret_msg = do_command_msg(msg); @@ -1556,12 +1988,12 @@ static void * mainloop(void * o) } if (ret_msg->result == -EPIPE) { - log_dbg("Terminated command: application closed socket."); + log_dbg("Terminated command: remote closed socket."); goto fail; } if (ret_msg->result == -EIRMD) { - log_dbg("Terminated command: IRMd not in running state."); + log_dbg("Terminated command: IRMd not running."); goto fail; } @@ -1596,54 +2028,20 @@ static void * mainloop(void * o) pthread_cleanup_pop(true); pthread_cleanup_pop(true); - tpm_inc(irmd.tpm); + tpm_end_work(irmd.tpm); continue; fail: irm_msg__free_unpacked(ret_msg, NULL); fail_msg: close(sfd); - tpm_inc(irmd.tpm); + tpm_end_work(irmd.tpm); continue; } 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) { @@ -1719,6 +2117,79 @@ void * irm_sanitize(void * o) return (void *) 0; } +static int irm_load_store(char * dpath) +{ + struct stat st; + struct dirent * dent; + DIR * dir; + void * crt; + + if (stat(dpath, &st) == -1) { + log_dbg("Store directory %s not found.", dpath); + return 0; + } + + if (!S_ISDIR(st.st_mode)) { + log_err("%s is not a directory.", dpath); + goto fail_dir; + } + + /* loop through files in directory and load certificates */ + dir = opendir(dpath); + if (dir == NULL) { + log_err("Failed to open %s.", dpath); + goto fail_dir; + } + + while ((dent = readdir(dir)) != NULL) { + char path[NAME_PATH_SIZE + 1]; + + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + + snprintf(path, sizeof(path), "%s/%s", dpath, + dent->d_name); + + if (stat(path, &st) == -1) { + log_dbg("Failed to stat %s.", path); + continue; + } + + if (!S_ISREG(st.st_mode)) { + log_dbg("%s is not a regular file.", path); + goto fail_file; + } + + if (crypt_load_crt_file(path, &crt) < 0) { + log_err("Failed to load certificate from %s.", path); + goto fail_file; + } + + if (auth_add_crt_to_store(irmd.auth.ctx, crt) < 0) { + log_err("Failed to add certificate from %s to store.", + path); + goto fail_crt_add; + } + + log_dbg("Loaded certificate: %s.", path); + + crypt_free_crt(crt); + } + + closedir(dir); + + log_info("Loaded certificates from %s.", dpath); + + return 0; + + fail_crt_add: + crypt_free_crt(crt); + fail_file: + closedir(dir); + fail_dir: + return -1; +} static int irm_init(void) { @@ -1819,6 +2290,30 @@ static int irm_init(void) log_err("Failed to greate thread pool."); goto fail_tpm_create; } + + if (pthread_mutex_init(&irmd.auth.mtx, NULL) < 0) { + log_err("Failed to initialize auth mutex."); + goto fail_auth_mtx; + } + + irmd.auth.ctx = auth_create_ctx(); + if (irmd.auth.ctx == NULL) { + log_err("Failed to create auth store context."); + goto fail_auth_ctx; + } + + list_head_init(&irmd.auth.list); + + if (irm_load_store(OUROBOROS_CA_CRT_DIR) < 0) { + log_err("Failed to load CA certificates."); + goto fail_auth_ctx; + } + + if (irm_load_store(OUROBOROS_CHAIN_DIR) < 0) { + log_err("Failed to load intermediate certificates."); + goto fail_auth_ctx; + } + #ifdef HAVE_FUSE mask = umask(0); @@ -1844,6 +2339,8 @@ static int irm_init(void) gcry_control(GCRYCTL_INITIALIZATION_FINISHED); #endif + irmd_set_state(IRMD_INIT); + return 0; #ifdef HAVE_LIBGCRYPT @@ -1851,8 +2348,12 @@ static int irm_init(void) #ifdef HAVE_FUSE rmdir(FUSE_PREFIX); #endif - tpm_destroy(irmd.tpm); + auth_destroy_ctx(irmd.auth.ctx); #endif + fail_auth_ctx: + pthread_mutex_destroy(&irmd.auth.mtx); + fail_auth_mtx: + tpm_destroy(irmd.tpm); fail_tpm_create: shm_rdrbuff_destroy(irmd.rdrb); fail_rdrbuff: @@ -1872,6 +2373,69 @@ static int irm_init(void) return -1; } +static void irm_fini(void) +{ + struct list_head * p; + struct list_head * h; +#ifdef HAVE_FUSE + struct timespec wait = TIMESPEC_INIT_MS(1); + int retries = 5; +#endif + if (irmd_get_state() != IRMD_INIT) + log_warn("Unsafe destroy."); + + pthread_mutex_lock(&irmd.auth.mtx); + + list_for_each_safe(p, h, &irmd.auth.list) { + struct oaph * oaph = list_entry(p, struct oaph, next); + list_del(&oaph->next); + free(oaph); + } + + pthread_mutex_unlock(&irmd.auth.mtx); + pthread_mutex_destroy(&irmd.auth.mtx); + + auth_destroy_ctx(irmd.auth.ctx); + + 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_lock(&irmd.cmd_lock); + + list_for_each_safe(p, h, &irmd.cmds) { + struct cmd * cmd = list_entry(p, struct cmd, next); + list_del(&cmd->next); + close(cmd->fd); + free(cmd); + } + + pthread_mutex_unlock(&irmd.cmd_lock); + + 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 + assert(list_is_empty(&irmd.cmds)); + + irmd.state = IRMD_NULL; +} + static void usage(void) { printf("Usage: irmd \n" @@ -1885,11 +2449,11 @@ static void usage(void) static int irm_start(void) { + irmd_set_state(IRMD_RUNNING); + if (tpm_start(irmd.tpm)) goto fail_tpm_start; - irmd_set_state(IRMD_RUNNING); - if (pthread_create(&irmd.irm_sanitize, NULL, irm_sanitize, NULL)) goto fail_irm_sanitize; @@ -1904,9 +2468,9 @@ static int irm_start(void) 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: + irmd_set_state(IRMD_INIT); return -1; } @@ -1947,7 +2511,7 @@ static void irm_stop(void) tpm_stop(irmd.tpm); - irmd_set_state(IRMD_NULL); + irmd_set_state(IRMD_INIT); } static void irm_argparse(int argc, @@ -1998,8 +2562,8 @@ static void * kill_dash_nine(void * o) slept += intv; } - log_dbg("I am become Death, destroyer of hung processes."); - + log_dbg("I guess I’ll have to shut you down for good this time,"); + log_dbg("already tried a SIGQUIT, so now it’s KILL DASH 9."); #ifdef IRMD_KILL_ALL_PROCESSES reg_kill_all_proc(SIGKILL); nanosleep(&ts, NULL); @@ -2058,7 +2622,7 @@ int main(int argc, if (geteuid() != 0) { printf("IPC Resource Manager must be run as root.\n"); - exit(EXIT_FAILURE); + goto fail_irm_init; } if (irm_init() < 0) @@ -2076,7 +2640,7 @@ int main(int argc, #ifdef HAVE_TOML if (irm_configure(irmd.cfg_file) < 0) { - irmd_set_state(IRMD_NULL); + irmd_set_state(IRMD_SHUTDOWN); ret = EXIT_FAILURE; } #endif diff --git a/src/irmd/oap.c b/src/irmd/oap.c new file mode 100644 index 00000000..500da6f1 --- /dev/null +++ b/src/irmd/oap.c @@ -0,0 +1,288 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * Ouroboros flow allocation protocol header + * + * 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 + +#define OUROBOROS_PREFIX "irmd/oap" + +#include <ouroboros/crypt.h> +#include <ouroboros/endian.h> +#include <ouroboros/logs.h> +#include <ouroboros/rib.h> +#include <ouroboros/time.h> + +#include "config.h" + +#include "oap.h" + +#include <assert.h> + +int oap_hdr_init(buffer_t id, + void * pkp, + void * pubcrt, + buffer_t ephkey, + buffer_t data, + struct oap_hdr * oap_hdr) +{ + struct timespec now; + uint64_t stamp; + buffer_t hdr; + buffer_t der = BUF_INIT; + buffer_t sig = BUF_INIT; + buffer_t sign; + uint16_t len; + off_t offset; + + assert(id.data != NULL && id.len == OAP_ID_SIZE); + assert(oap_hdr != NULL); + memset(oap_hdr, 0, sizeof(*oap_hdr)); + + clock_gettime(CLOCK_REALTIME, &now); + stamp = hton64(TS_TO_UINT64(now)); + + if (pubcrt != NULL && crypt_crt_der(pubcrt, &der) < 0) + goto fail_der; + + hdr.len = id.len + + sizeof(stamp) + + sizeof(len) + der.len + + sizeof(len) + ephkey.len + + sizeof(len) + data.len + + sizeof(len); /* sig len */ + + hdr.data = malloc(hdr.len); + if (hdr.data == NULL) + goto fail_hdr; + + offset = 0; + + memcpy(hdr.data, id.data, id.len); + offset += id.len; + + memcpy(hdr.data + offset, &stamp, sizeof(stamp)); + offset += sizeof(stamp); + + /* pubcrt */ + len = hton16((uint16_t) der.len); + memcpy(hdr.data + offset, &len, sizeof(len)); + offset += sizeof(len); + if (der.len != 0) + memcpy(hdr.data + offset, der.data, der.len); + offset += der.len; + + /* ephkey */ + len = hton16((uint16_t) ephkey.len); + memcpy(hdr.data + offset, &len, sizeof(len)); + offset += sizeof(len); + if (ephkey.len != 0) + memcpy(hdr.data + offset, ephkey.data, ephkey.len); + offset += ephkey.len; + + /* data */ + len = hton16((uint16_t) data.len); + memcpy(hdr.data + offset, &len, sizeof(len)); + offset += sizeof(len); + if (data.len != 0) + memcpy(hdr.data + offset, data.data, data.len); + offset += data.len; + + sign.data = hdr.data; + sign.len = hdr.len - sizeof(len); + + if (pkp != NULL && auth_sign(pkp, sign, &sig) < 0) + goto fail_sig; + + len = hton16((uint16_t) sig.len); + memcpy(hdr.data + offset, &len, sizeof(len)); + offset += sizeof(len); + + oap_hdr->hdr = hdr; + + assert((size_t) offset == hdr.len); + + if (sig.len > 0) { + oap_hdr->hdr.len += sig.len; + oap_hdr->hdr.data = realloc(hdr.data, oap_hdr->hdr.len); + if (oap_hdr->hdr.data == NULL) + goto fail_oap_hdr; + + memcpy(oap_hdr->hdr.data + offset, sig.data, sig.len); + clrbuf(hdr); + } + + if (oap_hdr_decode(oap_hdr->hdr, oap_hdr) < 0) + goto fail_decode; + + freebuf(der); + freebuf(sig); + + return 0; + + fail_decode: + oap_hdr_fini(oap_hdr); + fail_oap_hdr: + freebuf(sig); + fail_sig: + freebuf(hdr); + fail_hdr: + freebuf(der); + fail_der: + memset(oap_hdr, 0, sizeof(*oap_hdr)); + return -1; +} + +void oap_hdr_fini(struct oap_hdr * oap_hdr) +{ + assert(oap_hdr != NULL); + + freebuf(oap_hdr->hdr); + memset(oap_hdr, 0, sizeof(*oap_hdr)); +} + +int oap_hdr_decode(buffer_t hdr, + struct oap_hdr * oap_hdr) +{ + off_t offset; + + assert(oap_hdr != NULL); + memset(oap_hdr, 0, sizeof(*oap_hdr)); + + if (hdr.len < OAP_HDR_MIN_SIZE) + goto fail_decode; + + oap_hdr->id.data = hdr.data; + oap_hdr->id.len = OAP_ID_SIZE; + + offset = OAP_ID_SIZE; + + oap_hdr->timestamp = ntoh64(*(uint64_t *)(hdr.data + offset)); + + offset += sizeof(uint64_t); + + oap_hdr->crt.len = (size_t) ntoh16(*(uint16_t *)(hdr.data + offset)); + oap_hdr->crt.data = hdr.data + offset + sizeof(uint16_t); + + offset += sizeof(uint16_t) + oap_hdr->crt.len; + + if ((size_t) offset + sizeof(uint16_t) >= hdr.len) + goto fail_decode; + + oap_hdr->eph.len = (size_t) ntoh16(*(uint16_t *)(hdr.data + offset)); + oap_hdr->eph.data = hdr.data + offset + sizeof(uint16_t); + + offset += sizeof(uint16_t) + oap_hdr->eph.len; + + if ((size_t) offset + sizeof(uint16_t) >= hdr.len) + goto fail_decode; + + oap_hdr->data.len = (size_t) ntoh16(*(uint16_t *)(hdr.data + offset)); + oap_hdr->data.data = hdr.data + offset + sizeof(uint16_t); + + offset += sizeof(uint16_t) + oap_hdr->data.len; + + if ((size_t) offset + sizeof(uint16_t) > hdr.len) + goto fail_decode; + + oap_hdr->sig.len = (size_t) ntoh16(*(uint16_t *)(hdr.data + offset)); + oap_hdr->sig.data = hdr.data + offset + sizeof(uint16_t); + + offset += sizeof(uint16_t) + oap_hdr->sig.len; + + if ((size_t) offset != hdr.len) + goto fail_decode; + + oap_hdr->hdr = hdr; + + return 0; + + fail_decode: + memset(oap_hdr, 0, sizeof(*oap_hdr)); + return -1; +} + +#ifdef DEBUG_PROTO_OAP +static void debug_oap_hdr(const struct oap_hdr * hdr) +{ + assert(hdr); + + if (hdr->crt.len > 0) + log_proto(" Certificate: [%zu bytes]", hdr->crt.len); + else + log_proto(" Certificate: <none>"); + + if (hdr->eph.len > 0) + log_proto(" Ephemeral Public Key: [%zu bytes]", hdr->eph.len); + else + log_proto(" Ephemeral Public Key: <none>"); + if (hdr->data.len > 0) + log_proto(" Data: [%zu bytes]", hdr->data.len); + else + log_proto(" Data: <none>"); + if (hdr->sig.len > 0) + log_proto(" Signature: [%zu bytes]", hdr->sig.len); + else + log_proto(" Signature: <none>"); +} + +void debug_oap_hdr_rcv(const struct oap_hdr * hdr) +{ + struct tm * tm; + char tmstr[RIB_TM_STRLEN]; + time_t stamp; + + assert(hdr); + + stamp = (time_t) hdr->timestamp / BILLION; + + tm = gmtime(&stamp); + strftime(tmstr, sizeof(tmstr), RIB_TM_FORMAT, tm); + + log_proto("OAP_HDR [" HASH_FMT64 " @ %s ] <--", + HASH_VAL64(hdr->id.data), tmstr); + + debug_oap_hdr(hdr); +} + +void debug_oap_hdr_snd(const struct oap_hdr * hdr) +{ + struct tm * tm; + char tmstr[RIB_TM_STRLEN]; + time_t stamp; + + assert(hdr); + + stamp = (time_t) hdr->timestamp / BILLION; + + tm = gmtime(&stamp); + strftime(tmstr, sizeof(tmstr), RIB_TM_FORMAT, tm); + + log_proto("OAP_HDR [" HASH_FMT64 " @ %s ] -->", + HASH_VAL64(hdr->id.data), tmstr); + + debug_oap_hdr(hdr); +} +#endif + diff --git a/src/irmd/oap.h b/src/irmd/oap.h new file mode 100644 index 00000000..ccdfa804 --- /dev/null +++ b/src/irmd/oap.h @@ -0,0 +1,94 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * Ouroboros flow allocation protocol header + * + * 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_OAP_H +#define OUROBOROS_IRMD_OAP_H + +#include <ouroboros/utils.h> + +#define OAP_ID_SIZE (16) +#define OAP_HDR_MIN_SIZE (OAP_ID_SIZE + sizeof(uint64_t) + 4 * sizeof(uint16_t)) + + +/* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +---------------------------------------------------------------+ + * | | + * | id (128 bits) | + * | | + * | | + * +---------------------------------------------------------------+ + * | timestamp (64 bits) | + * | | + * +---------------------------------------------------------------+ + * | crt_len (16 bits) | | + * +-----------+-----------------+ | + * | certificate | + * | | + * +---------------------------------------------------------------+ + * | eph_len (16 bits) | | + * +-----------+-----------------+ | + * | public key for ECDHE | + * | | + * +---------------------------------------------------------------+ + * | data_len (16 bits) | | + * +-----------+-----------------+ | + * | piggy backed application data | + * | | + * +---------------------------------------------------------------+ + * | sig_len (16 bits) | | + * +-----------+-----------------+ | + * | signature | + * | | + * +---------------------------------------------------------------+ + */ + +struct oap_hdr { + uint64_t timestamp; + buffer_t id; + buffer_t crt; + buffer_t eph; + buffer_t data; + buffer_t sig; + buffer_t hdr; +}; + +int oap_hdr_init(buffer_t id, + void * pkp, + void * pubcrt, + buffer_t ephkey, + buffer_t data, + struct oap_hdr * oap_hdr); + +void oap_hdr_fini(struct oap_hdr * oap_hdr); + +int oap_hdr_decode(buffer_t hdr, + struct oap_hdr * oap_hdr); + +#ifdef DEBUG_PROTO_OAP +void debug_oap_hdr_snd(const struct oap_hdr * hdr); + +void debug_oap_hdr_rcv(const struct oap_hdr * hdr); +#endif /* DEBUG_PROTO_OAP */ + +#endif /* OUROBOROS_IRMD_OAP_H */ diff --git a/src/irmd/reg/flow.h b/src/irmd/reg/flow.h index 75ada971..d1e4811c 100644 --- a/src/irmd/reg/flow.h +++ b/src/irmd/reg/flow.h @@ -25,6 +25,7 @@ #include <ouroboros/list.h> #include <ouroboros/flow.h> +#include <ouroboros/name.h> #include <ouroboros/pthread.h> #include <ouroboros/qos.h> #include <ouroboros/shm_rbuff.h> @@ -34,12 +35,15 @@ #include <time.h> struct reg_flow { - struct list_head next; + struct list_head next; - struct flow_info info; + struct flow_info info; + int response; - buffer_t data; - struct timespec t0; + buffer_t data; + struct timespec t0; + + char name[NAME_SIZE + 1]; struct shm_rbuff * n_rb; struct shm_rbuff * n_1_rb; diff --git a/src/irmd/reg/ipcp.c b/src/irmd/reg/ipcp.c index 6580cb5b..474527a4 100644 --- a/src/irmd/reg/ipcp.c +++ b/src/irmd/reg/ipcp.c @@ -40,7 +40,7 @@ struct reg_ipcp * reg_ipcp_create(const struct ipcp_info * info) struct reg_ipcp * ipcp; assert(info != NULL); - assert(info->state == IPCP_BOOT); + assert(info->state == IPCP_INIT); ipcp = malloc(sizeof(*ipcp)); if (ipcp == NULL) { @@ -54,7 +54,7 @@ struct reg_ipcp * reg_ipcp_create(const struct ipcp_info * info) list_head_init(&ipcp->next); ipcp->info = *info; - ipcp->info.state = IPCP_BOOT; + ipcp->info.state = IPCP_INIT; strcpy(ipcp->layer.name, "Not enrolled."); @@ -77,7 +77,7 @@ void reg_ipcp_update(struct reg_ipcp * ipcp, const struct ipcp_info * info) { assert(ipcp != NULL); - assert(info->state != IPCP_INIT); + assert(info->state != IPCP_NULL); ipcp->info = *info; } @@ -86,7 +86,7 @@ void reg_ipcp_set_layer(struct reg_ipcp * ipcp, const struct layer_info * info) { assert(ipcp != NULL); - assert(ipcp->info.state == IPCP_OPERATIONAL); + assert(ipcp->info.state == IPCP_BOOT); ipcp->layer = *info; } diff --git a/src/irmd/reg/name.c b/src/irmd/reg/name.c index 1ac939a5..4e609711 100644 --- a/src/irmd/reg/name.c +++ b/src/irmd/reg/name.c @@ -66,15 +66,14 @@ struct reg_name * reg_name_create(const struct name_info * info) goto fail_malloc; } + memset(name, 0, sizeof(*name)); + list_head_init(&name->next); - list_head_init(&name->progs); - list_head_init(&name->procs); - list_head_init(&name->active); + list_head_init(&name->progs.list); + list_head_init(&name->procs.list); + list_head_init(&name->active.list); - name->info = *info; - name->n_progs = 0; - name->n_procs = 0; - name->n_active = 0; + name->info = *info; return name; @@ -88,13 +87,13 @@ void reg_name_destroy(struct reg_name * name) assert(list_is_empty(&name->next)); - assert(name->n_progs == 0); - assert(name->n_procs == 0); - assert(name->n_active == 0); + assert(name->progs.len == 0); + assert(name->procs.len == 0); + assert(name->active.len == 0); - assert(list_is_empty(&name->progs)); - assert(list_is_empty(&name->procs)); - assert(list_is_empty(&name->active)); + assert(list_is_empty(&name->progs.list)); + assert(list_is_empty(&name->procs.list)); + assert(list_is_empty(&name->active.list)); free(name); } @@ -107,7 +106,7 @@ static struct proc_entry * __reg_name_get_active(const struct reg_name * name, assert(name != NULL); assert(pid > 0); - list_for_each(p, &name->active) { + list_for_each(p, &name->active.list) { struct proc_entry * entry; entry = list_entry(p, struct proc_entry, next); if (entry->pid == pid) @@ -123,13 +122,13 @@ static void __reg_name_del_all_active(struct reg_name * name, struct list_head * p; struct list_head * h; - list_for_each_safe(p, h, &name->active) { + list_for_each_safe(p, h, &name->active.list) { 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--; + --name->active.len; } } } @@ -142,7 +141,7 @@ static struct proc_entry * __reg_name_get_proc(const struct reg_name * name, assert(name != NULL); assert(pid > 0); - list_for_each(p, &name->procs) { + list_for_each(p, &name->procs.list) { struct proc_entry * entry; entry = list_entry(p, struct proc_entry, next); if (entry->pid == pid) @@ -160,7 +159,7 @@ static struct prog_entry * __reg_name_get_prog(const struct reg_name * name, assert(name != NULL); assert(prog != NULL); - list_for_each(p, &name->progs) { + list_for_each(p, &name->progs.list) { struct prog_entry * entry; entry = list_entry(p, struct prog_entry, next); if (strcmp(entry->exec[0], prog) == 0) @@ -195,16 +194,16 @@ int reg_name_add_active(struct reg_name * name, switch (name->info.pol_lb) { case LB_RR: /* Round robin policy. */ - list_add_tail(&entry->next, &name->active); + list_add_tail(&entry->next, &name->active.list); break; case LB_SPILL: /* Keep accepting flows on the current process */ - list_add(&entry->next, &name->active); + list_add(&entry->next, &name->active.list); break; default: goto fail_unreachable; } - name->n_active++; + ++name->active.len; return 0; @@ -226,19 +225,23 @@ void reg_name_del_active(struct reg_name * name, list_del(&entry->next); - name->n_active--; + --name->active.len; free(entry); } pid_t reg_name_get_active(struct reg_name * name) { + struct proc_entry * e; + assert(name != NULL); - if (list_is_empty(&name->active)) + if (list_is_empty(&name->active.list)) return -1; - return list_first_entry(&name->active, struct proc_entry, next)->pid; + e = list_first_entry(&name->active.list, struct proc_entry, next); + + return e->pid; } int reg_name_add_proc(struct reg_name * name, @@ -259,9 +262,9 @@ int reg_name_add_proc(struct reg_name * name, entry->pid = pid; - list_add(&entry->next, &name->procs); + list_add(&entry->next, &name->procs.list); - name->n_procs++; + ++name->procs.len; return 0; @@ -287,7 +290,7 @@ void reg_name_del_proc(struct reg_name * name, free(entry); - name->n_procs--; + --name->procs.len; assert(__reg_name_get_proc(name, pid) == NULL); } @@ -296,8 +299,7 @@ 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) @@ -322,11 +324,11 @@ int reg_name_add_prog(struct reg_name * name, goto fail_exec; } - list_add(&entry->next, &name->progs); + list_add(&entry->next, &name->progs.list); log_dbg("Add prog %s to name %s.", exec[0], name->info.name); - name->n_progs++; + ++name->progs.len; return 0; @@ -352,7 +354,7 @@ void reg_name_del_prog(struct reg_name * name, __free_prog_entry(entry); - name->n_progs--; + --name->progs.len; assert(__reg_name_get_prog(name, prog) == NULL); } @@ -368,8 +370,12 @@ bool reg_name_has_prog(const struct reg_name * name, char ** reg_name_get_exec(const struct reg_name * name) { - if (list_is_empty(&name->progs)) + struct prog_entry * e; + + if (list_is_empty(&name->progs.list)) return NULL; - return list_first_entry(&name->progs, struct prog_entry, next)->exec; + e = list_first_entry(&name->progs.list, struct prog_entry, next); + + return e->exec; } diff --git a/src/irmd/reg/name.h b/src/irmd/reg/name.h index 97ca7f04..30a64e1c 100644 --- a/src/irmd/reg/name.h +++ b/src/irmd/reg/name.h @@ -33,14 +33,25 @@ struct reg_name { 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 { + void * key; + void * crt; + } cache; + + struct { + struct list_head list; + size_t len; + } progs; /* autostart programs for this name */ + + struct { + struct list_head list; + size_t len; + } procs; /* processes bound to this name */ + + struct { + struct list_head list; + size_t len; + } active; /* processes actively calling accept */ }; struct reg_name * reg_name_create(const struct name_info * info); @@ -74,5 +85,4 @@ 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.h b/src/irmd/reg/proc.h index 99f74fef..499ecc72 100644 --- a/src/irmd/reg/proc.h +++ b/src/irmd/reg/proc.h @@ -32,8 +32,8 @@ struct reg_proc { struct proc_info info; - struct list_head names; /* names for which process accepts flows */ - size_t n_names; /* number of names */ + struct list_head names; /* process accepts flows for names */ + size_t n_names; /* number of names */ struct shm_flow_set * set; }; diff --git a/src/irmd/reg/reg.c b/src/irmd/reg/reg.c index d95a4722..a24a9d1d 100644 --- a/src/irmd/reg/reg.c +++ b/src/irmd/reg/reg.c @@ -28,6 +28,7 @@ The IPC Resource Manager - Registry #include <ouroboros/errno.h> #include <ouroboros/list.h> #include <ouroboros/logs.h> +#include <ouroboros/protobuf.h> #include <ouroboros/pthread.h> #include "reg.h" @@ -46,6 +47,7 @@ The IPC Resource Manager - Registry struct { struct bmp * flow_ids; /* flow_ids for flows */ + struct list_head flows; /* flow information */ size_t n_flows; /* number of flows */ @@ -151,16 +153,23 @@ static struct reg_ipcp * __reg_get_ipcp_by_layer(const char * layer) return NULL; } -static struct list_head * __reg_after_ipcp(pid_t pid) + +static struct list_head * __reg_after_ipcp(const struct ipcp_info * info) { struct list_head * p; - assert(pid > 0); + assert(info != NULL); list_for_each(p, ®.ipcps) { struct reg_ipcp * entry; entry = list_entry(p, struct reg_ipcp, next); - if (entry->info.pid > pid) + if (entry->info.type < info->type) + continue; + + if (entry->info.type > info->type) + break; + + if (entry->info.pid > info->pid) break; } @@ -183,41 +192,17 @@ static struct reg_name * __reg_get_name(const char * name) 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) +static int __reg_get_pending_flow_id(const char * name) { struct reg_name * entry; struct reg_flow * flow; pid_t pid; - entry =__reg_get_name_by_hash(algo, hash); + assert(name != NULL); + assert(strlen(name) > 0); + assert(strlen(name) < NAME_SIZE + 1); + + entry =__reg_get_name(name); if (entry == NULL) return -ENAME; @@ -226,7 +211,10 @@ static int __reg_get_pending_flow_id_for_hash(enum hash_algo algo, return -EAGAIN; flow = __reg_get_accept_flow(pid); - assert(flow != NULL); + if (flow == NULL) /* compiler barks, this can't be NULL */ + return -EAGAIN; + + strcpy(flow->name, name); return flow->info.id; } @@ -388,30 +376,17 @@ static struct reg_prog * __reg_get_prog(const char * name) return NULL; } -static char ** __reg_get_exec(enum hash_algo algo, - const uint8_t * hash) +static char ** __reg_get_exec(const char * name) { 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); + if (strcmp(entry->info.name, name) == 0) return reg_name_get_exec(entry); - } } - free(buf); - return NULL; } @@ -614,6 +589,14 @@ void reg_clear(void) reg.n_procs--; } + 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--; + } + list_for_each_safe(p, h, ®.names) { struct reg_name * entry; entry = list_entry(p, struct reg_name, next); @@ -630,14 +613,6 @@ void reg_clear(void) 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); } @@ -757,7 +732,7 @@ int reg_create_ipcp(const struct ipcp_info * info) assert(info != NULL); assert(info->pid != 0); - assert(info->state == IPCP_BOOT); + assert(info->state == IPCP_INIT); pthread_mutex_lock(®.mtx); @@ -780,7 +755,7 @@ int reg_create_ipcp(const struct ipcp_info * info) entry->pid = info->pid; - list_add(&ipcp->next, __reg_after_ipcp(info->pid)); + list_add_tail(&ipcp->next, __reg_after_ipcp(info)); list_add(&entry->next, __reg_after_spawned(info->pid)); reg.n_ipcps++; @@ -848,11 +823,11 @@ static int __get_ipcp_info(ipcp_list_msg_t ** msg, (*msg)->name = strdup(ipcp->info.name); if ((*msg)->name == NULL) - goto fail_name; + goto fail_msg; (*msg)->layer = strdup(ipcp->layer.name); if ((*msg)->layer == NULL) - goto fail_layer; + goto fail_msg; (*msg)->pid = ipcp->info.pid; (*msg)->type = ipcp->info.type; @@ -860,10 +835,8 @@ static int __get_ipcp_info(ipcp_list_msg_t ** msg, return 0; - fail_layer: - free((*msg)->name); - fail_name: - free(*msg); + fail_msg: + ipcp_list_msg__free_unpacked(*msg, NULL); *msg = NULL; fail: return -1; @@ -876,10 +849,8 @@ int reg_list_ipcps(ipcp_list_msg_t *** ipcps) pthread_mutex_lock(®.mtx); - if (reg.n_ipcps == 0) { - *ipcps = NULL; + if (reg.n_ipcps == 0) goto finish; - } *ipcps = malloc(reg.n_ipcps * sizeof(**ipcps)); if (*ipcps == NULL) { @@ -890,24 +861,19 @@ int reg_list_ipcps(ipcp_list_msg_t *** ipcps) 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."); + if (__get_ipcp_info(&(*ipcps)[i], entry) < 0) goto fail; - } - ++i; + 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); - + while (i-- > 0) + ipcp_list_msg__free_unpacked((*ipcps)[i], NULL); free(*ipcps); fail_malloc: pthread_mutex_unlock(®.mtx); @@ -993,28 +959,84 @@ bool reg_has_name(const char * name) return ret; } -static int __get_name_info(name_info_msg_t ** msg, - struct reg_name * n) +int reg_get_name_info(const char * name, + struct name_info * info) { - *msg = malloc(sizeof(**msg)); - if (*msg == NULL) - goto fail; + struct reg_name * n; - name_info_msg__init(*msg); + assert(name != NULL); + assert(info != NULL); - (*msg)->name = strdup(n->info.name); - if ((*msg)->name == NULL) - goto fail_name; + pthread_mutex_lock(®.mtx); + + n = __reg_get_name(name); + if (n == NULL) { + log_err("Name %s does not exist.", name); + goto no_name; + } - (*msg)->pol_lb = n->info.pol_lb; + *info = n->info; + + pthread_mutex_unlock(®.mtx); return 0; - fail_name: - free(*msg); - *msg = NULL; - fail: - return -1; + no_name: + pthread_mutex_unlock(®.mtx); + return -ENOENT; + +} + +int reg_get_name_for_hash(char * buf, + enum hash_algo algo, + const uint8_t * hash) +{ + struct list_head * p; + uint8_t * thash; + size_t len; + char * name = NULL; + + len = hash_len(algo); + + thash = malloc(len); + if (thash == NULL) + return -ENOMEM; + + pthread_mutex_lock(®.mtx); + + 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) { + name = n->info.name; + break; + } + } + + if (name != NULL) + strcpy(buf, name); + + pthread_mutex_unlock(®.mtx); + + free(thash); + + return name == NULL ? -ENOENT : 0; +} + +int reg_get_name_for_flow_id(char * buf, + int flow_id) +{ + struct reg_flow * f; + + pthread_mutex_lock(®.mtx); + + f = __reg_get_flow(flow_id); + if (f != NULL) + strcpy(buf, f->name); + + pthread_mutex_unlock(®.mtx); + + return f == NULL ? -ENOENT : 0; } int reg_list_names(name_info_msg_t *** names) @@ -1036,24 +1058,31 @@ int reg_list_names(name_info_msg_t *** names) 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) { + (*names)[i] = name_info_s_to_msg(&entry->info); + if ((*names)[i] == NULL) { log_err("Failed to create name list info."); goto fail; } - - ++i; + /* wipe security info to avoid huge messages */ + free((*names)[i]->scrt); + (*names)[i]->scrt = NULL; + free((*names)[i]->skey); + (*names)[i]->skey = NULL; + free((*names)[i]->ccrt); + (*names)[i]->ccrt = NULL; + free((*names)[i]->ckey); + (*names)[i]->ckey = NULL; + + 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); - + while (i-- > 0) + name_info_msg__free_unpacked((*names)[i], NULL); free(*names); fail_malloc: pthread_mutex_unlock(®.mtx); @@ -1419,19 +1448,18 @@ bool reg_has_prog(const char * name) return ret; } -int reg_get_exec(enum hash_algo algo, - const uint8_t * hash, - char *** prog) +int reg_get_exec(const char * name, + char *** prog) { char ** exec; int ret = 0; - assert(hash != NULL); + assert(name != NULL); assert(prog != NULL); pthread_mutex_lock(®.mtx); - exec = __reg_get_exec(algo, hash); + exec = __reg_get_exec(name); if (exec == NULL) { ret = -EPERM; goto finish; @@ -1444,12 +1472,9 @@ int reg_get_exec(enum hash_algo algo, goto finish; } - pthread_mutex_unlock(®.mtx); - - return 0; - finish: pthread_mutex_unlock(®.mtx); + return ret; } @@ -1557,8 +1582,7 @@ int reg_set_layer_for_ipcp(struct ipcp_info * info, struct reg_ipcp * ipcp; assert(info != NULL); - assert(info->state > IPCP_BOOT); - assert(info->state < IPCP_SHUTDOWN); + assert(info->state == IPCP_BOOT); pthread_mutex_lock(®.mtx); @@ -1690,7 +1714,7 @@ int reg_wait_flow_allocated(struct flow_info * info, stop = true; break; case FLOW_DEALLOCATED: - ret = -1; + ret = flow->response; stop = true; break; default: @@ -1722,7 +1746,8 @@ int reg_wait_flow_allocated(struct flow_info * info, } int reg_respond_alloc(struct flow_info * info, - buffer_t * pbuf) + buffer_t * pbuf, + int response) { struct reg_flow * flow; @@ -1755,7 +1780,9 @@ int reg_respond_alloc(struct flow_info * info, if (reg_flow_update(flow, info) < 0) { log_err("Failed to create flow structs."); goto fail_flow; - }; + } + + flow->response = response; if (info->state == FLOW_ALLOCATED) reg_flow_set_data(flow, pbuf); @@ -1771,8 +1798,7 @@ int reg_respond_alloc(struct flow_info * info, return -1; } -int reg_prepare_flow_accept(struct flow_info * info, - buffer_t * pbuf) +int reg_prepare_flow_accept(struct flow_info * info) { struct reg_flow * flow; int ret; @@ -1790,8 +1816,6 @@ int reg_prepare_flow_accept(struct flow_info * info, ret = reg_flow_update(flow, info); - reg_flow_set_data(flow, pbuf); - pthread_mutex_unlock(®.mtx); return ret; @@ -1824,8 +1848,6 @@ int reg_wait_flow_accepted(struct flow_info * info, 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); @@ -1883,13 +1905,12 @@ int reg_wait_flow_accepted(struct flow_info * info, return -1; } -int reg_wait_flow_accepting(enum hash_algo algo, - const uint8_t * hash, +int reg_wait_flow_accepting(const char * name, const struct timespec * abstime) { int ret; - assert(hash != NULL); + assert(name != NULL); assert(abstime != NULL); pthread_mutex_lock(®.mtx); @@ -1897,7 +1918,7 @@ int reg_wait_flow_accepting(enum hash_algo algo, pthread_cleanup_push(__cleanup_mutex_unlock, ®.mtx); while (true) { - ret = __reg_get_pending_flow_id_for_hash(algo, hash); + ret = __reg_get_pending_flow_id(name); if (ret != -EAGAIN) break; @@ -1915,7 +1936,6 @@ 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); @@ -1933,11 +1953,8 @@ int reg_respond_accept(struct flow_info * info, 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; - } + reg_flow_set_data(flow, pbuf); + clrbuf(pbuf); if (reg_flow_update(flow, info) < 0) { log_err("Failed to create flow structs."); @@ -1970,12 +1987,14 @@ void reg_dealloc_flow(struct flow_info * info) 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; + memset(flow->name, 0, sizeof(flow->name)); + reg_flow_update(flow, info); pthread_mutex_unlock(®.mtx); @@ -2043,7 +2062,7 @@ int reg_wait_ipcp_boot(struct ipcp_info * info, int ret; bool stop = false; - assert(info->state == IPCP_BOOT); + assert(info->state == IPCP_INIT); pthread_mutex_lock(®.mtx); @@ -2063,16 +2082,18 @@ int reg_wait_ipcp_boot(struct ipcp_info * info, ret = -1; stop = true; break; + case IPCP_BOOT: + /* FALLTHRU*/ case IPCP_OPERATIONAL: ret = 0; stop = true; break; - case IPCP_BOOT: + case IPCP_INIT: ret = -__timedwait(®.cond, ®.mtx, abstime); break; default: assert(false); - continue; /* Shut up static analyzer. */ + break; /* Shut up static analyzer. */ } ipcp = __reg_get_ipcp(info->pid); diff --git a/src/irmd/reg/reg.h b/src/irmd/reg/reg.h index 17dfcc32..7728c80f 100644 --- a/src/irmd/reg/reg.h +++ b/src/irmd/reg/reg.h @@ -90,6 +90,16 @@ int reg_destroy_name(const char * name); bool reg_has_name(const char * name); +int reg_get_name_info(const char * name, + struct name_info * info); + +int reg_get_name_for_hash(char * buf, + enum hash_algo algo, + const uint8_t * hash); + +int reg_get_name_for_flow_id(char * buf, + int flow_id); + /* TODO don't rely on protobuf here */ int reg_list_names(name_info_msg_t *** names); @@ -99,9 +109,8 @@ 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_get_exec(const char * name, + char *** exec); int reg_bind_prog(const char * name, char ** exec, @@ -117,17 +126,16 @@ int reg_wait_flow_allocated(struct flow_info * info, const struct timespec * abstime); int reg_respond_alloc(struct flow_info * info, - buffer_t * pbuf); + buffer_t * pbuf, + int response); -int reg_prepare_flow_accept(struct flow_info * info, - buffer_t * pbuf); +int reg_prepare_flow_accept(struct flow_info * info); 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, +int reg_wait_flow_accepting(const char * name, const struct timespec * abstime); int reg_respond_accept(struct flow_info * info, diff --git a/src/irmd/reg/tests/CMakeLists.txt b/src/irmd/reg/tests/CMakeLists.txt index bc1354ed..7bc98571 100644 --- a/src/irmd/reg/tests/CMakeLists.txt +++ b/src/irmd/reg/tests/CMakeLists.txt @@ -21,7 +21,11 @@ endif () 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) diff --git a/src/irmd/reg/tests/flow_test.c b/src/irmd/reg/tests/flow_test.c index f9d23fd1..27fd61b0 100644 --- a/src/irmd/reg/tests/flow_test.c +++ b/src/irmd/reg/tests/flow_test.c @@ -28,7 +28,7 @@ #define TEST_DATA "testpiggybackdata" -static int test_reg_flow_create(void) +static int test_reg_flow_create_destroy(void) { struct reg_flow * f; @@ -51,10 +51,10 @@ static int test_reg_flow_create(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static int test_reg_flow_create_no_id(void) { @@ -67,7 +67,7 @@ static int test_reg_flow_create_no_id(void) { reg_flow_create(&info); /* assert fail */ - return 0; + return TEST_RC_SUCCESS; } static int test_reg_flow_create_no_pid(void) { @@ -80,7 +80,7 @@ static int test_reg_flow_create_no_pid(void) { reg_flow_create(&info); /* assert fail */ - return 0; + return TEST_RC_SUCCESS; } static int test_reg_flow_create_has_n_1_pid(void) { @@ -94,7 +94,7 @@ static int test_reg_flow_create_has_n_1_pid(void) { reg_flow_create(&info); /* assert fail */ - return 0; + return TEST_RC_SUCCESS; } static int test_reg_flow_create_wrong_state(void) { @@ -108,7 +108,7 @@ static int test_reg_flow_create_wrong_state(void) { reg_flow_create(&info); /* assert fail */ - return 0; + return TEST_RC_SUCCESS; } static int test_reg_flow_create_has_mpl(void) { @@ -123,7 +123,7 @@ static int test_reg_flow_create_has_mpl(void) { reg_flow_create(&info); /* assert fail */ - return 0; + return TEST_RC_SUCCESS; } static int test_reg_flow_update(void) @@ -163,10 +163,10 @@ static int test_reg_flow_update(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static int test_reg_flow_update_wrong_id(void) @@ -199,10 +199,10 @@ static int test_reg_flow_update_wrong_id(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static int test_reg_flow_assert_fails(void) @@ -210,15 +210,10 @@ 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; @@ -237,7 +232,7 @@ static int test_flow_data(void) char * data; buffer_t buf; - buffer_t rcv = {NULL, 0}; + buffer_t rcv = {0, NULL}; TEST_START(); @@ -267,11 +262,11 @@ static int test_flow_data(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: free(data); TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } int flow_test(int argc, @@ -282,12 +277,9 @@ int flow_test(int argc, (void) argc; (void) argv; - ret |= test_reg_flow_create(); - + ret |= test_reg_flow_create_destroy(); 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 index fb8ba71b..d7d8e524 100644 --- a/src/irmd/reg/tests/ipcp_test.c +++ b/src/irmd/reg/tests/ipcp_test.c @@ -31,7 +31,7 @@ static int test_reg_ipcp_create(void) struct reg_ipcp * ipcp; struct ipcp_info info = { .pid = TEST_PID, - .state = IPCP_BOOT + .state = IPCP_INIT }; struct layer_info layer = { .name = "testlayer", @@ -51,7 +51,7 @@ static int test_reg_ipcp_create(void) goto fail; } - ipcp->info.state = IPCP_OPERATIONAL; + ipcp->info.state = IPCP_BOOT; reg_ipcp_set_layer(ipcp, &layer); @@ -60,11 +60,6 @@ static int test_reg_ipcp_create(void) goto fail; } - if (ipcp->info.state != IPCP_OPERATIONAL) { - printf("IPCP state was not set.\n"); - goto fail; - } - reg_ipcp_destroy(ipcp); TEST_SUCCESS(); diff --git a/src/irmd/reg/tests/name_test.c b/src/irmd/reg/tests/name_test.c index 48f132e9..9071364b 100644 --- a/src/irmd/reg/tests/name_test.c +++ b/src/irmd/reg/tests/name_test.c @@ -20,8 +20,11 @@ * Foundation, Inc., http://www.fsf.org/about/contact/. */ + #include "../name.c" +#include <ouroboros/test.h> + #define TEST_PID 65534 #define TEST_PROG "/usr/bin/testprog" #define TEST_NAME "testservicename" @@ -34,6 +37,8 @@ static int test_reg_name_create(void) .pol_lb = LB_RR, }; + TEST_START(); + n = reg_name_create(&info); if (n == NULL) { printf("Failed to create name %s.\n", info.name); @@ -42,9 +47,12 @@ static int test_reg_name_create(void) reg_name_destroy(n); - return 0; + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; fail: - return -1; + TEST_FAIL(); + return TEST_RC_FAIL; } static int test_reg_name_add_proc(void) @@ -55,6 +63,8 @@ static int test_reg_name_add_proc(void) .pol_lb = LB_RR, }; + TEST_START(); + n = reg_name_create(&info); if (n == NULL) { printf("Failed to create name %s.\n", info.name); @@ -66,8 +76,8 @@ static int test_reg_name_add_proc(void) goto fail; } - if (n->n_procs != 1) { - printf("n_procs not updated.\n"); + if (n->procs.len != 1) { + printf("Proc not added to list.\n"); goto fail; } @@ -78,16 +88,19 @@ static int test_reg_name_add_proc(void) reg_name_del_proc(n, TEST_PID); - if (n->n_procs != 0) { - printf("n_procs not updated.\n"); + if (n->procs.len != 0) { + printf("Proc not removed from list.\n"); goto fail; } reg_name_destroy(n); - return 0; + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; fail: - return -1; + TEST_FAIL(); + return TEST_RC_FAIL; } static int test_reg_name_add_prog(void) @@ -100,6 +113,8 @@ static int test_reg_name_add_prog(void) char * exec[] = { TEST_PROG, "--argswitch", "argvalue", NULL}; + TEST_START(); + n = reg_name_create(&info); if (n == NULL) { printf("Failed to create name %s.\n", info.name); @@ -111,8 +126,8 @@ static int test_reg_name_add_prog(void) goto fail; } - if (n->n_progs != 1) { - printf("n_progs not updated.\n"); + if (n->progs.len != 1) { + printf("Prog not added to list.\n"); goto fail; } @@ -123,16 +138,19 @@ static int test_reg_name_add_prog(void) reg_name_del_prog(n, TEST_PROG); - if (n->n_progs != 0) { - printf("n_progs not updated.\n"); + if (n->progs.len != 0) { + printf("Prog not removed from list.\n"); goto fail; } reg_name_destroy(n); - return 0; + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; fail: - return -1; + TEST_FAIL(); + return TEST_RC_FAIL; } static int test_reg_name_add_active(enum pol_balance lb) @@ -144,6 +162,8 @@ static int test_reg_name_add_active(enum pol_balance lb) .pol_lb = lb, }; + TEST_START(); + n = reg_name_create(&info); if (n == NULL) { printf("Failed to create name %s.\n", info.name); @@ -175,8 +195,8 @@ static int test_reg_name_add_active(enum pol_balance lb) goto fail; } - if (n->n_active != 1) { - printf("n_active not updated.\n"); + if (n->active.len != 1) { + printf("Active list not updated.\n"); goto fail; } @@ -206,13 +226,13 @@ static int test_reg_name_add_active(enum pol_balance lb) goto fail; } - if (n->n_procs != 3) { - printf("n_procs not updated.\n"); + if (n->procs.len != 3) { + printf("Procs list not updated.\n"); goto fail; } - if (n->n_active != 4) { - printf("n_active not updated.\n"); + if (n->active.len != 4) { + printf("Active list not updated.\n"); goto fail; } @@ -243,41 +263,39 @@ static int test_reg_name_add_active(enum pol_balance lb) reg_name_del_proc(n, TEST_PID); - if (n->n_procs != 0) { - printf("n_procs not updated.\n"); + if (n->procs.len != 0) { + printf("Procs list not cleared.\n"); goto fail; } - if (n->n_active != 0) { - printf("n_active not updated.\n"); + if (n->active.len != 0) { + printf("Active list not cleared.\n"); goto fail; } reg_name_destroy(n); - return 0; + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; fail: - return -1; + TEST_FAIL(); + return TEST_RC_FAIL; } - int name_test(int argc, char ** argv) { - int res = 0; + int rc = 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); + rc |= test_reg_name_create(); + rc |= test_reg_name_add_proc(); + rc |= test_reg_name_add_prog(); + rc |= test_reg_name_add_active(LB_RR); + rc |= test_reg_name_add_active(LB_SPILL); - return res; + return rc; } diff --git a/src/irmd/reg/tests/proc_test.c b/src/irmd/reg/tests/proc_test.c index 5c9dd865..df0527fb 100644 --- a/src/irmd/reg/tests/proc_test.c +++ b/src/irmd/reg/tests/proc_test.c @@ -22,10 +22,12 @@ #include "../proc.c" +#include <ouroboros/test.h> + #define TEST_PID 65534 #define TEST_PROG "usr/bin/testprog" -static int test_reg_proc_create(void) +static int test_reg_proc_create_destroy(void) { struct reg_proc * proc; struct proc_info info = { @@ -33,6 +35,8 @@ static int test_reg_proc_create(void) .prog = TEST_PROG }; + TEST_START(); + proc = reg_proc_create(&info); if (proc == NULL) { printf("Failed to create proc.\n"); @@ -41,9 +45,12 @@ static int test_reg_proc_create(void) reg_proc_destroy(proc); - return 0; + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; fail: - return -1; + TEST_FAIL(); + return TEST_RC_FAIL; } static int test_reg_proc_add_name(void) @@ -56,6 +63,8 @@ static int test_reg_proc_add_name(void) char * name = "testname"; + TEST_START(); + proc = reg_proc_create(&info); if (proc == NULL) { printf("Failed to create proc.\n"); @@ -86,9 +95,12 @@ static int test_reg_proc_add_name(void) reg_proc_destroy(proc); - return 0; + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; fail: - return -1; + TEST_FAIL(); + return TEST_RC_FAIL; } int proc_test(int argc, @@ -99,8 +111,7 @@ int proc_test(int argc, (void) argc; (void) argv; - res |= test_reg_proc_create(); - + res |= test_reg_proc_create_destroy(); 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 index 5e6931d8..c394c222 100644 --- a/src/irmd/reg/tests/prog_test.c +++ b/src/irmd/reg/tests/prog_test.c @@ -22,8 +22,9 @@ #include "../prog.c" -#define TEST_PROG "usr/bin/testprog" +#include <ouroboros/test.h> +#define TEST_PROG "usr/bin/testprog" static int test_reg_prog_create(void) { @@ -32,6 +33,8 @@ static int test_reg_prog_create(void) .name = TEST_PROG }; + TEST_START(); + prog = reg_prog_create(&info); if (prog == NULL) { printf("Failed to create prog.\n"); @@ -40,9 +43,12 @@ static int test_reg_prog_create(void) reg_prog_destroy(prog); - return 0; + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; fail: - return -1; + TEST_FAIL(); + return TEST_RC_FAIL; } static int test_reg_prog_add_name(void) @@ -54,6 +60,8 @@ static int test_reg_prog_add_name(void) char * name = "testname"; + TEST_START(); + prog = reg_prog_create(&info); if (prog == NULL) { printf("Failed to create prog.\n"); @@ -84,9 +92,12 @@ static int test_reg_prog_add_name(void) reg_prog_destroy(prog); - return 0; + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; fail: - return -1; + TEST_FAIL(); + return TEST_RC_FAIL; } int prog_test(int argc, @@ -98,7 +109,6 @@ int prog_test(int 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 index c341c297..4699beab 100644 --- a/src/irmd/reg/tests/reg_test.c +++ b/src/irmd/reg/tests/reg_test.c @@ -36,7 +36,7 @@ #define TEST_DATA2 "testpbufdata2" #define TEST_LAYER "testlayer" #define REG_TEST_FAIL() \ - do { TEST_FAIL(); memset(®, 0, sizeof(reg)); } while(0) + do { TEST_FAIL(); memset(®, 0, sizeof(reg)); abort();} while(0) static int test_reg_init(void) { @@ -51,10 +51,10 @@ static int test_reg_init(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static int test_reg_create_flow(void) @@ -105,18 +105,17 @@ static int test_reg_create_flow(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } 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}; + buffer_t rbuf = BUF_INIT; struct flow_info info = { .n_pid = TEST_PID, @@ -125,14 +124,6 @@ static int test_reg_allocate_flow_timeout(void) 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); @@ -147,7 +138,7 @@ static int test_reg_allocate_flow_timeout(void) goto fail; } - if (reg_prepare_flow_accept(&info, &pbuf) < 0) { + if (reg_prepare_flow_accept(&info) < 0) { printf("Failed to prepare flow for accept.\n"); goto fail; } @@ -162,12 +153,6 @@ static int test_reg_allocate_flow_timeout(void) 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) { @@ -179,16 +164,19 @@ static int test_reg_allocate_flow_timeout(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static void * test_flow_respond_alloc(void * o) { struct flow_info * info = (struct flow_info *) o; - buffer_t pbuf = {NULL, 0}; + buffer_t pbuf = BUF_INIT; + int response; + + response = (info->state == FLOW_ALLOCATED) ? 0 : -1; if (info->state == FLOW_ALLOCATED) { pbuf.data = (uint8_t *) strdup(TEST_DATA2); @@ -199,7 +187,7 @@ static void * test_flow_respond_alloc(void * o) pbuf.len = strlen((char *) pbuf.data) + 1; } - reg_respond_alloc(info, &pbuf); + reg_respond_alloc(info, &pbuf, response); return (void *) 0; fail: @@ -220,13 +208,6 @@ static void * test_flow_respond_accept(void * o) 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; @@ -237,8 +218,7 @@ 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}; + buffer_t rbuf = BUF_INIT; struct flow_info info = { .n_pid = TEST_PID, @@ -247,7 +227,7 @@ static int test_reg_accept_flow_success(void) struct flow_info n_1_info = { .n_1_pid = TEST_N_1_PID, - .qs = qos_data_crypt, + .qs = qos_data, .state = FLOW_ALLOCATED /* RESPONSE SUCCESS */ }; @@ -267,7 +247,7 @@ static int test_reg_accept_flow_success(void) goto fail; } - if (reg_prepare_flow_accept(&info, &pbuf) < 0) { + if (reg_prepare_flow_accept(&info) < 0) { printf("Failed to prepare flow for accept.\n"); goto fail; } @@ -277,7 +257,7 @@ static int test_reg_accept_flow_success(void) pthread_create(&thr, NULL, test_flow_respond_accept, &n_1_info); - if (reg_wait_flow_accepted(&info, &rbuf, &abstime) < 0 ) { + if (reg_wait_flow_accepted(&info, &rbuf, &abstime) < 0) { printf("Flow allocation failed.\n"); goto fail; } @@ -321,10 +301,10 @@ static int test_reg_accept_flow_success(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static int test_reg_accept_flow_success_no_crypt(void) @@ -332,8 +312,7 @@ 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}; + buffer_t rbuf = BUF_INIT; struct flow_info info = { .n_pid = TEST_PID, @@ -362,7 +341,7 @@ static int test_reg_accept_flow_success_no_crypt(void) goto fail; } - if (reg_prepare_flow_accept(&info, &pbuf) < 0) { + if (reg_prepare_flow_accept(&info) < 0) { printf("Failed to prepare flow for accept.\n"); goto fail; } @@ -389,10 +368,7 @@ static int test_reg_accept_flow_success_no_crypt(void) goto fail; } - if (strcmp((char *) rbuf.data, TEST_DATA) != 0) { - printf("Data was updated.\n"); - goto fail; - } + freebuf(rbuf); n_1_info.state = FLOW_DEALLOCATED; @@ -416,16 +392,16 @@ static int test_reg_accept_flow_success_no_crypt(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static int test_reg_allocate_flow_fail(void) { - buffer_t buf = {NULL, 0}; + buffer_t buf = BUF_INIT; pthread_t thr; struct timespec abstime; struct timespec timeo = TIMESPEC_INIT_S(1); @@ -486,26 +462,22 @@ static int test_reg_allocate_flow_fail(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static int test_reg_flow(void) { - int ret = 0; + int rc = 0; - ret |= test_reg_create_flow(); + rc |= test_reg_create_flow(); + rc |= test_reg_allocate_flow_timeout(); + rc |= test_reg_accept_flow_success(); + rc |= test_reg_accept_flow_success_no_crypt(); + rc |= test_reg_allocate_flow_fail(); - 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; + return rc; } static int test_reg_create_ipcp(void) @@ -513,7 +485,7 @@ static int test_reg_create_ipcp(void) struct ipcp_info info = { .name = TEST_IPCP, .pid = TEST_PID, - .state = IPCP_BOOT /* set by spawn_ipcp */ + .state = IPCP_INIT /* set by spawn_ipcp */ }; TEST_START(); @@ -552,10 +524,130 @@ static int test_reg_create_ipcp(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; + fail: + REG_TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_reg_list_ipcps(void) +{ + ipcp_list_msg_t ** ipcps; + int i; + ssize_t len; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + for (i = 0; i < 10; i++) { + struct ipcp_info info = { + .pid = TEST_PID + i, + .state = IPCP_INIT /* set by spawn_ipcp */ + }; + + sprintf(info.name, "%s%d", TEST_IPCP, i); + + if (reg_create_ipcp(&info) < 0) { + printf("Failed to create ipcp %d.\n", i); + goto fail; + } + } + + len = reg_list_ipcps(&ipcps); + if (len < 0) { + printf("Failed to list ipcps.\n"); + goto fail; + } + + if (len != 10) { + printf("Failed to list all ipcps.\n"); + goto fail; + } + + while (len-- > 0) + ipcp_list_msg__free_unpacked(ipcps[len], NULL); + free(ipcps); + + for (i = 0; i < 10; i++) + reg_destroy_proc(TEST_PID + i); + + reg_fini(); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; +} + +static int test_insert_ipcps(void) +{ + ipcp_list_msg_t ** ipcps; + struct ipcp_info info; + size_t i; + size_t len; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + for (i = 0; i < 100; i++) { + sprintf(info.name, "%s-%zd", TEST_IPCP, i); + info.pid = TEST_PID + rand() % 10000; + info.type = rand() % IPCP_INVALID; + info.state = IPCP_INIT; /* set by spawn_ipcp */ + + if (reg_create_ipcp(&info) < 0) { + printf("Failed to create ipcp %s.\n", info.name); + goto fail; + } + } + + len = reg_list_ipcps(&ipcps); + if (len != 100) { + printf("Failed to list all ipcps.\n"); + goto fail; + } + + for (i = 1; i < len; i++) { + if (ipcps[i]->type < ipcps[i - 1]->type) { + printf("IPCPS not sorted by type.\n"); + goto fail; + } + + if (ipcps[i]->type != ipcps[i - 1]->type) + continue; + + /* allow occasional duplicate PID in test */ + if (ipcps[i]->pid < ipcps[i - 1]->pid) { + printf("IPCPS not sorted by pid.\n"); + goto fail; + } + } + + while (len-- > 0) + ipcp_list_msg__free_unpacked(ipcps[len], NULL); + free(ipcps); + + reg_clear(); + + reg_fini(); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; +fail: + REG_TEST_FAIL(); + return TEST_RC_FAIL; } static int test_set_layer(void) @@ -564,7 +656,7 @@ static int test_set_layer(void) struct ipcp_info info = { .name = TEST_IPCP, .pid = TEST_PID, - .state = IPCP_BOOT /* set by spawn_ipcp */ + .state = IPCP_INIT /* set by spawn_ipcp */ }; struct layer_info layer = { .name = TEST_LAYER, @@ -588,8 +680,9 @@ static int test_set_layer(void) } ipcp = __reg_get_ipcp(info.pid); - ipcp->info.state = IPCP_OPERATIONAL; - info.state = IPCP_ENROLLED; + + ipcp->info.state = IPCP_BOOT; + info.state = IPCP_BOOT; reg_set_layer_for_ipcp(&info, &layer); @@ -614,21 +707,22 @@ static int test_set_layer(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static int test_reg_ipcp(void) { - int ret = 0; - - ret |= test_reg_create_ipcp(); + int rc = 0; - ret |= test_set_layer(); + rc |= test_reg_create_ipcp(); + rc |= test_reg_list_ipcps(); + rc |= test_insert_ipcps(); + rc |= test_set_layer(); - return ret; + return rc; } static int test_reg_create_name(void) @@ -674,19 +768,77 @@ static int test_reg_create_name(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; +} + +static int test_reg_list_names(void) +{ + name_info_msg_t ** names; + int i; + ssize_t len; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + for (i = 0; i < 10; i++) { + struct name_info info = { + .pol_lb = LB_RR + }; + + sprintf(info.name, "%s%d", TEST_NAME, i); + + if (reg_create_name(&info) < 0) { + printf("Failed to create name %d.\n", i); + goto fail; + } + } + + len = reg_list_names(&names); + if (len < 0) { + printf("Failed to list names.\n"); + goto fail; + } + + if (len != 10) { + printf("Failed to list all names.\n"); + goto fail; + } + + for (i = 0; i < len; i++) + name_info_msg__free_unpacked(names[i], NULL); + free(names); + + for (i = 0; i < 10; i++) { + char name[NAME_MAX]; + sprintf(name, "%s%d", TEST_NAME, i); + reg_destroy_name(name); + } + + reg_fini(); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail: + REG_TEST_FAIL(); + return TEST_RC_FAIL; } static int test_reg_name(void) { - int ret = 0; + int rc = 0; - ret |= test_reg_create_name(); + rc |= test_reg_create_name(); + rc |= test_reg_list_names(); - return ret; + return rc; } static int test_reg_create_proc(void) @@ -732,19 +884,19 @@ static int test_reg_create_proc(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static int test_reg_proc(void) { - int ret = 0; + int rc = 0; - ret |= test_reg_create_proc(); + rc |= test_reg_create_proc(); - return ret; + return rc; } static int test_reg_spawned(void) @@ -785,10 +937,10 @@ static int test_reg_spawned(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static int test_reg_create_prog(void) @@ -833,19 +985,19 @@ static int test_reg_create_prog(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static int test_reg_prog(void) { - int ret = 0; + int rc = 0; - ret |= test_reg_create_prog(); + rc |= test_reg_create_prog(); - return ret; + return rc; } static int test_bind_proc(void) @@ -900,10 +1052,10 @@ static int test_bind_proc(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static int test_bind_prog(void) @@ -989,10 +1141,10 @@ static int test_bind_prog(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static int test_inherit_prog(void) @@ -1060,10 +1212,10 @@ static int test_inherit_prog(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static int test_wait_accepting_timeout(void) @@ -1071,7 +1223,6 @@ 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 @@ -1089,12 +1240,10 @@ static int test_wait_accepting_timeout(void) 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); + flow_id = reg_wait_flow_accepting(ninfo.name, &abstime); if (flow_id != -ETIMEDOUT) { printf("Wait accept did not time out: %d.\n", flow_id); goto fail; @@ -1106,10 +1255,10 @@ static int test_wait_accepting_timeout(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static int test_wait_accepting_fail_name(void) @@ -1117,7 +1266,6 @@ 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(); @@ -1128,11 +1276,10 @@ static int test_wait_accepting_fail_name(void) 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); + flow_id = reg_wait_flow_accepting(TEST_NAME, &abstime); if (flow_id != -ENAME) { - printf("Wait accept did not fail on name: %d.\n", flow_id); + printf("Wait accept did not fail: %d.\n", flow_id); goto fail; } @@ -1140,17 +1287,17 @@ static int test_wait_accepting_fail_name(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static void * test_call_flow_accept(void * o) { struct timespec abstime; struct timespec timeo = TIMESPEC_INIT_MS(1); - buffer_t pbuf = {NULL, 0}; + buffer_t pbuf = BUF_INIT; struct proc_info pinfo = { .pid = TEST_PID, @@ -1182,7 +1329,7 @@ static void * test_call_flow_accept(void * o) clock_gettime(PTHREAD_COND_CLOCK, &abstime); ts_add(&abstime, &timeo, &abstime); - reg_prepare_flow_accept(&info, &pbuf); + reg_prepare_flow_accept(&info); if (reg_wait_flow_accepted(&info, &pbuf, &abstime) != -ETIMEDOUT) { printf("Wait allocated did not timeout.\n"); @@ -1201,14 +1348,14 @@ 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]; + int flow_id; struct name_info ninfo = { .name = TEST_NAME, .pol_lb = LB_RR }; + TEST_START(); if (reg_init()) { @@ -1226,9 +1373,7 @@ static int test_wait_accepting_success(void) 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); + flow_id = reg_wait_flow_accepting(ninfo.name, &abstime); if (flow_id < 0) { printf("Wait accept did not return a flow id: %d.", flow_id); goto fail; @@ -1242,23 +1387,21 @@ static int test_wait_accepting_success(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static int test_wait_accepting(void) { - int ret = 0; - - ret |= test_wait_accepting_timeout(); + int rc = 0; - ret |= test_wait_accepting_fail_name(); + rc |= test_wait_accepting_timeout(); + rc |= test_wait_accepting_fail_name(); + rc |= test_wait_accepting_success(); - ret |= test_wait_accepting_success(); - - return ret; + return rc; } static int test_wait_ipcp_boot_timeout(void) @@ -1268,7 +1411,7 @@ static int test_wait_ipcp_boot_timeout(void) struct ipcp_info info = { .name = TEST_IPCP, .pid = TEST_PID, - .state = IPCP_BOOT /* set by spawn_ipcp */ + .state = IPCP_INIT /* set by spawn_ipcp */ }; TEST_START(); @@ -1300,10 +1443,10 @@ static int test_wait_ipcp_boot_timeout(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static void * test_ipcp_respond(void * o) @@ -1323,12 +1466,12 @@ static int test_wait_ipcp_boot_fail(void) struct ipcp_info info = { .name = TEST_IPCP, .pid = TEST_PID, - .state = IPCP_BOOT /* set by spawn_ipcp */ + .state = IPCP_INIT /* set by spawn_ipcp */ }; struct ipcp_info resp_info = { .name = TEST_IPCP, .pid = TEST_PID, - .state = IPCP_NULL + .state = IPCP_INIT }; TEST_START(); @@ -1348,7 +1491,7 @@ static int test_wait_ipcp_boot_fail(void) clock_gettime(PTHREAD_COND_CLOCK, &abstime); ts_add(&abstime, &timeo, &abstime); - info.state = IPCP_BOOT; + info.state = IPCP_INIT; if (reg_wait_ipcp_boot(&info, &abstime) == 0) { printf("IPCP boot reported success.\n"); @@ -1371,10 +1514,10 @@ static int test_wait_ipcp_boot_fail(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static int test_wait_ipcp_boot_success(void) @@ -1385,7 +1528,7 @@ static int test_wait_ipcp_boot_success(void) struct ipcp_info info = { .name = TEST_IPCP, .pid = TEST_PID, - .state = IPCP_BOOT /* set by spawn_ipcp */ + .state = IPCP_INIT /* set by spawn_ipcp */ }; struct ipcp_info resp_info = { .name = TEST_IPCP, @@ -1410,7 +1553,7 @@ static int test_wait_ipcp_boot_success(void) clock_gettime(PTHREAD_COND_CLOCK, &abstime); ts_add(&abstime, &timeo, &abstime); - info.state = IPCP_BOOT; + info.state = IPCP_INIT; if (reg_wait_ipcp_boot(&info, &abstime) < 0) { printf("IPCP boot failed.\n"); @@ -1433,23 +1576,21 @@ static int test_wait_ipcp_boot_success(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static int test_wait_ipcp_boot(void) { - int ret = 0; - - ret |= test_wait_ipcp_boot_timeout(); - - ret |= test_wait_ipcp_boot_fail(); + int rc = 0; - ret |= test_wait_ipcp_boot_success(); + rc |= test_wait_ipcp_boot_timeout(); + rc |= test_wait_ipcp_boot_fail(); + rc |= test_wait_ipcp_boot_success(); - return ret; + return rc; } static int test_wait_proc_timeout(void) @@ -1477,10 +1618,10 @@ static int test_wait_proc_timeout(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static void * test_proc(void * o) @@ -1527,57 +1668,43 @@ static int test_wait_proc_success(void) TEST_SUCCESS(); - return 0; + return TEST_RC_SUCCESS; fail: REG_TEST_FAIL(); - return -1; + return TEST_RC_FAIL; } static int test_wait_proc(void) { - int ret = 0; + int rc = 0; - ret |= test_wait_proc_timeout(); + rc |= test_wait_proc_timeout(); + rc |= test_wait_proc_success(); - ret |= test_wait_proc_success(); - - return ret; + return rc; } - int reg_test(int argc, char ** argv) { - int ret = 0; + int rc = 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; + rc |= test_reg_init(); + rc |= test_reg_flow(); + rc |= test_reg_ipcp(); + rc |= test_reg_name(); + rc |= test_reg_proc(); + rc |= test_reg_prog(); + rc |= test_reg_spawned(); + rc |= test_bind_proc(); + rc |= test_bind_prog(); + rc |= test_inherit_prog(); + rc |= test_wait_accepting(); + rc |= test_wait_ipcp_boot(); + rc |= test_wait_proc(); + + return rc; } diff --git a/src/irmd/tests/CMakeLists.txt b/src/irmd/tests/CMakeLists.txt index e005d194..e860acce 100644 --- a/src/irmd/tests/CMakeLists.txt +++ b/src/irmd/tests/CMakeLists.txt @@ -3,6 +3,8 @@ get_filename_component(src_folder "${tmp}" NAME) create_test_sourcelist(${src_folder}_tests test_suite.c # Add new tests here + irm_test.c + oap_test.c ) add_executable(${src_folder}_test EXCLUDE_FROM_ALL ${${src_folder}_tests}) @@ -11,9 +13,15 @@ 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(irmd/${test_name} ${C_TEST_PATH}/${src_folder}_test ${test_name}) endforeach(test) + +set_property(TEST irmd/oap_test PROPERTY SKIP_RETURN_CODE 1) diff --git a/src/irmd/tests/irm_test.c b/src/irmd/tests/irm_test.c new file mode 100644 index 00000000..d440289c --- /dev/null +++ b/src/irmd/tests/irm_test.c @@ -0,0 +1,33 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * Unit tests of IRMd 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/. + */ + + + +int irm_test(int argc, + char **argv) +{ + int ret = 0; + + (void) argc; + (void) argv; + + return ret; +} diff --git a/src/irmd/tests/oap_test.c b/src/irmd/tests/oap_test.c new file mode 100644 index 00000000..4e7fb2d1 --- /dev/null +++ b/src/irmd/tests/oap_test.c @@ -0,0 +1,285 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * Unit tests of Ouroboros flow allocation protocol + * 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" + +#include "oap.c" + +#include <ouroboros/random.h> +#include <ouroboros/test.h> + +static const char * pkp_str = \ +"-----BEGIN EC PRIVATE KEY-----\n" +"MHcCAQEEIC13y+5jdKe80HBJD7WITpQamcn3rrkTX1r0v+JwSk4NoAoGCCqGSM49\n" +"AwEHoUQDQgAEcC0yLAfUtufH8cdLybrdWPc6U+xRuhDhqqrEcBO5+eob2xyqEaNk\n" +"nIV/86724zPptGRahWz0rzW2PvNppJdNBg==\n" +"-----END EC PRIVATE KEY-----\n"; + +/* Valid signed server certificate for server-2.unittest.o7s */ +static const char * crt_str = \ +"-----BEGIN CERTIFICATE-----\n" +"MIIDgjCCAyigAwIBAgICEAIwCgYIKoZIzj0EAwIwWzELMAkGA1UEBhMCQkUxDDAK\n" +"BgNVBAgMA09WTDEMMAoGA1UECgwDbzdzMRUwEwYDVQQLDAx1bml0dGVzdC5vN3Mx\n" +"GTAXBgNVBAMMEGltMi51bml0dGVzdC5vN3MwHhcNMjUwNzA0MTMxODI5WhcNMzUw\n" +"NzAyMTMxODI5WjBwMQswCQYDVQQGEwJCRTEMMAoGA1UECAwDT1ZMMQ4wDAYDVQQH\n" +"DAVHaGVudDEMMAoGA1UECgwDbzdzMRUwEwYDVQQLDAx1bml0dGVzdC5vN3MxHjAc\n" +"BgNVBAMMFXNlcnZlci0yLnVuaXR0ZXN0Lm83czBZMBMGByqGSM49AgEGCCqGSM49\n" +"AwEHA0IABHAtMiwH1Lbnx/HHS8m63Vj3OlPsUboQ4aqqxHATufnqG9scqhGjZJyF\n" +"f/Ou9uMz6bRkWoVs9K81tj7zaaSXTQajggHFMIIBwTAJBgNVHRMEAjAAMBEGCWCG\n" +"SAGG+EIBAQQEAwIGQDA6BglghkgBhvhCAQ0ELRYrR3JpbGxlZCBDaGVlc2UgR2Vu\n" +"ZXJhdGVkIFNlcnZlciBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUTt3xHTwE9amoglxh\n" +"cEMqWv+PpDMwgb8GA1UdIwSBtzCBtIAUFfeZRx8QWWKQr7Aw8zjDu2shvcShgZek\n" +"gZQwgZExCzAJBgNVBAYTAkJFMQwwCgYDVQQIDANPVkwxDjAMBgNVBAcMBUdoZW50\n" +"MQwwCgYDVQQKDANvN3MxFTATBgNVBAsMDHVuaXR0ZXN0Lm83czEZMBcGA1UEAwwQ\n" +"Y2EyLnVuaXR0ZXN0Lm83czEkMCIGCSqGSIb3DQEJARYVZHVtbXlAb3Vyb2Jvcm9z\n" +"LnJvY2tzggIQAjAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEw\n" +"EQYDVR0fBAowCDAGoASgAoYAMCoGCCsGAQUFBwEBBB4wHDAMBggrBgEFBQcwAoYA\n" +"MAwGCCsGAQUFBzABhgAwIAYDVR0RBBkwF4IVc2VydmVyLTEudW5pdHRlc3Qubzdz\n" +"MAoGCCqGSM49BAMCA0gAMEUCIQDHuDb62w/Uah4nKwUFoJVkr4rgdNGh2Rn3SWaK\n" +"0FV/gAIgOLKorTwSgrTFdyOUkuPOhRs8BEMpah+dp8UTO8AnLvY=\n" +"-----END CERTIFICATE-----\n"; + +static int test_oap_hdr_init_fini(void) +{ + struct oap_hdr oap_hdr; + struct timespec now; + uint64_t stamp; + buffer_t ephkey = BUF_INIT; + buffer_t data = BUF_INIT; + uint8_t buf[OAP_ID_SIZE]; + buffer_t id; + void * pkp = NULL; + void * pubcrt = NULL; + + TEST_START(); + + random_buffer(buf, OAP_ID_SIZE); + id.data = buf; + id.len = OAP_ID_SIZE; + + clock_gettime(CLOCK_REALTIME, &now); + stamp = TS_TO_UINT64(now); + + if (oap_hdr_init(id, pkp, pubcrt, ephkey, data, &oap_hdr) < 0) { + printf("Failed to init OAP request header.\n"); + goto fail_req_hdr; + } + + if (oap_hdr.hdr.len != OAP_HDR_MIN_SIZE) { + printf("OAP request header wrong: %zu < %zu.\n", + oap_hdr.hdr.len, OAP_HDR_MIN_SIZE); + goto fail_req_hdr_chk; + } + + if (oap_hdr.id.len != OAP_ID_SIZE) { + printf("OAP request header ID wrong size: %zu != %zu.\n", + oap_hdr.id.len, (size_t) OAP_ID_SIZE); + goto fail_req_hdr_chk; + } + + if (memcmp(oap_hdr.id.data, id.data, OAP_ID_SIZE) != 0) { + printf("OAP request header ID mismatch.\n"); + goto fail_req_hdr_chk; + } + + if (oap_hdr.timestamp < stamp) { + printf("OAP request header timestamp is too old.\n"); + goto fail_req_hdr_chk; + } + + if (oap_hdr.timestamp > stamp + 1 * BILLION) { + printf("OAP request header timestamp is too new.\n"); + goto fail_req_hdr_chk; + } + + oap_hdr_fini(&oap_hdr); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + + fail_req_hdr_chk: + oap_hdr_fini(&oap_hdr); + fail_req_hdr: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_oap_hdr_init_fini_data(void) + +{ + struct oap_hdr oap_hdr; + buffer_t data; + buffer_t ephkey = BUF_INIT; + uint8_t buf[OAP_ID_SIZE]; + buffer_t id; + void * pkp = NULL; + void * pubcrt = NULL; + + TEST_START(); + + random_buffer(buf, OAP_ID_SIZE); + id.data = buf; + id.len = OAP_ID_SIZE; + + data.len = 100; + data.data = malloc(data.len); + if (data.data == NULL) { + printf("Failed to allocate data buffer.\n"); + goto fail_data; + } + + random_buffer(data.data, data.len); + + if (oap_hdr_init(id, pkp, pubcrt, ephkey, data, &oap_hdr) < 0) { + printf("Failed to create OAP request header.\n"); + goto fail_req_hdr; + } + + if (oap_hdr.hdr.len != OAP_HDR_MIN_SIZE + data.len) { + printf("OAP request header wrong: %zu < %zu.\n", + oap_hdr.hdr.len, OAP_HDR_MIN_SIZE + data.len); + goto fail_req_hdr_sz; + } + + freebuf(data); + oap_hdr_fini(&oap_hdr); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + + fail_req_hdr_sz: + oap_hdr_fini(&oap_hdr); + fail_req_hdr: + freebuf(data); + fail_data: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_oap_hdr_init_fini_signed(void) +{ + struct oap_hdr oap_hdr; + buffer_t ephkey = BUF_INIT; + buffer_t data = BUF_INIT; + buffer_t sign; + buffer_t id; + uint8_t buf[OAP_ID_SIZE]; + void * pkp; + void * pk; + void * pubcrt; + void * pubcrt2; + + TEST_START(); + + random_buffer(buf, OAP_ID_SIZE); + id.data = buf; + id.len = OAP_ID_SIZE; + + if (crypt_load_privkey_str(pkp_str, &pkp) < 0) { + printf("Failed to load private key.\n"); + goto fail_pkp; + } + + if (crypt_load_crt_str(crt_str, &pubcrt) < 0) { + printf("Failed to load public certificate.\n"); + goto fail_pubcrt; + } + + if (oap_hdr_init(id, pkp, pubcrt, ephkey, data, &oap_hdr) < 0) { + printf("Failed to create OAP request header.\n"); + goto fail_req_hdr; + } + + if (oap_hdr.crt.len == 0) { + printf("OAP request header has no public certificate.\n"); + goto fail_req_hdr; + } + + if (oap_hdr.sig.len == 0) { + printf("OAP request header no signature.\n"); + goto fail_req_hdr; + } + + if (crypt_load_crt_der(oap_hdr.crt, &pubcrt2) < 0) { + printf("Failed to load public certificate from DER.\n"); + goto fail_crt_der; + } + + if (crypt_get_pubkey_crt(pubcrt2, &pk) < 0) { + printf("Failed to get public key from certificate.\n"); + goto fail_crt_pk; + } + + sign = oap_hdr.hdr; + sign.len -= (oap_hdr.sig.len + sizeof(uint16_t)); + + if (auth_verify_sig(pk, sign, oap_hdr.sig) < 0) { + printf("Failed to verify OAP request header signature.\n"); + goto fail_check_sig; + } + + oap_hdr_fini(&oap_hdr); + + crypt_free_crt(pubcrt2); + crypt_free_crt(pubcrt); + crypt_free_key(pk); + crypt_free_key(pkp); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + + fail_check_sig: + crypt_free_key(pk); + fail_crt_pk: + crypt_free_crt(pubcrt2); + fail_crt_der: + oap_hdr_fini(&oap_hdr); + fail_req_hdr: + crypt_free_crt(pubcrt); + fail_pubcrt: + crypt_free_key(pkp); + fail_pkp: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +int oap_test(int argc, + char **argv) +{ + int ret = 0; + + (void) argc; + (void) argv; + + ret |= test_oap_hdr_init_fini(); + ret |= test_oap_hdr_init_fini_data(); +#ifdef HAVE_OPENSSL + ret |= test_oap_hdr_init_fini_signed(); +#else + (void) test_oap_hdr_init_fini_signed; + + ret = TEST_RC_SKIP; +#endif + return ret; +} |