From 5274cb3ce09c40cccd29ec771ad49a2069aa37c4 Mon Sep 17 00:00:00 2001 From: Dimitri Staessens Date: Sun, 17 Aug 2025 12:09:12 +0200 Subject: ipcpd: Add ipcpd over UDP/IPv6 This adds an IPCP that runs over UDP/IPv6. It's structured like the eth-dix and eth-llc in that it builds two separate binaries: ipcpd-udp4 and ipcpd-udp6. The IRM CLI is backwards compatible in that type 'udp' will resolve to type 'udp4'. Signed-off-by: Dimitri Staessens Signed-off-by: Sander Vrijders --- src/ipcpd/eth/CMakeLists.txt | 8 +- src/ipcpd/eth/eth.c | 17 +- src/ipcpd/ipcp.c | 13 +- src/ipcpd/shim-data.c | 34 +- src/ipcpd/shim-data.h | 19 +- src/ipcpd/udp/CMakeLists.txt | 34 +- src/ipcpd/udp/main.c | 1148 ------------------------------------ src/ipcpd/udp/udp.c | 1197 ++++++++++++++++++++++++++++++++++++++ src/ipcpd/udp/udp4.c | 42 ++ src/ipcpd/udp/udp6.c | 42 ++ src/ipcpd/unicast/CMakeLists.txt | 5 +- 11 files changed, 1357 insertions(+), 1202 deletions(-) delete mode 100644 src/ipcpd/udp/main.c create mode 100644 src/ipcpd/udp/udp.c create mode 100644 src/ipcpd/udp/udp4.c create mode 100644 src/ipcpd/udp/udp6.c (limited to 'src/ipcpd') diff --git a/src/ipcpd/eth/CMakeLists.txt b/src/ipcpd/eth/CMakeLists.txt index 17ae74fc..44299a59 100644 --- a/src/ipcpd/eth/CMakeLists.txt +++ b/src/ipcpd/eth/CMakeLists.txt @@ -90,13 +90,13 @@ if (HAVE_ETH) set(ETH_LLC_SOURCES # Add source files here - ${CMAKE_CURRENT_SOURCE_DIR}/llc.c - ) + llc.c + ) set(ETH_DIX_SOURCES # Add source files here - ${CMAKE_CURRENT_SOURCE_DIR}/dix.c - ) + dix.c + ) set(IPCP_ETH_LLC_TARGET ipcpd-eth-llc CACHE INTERNAL "") set(IPCP_ETH_DIX_TARGET ipcpd-eth-dix CACHE INTERNAL "") diff --git a/src/ipcpd/eth/eth.c b/src/ipcpd/eth/eth.c index 1028ee03..d39b478c 100644 --- a/src/ipcpd/eth/eth.c +++ b/src/ipcpd/eth/eth.c @@ -113,7 +113,6 @@ #define ETH_MTU_MAX 1500 #endif /* BUILD_ETH_DIX */ -#define MAC_SIZE 6 #define ETH_TYPE_LENGTH_SIZE sizeof(uint16_t) #define ETH_HEADER_SIZE (2 * MAC_SIZE + ETH_TYPE_LENGTH_SIZE) @@ -694,11 +693,11 @@ static int eth_ipcp_name_query_req(const uint8_t * hash, static int eth_ipcp_name_query_reply(const uint8_t * hash, uint8_t * r_addr) { - uint64_t address = 0; + struct addr addr; - memcpy(&address, r_addr, MAC_SIZE); + memcpy(&addr.mac, r_addr, MAC_SIZE); - shim_data_dir_add_entry(eth_data.shim_data, hash, address); + shim_data_dir_add_entry(eth_data.shim_data, hash, addr); shim_data_dir_query_respond(eth_data.shim_data, hash); @@ -1699,7 +1698,7 @@ static int eth_ipcp_flow_alloc(int fd, uint8_t ssap = 0; #endif uint8_t r_addr[MAC_SIZE]; - uint64_t addr = 0; + struct addr addr; assert(hash); @@ -1708,10 +1707,12 @@ static int eth_ipcp_flow_alloc(int fd, HASH_VAL32(hash)); return -1; } + addr = shim_data_dir_get_addr(eth_data.shim_data, hash); + memcpy(r_addr, &addr.mac, MAC_SIZE); - pthread_rwlock_wrlock(ð_data.flows_lock); #ifdef BUILD_ETH_LLC + pthread_rwlock_wrlock(ð_data.flows_lock); ssap = bmp_allocate(eth_data.saps); if (!bmp_is_id_valid(eth_data.saps, ssap)) { pthread_rwlock_unlock(ð_data.flows_lock); @@ -1721,10 +1722,8 @@ static int eth_ipcp_flow_alloc(int fd, eth_data.fd_to_ef[fd].sap = ssap; eth_data.ef_to_fd[ssap] = fd; -#endif pthread_rwlock_unlock(ð_data.flows_lock); - - memcpy(r_addr, &addr, MAC_SIZE); +#endif if (eth_ipcp_alloc(r_addr, #if defined(BUILD_ETH_DIX) diff --git a/src/ipcpd/ipcp.c b/src/ipcpd/ipcp.c index a47b6226..62995727 100644 --- a/src/ipcpd/ipcp.c +++ b/src/ipcpd/ipcp.c @@ -74,7 +74,8 @@ static char * ipcp_type_str[] = { "broadcast", "eth-llc", "eth-dix", - "udp" + "udp4", + "udp6" }; static char * dir_hash_str[] = { @@ -228,8 +229,10 @@ static int ipcp_rib_read(const char * path, strcpy(buf, "eth-llc\n"); else if (ipcpd.type == IPCP_ETH_DIX) strcpy(buf, "eth-dix\n"); - else if (ipcpd.type == IPCP_UDP) - strcpy(buf, "udp\n"); + else if (ipcpd.type == IPCP_UDP4) + strcpy(buf, "udp4\n"); + else if (ipcpd.type == IPCP_UDP6) + strcpy(buf, "udp6\n"); else strcpy(buf, "bug\n"); } @@ -455,7 +458,9 @@ static void do_bootstrap(ipcp_config_msg_t * conf_msg, conf = ipcp_config_msg_to_s(conf_msg); switch(conf.type) { /* FIXED algorithms */ - case IPCP_UDP: + case IPCP_UDP4: + /* FALLTHRU */ + case IPCP_UDP6: conf.layer_info.dir_hash_algo = (enum pol_dir_hash) HASH_MD5; break; case IPCP_BROADCAST: diff --git a/src/ipcpd/shim-data.c b/src/ipcpd/shim-data.c index 1fac63ac..8801213a 100644 --- a/src/ipcpd/shim-data.c +++ b/src/ipcpd/shim-data.c @@ -51,7 +51,7 @@ struct reg_entry { struct dir_entry { struct list_head list; uint8_t * hash; - uint64_t addr; + struct addr addr; }; static void destroy_dir_query(struct dir_query * query) @@ -108,14 +108,12 @@ static void reg_entry_destroy(struct reg_entry * entry) { assert(entry); - if (entry->hash != NULL) - free(entry->hash); - + free(entry->hash); free(entry); } -static struct dir_entry * dir_entry_create(uint8_t * hash, - uint64_t addr) +static struct dir_entry * dir_entry_create(uint8_t * hash, + struct addr addr) { struct dir_entry * entry = malloc(sizeof(*entry)); if (entry == NULL) @@ -133,9 +131,7 @@ static void dir_entry_destroy(struct dir_entry * entry) { assert(entry); - if (entry->hash != NULL) - free(entry->hash); - + free(entry->hash); free(entry); } @@ -258,13 +254,15 @@ static struct reg_entry * find_reg_entry_by_hash(struct shim_data * data, static struct dir_entry * find_dir_entry(struct shim_data * data, const uint8_t * hash, - uint64_t addr) + struct addr addr) { struct list_head * h; list_for_each(h, &data->directory) { struct dir_entry * e = list_entry(h, struct dir_entry, list); - if (e->addr == addr && - !memcmp(e->hash, hash, ipcp_dir_hash_len())) + if (memcmp(&e->addr, &addr, sizeof(addr)) != 0) + continue; + + if (memcmp(e->hash, hash, ipcp_dir_hash_len()) == 0) return e; } @@ -364,7 +362,7 @@ bool shim_data_reg_has(struct shim_data * data, int shim_data_dir_add_entry(struct shim_data * data, const uint8_t * hash, - uint64_t addr) + struct addr addr) { struct dir_entry * entry; uint8_t * entry_hash; @@ -400,7 +398,7 @@ int shim_data_dir_add_entry(struct shim_data * data, int shim_data_dir_del_entry(struct shim_data * data, const uint8_t * hash, - uint64_t addr) + struct addr addr) { struct dir_entry * e; if (data == NULL) @@ -437,11 +435,11 @@ bool shim_data_dir_has(struct shim_data * data, return ret; } -uint64_t shim_data_dir_get_addr(struct shim_data * data, - const uint8_t * hash) +struct addr shim_data_dir_get_addr(struct shim_data * data, + const uint8_t * hash) { struct dir_entry * entry; - uint64_t addr; + struct addr addr = {0}; pthread_rwlock_rdlock(&data->dir_lock); @@ -449,7 +447,7 @@ uint64_t shim_data_dir_get_addr(struct shim_data * data, if (entry == NULL) { pthread_rwlock_unlock(&data->dir_lock); log_warn("No address for " HASH_FMT32 ".", HASH_VAL32(hash)); - return 0; /* undefined behaviour, 0 may be a valid address */ + return addr; /* undefined behaviour, 0 may be a valid address */ } addr = entry->addr; diff --git a/src/ipcpd/shim-data.h b/src/ipcpd/shim-data.h index 372b4ea7..ea4ce413 100644 --- a/src/ipcpd/shim-data.h +++ b/src/ipcpd/shim-data.h @@ -25,9 +25,12 @@ #include -#include #include #include +#include +#include + +#define MAC_SIZE 6 enum dir_query_state { QUERY_INIT = 0, @@ -46,6 +49,14 @@ struct dir_query { pthread_cond_t cond; }; +struct addr { + union { + uint8_t mac[MAC_SIZE]; + struct in_addr ip4; + struct in6_addr ip6; + }; +}; + struct shim_data { struct list_head registry; pthread_rwlock_t reg_lock; @@ -72,16 +83,16 @@ bool shim_data_reg_has(struct shim_data * data, int shim_data_dir_add_entry(struct shim_data * data, const uint8_t * hash, - uint64_t addr); + struct addr addr); int shim_data_dir_del_entry(struct shim_data * data, const uint8_t * hash, - uint64_t addr); + struct addr addr); bool shim_data_dir_has(struct shim_data * data, const uint8_t * hash); -uint64_t shim_data_dir_get_addr(struct shim_data * data, +struct addr shim_data_dir_get_addr(struct shim_data * data, const uint8_t * hash); struct dir_query * shim_data_dir_query_create(struct shim_data * data, diff --git a/src/ipcpd/udp/CMakeLists.txt b/src/ipcpd/udp/CMakeLists.txt index 5abf5a00..27e32094 100644 --- a/src/ipcpd/udp/CMakeLists.txt +++ b/src/ipcpd/udp/CMakeLists.txt @@ -12,16 +12,25 @@ include_directories(${CURRENT_BINARY_PARENT_DIR}) include_directories(${CMAKE_SOURCE_DIR}/include) include_directories(${CMAKE_BINARY_DIR}/include) -set(IPCP_UDP_TARGET ipcpd-udp CACHE INTERNAL "") +set(IPCP_UDP4_TARGET ipcpd-udp4 CACHE INTERNAL "") +set(IPCP_UDP6_TARGET ipcpd-udp6 CACHE INTERNAL "") -set(UDP_SOURCES +set(UDP4_SOURCES # Add source files here - ${CMAKE_CURRENT_SOURCE_DIR}/main.c - ) + udp4.c +) -add_executable(ipcpd-udp ${UDP_SOURCES} ${IPCP_SOURCES}) +set(UDP6_SOURCES + # Add source files here + udp6.c +) + +add_executable(ipcpd-udp4 ${UDP4_SOURCES} ${IPCP_SOURCES}) +target_link_libraries(ipcpd-udp4 LINK_PUBLIC ouroboros-dev) + +add_executable(ipcpd-udp6 ${UDP6_SOURCES} ${IPCP_SOURCES}) +target_link_libraries(ipcpd-udp6 LINK_PUBLIC ouroboros-dev) -target_link_libraries(ipcpd-udp LINK_PUBLIC ouroboros-dev) # Find the nsupdate executable find_program(NSUPDATE_EXECUTABLE @@ -55,15 +64,18 @@ else () endif () set(IPCP_UDP_RD_THR 3 CACHE STRING - "Number of reader threads in UDP IPCP") + "Number of reader threads in UDP IPCPs") set(IPCP_UDP_WR_THR 3 CACHE STRING - "Number of writer threads in UDP IPCP") + "Number of writer threads in UDP IPCPs") set(IPCP_UDP_MPL 5000 CACHE STRING - "Default maximum packet lifetime for the UDP IPCP, in ms") + "Default maximum packet lifetime for the UDP IPCPs, in ms") include(AddCompileFlags) if (CMAKE_BUILD_TYPE MATCHES "Debug*") - add_compile_flags(ipcpd-udp -DCONFIG_OUROBOROS_DEBUG) + add_compile_flags(ipcpd-udp4 -DCONFIG_OUROBOROS_DEBUG) + add_compile_flags(ipcpd-udp6 -DCONFIG_OUROBOROS_DEBUG) endif () -install(TARGETS ipcpd-udp RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}) +install(TARGETS ipcpd-udp4 RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}) +install(TARGETS ipcpd-udp6 RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}) + diff --git a/src/ipcpd/udp/main.c b/src/ipcpd/udp/main.c deleted file mode 100644 index 5f770a61..00000000 --- a/src/ipcpd/udp/main.c +++ /dev/null @@ -1,1148 +0,0 @@ -/* - * Ouroboros - Copyright (C) 2016 - 2024 - * - * IPC process over UDP - * - * Dimitri Staessens - * Sander Vrijders - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., http://www.fsf.org/about/contact/. - */ - -#if defined(__linux__) || defined(__CYGWIN__) -#define _DEFAULT_SOURCE -#else -#define _POSIX_C_SOURCE 200112L -#endif - -#include "config.h" - -#define OUROBOROS_PREFIX "ipcpd/udp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ipcp.h" -#include "shim-data.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define FLOW_REQ 1 -#define FLOW_REPLY 2 - -#define THIS_TYPE IPCP_UDP -#define IPCP_UDP_MAX_PACKET_SIZE 8980 -#define OUR_HEADER_LEN sizeof(uint32_t) /* adds eid */ - -#define IPCP_UDP_BUF_SIZE 8980 -#define IPCP_UDP_MSG_SIZE 8980 -#define DNS_TTL 86400 - -#define SADDR ((struct sockaddr *) &udp_data.s_saddr) -#define SADDR_SIZE (sizeof(udp_data.s_saddr)) -#define LOCAL_IP (udp_data.s_saddr.sin_addr.s_addr) - -#define MGMT_EID 0 -#define MGMT_FRAME_SIZE (sizeof(struct mgmt_msg)) -#define MGMT_FRAME_BUF_SIZE 2048 - -#ifdef __linux__ -#define SENDTO_FLAGS MSG_CONFIRM -#else -#define SENDTO_FLAGS 0 -#endif - -/* Keep order for alignment. */ -struct mgmt_msg { - uint32_t eid; - uint32_t s_eid; - uint32_t d_eid; - uint8_t code; - int8_t response; - /* QoS parameters from spec */ - uint8_t availability; - uint8_t in_order; - uint64_t bandwidth; - uint32_t delay; - uint32_t loss; - uint32_t ber; - uint32_t max_gap; - uint32_t timeout; - uint16_t cypher_s; - -} __attribute__((packed)); - -struct mgmt_frame { - struct list_head next; - struct sockaddr_in r_saddr; - uint8_t buf[MGMT_FRAME_BUF_SIZE]; - size_t len; -}; - -/* UDP flow */ -struct uf { - int d_eid; - struct sockaddr_in r_saddr; -}; - -struct { - struct shim_data * shim_data; - - uint32_t dns_addr; - - struct sockaddr_in s_saddr; - int s_fd; - - fset_t * np1_flows; - struct uf fd_to_uf[SYS_MAX_FLOWS]; - pthread_rwlock_t flows_lock; - - pthread_t packet_writer[IPCP_UDP_WR_THR]; - pthread_t packet_reader[IPCP_UDP_RD_THR]; - - /* Handle mgmt frames in a different thread */ - pthread_t mgmt_handler; - pthread_mutex_t mgmt_lock; - pthread_cond_t mgmt_cond; - struct list_head mgmt_frames; -} udp_data; - -static int udp_data_init(void) -{ - int i; - pthread_condattr_t cattr; - - if (pthread_rwlock_init(&udp_data.flows_lock, NULL)) - goto fail_rwlock_init; - - if (pthread_condattr_init(&cattr)) - goto fail_condattr; -#ifndef __APPLE__ - pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK); -#endif - if (pthread_cond_init(&udp_data.mgmt_cond, &cattr)) - goto fail_mgmt_cond; - - if (pthread_mutex_init(&udp_data.mgmt_lock, NULL)) - goto fail_mgmt_lock; - - for (i = 0; i < SYS_MAX_FLOWS; ++i) - udp_data.fd_to_uf[i].d_eid = -1; - - udp_data.np1_flows = fset_create(); - if (udp_data.np1_flows == NULL) - goto fail_fset; - - udp_data.shim_data = shim_data_create(); - if (udp_data.shim_data == NULL) - goto fail_data; - - pthread_condattr_destroy(&cattr); - - list_head_init(&udp_data.mgmt_frames); - - return 0; - - fail_data: - fset_destroy(udp_data.np1_flows); - fail_fset: - pthread_mutex_destroy(&udp_data.mgmt_lock); - fail_mgmt_lock: - pthread_cond_destroy(&udp_data.mgmt_cond); - fail_mgmt_cond: - pthread_condattr_destroy(&cattr); - fail_condattr: - pthread_rwlock_destroy(&udp_data.flows_lock); - fail_rwlock_init: - return -1; -} - -static void udp_data_fini(void) -{ - shim_data_destroy(udp_data.shim_data); - - fset_destroy(udp_data.np1_flows); - - pthread_rwlock_destroy(&udp_data.flows_lock); - pthread_cond_destroy(&udp_data.mgmt_cond); - pthread_mutex_destroy(&udp_data.mgmt_lock); -} - -static int udp_ipcp_port_alloc(const struct sockaddr_in * r_saddr, - uint32_t s_eid, - const uint8_t * dst, - qosspec_t qs, - const buffer_t * data) -{ - uint8_t * buf; - struct mgmt_msg * msg; - size_t len; - - assert(data->len > 0 ? data->data != NULL : data->data == NULL); - - len = sizeof(*msg) + ipcp_dir_hash_len(); - - buf = malloc(len + data->len); - if (buf == NULL) - return -1; - - msg = (struct mgmt_msg *) buf; - msg->eid = hton32(MGMT_EID); - msg->code = FLOW_REQ; - msg->s_eid = hton32(s_eid); - msg->delay = hton32(qs.delay); - msg->bandwidth = hton64(qs.bandwidth); - msg->availability = qs.availability; - msg->loss = hton32(qs.loss); - msg->ber = hton32(qs.ber); - msg->in_order = qs.in_order; - msg->max_gap = hton32(qs.max_gap); - msg->cypher_s = hton16(qs.cypher_s); - msg->timeout = hton32(qs.timeout); - - memcpy(msg + 1, dst, ipcp_dir_hash_len()); - if (data->len > 0) - memcpy(buf + len, data->data, data->len); - - if (sendto(udp_data.s_fd, msg, len + data->len, - SENDTO_FLAGS, - (const struct sockaddr *) r_saddr, sizeof(*r_saddr)) < 0) { - free(buf); - return -1; - } - - free(buf); - - return 0; -} - -static int udp_ipcp_port_alloc_resp(const struct sockaddr_in * r_saddr, - uint32_t s_eid, - uint32_t d_eid, - int8_t response, - const buffer_t * data) -{ - struct mgmt_msg * msg; - - msg = malloc(sizeof(*msg) + data->len); - if (msg == NULL) - return -1; - - msg->eid = hton32(MGMT_EID); - msg->code = FLOW_REPLY; - msg->s_eid = hton32(s_eid); - msg->d_eid = hton32(d_eid); - msg->response = response; - - if (data->len > 0) - memcpy(msg + 1, data->data, data->len); - - if (sendto(udp_data.s_fd, msg, sizeof(*msg) + data->len, - SENDTO_FLAGS, - (const struct sockaddr *) r_saddr, sizeof(*r_saddr)) < 0 ) { - free(msg); - return -1; - } - - free(msg); - - return 0; -} - -static int udp_ipcp_port_req(struct sockaddr_in * c_saddr, - int d_eid, - const uint8_t * dst, - qosspec_t qs, - const buffer_t * data) -{ - int fd; - - fd = ipcp_wait_flow_req_arr(dst, qs, IPCP_UDP_MPL, data); - if (fd < 0) { - log_err("Could not get new flow from IRMd."); - return -1; - } - - pthread_rwlock_wrlock(&udp_data.flows_lock); - - udp_data.fd_to_uf[fd].r_saddr = *c_saddr; - udp_data.fd_to_uf[fd].d_eid = d_eid; - - pthread_rwlock_unlock(&udp_data.flows_lock); - - log_dbg("Pending allocation request, fd %d, remote eid %d.", - fd, d_eid); - - return 0; -} - -static int udp_ipcp_port_alloc_reply(const struct sockaddr_in * saddr, - uint32_t s_eid, - uint32_t d_eid, - int8_t response, - const buffer_t * data) -{ - time_t mpl = IPCP_UDP_MPL; - - pthread_rwlock_wrlock(&udp_data.flows_lock); - - if (memcmp(&udp_data.fd_to_uf[s_eid].r_saddr, saddr, sizeof(*saddr))) { - pthread_rwlock_unlock(&udp_data.flows_lock); - log_err("Flow allocation reply for %u from wrong source.", - s_eid); - return -1; - } - - if (response == 0) - udp_data.fd_to_uf[s_eid].d_eid = d_eid; - - pthread_rwlock_unlock(&udp_data.flows_lock); - - if (ipcp_flow_alloc_reply(s_eid, response, mpl, data) < 0) { - log_err("Failed to reply to flow allocation."); - return -1; - } - - log_dbg("Flow allocation completed on eids (%d, %d).", - s_eid, d_eid); - - return 0; -} - -static int udp_ipcp_mgmt_frame(const uint8_t * buf, - size_t len, - struct sockaddr_in c_saddr) -{ - struct mgmt_msg * msg; - size_t msg_len; - qosspec_t qs; - buffer_t data; - - msg = (struct mgmt_msg *) buf; - - switch (msg->code) { - case FLOW_REQ: - msg_len = sizeof(*msg) + ipcp_dir_hash_len(); - - assert(len >= msg_len); - - data.len = len - msg_len; - data.data = (uint8_t *) buf + msg_len; - - - qs.delay = ntoh32(msg->delay); - qs.bandwidth = ntoh64(msg->bandwidth); - qs.availability = msg->availability; - qs.loss = ntoh32(msg->loss); - qs.ber = ntoh32(msg->ber); - qs.in_order = msg->in_order; - qs.max_gap = ntoh32(msg->max_gap); - qs.cypher_s = ntoh16(msg->cypher_s); - qs.timeout = ntoh32(msg->timeout); - - return udp_ipcp_port_req(&c_saddr, ntoh32(msg->s_eid), - (uint8_t *) (msg + 1), qs, - &data); - case FLOW_REPLY: - assert(len >= sizeof(*msg)); - - data.len = len - sizeof(*msg); - data.data = (uint8_t *) buf + sizeof(*msg); - - return udp_ipcp_port_alloc_reply(&c_saddr, - ntoh32(msg->s_eid), - ntoh32(msg->d_eid), - msg->response, - &data); - default: - log_err("Unknown message received %d.", msg->code); - return -1; - } -} - -static void * udp_ipcp_mgmt_handler(void * o) -{ - (void) o; - - pthread_cleanup_push(__cleanup_mutex_unlock, &udp_data.mgmt_lock); - - while (true) { - struct mgmt_frame * frame; - - pthread_mutex_lock(&udp_data.mgmt_lock); - - while (list_is_empty(&udp_data.mgmt_frames)) - pthread_cond_wait(&udp_data.mgmt_cond, - &udp_data.mgmt_lock); - - frame = list_first_entry((&udp_data.mgmt_frames), - struct mgmt_frame, next); - assert(frame != NULL); - list_del(&frame->next); - - pthread_mutex_unlock(&udp_data.mgmt_lock); - - udp_ipcp_mgmt_frame(frame->buf, frame->len, frame->r_saddr); - - free(frame); - } - - pthread_cleanup_pop(false); - - return (void *) 0; -} - -static void * udp_ipcp_packet_reader(void * o) -{ - uint8_t buf[IPCP_UDP_MAX_PACKET_SIZE]; - uint8_t * data; - ssize_t n; - uint32_t eid; - uint32_t * eid_p; - - (void) o; - - ipcp_lock_to_core(); - - data = buf + sizeof(uint32_t); - eid_p = (uint32_t *) buf; - - while (true) { - struct mgmt_frame * frame; - struct sockaddr_in r_saddr; - socklen_t len; - struct shm_du_buff * sdb; - uint8_t * head; - - len = sizeof(r_saddr); - - n = recvfrom(udp_data.s_fd, buf, IPCP_UDP_MAX_PACKET_SIZE, 0, - (struct sockaddr *) &r_saddr, &len); - if (n < 0) - continue; - - if (n == 0) - log_dbg("Got a 0 frame."); - - if ((size_t) n < sizeof(eid)) { - log_dbg("Dropped bad frame."); - continue; - } - - eid = ntoh32(*eid_p); - - /* pass onto mgmt queue */ - if (eid == MGMT_EID) { - if ((size_t) n < MGMT_FRAME_SIZE) { - log_warn("Dropped runt mgmt frame."); - continue; - } - - frame = malloc(sizeof(*frame)); - if (frame == NULL) - continue; - - memcpy(frame->buf, buf, n); - memcpy(&frame->r_saddr, &r_saddr, sizeof(r_saddr)); - frame->len = n; - - pthread_mutex_lock(&udp_data.mgmt_lock); - list_add(&frame->next, &udp_data.mgmt_frames); - pthread_cond_signal(&udp_data.mgmt_cond); - pthread_mutex_unlock(&udp_data.mgmt_lock); - continue; - } - - n-= sizeof(eid); - - if (ipcp_sdb_reserve(&sdb, n)) - continue; - - head = shm_du_buff_head(sdb); - memcpy(head, data, n); - if (np1_flow_write(eid, sdb) < 0) - ipcp_sdb_release(sdb); - } - - return (void *) 0; -} - -static void cleanup_fqueue(void * fq) -{ - fqueue_destroy((fqueue_t *) fq); -} - -static void cleanup_sdb(void * sdb) -{ - ipcp_sdb_release((struct shm_du_buff *) sdb); -} - -static void * udp_ipcp_packet_writer(void * o) -{ - fqueue_t * fq; - - fq = fqueue_create(); - if (fq == NULL) - return (void *) -1; - - (void) o; - - ipcp_lock_to_core(); - - pthread_cleanup_push(cleanup_fqueue, fq); - - while (true) { - struct sockaddr_in saddr; - int eid; - int fd; - fevent(udp_data.np1_flows, fq, NULL); - while ((fd = fqueue_next(fq)) >= 0) { - struct shm_du_buff * sdb; - uint8_t * buf; - uint16_t len; - - if (fqueue_type(fq) != FLOW_PKT) - continue; - - if (np1_flow_read(fd, &sdb)) { - log_dbg("Bad read from fd %d.", fd); - continue; - } - - len = shm_du_buff_len(sdb); - if (len > IPCP_UDP_MAX_PACKET_SIZE) { - log_dbg("Packet length exceeds MTU."); - ipcp_sdb_release(sdb); - continue; - } - - buf = shm_du_buff_head_alloc(sdb, OUR_HEADER_LEN); - if (buf == NULL) { - log_dbg("Failed to allocate header."); - ipcp_sdb_release(sdb); - continue; - } - - pthread_rwlock_rdlock(&udp_data.flows_lock); - - eid = hton32(udp_data.fd_to_uf[fd].d_eid); - saddr = udp_data.fd_to_uf[fd].r_saddr; - - pthread_rwlock_unlock(&udp_data.flows_lock); - - memcpy(buf, &eid, sizeof(eid)); - - pthread_cleanup_push(cleanup_sdb, sdb); - - if (sendto(udp_data.s_fd, buf, len + OUR_HEADER_LEN, - SENDTO_FLAGS, - (const struct sockaddr *) &saddr, - sizeof(saddr)) < 0) - log_err("Failed to send packet."); - - pthread_cleanup_pop(true); - } - } - - pthread_cleanup_pop(true); - - return (void *) 1; -} - -static const char * inet4_ntop(const void * addr, - char * buf) -{ - return inet_ntop(AF_INET, addr, buf, INET_ADDRSTRLEN); -} - -static int udp_ipcp_bootstrap(struct ipcp_config * conf) -{ - char ipstr[INET_ADDRSTRLEN]; - char dnsstr[INET_ADDRSTRLEN]; - int i = 1; - - assert(conf != NULL); - assert(conf->type == THIS_TYPE); - assert(conf->layer_info.dir_hash_algo == (enum pol_dir_hash) HASH_MD5); - - if (inet4_ntop(&conf->udp.ip_addr, ipstr) == NULL) { - log_err("Failed to convert IP address."); - return -1; - } - - if (conf->udp.dns_addr != 0) { - if (inet4_ntop(&conf->udp.dns_addr, dnsstr) == NULL) { - log_err("Failed to convert DNS address."); - return -1; - } -#ifndef HAVE_DDNS - log_warn("DNS disabled at compile time, address ignored."); -#endif - } else { - strcpy(dnsstr, "not set"); - } - - /* UDP listen server */ - udp_data.s_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (udp_data.s_fd < 0) { - log_err("Can't create socket: %s", strerror(errno)); - goto fail_socket; - } - - memset((char *) &udp_data.s_saddr, 0, sizeof(udp_data.s_saddr)); - udp_data.s_saddr.sin_family = AF_INET; - udp_data.s_saddr.sin_addr.s_addr = conf->udp.ip_addr; - udp_data.s_saddr.sin_port = htons(conf->udp.port); - - if (bind(udp_data.s_fd, SADDR, SADDR_SIZE) < 0) { - log_err("Couldn't bind to %s:%d. %s.", - ipstr, conf->udp.port, strerror(errno)); - goto fail_bind; - } - - udp_data.dns_addr = conf->udp.dns_addr; - - if (pthread_create(&udp_data.mgmt_handler, NULL, - udp_ipcp_mgmt_handler, NULL)) { - log_err("Failed to create management thread."); - goto fail_bind; - } - - for (i = 0; i < IPCP_UDP_RD_THR; ++i) { - if (pthread_create(&udp_data.packet_reader[i], NULL, - udp_ipcp_packet_reader, NULL)) { - log_err("Failed to create reader thread."); - goto fail_packet_reader; - } - } - - for (i = 0; i < IPCP_UDP_WR_THR; ++i) { - if (pthread_create(&udp_data.packet_writer[i], NULL, - udp_ipcp_packet_writer, NULL)) { - log_err("Failed to create writer thread."); - goto fail_packet_writer; - } - } - - log_dbg("Bootstrapped IPCP over UDP with pid %d.", getpid()); - log_dbg("Bound to IP address %s.", ipstr); - log_dbg("Using port %u.", conf->udp.port); - if (conf->udp.dns_addr != 0) - log_dbg("DNS server address is %s.", dnsstr); - else - log_dbg("DNS server not in use."); - - return 0; - - fail_packet_writer: - while (i-- > 0) { - pthread_cancel(udp_data.packet_writer[i]); - pthread_join(udp_data.packet_writer[i], NULL); - } - i = IPCP_UDP_RD_THR; - fail_packet_reader: - while (i-- > 0) { - pthread_cancel(udp_data.packet_reader[i]); - pthread_join(udp_data.packet_reader[i], NULL); - } - pthread_cancel(udp_data.mgmt_handler); - pthread_join(udp_data.mgmt_handler, NULL); - fail_bind: - close(udp_data.s_fd); - fail_socket: - return -1; -} - -#ifdef HAVE_DDNS -/* FIXME: Dependency on nsupdate to be removed in the end */ -/* NOTE: Disgusted with this crap */ -static int ddns_send(char * cmd) -{ - pid_t pid; - int wstatus; - int pipe_fd[2]; - char * argv[] = {NSUPDATE_EXEC, 0}; - char * envp[] = {0}; - - if (pipe(pipe_fd)) { - log_err("Failed to create pipe: %s.", strerror(errno)); - return -1; - } - - pid = fork(); - if (pid == -1) { - log_err("Failed to fork: %s.", strerror(errno)); - close(pipe_fd[0]); - close(pipe_fd[1]); - return -1; - } - - if (pid == 0) { - close(pipe_fd[1]); - dup2(pipe_fd[0], 0); - execve(argv[0], &argv[0], envp); - log_err("Failed to execute: %s", strerror(errno)); - exit(1); - } - - close(pipe_fd[0]); - - if (write(pipe_fd[1], cmd, strlen(cmd)) == -1) { - log_err("Failed to communicate with nsupdate: %s.", - strerror(errno)); - close(pipe_fd[1]); - return -1; - } - - waitpid(pid, &wstatus, 0); - if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) - log_dbg("Succesfully communicated with DNS server."); - else - log_err("Failed to register with DNS server."); - - close(pipe_fd[1]); - - return 0; -} - -static uint32_t ddns_resolve(char * name, - uint32_t dns_addr) -{ - pid_t pid = -1; - int wstatus; - int pipe_fd[2]; - char dnsstr[INET_ADDRSTRLEN]; - char buf[IPCP_UDP_BUF_SIZE]; - ssize_t count = 0; - char * substr = NULL; - char * substr2 = NULL; - char * addr_str = "Address:"; - uint32_t ip_addr = 0; - - if (inet4_ntop(&dns_addr, dnsstr) == NULL) - return 0; - - if (pipe(pipe_fd)) { - log_err("Failed to create pipe: %s.", strerror(errno)); - return 0; - } - - pid = fork(); - if (pid == -1) { - log_err("Failed to fork: %s.", strerror(errno)); - close(pipe_fd[0]); - close(pipe_fd[1]); - return -1; - } - - if (pid == 0) { - char * argv[] = {NSLOOKUP_EXEC, name, dnsstr, 0}; - char * envp[] = {0}; - - close(pipe_fd[0]); - dup2(pipe_fd[1], 1); - execve(argv[0], &argv[0], envp); - log_err("Failed to execute: %s", strerror(errno)); - exit(1); - } - - close(pipe_fd[1]); - - count = read(pipe_fd[0], buf, IPCP_UDP_BUF_SIZE - 1); - if (count <= 0) { - log_err("Failed to communicate with nslookup."); - close(pipe_fd[0]); - return 0; - } - - close(pipe_fd[0]); - - waitpid(pid, &wstatus, 0); - if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0 && - count != IPCP_UDP_BUF_SIZE - 1) - log_dbg("Succesfully communicated with nslookup."); - else - log_err("Failed to resolve DNS address."); - - buf[count] = '\0'; - substr = strtok(buf, "\n"); - while (substr != NULL) { - substr2 = substr; - substr = strtok(NULL, "\n"); - } - - if (substr2 == NULL || strstr(substr2, addr_str) == NULL) { - log_err("Failed to resolve DNS address."); - return 0; - } - - if (inet_pton(AF_INET, substr2 + strlen(addr_str) + 1, &ip_addr) != 1) { - log_err("Failed to resolve DNS address."); - return 0; - } - - return ip_addr; -} -#endif - -static int udp_ipcp_reg(const uint8_t * hash) -{ -#ifdef HAVE_DDNS - char ipstr[INET_ADDRSTRLEN]; - char dnsstr[INET_ADDRSTRLEN]; - char cmd[1000]; - uint32_t dns_addr; - uint32_t ip_addr; -#endif - char * hashstr; - - hashstr = malloc(ipcp_dir_hash_strlen() + 1); - if (hashstr == NULL) { - log_err("Failed to malloc hashstr."); - return -1; - } - - assert(hash); - - ipcp_hash_str(hashstr, hash); - - if (shim_data_reg_add_entry(udp_data.shim_data, hash)) { - log_err("Failed to add " HASH_FMT32 " to local registry.", - HASH_VAL32(hash)); - free(hashstr); - return -1; - } - -#ifdef HAVE_DDNS - /* register application with DNS server */ - - dns_addr = udp_data.dns_addr; - - if (dns_addr != 0) { - ip_addr = udp_data.s_saddr.sin_addr.s_addr; - - if (inet4_ntop(&ip_addr, ipstr) == NULL) { - log_err("Failed to convert IP address to string."); - free(hashstr); - return -1; - } - - if (inet4_ntop(&dns_addr, dnsstr) == NULL) { - log_err("Failed to convert DNS address to string."); - free(hashstr); - return -1; - } - - sprintf(cmd, "server %s\nupdate add %s %d A %s\nsend\nquit\n", - dnsstr, hashstr, DNS_TTL, ipstr); - - if (ddns_send(cmd)) { - log_err("Failed to send DDNS message."); - shim_data_reg_del_entry(udp_data.shim_data, hash); - free(hashstr); - return -1; - } - } -#endif - free(hashstr); - - return 0; -} - -static int udp_ipcp_unreg(const uint8_t * hash) -{ -#ifdef HAVE_DDNS - char dnsstr[INET_ADDRSTRLEN]; - /* max DNS name length + max IP length + max command length */ - char cmd[100]; - uint32_t dns_addr; -#endif - char * hashstr; - - assert(hash); - - hashstr = malloc(ipcp_dir_hash_strlen() + 1); - if (hashstr == NULL) { - log_err("Failed to malloc hashstr."); - return -1; - } - - ipcp_hash_str(hashstr, hash); - -#ifdef HAVE_DDNS - /* unregister application with DNS server */ - - dns_addr = udp_data.dns_addr; - - if (dns_addr != 0) { - if (inet4_ntop(&dns_addr, dnsstr) == NULL) { - log_err("Failed to convert DNS address to string."); - free(hashstr); - return -1; - } - sprintf(cmd, "server %s\nupdate delete %s A\nsend\nquit\n", - dnsstr, hashstr); - - ddns_send(cmd); - } -#endif - - shim_data_reg_del_entry(udp_data.shim_data, hash); - - free(hashstr); - - return 0; -} - -static int udp_ipcp_query(const uint8_t * hash) -{ - uint32_t ip_addr = 0; - char * hashstr; - struct hostent * h; -#ifdef HAVE_DDNS - uint32_t dns_addr = 0; -#endif - assert(hash); - - hashstr = malloc(ipcp_dir_hash_strlen() + 1); - if (hashstr == NULL) { - log_err("Failed to malloc hashstr."); - return -ENOMEM; - } - - ipcp_hash_str(hashstr, hash); - - if (shim_data_dir_has(udp_data.shim_data, hash)) { - free(hashstr); - return 0; - } - -#ifdef HAVE_DDNS - dns_addr = udp_data.dns_addr; - - if (dns_addr != 0) { - ip_addr = ddns_resolve(hashstr, dns_addr); - if (ip_addr == 0) { - log_err("Could not resolve %s.", hashstr); - free(hashstr); - return -1; - } - } else { -#endif - h = gethostbyname(hashstr); - if (h == NULL) { - log_err("Could not resolve %s.", hashstr); - free(hashstr); - return -1; - } - - ip_addr = *((uint32_t *) (h->h_addr_list[0])); -#ifdef HAVE_DDNS - } -#endif - - if (shim_data_dir_add_entry(udp_data.shim_data, hash, ip_addr)) { - log_err("Failed to add directory entry."); - free(hashstr); - return -1; - } - - free(hashstr); - - return 0; -} - -static int udp_ipcp_flow_alloc(int fd, - const uint8_t * dst, - qosspec_t qs, - const buffer_t * data) -{ - struct sockaddr_in r_saddr; /* Server address */ - uint32_t ip_addr = 0; - char ipstr[INET_ADDRSTRLEN]; - - (void) qs; - - assert(dst); - - if (!shim_data_dir_has(udp_data.shim_data, dst)) { - log_err("Could not resolve destination."); - return -1; - } - - ip_addr = (uint32_t) shim_data_dir_get_addr(udp_data.shim_data, dst); - - if (inet4_ntop(&ip_addr, ipstr) == NULL) { - log_err("Could not convert IP address."); - return -1; - } - - log_dbg("Destination " HASH_FMT32 " resolved at IP %s.", - HASH_VAL32(dst), ipstr); - - memset((char *) &r_saddr, 0, sizeof(r_saddr)); - r_saddr.sin_family = AF_INET; - r_saddr.sin_addr.s_addr = ip_addr; - r_saddr.sin_port = udp_data.s_saddr.sin_port; - - if (udp_ipcp_port_alloc(&r_saddr, fd, dst, qs, data) < 0) { - log_err("Could not allocate port."); - return -1; - } - - pthread_rwlock_wrlock(&udp_data.flows_lock); - - udp_data.fd_to_uf[fd].d_eid = -1; - udp_data.fd_to_uf[fd].r_saddr = r_saddr; - - pthread_rwlock_unlock(&udp_data.flows_lock); - - fset_add(udp_data.np1_flows, fd); - - return 0; -} - -static int udp_ipcp_flow_alloc_resp(int fd, - int resp, - const buffer_t * data) -{ - struct sockaddr_in saddr; - int d_eid; - - if (ipcp_wait_flow_resp(fd) < 0) { - log_err("Failed to wait for flow response."); - return -1; - } - - pthread_rwlock_rdlock(&udp_data.flows_lock); - - saddr = udp_data.fd_to_uf[fd].r_saddr; - d_eid = udp_data.fd_to_uf[fd].d_eid; - - pthread_rwlock_unlock(&udp_data.flows_lock); - - if (udp_ipcp_port_alloc_resp(&saddr, d_eid, fd, resp, data) < 0) { - fset_del(udp_data.np1_flows, fd); - log_err("Failed to respond to flow request."); - return -1; - } - - fset_add(udp_data.np1_flows, fd); - - return 0; -} - -static int udp_ipcp_flow_dealloc(int fd) -{ - ipcp_flow_fini(fd); - - fset_del(udp_data.np1_flows, fd); - - pthread_rwlock_wrlock(&udp_data.flows_lock); - - udp_data.fd_to_uf[fd].d_eid = -1; - memset(&udp_data.fd_to_uf[fd].r_saddr, 0, SADDR_SIZE); - - pthread_rwlock_unlock(&udp_data.flows_lock); - - ipcp_flow_dealloc(fd); - - return 0; -} - -static struct ipcp_ops udp_ops = { - .ipcp_bootstrap = udp_ipcp_bootstrap, - .ipcp_enroll = NULL, - .ipcp_connect = NULL, - .ipcp_disconnect = NULL, - .ipcp_reg = udp_ipcp_reg, - .ipcp_unreg = udp_ipcp_unreg, - .ipcp_query = udp_ipcp_query, - .ipcp_flow_alloc = udp_ipcp_flow_alloc, - .ipcp_flow_join = NULL, - .ipcp_flow_alloc_resp = udp_ipcp_flow_alloc_resp, - .ipcp_flow_dealloc = udp_ipcp_flow_dealloc -}; - -int main(int argc, - char * argv[]) -{ - int i; - - - if (udp_data_init() < 0) { - log_err("Failed to init udp data."); - goto fail_data_init; - } - - if (ipcp_init(argc, argv, &udp_ops, THIS_TYPE) < 0) { - log_err("Failed to initialize IPCP."); - goto fail_init; - } - - if (ipcp_start() < 0) { - log_err("Failed to start IPCP."); - goto fail_start; - } - - ipcp_sigwait(); - - if (ipcp_get_state() == IPCP_SHUTDOWN) { - for (i = 0; i < IPCP_UDP_WR_THR; ++i) - pthread_cancel(udp_data.packet_writer[i]); - for (i = 0; i < IPCP_UDP_RD_THR; ++i) - pthread_cancel(udp_data.packet_reader[i]); - pthread_cancel(udp_data.mgmt_handler); - - for (i = 0; i < IPCP_UDP_WR_THR; ++i) - pthread_join(udp_data.packet_writer[i], NULL); - for (i = 0; i < IPCP_UDP_RD_THR; ++i) - pthread_join(udp_data.packet_reader[i], NULL); - pthread_join(udp_data.mgmt_handler, NULL); - close(udp_data.s_fd); - } - - ipcp_stop(); - - ipcp_fini(); - - udp_data_fini(); - - exit(EXIT_SUCCESS); - - fail_start: - ipcp_fini(); - fail_init: - udp_data_fini(); - fail_data_init: - exit(EXIT_FAILURE); -} diff --git a/src/ipcpd/udp/udp.c b/src/ipcpd/udp/udp.c new file mode 100644 index 00000000..d6d38362 --- /dev/null +++ b/src/ipcpd/udp/udp.c @@ -0,0 +1,1197 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * IPC process over UDP + * + * Dimitri Staessens + * Sander Vrijders + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipcp.h" +#include "shim-data.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FLOW_REQ 1 +#define FLOW_REPLY 2 + +#define OUR_HEADER_LEN sizeof(uint32_t) /* adds eid */ + +#define IPCP_UDP_BUF_SIZE IPCP_UDP_MAX_PACKET_SIZE +#define IPCP_UDP_MSG_SIZE IPCP_UDP_MAX_PACKET_SIZE + +#define DNS_TTL 86400 + +#define SADDR ((struct sockaddr *) &udp_data.s_saddr) +#define SADDR_SIZE (sizeof(udp_data.s_saddr)) +#define LOCAL_IP (udp_data.s_saddr.sin_addr.s_addr) + +#define MGMT_EID 0 +#define MGMT_FRAME_SIZE (sizeof(struct mgmt_msg)) +#define MGMT_FRAME_BUF_SIZE 2048 + +#ifdef __linux__ +#define SENDTO_FLAGS MSG_CONFIRM +#else +#define SENDTO_FLAGS 0 +#endif + +/* Keep order for alignment. */ +struct mgmt_msg { + uint32_t eid; + uint32_t s_eid; + uint32_t d_eid; + uint8_t code; + int8_t response; + /* QoS parameters from spec */ + uint8_t availability; + uint8_t in_order; + uint64_t bandwidth; + uint32_t delay; + uint32_t loss; + uint32_t ber; + uint32_t max_gap; + uint32_t timeout; + uint16_t cypher_s; + +} __attribute__((packed)); + +struct mgmt_frame { + struct list_head next; + struct __SOCKADDR r_saddr; + uint8_t buf[MGMT_FRAME_BUF_SIZE]; + size_t len; +}; + +/* UDP flow */ +struct uf { + int d_eid; + struct __SOCKADDR r_saddr; +}; + +struct { + struct shim_data * shim_data; + + struct __ADDR dns_addr; + struct __SOCKADDR s_saddr; + int s_fd; + + fset_t * np1_flows; + struct uf fd_to_uf[SYS_MAX_FLOWS]; + pthread_rwlock_t flows_lock; + + pthread_t packet_writer[IPCP_UDP_WR_THR]; + pthread_t packet_reader[IPCP_UDP_RD_THR]; + + /* Handle mgmt frames in a different thread */ + pthread_t mgmt_handler; + pthread_mutex_t mgmt_lock; + pthread_cond_t mgmt_cond; + struct list_head mgmt_frames; +} udp_data; + +static const char * __inet_ntop(const struct __ADDR * addr, + char * buf) +{ + return inet_ntop(__AF, addr, buf, __ADDRSTRLEN); +} + +static int udp_data_init(void) +{ + int i; + pthread_condattr_t cattr; + + if (pthread_rwlock_init(&udp_data.flows_lock, NULL)) + goto fail_rwlock_init; + + if (pthread_condattr_init(&cattr)) + goto fail_condattr; +#ifndef __APPLE__ + pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK); +#endif + if (pthread_cond_init(&udp_data.mgmt_cond, &cattr)) + goto fail_mgmt_cond; + + if (pthread_mutex_init(&udp_data.mgmt_lock, NULL)) + goto fail_mgmt_lock; + + for (i = 0; i < SYS_MAX_FLOWS; ++i) + udp_data.fd_to_uf[i].d_eid = -1; + + udp_data.np1_flows = fset_create(); + if (udp_data.np1_flows == NULL) + goto fail_fset; + + udp_data.shim_data = shim_data_create(); + if (udp_data.shim_data == NULL) + goto fail_data; + + pthread_condattr_destroy(&cattr); + + list_head_init(&udp_data.mgmt_frames); + + return 0; + + fail_data: + fset_destroy(udp_data.np1_flows); + fail_fset: + pthread_mutex_destroy(&udp_data.mgmt_lock); + fail_mgmt_lock: + pthread_cond_destroy(&udp_data.mgmt_cond); + fail_mgmt_cond: + pthread_condattr_destroy(&cattr); + fail_condattr: + pthread_rwlock_destroy(&udp_data.flows_lock); + fail_rwlock_init: + return -1; +} + +static void udp_data_fini(void) +{ + shim_data_destroy(udp_data.shim_data); + + fset_destroy(udp_data.np1_flows); + + pthread_rwlock_destroy(&udp_data.flows_lock); + pthread_cond_destroy(&udp_data.mgmt_cond); + pthread_mutex_destroy(&udp_data.mgmt_lock); +} + +static int udp_ipcp_port_alloc(const struct __SOCKADDR * r_saddr, + uint32_t s_eid, + const uint8_t * dst, + qosspec_t qs, + const buffer_t * data) +{ + uint8_t * buf; + struct mgmt_msg * msg; + size_t len; + + assert(data->len > 0 ? data->data != NULL : data->data == NULL); + + len = sizeof(*msg) + ipcp_dir_hash_len(); + + buf = malloc(len + data->len); + if (buf == NULL) + return -1; + + msg = (struct mgmt_msg *) buf; + msg->eid = hton32(MGMT_EID); + msg->code = FLOW_REQ; + msg->s_eid = hton32(s_eid); + msg->delay = hton32(qs.delay); + msg->bandwidth = hton64(qs.bandwidth); + msg->availability = qs.availability; + msg->loss = hton32(qs.loss); + msg->ber = hton32(qs.ber); + msg->in_order = qs.in_order; + msg->max_gap = hton32(qs.max_gap); + msg->cypher_s = hton16(qs.cypher_s); + msg->timeout = hton32(qs.timeout); + + memcpy(msg + 1, dst, ipcp_dir_hash_len()); + if (data->len > 0) + memcpy(buf + len, data->data, data->len); + + if (sendto(udp_data.s_fd, msg, len + data->len, + SENDTO_FLAGS, + (const struct sockaddr *) r_saddr, sizeof(*r_saddr)) < 0) { + log_err("Failed to send flow allocation request: %s.", + strerror(errno)); + free(buf); + return -1; + } + + free(buf); + + return 0; +} + +static int udp_ipcp_port_alloc_resp(const struct __SOCKADDR * r_saddr, + uint32_t s_eid, + uint32_t d_eid, + int8_t response, + const buffer_t * data) +{ + struct mgmt_msg * msg; + + msg = malloc(sizeof(*msg) + data->len); + if (msg == NULL) + return -1; + + msg->eid = hton32(MGMT_EID); + msg->code = FLOW_REPLY; + msg->s_eid = hton32(s_eid); + msg->d_eid = hton32(d_eid); + msg->response = response; + + if (data->len > 0) + memcpy(msg + 1, data->data, data->len); + + if (sendto(udp_data.s_fd, msg, sizeof(*msg) + data->len, + SENDTO_FLAGS, + (const struct sockaddr *) r_saddr, sizeof(*r_saddr)) < 0 ) { + free(msg); + return -1; + } + + free(msg); + + return 0; +} + +static int udp_ipcp_port_req(struct __SOCKADDR * c_saddr, + int d_eid, + const uint8_t * dst, + qosspec_t qs, + const buffer_t * data) +{ + int fd; + + fd = ipcp_wait_flow_req_arr(dst, qs, IPCP_UDP_MPL, data); + if (fd < 0) { + log_err("Could not get new flow from IRMd."); + return -1; + } + + pthread_rwlock_wrlock(&udp_data.flows_lock); + + udp_data.fd_to_uf[fd].r_saddr = *c_saddr; + udp_data.fd_to_uf[fd].d_eid = d_eid; + + pthread_rwlock_unlock(&udp_data.flows_lock); + + log_dbg("Pending allocation request, fd %d, remote eid %d.", + fd, d_eid); + + return 0; +} + +static int udp_ipcp_port_alloc_reply(const struct __SOCKADDR * saddr, + uint32_t s_eid, + uint32_t d_eid, + int8_t response, + const buffer_t * data) +{ + time_t mpl = IPCP_UDP_MPL; + + pthread_rwlock_wrlock(&udp_data.flows_lock); + + if (memcmp(&udp_data.fd_to_uf[s_eid].r_saddr, saddr, sizeof(*saddr))) { + char ipstr[__ADDRSTRLEN]; + pthread_rwlock_unlock(&udp_data.flows_lock); + #ifdef BUILD_IPCP_UDP4 + __inet_ntop(&saddr->sin_addr, ipstr); + #else + __inet_ntop(&saddr->sin6_addr, ipstr); + #endif + log_err("Flow allocation reply for %u from wrong source %s.", + s_eid, ipstr); + return -1; + } + + if (response == 0) + udp_data.fd_to_uf[s_eid].d_eid = d_eid; + + pthread_rwlock_unlock(&udp_data.flows_lock); + + if (ipcp_flow_alloc_reply(s_eid, response, mpl, data) < 0) { + log_err("Failed to reply to flow allocation."); + return -1; + } + + log_dbg("Flow allocation completed on eids (%d, %d).", + s_eid, d_eid); + + return 0; +} + +static int udp_ipcp_mgmt_frame(struct __SOCKADDR c_saddr, + const uint8_t * buf, + size_t len) +{ + struct mgmt_msg * msg; + size_t msg_len; + qosspec_t qs; + buffer_t data; + + msg = (struct mgmt_msg *) buf; + + switch (msg->code) { + case FLOW_REQ: + msg_len = sizeof(*msg) + ipcp_dir_hash_len(); + + assert(len >= msg_len); + + data.len = len - msg_len; + data.data = (uint8_t *) buf + msg_len; + + + qs.delay = ntoh32(msg->delay); + qs.bandwidth = ntoh64(msg->bandwidth); + qs.availability = msg->availability; + qs.loss = ntoh32(msg->loss); + qs.ber = ntoh32(msg->ber); + qs.in_order = msg->in_order; + qs.max_gap = ntoh32(msg->max_gap); + qs.cypher_s = ntoh16(msg->cypher_s); + qs.timeout = ntoh32(msg->timeout); + + return udp_ipcp_port_req(&c_saddr, ntoh32(msg->s_eid), + (uint8_t *) (msg + 1), qs, + &data); + case FLOW_REPLY: + assert(len >= sizeof(*msg)); + + data.len = len - sizeof(*msg); + data.data = (uint8_t *) buf + sizeof(*msg); + + return udp_ipcp_port_alloc_reply(&c_saddr, + ntoh32(msg->s_eid), + ntoh32(msg->d_eid), + msg->response, + &data); + default: + log_err("Unknown message received %d.", msg->code); + return -1; + } +} + +static void * udp_ipcp_mgmt_handler(void * o) +{ + (void) o; + + pthread_cleanup_push(__cleanup_mutex_unlock, &udp_data.mgmt_lock); + + while (true) { + struct mgmt_frame * frame; + + pthread_mutex_lock(&udp_data.mgmt_lock); + + while (list_is_empty(&udp_data.mgmt_frames)) + pthread_cond_wait(&udp_data.mgmt_cond, + &udp_data.mgmt_lock); + + frame = list_first_entry((&udp_data.mgmt_frames), + struct mgmt_frame, next); + assert(frame != NULL); + list_del(&frame->next); + + pthread_mutex_unlock(&udp_data.mgmt_lock); + + udp_ipcp_mgmt_frame(frame->r_saddr, frame->buf, frame->len); + + free(frame); + } + + pthread_cleanup_pop(false); + + return (void *) 0; +} + +static void * udp_ipcp_packet_reader(void * o) +{ + uint8_t buf[IPCP_UDP_MAX_PACKET_SIZE]; + uint8_t * data; + ssize_t n; + uint32_t eid; + uint32_t * eid_p; + + (void) o; + + ipcp_lock_to_core(); + + data = buf + sizeof(uint32_t); + eid_p = (uint32_t *) buf; + + while (true) { + struct mgmt_frame * frame; + struct __SOCKADDR r_saddr; + socklen_t len; + struct shm_du_buff * sdb; + uint8_t * head; + + len = sizeof(r_saddr); + + n = recvfrom(udp_data.s_fd, buf, IPCP_UDP_MAX_PACKET_SIZE, 0, + (struct sockaddr *) &r_saddr, &len); + if (n < 0) + continue; + + if (n == 0) + log_dbg("Got a 0 frame."); + + if ((size_t) n < sizeof(eid)) { + log_dbg("Dropped bad frame."); + continue; + } + + eid = ntoh32(*eid_p); + + /* pass onto mgmt queue */ + if (eid == MGMT_EID) { + if ((size_t) n < MGMT_FRAME_SIZE) { + log_warn("Dropped runt mgmt frame."); + continue; + } + + frame = malloc(sizeof(*frame)); + if (frame == NULL) + continue; + + memcpy(frame->buf, buf, n); + memcpy(&frame->r_saddr, &r_saddr, sizeof(r_saddr)); + frame->len = n; + + pthread_mutex_lock(&udp_data.mgmt_lock); + list_add(&frame->next, &udp_data.mgmt_frames); + pthread_cond_signal(&udp_data.mgmt_cond); + pthread_mutex_unlock(&udp_data.mgmt_lock); + continue; + } + + n-= sizeof(eid); + + if (ipcp_sdb_reserve(&sdb, n)) + continue; + + head = shm_du_buff_head(sdb); + memcpy(head, data, n); + if (np1_flow_write(eid, sdb) < 0) + ipcp_sdb_release(sdb); + } + + return (void *) 0; +} + +static void cleanup_fqueue(void * fq) +{ + fqueue_destroy((fqueue_t *) fq); +} + +static void cleanup_sdb(void * sdb) +{ + ipcp_sdb_release((struct shm_du_buff *) sdb); +} + +static void * udp_ipcp_packet_writer(void * o) +{ + fqueue_t * fq; + + fq = fqueue_create(); + if (fq == NULL) + return (void *) -1; + + (void) o; + + ipcp_lock_to_core(); + + pthread_cleanup_push(cleanup_fqueue, fq); + + while (true) { + struct __SOCKADDR saddr; + int eid; + int fd; + fevent(udp_data.np1_flows, fq, NULL); + while ((fd = fqueue_next(fq)) >= 0) { + struct shm_du_buff * sdb; + uint8_t * buf; + uint16_t len; + + if (fqueue_type(fq) != FLOW_PKT) + continue; + + if (np1_flow_read(fd, &sdb)) { + log_dbg("Bad read from fd %d.", fd); + continue; + } + + len = shm_du_buff_len(sdb); + if (len > IPCP_UDP_MAX_PACKET_SIZE) { + log_dbg("Packet length exceeds MTU."); + ipcp_sdb_release(sdb); + continue; + } + + buf = shm_du_buff_head_alloc(sdb, OUR_HEADER_LEN); + if (buf == NULL) { + log_dbg("Failed to allocate header."); + ipcp_sdb_release(sdb); + continue; + } + + pthread_rwlock_rdlock(&udp_data.flows_lock); + + eid = hton32(udp_data.fd_to_uf[fd].d_eid); + saddr = udp_data.fd_to_uf[fd].r_saddr; + + pthread_rwlock_unlock(&udp_data.flows_lock); + + memcpy(buf, &eid, sizeof(eid)); + + pthread_cleanup_push(cleanup_sdb, sdb); + + if (sendto(udp_data.s_fd, buf, len + OUR_HEADER_LEN, + SENDTO_FLAGS, + (const struct sockaddr *) &saddr, + sizeof(saddr)) < 0) + log_err("Failed to send packet."); + + pthread_cleanup_pop(true); + } + } + + pthread_cleanup_pop(true); + + return (void *) 1; +} + +static bool is_addr_specified(const struct __ADDR * addr) +{ +#ifdef BUILD_IPCP_UDP4 + return addr->s_addr != 0; +#else + return !IN6_IS_ADDR_UNSPECIFIED(addr); +#endif +} + +static int udp_ipcp_bootstrap(struct ipcp_config * conf) +{ + char ipstr[__ADDRSTRLEN]; + char dnsstr[__ADDRSTRLEN]; + int i = 1; +#ifdef BUILD_IPCP_UDP4 + struct udp4_config * udp; + udp = &conf->udp4; +#else + struct udp6_config * udp; + udp = &conf->udp6; +#endif + + assert(conf != NULL); + assert(conf->type == THIS_TYPE); + assert(conf->layer_info.dir_hash_algo == (enum pol_dir_hash) HASH_MD5); + + if (__inet_ntop(&udp->ip_addr, ipstr) == NULL) { + log_err("Failed to convert IP address."); + return -1; + } + + if (is_addr_specified(&udp->dns_addr)) { + if (__inet_ntop(&udp->dns_addr, dnsstr) == NULL) { + log_err("Failed to convert DNS address."); + return -1; + } +#ifndef HAVE_DDNS + log_warn("DNS disabled at compile time, address ignored."); +#endif + } else { + strcpy(dnsstr, "not set"); + } + + /* UDP listen server */ + udp_data.s_fd = socket(__AF, SOCK_DGRAM, IPPROTO_UDP); + if (udp_data.s_fd < 0) { + log_err("Can't create socket: %s", strerror(errno)); + goto fail_socket; + } + + memset((char *) &udp_data.s_saddr, 0, sizeof(udp_data.s_saddr)); +#ifdef BUILD_IPCP_UDP4 + udp_data.s_saddr.sin_family = AF_INET; + udp_data.s_saddr.sin_addr = udp->ip_addr; + udp_data.s_saddr.sin_port = htons(udp->port); +#else + udp_data.s_saddr.sin6_family = AF_INET6; + udp_data.s_saddr.sin6_addr = udp->ip_addr; + udp_data.s_saddr.sin6_port = htons(udp->port); +#endif + if (bind(udp_data.s_fd, SADDR, SADDR_SIZE) < 0) { + log_err("Couldn't bind to %s:%d. %s.", + ipstr, udp->port, strerror(errno)); + goto fail_bind; + } + + udp_data.dns_addr = udp->dns_addr; + + if (pthread_create(&udp_data.mgmt_handler, NULL, + udp_ipcp_mgmt_handler, NULL)) { + log_err("Failed to create management thread."); + goto fail_bind; + } + + for (i = 0; i < IPCP_UDP_RD_THR; ++i) { + if (pthread_create(&udp_data.packet_reader[i], NULL, + udp_ipcp_packet_reader, NULL)) { + log_err("Failed to create reader thread."); + goto fail_packet_reader; + } + } + + for (i = 0; i < IPCP_UDP_WR_THR; ++i) { + if (pthread_create(&udp_data.packet_writer[i], NULL, + udp_ipcp_packet_writer, NULL)) { + log_err("Failed to create writer thread."); + goto fail_packet_writer; + } + } + + log_dbg("Bootstrapped " TYPE_STR " with pid %d.", getpid()); + log_dbg("Bound to IP address %s.", ipstr); + log_dbg("Using port %u.", udp->port); + if (is_addr_specified(&udp_data.dns_addr)) + log_dbg("DNS server address is %s.", dnsstr); + else + log_dbg("DNS server not in use."); + + return 0; + + fail_packet_writer: + while (i-- > 0) { + pthread_cancel(udp_data.packet_writer[i]); + pthread_join(udp_data.packet_writer[i], NULL); + } + i = IPCP_UDP_RD_THR; + fail_packet_reader: + while (i-- > 0) { + pthread_cancel(udp_data.packet_reader[i]); + pthread_join(udp_data.packet_reader[i], NULL); + } + pthread_cancel(udp_data.mgmt_handler); + pthread_join(udp_data.mgmt_handler, NULL); + fail_bind: + close(udp_data.s_fd); + fail_socket: + return -1; +} + +#ifdef HAVE_DDNS +/* FIXME: Dependency on nsupdate to be removed in the end */ +/* NOTE: Disgusted with this crap */ +static int ddns_send(char * cmd) +{ + pid_t pid; + int wstatus; + int pipe_fd[2]; + char * argv[] = {NSUPDATE_EXEC, 0}; + char * envp[] = {0}; + + if (pipe(pipe_fd)) { + log_err("Failed to create pipe: %s.", strerror(errno)); + return -1; + } + + pid = fork(); + if (pid == -1) { + log_err("Failed to fork: %s.", strerror(errno)); + close(pipe_fd[0]); + close(pipe_fd[1]); + return -1; + } + + if (pid == 0) { + close(pipe_fd[1]); + dup2(pipe_fd[0], 0); + execve(argv[0], &argv[0], envp); + log_err("Failed to execute: %s", strerror(errno)); + exit(1); + } + + close(pipe_fd[0]); + + if (write(pipe_fd[1], cmd, strlen(cmd)) == -1) { + log_err("Failed to communicate with nsupdate: %s.", + strerror(errno)); + close(pipe_fd[1]); + return -1; + } + + waitpid(pid, &wstatus, 0); + if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) + log_dbg("Succesfully communicated with DNS server."); + else + log_err("Failed to register with DNS server."); + + close(pipe_fd[1]); + + return 0; +} + +static struct __ADDR ddns_resolve(char * name, + struct __ADDR dns_addr) +{ + pid_t pid = -1; + int wstatus; + int pipe_fd[2]; + char dnsstr[__ADDRSTRLEN]; + char buf[IPCP_UDP_BUF_SIZE]; + ssize_t count = 0; + char * substr = NULL; + char * substr2 = NULL; + char * addr_str = "Address:"; + struct __ADDR ip_addr = __ADDR_ANY_INIT; + + if (__inet_ntop(&dns_addr, dnsstr) == NULL) + return ip_addr; + + if (pipe(pipe_fd)) { + log_err("Failed to create pipe: %s.", strerror(errno)); + return ip_addr; + } + + pid = fork(); + if (pid == -1) { + log_err("Failed to fork: %s.", strerror(errno)); + close(pipe_fd[0]); + close(pipe_fd[1]); + return ip_addr; + } + + if (pid == 0) { + char * argv[] = {NSLOOKUP_EXEC, name, dnsstr, 0}; + char * envp[] = {0}; + + close(pipe_fd[0]); + dup2(pipe_fd[1], 1); + execve(argv[0], &argv[0], envp); + log_err("Failed to execute: %s", strerror(errno)); + exit(1); + } + + close(pipe_fd[1]); + + count = read(pipe_fd[0], buf, IPCP_UDP_BUF_SIZE - 1); + if (count <= 0) { + log_err("Failed to communicate with nslookup."); + close(pipe_fd[0]); + return ip_addr; + } + + close(pipe_fd[0]); + + waitpid(pid, &wstatus, 0); + if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0 && + count != IPCP_UDP_BUF_SIZE - 1) + log_dbg("Succesfully communicated with nslookup."); + else + log_err("Failed to resolve DNS address."); + + buf[count] = '\0'; + substr = strtok(buf, "\n"); + while (substr != NULL) { + substr2 = substr; + substr = strtok(NULL, "\n"); + } + + if (substr2 == NULL || strstr(substr2, addr_str) == NULL) { + log_err("Failed to resolve DNS address."); + return ip_addr; + } + + if (inet_pton(__AF, substr2 + strlen(addr_str) + 1, &ip_addr) != 1) { + log_err("Failed to resolve DNS address."); + assert(!is_addr_specified(&ip_addr)); + return ip_addr; + } + + return ip_addr; +} +#endif + +static int udp_ipcp_reg(const uint8_t * hash) +{ +#ifdef HAVE_DDNS + char ipstr[__ADDRSTRLEN]; + char dnsstr[__ADDRSTRLEN]; + char cmd[1000]; + struct __ADDR dns_addr; + struct __ADDR ip_addr; +#endif + char * hashstr; + + hashstr = malloc(ipcp_dir_hash_strlen() + 1); + if (hashstr == NULL) { + log_err("Failed to malloc hashstr."); + return -1; + } + + assert(hash); + + ipcp_hash_str(hashstr, hash); + + if (shim_data_reg_add_entry(udp_data.shim_data, hash)) { + log_err("Failed to add " HASH_FMT32 " to local registry.", + HASH_VAL32(hash)); + free(hashstr); + return -1; + } + +#ifdef HAVE_DDNS + /* register application with DNS server */ + + dns_addr = udp_data.dns_addr; + + if (is_addr_specified(&dns_addr)) { +#ifdef BUILD_IPCP_UDP4 + ip_addr = udp_data.s_saddr.sin_addr; +#else + ip_addr = udp_data.s_saddr.sin6_addr; +#endif + if (__inet_ntop(&ip_addr, ipstr) == NULL) { + log_err("Failed to convert IP address to string."); + free(hashstr); + return -1; + } + + if (__inet_ntop(&dns_addr, dnsstr) == NULL) { + log_err("Failed to convert DNS address to string."); + free(hashstr); + return -1; + } + + sprintf(cmd, "server %s\nupdate add %s %d A %s\nsend\nquit\n", + dnsstr, hashstr, DNS_TTL, ipstr); + + if (ddns_send(cmd)) { + log_err("Failed to send DDNS message."); + shim_data_reg_del_entry(udp_data.shim_data, hash); + free(hashstr); + return -1; + } + } +#endif + free(hashstr); + + return 0; +} + +static int udp_ipcp_unreg(const uint8_t * hash) +{ +#ifdef HAVE_DDNS + char dnsstr[__ADDRSTRLEN]; + /* max DNS name length + max IP length + max command length */ + char cmd[100]; + struct __ADDR dns_addr; +#endif + char * hashstr; + + assert(hash); + + hashstr = malloc(ipcp_dir_hash_strlen() + 1); + if (hashstr == NULL) { + log_err("Failed to malloc hashstr."); + return -1; + } + + ipcp_hash_str(hashstr, hash); + +#ifdef HAVE_DDNS + /* unregister application with DNS server */ + + dns_addr = udp_data.dns_addr; + + if (is_addr_specified(&dns_addr)) { + if (__inet_ntop(&dns_addr, dnsstr) == NULL) { + log_err("Failed to convert DNS address to string."); + free(hashstr); + return -1; + } + sprintf(cmd, "server %s\nupdate delete %s A\nsend\nquit\n", + dnsstr, hashstr); + + ddns_send(cmd); + } +#endif + + shim_data_reg_del_entry(udp_data.shim_data, hash); + + free(hashstr); + + return 0; +} + +static int udp_ipcp_query(const uint8_t * hash) +{ + struct addr addr = {}; + char * hashstr; + struct addrinfo hints; + struct addrinfo * ai; +#ifdef HAVE_DDNS + struct __ADDR dns_addr = __ADDR_ANY_INIT; + struct __ADDR ip_addr = __ADDR_ANY_INIT; +#endif + assert(hash); + + hashstr = malloc(ipcp_dir_hash_strlen() + 1); + if (hashstr == NULL) { + log_err("Failed to malloc hashstr."); + return -ENOMEM; + } + + ipcp_hash_str(hashstr, hash); + + if (shim_data_dir_has(udp_data.shim_data, hash)) { + free(hashstr); + return 0; + } + +#ifdef HAVE_DDNS + dns_addr = udp_data.dns_addr; + + if (is_addr_specified(&dns_addr)) { + ip_addr = ddns_resolve(hashstr, dns_addr); + if (!is_addr_specified(&ip_addr)) { + log_err("Could not resolve %s.", hashstr); + free(hashstr); + return -1; + } + } else { +#endif + memset(&hints, 0, sizeof(hints)); + + hints.ai_family = __AF; + if (getaddrinfo(hashstr, NULL, &hints, &ai) != 0) { + log_err("Could not resolve %s: %s.", hashstr, + gai_strerror(errno)); + free(hashstr); + return -1; + } + + if (ai->ai_family != __AF) { + log_err("Wrong addres family for %s.", hashstr); + freeaddrinfo(ai); + free(hashstr); + return -1; + } + + #ifdef BUILD_IPCP_UDP4 + addr.ip4 = ((struct sockaddr_in *) (ai->ai_addr))->sin_addr; + #else + addr.ip6 = ((struct sockaddr_in6 *) (ai->ai_addr))->sin6_addr; + #endif + freeaddrinfo(ai); +#ifdef HAVE_DDNS + } +#endif + if (shim_data_dir_add_entry(udp_data.shim_data, hash, addr)) { + log_err("Failed to add directory entry."); + free(hashstr); + return -1; + } + + free(hashstr); + + return 0; +} + +static int udp_ipcp_flow_alloc(int fd, + const uint8_t * dst, + qosspec_t qs, + const buffer_t * data) +{ + struct __SOCKADDR r_saddr; /* Server address */ + struct __ADDR ip_addr; + struct addr addr; + char ipstr[__ADDRSTRLEN]; + + (void) qs; + + assert(dst); + + if (!shim_data_dir_has(udp_data.shim_data, dst)) { + log_err("Could not resolve destination."); + return -1; + } + + addr = shim_data_dir_get_addr(udp_data.shim_data, dst); +#ifdef BUILD_IPCP_UDP4 + ip_addr = addr.ip4; +#else + ip_addr = addr.ip6; +#endif + if (__inet_ntop(&ip_addr, ipstr) == NULL) { + log_err("Could not convert IP address."); + return -1; + } + + log_dbg("Destination " HASH_FMT32 " resolved at IP %s.", + HASH_VAL32(dst), ipstr); + + memset((char *) &r_saddr, 0, sizeof(r_saddr)); +#ifdef BUILD_IPCP_UDP4 + r_saddr.sin_family = AF_INET; + r_saddr.sin_addr = addr.ip4; + r_saddr.sin_port = udp_data.s_saddr.sin_port; +#else + r_saddr.sin6_family = AF_INET6; + r_saddr.sin6_addr = addr.ip6; + r_saddr.sin6_port = udp_data.s_saddr.sin6_port; +#endif + + if (udp_ipcp_port_alloc(&r_saddr, fd, dst, qs, data) < 0) { + log_err("Could not allocate port."); + return -1; + } + + pthread_rwlock_wrlock(&udp_data.flows_lock); + + udp_data.fd_to_uf[fd].d_eid = -1; + udp_data.fd_to_uf[fd].r_saddr = r_saddr; + + pthread_rwlock_unlock(&udp_data.flows_lock); + + fset_add(udp_data.np1_flows, fd); + + return 0; +} + +static int udp_ipcp_flow_alloc_resp(int fd, + int resp, + const buffer_t * data) +{ + struct __SOCKADDR saddr; + int d_eid; + + if (ipcp_wait_flow_resp(fd) < 0) { + log_err("Failed to wait for flow response."); + return -1; + } + + pthread_rwlock_rdlock(&udp_data.flows_lock); + + saddr = udp_data.fd_to_uf[fd].r_saddr; + d_eid = udp_data.fd_to_uf[fd].d_eid; + + pthread_rwlock_unlock(&udp_data.flows_lock); + + if (udp_ipcp_port_alloc_resp(&saddr, d_eid, fd, resp, data) < 0) { + fset_del(udp_data.np1_flows, fd); + log_err("Failed to respond to flow request."); + return -1; + } + + fset_add(udp_data.np1_flows, fd); + + return 0; +} + +static int udp_ipcp_flow_dealloc(int fd) +{ + ipcp_flow_fini(fd); + + fset_del(udp_data.np1_flows, fd); + + pthread_rwlock_wrlock(&udp_data.flows_lock); + + udp_data.fd_to_uf[fd].d_eid = -1; + memset(&udp_data.fd_to_uf[fd].r_saddr, 0, SADDR_SIZE); + + pthread_rwlock_unlock(&udp_data.flows_lock); + + ipcp_flow_dealloc(fd); + + return 0; +} + +static struct ipcp_ops udp_ops = { + .ipcp_bootstrap = udp_ipcp_bootstrap, + .ipcp_enroll = NULL, + .ipcp_connect = NULL, + .ipcp_disconnect = NULL, + .ipcp_reg = udp_ipcp_reg, + .ipcp_unreg = udp_ipcp_unreg, + .ipcp_query = udp_ipcp_query, + .ipcp_flow_alloc = udp_ipcp_flow_alloc, + .ipcp_flow_join = NULL, + .ipcp_flow_alloc_resp = udp_ipcp_flow_alloc_resp, + .ipcp_flow_dealloc = udp_ipcp_flow_dealloc +}; + +int main(int argc, + char * argv[]) +{ + int i; + + + if (udp_data_init() < 0) { + log_err("Failed to init udp data."); + goto fail_data_init; + } + + if (ipcp_init(argc, argv, &udp_ops, THIS_TYPE) < 0) { + log_err("Failed to initialize IPCP."); + goto fail_init; + } + + if (ipcp_start() < 0) { + log_err("Failed to start IPCP."); + goto fail_start; + } + + ipcp_sigwait(); + + if (ipcp_get_state() == IPCP_SHUTDOWN) { + for (i = 0; i < IPCP_UDP_WR_THR; ++i) + pthread_cancel(udp_data.packet_writer[i]); + for (i = 0; i < IPCP_UDP_RD_THR; ++i) + pthread_cancel(udp_data.packet_reader[i]); + pthread_cancel(udp_data.mgmt_handler); + + for (i = 0; i < IPCP_UDP_WR_THR; ++i) + pthread_join(udp_data.packet_writer[i], NULL); + for (i = 0; i < IPCP_UDP_RD_THR; ++i) + pthread_join(udp_data.packet_reader[i], NULL); + pthread_join(udp_data.mgmt_handler, NULL); + close(udp_data.s_fd); + } + + ipcp_stop(); + + ipcp_fini(); + + udp_data_fini(); + + exit(EXIT_SUCCESS); + + fail_start: + ipcp_fini(); + fail_init: + udp_data_fini(); + fail_data_init: + exit(EXIT_FAILURE); +} diff --git a/src/ipcpd/udp/udp4.c b/src/ipcpd/udp/udp4.c new file mode 100644 index 00000000..07d5f818 --- /dev/null +++ b/src/ipcpd/udp/udp4.c @@ -0,0 +1,42 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * IPC process over UDP/IPv4 + * + * Dimitri Staessens + * Sander Vrijders + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#if defined(__linux__) || defined(__CYGWIN__) +#define _DEFAULT_SOURCE +#else +#define _POSIX_C_SOURCE 200112L +#endif + +#include + +#define BUILD_IPCP_UDP4 +#define THIS_TYPE IPCP_UDP4 +#define TYPE_STR "IPCP over UDP/IPv4" +#define OUROBOROS_PREFIX "ipcpd/udp4" +#define IPCP_UDP_MAX_PACKET_SIZE 8980 +#define __AF AF_INET +#define __ADDRSTRLEN INET_ADDRSTRLEN +#define __SOCKADDR sockaddr_in +#define __ADDR in_addr +#define __ADDR_ANY_INIT { .s_addr = INADDR_ANY } + +#include "udp.c" diff --git a/src/ipcpd/udp/udp6.c b/src/ipcpd/udp/udp6.c new file mode 100644 index 00000000..b7924a3f --- /dev/null +++ b/src/ipcpd/udp/udp6.c @@ -0,0 +1,42 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * IPC process over UDP/IPv6 + * + * Dimitri Staessens + * Sander Vrijders + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#if defined(__linux__) || defined(__CYGWIN__) +#define _DEFAULT_SOURCE +#else +#define _POSIX_C_SOURCE 200112L +#endif + +#include + +#define BUILD_IPCP_UDP6 +#define THIS_TYPE IPCP_UDP6 +#define TYPE_STR "IPCP over UDP/IPv6" +#define OUROBOROS_PREFIX "ipcpd/udp6" +#define IPCP_UDP_MAX_PACKET_SIZE 8952 +#define __AF AF_INET6 +#define __ADDRSTRLEN INET6_ADDRSTRLEN +#define __SOCKADDR sockaddr_in6 +#define __ADDR in6_addr +#define __ADDR_ANY_INIT IN6ADDR_ANY_INIT + +#include "udp.c" diff --git a/src/ipcpd/unicast/CMakeLists.txt b/src/ipcpd/unicast/CMakeLists.txt index 2df0bae0..ced045e6 100644 --- a/src/ipcpd/unicast/CMakeLists.txt +++ b/src/ipcpd/unicast/CMakeLists.txt @@ -73,7 +73,4 @@ install(TARGETS ipcpd-unicast RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}) add_subdirectory(pff/tests) add_subdirectory(routing/tests) - -if (NOT GNU) - add_subdirectory(dir/tests) -endif () +add_subdirectory(dir/tests) -- cgit v1.2.3