summaryrefslogtreecommitdiff
path: root/src/ipcpd/udp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ipcpd/udp')
-rw-r--r--src/ipcpd/udp/CMakeLists.txt75
-rw-r--r--src/ipcpd/udp/main.c1230
-rw-r--r--src/ipcpd/udp/udp.c1333
-rw-r--r--src/ipcpd/udp/udp4.c42
-rw-r--r--src/ipcpd/udp/udp6.c42
5 files changed, 1428 insertions, 1294 deletions
diff --git a/src/ipcpd/udp/CMakeLists.txt b/src/ipcpd/udp/CMakeLists.txt
index f1a29ef6..a98f0919 100644
--- a/src/ipcpd/udp/CMakeLists.txt
+++ b/src/ipcpd/udp/CMakeLists.txt
@@ -1,67 +1,14 @@
-get_filename_component(CURRENT_SOURCE_PARENT_DIR
- ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
-get_filename_component(CURRENT_BINARY_PARENT_DIR
- ${CMAKE_CURRENT_BINARY_DIR} DIRECTORY)
+# UDP IPCPs build configuration (UDP4 and UDP6)
+# DDNS detection is in cmake/dependencies/udp/ddns.cmake
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
+add_executable(${IPCP_UDP4_TARGET} udp4.c ${IPCP_SOURCES})
+add_executable(${IPCP_UDP6_TARGET} udp6.c ${IPCP_SOURCES})
-include_directories(${CURRENT_SOURCE_PARENT_DIR})
-include_directories(${CURRENT_BINARY_PARENT_DIR})
+foreach(target ${IPCP_UDP4_TARGET} ${IPCP_UDP6_TARGET})
+ target_include_directories(${target} PRIVATE ${IPCP_INCLUDE_DIRS})
+ target_link_libraries(${target} PRIVATE ouroboros-dev)
+ ouroboros_target_debug_definitions(${target})
+endforeach()
-include_directories(${CMAKE_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR}/include)
-
-set(IPCP_UDP_TARGET ipcpd-udp CACHE INTERNAL "")
-
-set(UDP_SOURCES
- # Add source files here
- ${CMAKE_CURRENT_SOURCE_DIR}/main.c
- )
-
-add_executable(ipcpd-udp ${UDP_SOURCES} ${IPCP_SOURCES})
-
-target_link_libraries(ipcpd-udp LINK_PUBLIC ouroboros-dev)
-
-# Find the nsupdate executable
-find_program(NSUPDATE_EXECUTABLE
- NAMES nsupdate
- DOC "The nsupdate tool that enables DDNS")
-
-# Find the nslookup executable
-find_program(NSLOOKUP_EXECUTABLE
- NAMES nslookup
- DOC "The nslookup tool that resolves DNS names")
-
-mark_as_advanced(NSLOOKUP_EXECUTABLE NSUPDATE_EXECUTABLE)
-
-if (NSLOOKUP_EXECUTABLE AND NSUPDATE_EXECUTABLE)
- set(DISABLE_DDNS FALSE CACHE BOOL "Disable DDNS support")
- if (NOT DISABLE_DNS)
- message(STATUS "DDNS support enabled")
- set(HAVE_DDNS TRUE CACHE INTERNAL "")
- else ()
- message(STATUS "DDNS support disabled by user")
- unset(HAVE_DDNS CACHE)
- endif ()
-else ()
- if (NSLOOKUP_EXECUTABLE)
- message(STATUS "Install nsupdate to enable DDNS support")
- elseif (NSUPDATE_EXECUTABLE)
- message(STATUS "Install nslookup to enable DDNS support")
- else ()
- message(STATUS "Install nslookup and nsupdate to enable DDNS support")
- endif ()
-endif ()
-
-set(IPCP_UDP_RD_THR 3 CACHE STRING
- "Number of reader threads in UDP IPCP")
-set(IPCP_UDP_WR_THR 3 CACHE STRING
- "Number of writer threads in UDP IPCP")
-
-include(AddCompileFlags)
-if (CMAKE_BUILD_TYPE MATCHES "Debug*")
- add_compile_flags(ipcpd-udp -DCONFIG_OUROBOROS_DEBUG)
-endif ()
-
-install(TARGETS ipcpd-udp RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR})
+install(TARGETS ${IPCP_UDP4_TARGET} ${IPCP_UDP6_TARGET}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR})
diff --git a/src/ipcpd/udp/main.c b/src/ipcpd/udp/main.c
deleted file mode 100644
index 9e1900ef..00000000
--- a/src/ipcpd/udp/main.c
+++ /dev/null
@@ -1,1230 +0,0 @@
-/*
- * Ouroboros - Copyright (C) 2016 - 2020
- *
- * IPC process over UDP
- *
- * Dimitri Staessens <dimitri.staessens@ugent.be>
- * Sander Vrijders <sander.vrijders@ugent.be>
- *
- * 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 <ouroboros/bitmap.h>
-#include <ouroboros/hash.h>
-#include <ouroboros/list.h>
-#include <ouroboros/utils.h>
-#include <ouroboros/dev.h>
-#include <ouroboros/ipcp-dev.h>
-#include <ouroboros/fqueue.h>
-#include <ouroboros/errno.h>
-#include <ouroboros/logs.h>
-
-#include "ipcp.h"
-#include "shim-data.h"
-
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <pthread.h>
-#include <sys/wait.h>
-#include <fcntl.h>
-
-#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 FD_UPDATE_TIMEOUT 100 /* microseconds */
-
-#define SERV_PORT udp_data.s_saddr.sin_port;
-#define SERV_SADDR ((struct sockaddr *) &udp_data.s_saddr)
-#define CLNT_SADDR ((struct sockaddr *) &udp_data.c_saddr)
-#define SERV_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
-
-struct ipcp ipcpi;
-
-/* 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;
- 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;
- /* IP details are stored through connect(). */
- int skfd;
-};
-
-struct {
- struct shim_data * shim_data;
-
- uint32_t dns_addr;
- /* server socket */
- struct sockaddr_in s_saddr;
- int s_fd;
- /* client port */
- int clt_port;
-
- 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;
-
- if (pthread_rwlock_init(&udp_data.flows_lock, NULL))
- goto fail_rwlock_init;
-
- if (pthread_cond_init(&udp_data.mgmt_cond, NULL))
- 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].skfd = -1;
- 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;
-
- 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_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 ipcp_udp_port_alloc(int skfd,
- uint32_t s_eid,
- const uint8_t * dst,
- qosspec_t qs,
- const void * data,
- size_t dlen)
-{
- uint8_t * buf;
- struct mgmt_msg * msg;
- size_t len;
-
- assert(dlen > 0 ? data != NULL : data == NULL);
-
- len = sizeof(*msg) + ipcp_dir_hash_len();
-
- buf = malloc(len + dlen);
- 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);
-
- memcpy(msg + 1, dst, ipcp_dir_hash_len());
- memcpy(buf + len, data, dlen);
-
- if (write(skfd, msg, len + dlen) < 0) {
- free(buf);
- return -1;
- }
-
- free(buf);
-
- return 0;
-}
-
-static int ipcp_udp_port_alloc_resp(int skfd,
- uint32_t s_eid,
- uint32_t d_eid,
- int8_t response,
- const void * data,
- size_t len)
-{
- struct mgmt_msg * msg;
-
- msg = malloc(sizeof(*msg) + 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;
-
- memcpy(msg + 1, data, len);
-
- if (write(skfd, msg, sizeof(*msg) + len) < 0) {
- free(msg);
- return -1;
- }
-
- free(msg);
-
- return 0;
-}
-
-static int ipcp_udp_port_req(struct sockaddr_in * c_saddr,
- int d_eid,
- const uint8_t * dst,
- qosspec_t qs,
- const void * data,
- size_t len)
-{
- struct timespec ts = {0, FD_UPDATE_TIMEOUT * 1000};
- struct timespec abstime;
- int skfd;
- int fd;
-
- skfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- if (skfd < 0) {
- log_err("Could not create UDP socket.");
- return -1;
- }
-
- /* Remote listens on server port. Mod of c_saddr allowed. */
- c_saddr->sin_port = udp_data.s_saddr.sin_port;
-
- /* Connect stores the remote address in the file descriptor. */
- if (connect(skfd, (struct sockaddr *) c_saddr, sizeof(*c_saddr)) < 0) {
- log_err("Could not connect to remote UDP client.");
- close(skfd);
- return -1;
- }
-
- clock_gettime(PTHREAD_COND_CLOCK, &abstime);
-
- pthread_mutex_lock(&ipcpi.alloc_lock);
-
- while (ipcpi.alloc_id != -1 && ipcp_get_state() == IPCP_OPERATIONAL) {
- ts_add(&abstime, &ts, &abstime);
- pthread_cond_timedwait(&ipcpi.alloc_cond, &ipcpi.alloc_lock,
- &abstime);
- }
-
- if (ipcp_get_state() != IPCP_OPERATIONAL) {
- log_dbg("Won't allocate over non-operational IPCP.");
- pthread_mutex_unlock(&ipcpi.alloc_lock);
- close(skfd);
- return -1;
- }
-
- /* reply to IRM */
- fd = ipcp_flow_req_arr(dst, ipcp_dir_hash_len(), qs, data, len);
- if (fd < 0) {
- pthread_mutex_unlock(&ipcpi.alloc_lock);
- log_err("Could not get new flow from IRMd.");
- close(skfd);
- return -1;
- }
-
- pthread_rwlock_wrlock(&udp_data.flows_lock);
-
- udp_data.fd_to_uf[fd].skfd = skfd;
- udp_data.fd_to_uf[fd].d_eid = d_eid;
-
- pthread_rwlock_unlock(&udp_data.flows_lock);
-
- ipcpi.alloc_id = fd;
- pthread_cond_broadcast(&ipcpi.alloc_cond);
-
- pthread_mutex_unlock(&ipcpi.alloc_lock);
-
- log_dbg("Pending allocation request, fd %d, remote eid %d.",
- fd, d_eid);
-
- return 0;
-}
-
-static int ipcp_udp_port_alloc_reply(uint32_t s_eid,
- uint32_t d_eid,
- int8_t response,
- const void * data,
- size_t len)
-{
- struct sockaddr_in t_saddr;
- socklen_t t_saddr_len;
- int ret = 0;
- int skfd = -1;
-
- t_saddr_len = sizeof(t_saddr);
-
- pthread_rwlock_wrlock(&udp_data.flows_lock);
-
- skfd = udp_data.fd_to_uf[s_eid].skfd;
- if (skfd < 0) {
- pthread_rwlock_unlock(&udp_data.flows_lock);
- log_err("Got reply for unknown UDP eid: %u.", s_eid);
- return -1;
- }
-
- udp_data.fd_to_uf[s_eid].d_eid = d_eid;
-
- pthread_rwlock_unlock(&udp_data.flows_lock);
-
- if (getpeername(skfd, (struct sockaddr *) &t_saddr, &t_saddr_len) < 0) {
- log_dbg("Flow with fd %d has no peer.", s_eid);
- close(skfd);
- return -1;
- }
-
- if (connect(skfd, (struct sockaddr *) &t_saddr, sizeof(t_saddr)) < 0) {
- log_dbg("Could not connect flow to remote.");
- close(skfd);
- return -1;
- }
-
- if (ipcp_flow_alloc_reply(s_eid, response, data, len) < 0) {
- log_dbg("Failed to reply to flow allocation.");
- return -1;
- }
-
- log_dbg("Flow allocation completed on eids (%d, %d).",
- s_eid, d_eid);
-
- return ret;
-}
-
-static int ipcp_udp_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;
-
- msg = (struct mgmt_msg *) buf;
-
- switch (msg->code) {
- case FLOW_REQ:
- msg_len = sizeof(*msg) + ipcp_dir_hash_len();
-
- assert(len >= msg_len);
-
- qs.delay = ntoh32(msg->delay);
- qs.bandwidth = ntoh64(msg->bandwidth);
- qs.availability = msg->availability;
- qs.loss = ntoh32(msg->loss);
- qs.ber = ntoh32(msg->ber);
- qs.in_order = msg->in_order;
- qs.max_gap = ntoh32(msg->max_gap);
- qs.cypher_s = ntoh16(msg->cypher_s);
-
- return ipcp_udp_port_req(&c_saddr, ntoh32(msg->s_eid),
- (uint8_t *) (msg + 1), qs,
- buf + msg_len,
- len - msg_len);
- case FLOW_REPLY:
- assert(len >= sizeof(*msg));
-
- return ipcp_udp_port_alloc_reply(ntoh32(msg->s_eid),
- ntoh32(msg->d_eid),
- msg->response,
- buf + sizeof(*msg),
- len - sizeof(*msg));
- default:
- log_err("Unknown message received %d.", msg->code);
- return -1;
- }
-}
-
-static void * ipcp_udp_mgmt_handler(void * o)
-{
- (void) o;
-
- pthread_cleanup_push((void (*)(void *)) pthread_mutex_unlock,
- (void *) &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);
-
- ipcp_udp_mgmt_frame(frame->buf, frame->len, frame->r_saddr);
-
- free(frame);
- }
-
- pthread_cleanup_pop(false);
-
- return (void *) 0;
-}
-
-static void * ipcp_udp_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;
-
- data = buf + sizeof(uint32_t);
- eid_p = (uint32_t *) buf;
-
- while (true) {
- struct mgmt_frame * frame;
- struct sockaddr_in r_saddr;
- socklen_t len;
-
- 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;
- }
-
- flow_write(eid, data, n - sizeof(eid));
- }
-
- return 0;
-}
-
-static void cleanup_writer(void * o)
-{
- fqueue_destroy((fqueue_t *) o);
-}
-
-static void * ipcp_udp_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_writer, fq);
-
- while (true) {
- int fd;
- int eid;
- 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 (ipcp_flow_read(fd, &sdb)) {
- log_dbg("Bad read from fd %d.", fd);
- continue;
- }
-
- len = shm_du_buff_tail(sdb) - shm_du_buff_head(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);
- fd = udp_data.fd_to_uf[fd].skfd;
-
- pthread_rwlock_unlock(&udp_data.flows_lock);
-
- memcpy(buf, &eid, sizeof(eid));
-
- pthread_cleanup_push((void (*)(void *))
- ipcp_sdb_release, (void *) sdb);
-
- if (write(fd, buf, len + OUR_HEADER_LEN) < 0)
- log_err("Failed to send packet.");
-
- pthread_cleanup_pop(true);
- }
- }
-
- pthread_cleanup_pop(true);
-
- return (void *) 1;
-}
-
-static int ipcp_udp_bootstrap(const struct ipcp_config * conf)
-{
- char ipstr[INET_ADDRSTRLEN];
- char dnsstr[INET_ADDRSTRLEN];
- char portstr[128]; /* port is max 64535 = 5 chars */
- int i = 1;
-
- assert(conf);
- assert(conf->type == THIS_TYPE);
-
- if (inet_ntop(AF_INET, &conf->ip_addr, ipstr, INET_ADDRSTRLEN)
- == NULL) {
- log_err("Failed to convert IP address");
- return -1;
- }
-
- if (conf->dns_addr != 0) {
- if (inet_ntop(AF_INET, &conf->dns_addr, dnsstr, INET_ADDRSTRLEN)
- == NULL) {
- log_err("Failed to convert DNS address");
- 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;
- }
-
- if (setsockopt(udp_data.s_fd, SOL_SOCKET, SO_REUSEADDR,
- &i, sizeof(i)) < 0)
- log_warn("Failed to set SO_REUSEADDR.");
-
- memset((char *) &udp_data.s_saddr, 0, sizeof(udp_data.s_saddr));
- udp_data.s_saddr.sin_family = AF_INET;
- udp_data.s_saddr.sin_addr.s_addr = conf->ip_addr;
- udp_data.s_saddr.sin_port = htons(conf->srv_port);
-
- if (bind(udp_data.s_fd, SERV_SADDR, SERV_SADDR_SIZE) < 0) {
- log_err("Couldn't bind to %s.", ipstr);
- goto fail_bind;
- }
-
- udp_data.dns_addr = conf->dns_addr;
- udp_data.clt_port = htons(conf->clt_port);
-
- ipcp_set_state(IPCP_OPERATIONAL);
-
- if (pthread_create(&udp_data.mgmt_handler, NULL,
- ipcp_udp_mgmt_handler, NULL)) {
- ipcp_set_state(IPCP_INIT);
- goto fail_bind;
- }
-
- for (i = 0; i < IPCP_UDP_RD_THR; ++i) {
- if (pthread_create(&udp_data.packet_reader[i], NULL,
- ipcp_udp_packet_reader, NULL)) {
- ipcp_set_state(IPCP_INIT);
- goto fail_packet_reader;
- }
- }
-
- for (i = 0; i < IPCP_UDP_WR_THR; ++i) {
- if (pthread_create(&udp_data.packet_writer[i], NULL,
- ipcp_udp_packet_writer, NULL)) {
- ipcp_set_state(IPCP_INIT);
- goto fail_packet_writer;
- }
- }
-
- sprintf(portstr, "%d", conf->clt_port);
-
- log_dbg("Bootstrapped IPCP over UDP with pid %d.", getpid());
- log_dbg("Bound to IP address %s.", ipstr);
- log_dbg("Client port is %s.", conf->clt_port == 0 ? "random" : portstr);
- log_dbg("Server port is %u.", conf->srv_port);
- log_dbg("DNS server address is %s.", dnsstr);
-
- 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 = -1;
- int wstatus;
- int pipe_fd[2];
- char * argv[] = {NSUPDATE_EXEC, 0};
- char * envp[] = {0};
-
- if (pipe(pipe_fd)) {
- log_err("Failed to create pipe.");
- return -1;
- }
-
- pid = fork();
- if (pid == -1) {
- log_err("Failed to fork.");
- return -1;
- }
-
- if (pid == 0) {
- close(pipe_fd[1]);
- dup2(pipe_fd[0], 0);
- execve(argv[0], &argv[0], envp);
- }
-
- close(pipe_fd[0]);
-
- if (write(pipe_fd[1], cmd, strlen(cmd)) == -1) {
- log_err("Failed to communicate with nsupdate.");
- 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 (inet_ntop(AF_INET, &dns_addr, dnsstr, INET_ADDRSTRLEN) == NULL)
- return 0;
-
- if (pipe(pipe_fd)) {
- log_err("Failed to create pipe.");
- return 0;
- }
-
- pid = fork();
- if (pid == -1) {
- log_err("Failed to fork.");
- return 0;
- }
-
- 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);
- }
-
- close(pipe_fd[1]);
-
- count = read(pipe_fd[0], buf, IPCP_UDP_BUF_SIZE);
- 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)
- 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 ipcp_udp_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)
- return -1;
-
- assert(hash);
-
- ipcp_hash_str(hashstr, hash);
-
- if (shim_data_reg_add_entry(udp_data.shim_data, hash)) {
- log_err("Failed to add " HASH_FMT " to local registry.",
- HASH_VAL(hash));
- 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 (inet_ntop(AF_INET, &ip_addr,
- ipstr, INET_ADDRSTRLEN) == NULL) {
- free(hashstr);
- return -1;
- }
-
- if (inet_ntop(AF_INET, &dns_addr,
- dnsstr, INET_ADDRSTRLEN) == NULL) {
- 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)) {
- shim_data_reg_del_entry(udp_data.shim_data, hash);
- free(hashstr);
- return -1;
- }
- }
-#endif
- log_dbg("Registered " HASH_FMT ".", HASH_VAL(hash));
-
- free(hashstr);
-
- return 0;
-}
-
-static int ipcp_udp_unreg(const uint8_t * hash)
-{
-#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)
- 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 (inet_ntop(AF_INET, &dns_addr, dnsstr, INET_ADDRSTRLEN)
- == NULL) {
- 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);
-
- log_dbg("Unregistered " HASH_FMT ".", HASH_VAL(hash));
-
- free(hashstr);
-
- return 0;
-}
-
-static int ipcp_udp_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)
- 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_dbg("Could not resolve %s.", hashstr);
- free(hashstr);
- return -1;
- }
- } else {
-#endif
- h = gethostbyname(hashstr);
- if (h == NULL) {
- log_dbg("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 ipcp_udp_flow_alloc(int fd,
- const uint8_t * dst,
- qosspec_t qs,
- const void * data,
- size_t len)
-{
- struct sockaddr_in r_saddr; /* Server address */
- struct sockaddr_in c_saddr; /* Client address */
- socklen_t c_saddr_len;
- int skfd;
- uint32_t ip_addr = 0;
- char ip_str[INET_ADDRSTRLEN];
-
- c_saddr_len = sizeof(c_saddr);
-
- log_dbg("Allocating flow to " HASH_FMT ".", HASH_VAL(dst));
-
- (void) qs;
-
- assert(dst);
-
- skfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- if (skfd < 0) {
- log_err("Could not create socket.");
- return -1;
- }
-
- /* This socket is for the flow. */
- memset((char *) &c_saddr, 0, sizeof(c_saddr));
- c_saddr.sin_family = AF_INET;
- c_saddr.sin_addr.s_addr = LOCAL_IP;
- c_saddr.sin_port = udp_data.clt_port;
-
- if (bind(skfd, (struct sockaddr *) &c_saddr, sizeof(c_saddr)) < 0) {
- log_dbg("Could not bind socket to client address.");
- close(skfd);
- return -1;
- }
-
- if (getsockname(skfd, (struct sockaddr *) &c_saddr, &c_saddr_len) < 0) {
- log_err("Could not get address from fd.");
- close(skfd);
- return -1;
- }
-
- if (!shim_data_dir_has(udp_data.shim_data, dst)) {
- log_dbg("Could not resolve destination.");
- close(skfd);
- return -1;
- }
-
- ip_addr = (uint32_t) shim_data_dir_get_addr(udp_data.shim_data, dst);
-
- inet_ntop(AF_INET, &ip_addr, ip_str, INET_ADDRSTRLEN);
- log_dbg("Destination UDP ipcp resolved at %s.", ip_str);
-
- /* Connect to server and store the remote IP address in the skfd. */
- 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 (connect(skfd, (struct sockaddr *) &r_saddr, sizeof(r_saddr)) < 0) {
- log_dbg("Could not connect socket to remote.");
- close(skfd);
- return -1;
- }
-
- if (ipcp_udp_port_alloc(skfd, fd, dst, qs, data, len) < 0) {
- log_err("Could not allocate port.");
- close(skfd);
- return -1;
- }
-
- pthread_rwlock_wrlock(&udp_data.flows_lock);
-
- udp_data.fd_to_uf[fd].d_eid = -1;
- udp_data.fd_to_uf[fd].skfd = skfd;
-
- pthread_rwlock_unlock(&udp_data.flows_lock);
-
- fset_add(udp_data.np1_flows, fd);
-
- log_dbg("Flow pending on fd %d, UDP src port %d, dst port %d.",
- fd, ntohs(c_saddr.sin_port), ntohs(r_saddr.sin_port));
-
- return 0;
-}
-
-static int ipcp_udp_flow_alloc_resp(int fd,
- int resp,
- const void * data,
- size_t len)
-{
- struct timespec ts = {0, FD_UPDATE_TIMEOUT * 1000};
- struct timespec abstime;
- int skfd;
- int d_eid;
-
- if (resp)
- return 0;
-
- clock_gettime(PTHREAD_COND_CLOCK, &abstime);
-
- pthread_mutex_lock(&ipcpi.alloc_lock);
-
- while (ipcpi.alloc_id != fd && ipcp_get_state() == IPCP_OPERATIONAL) {
- ts_add(&abstime, &ts, &abstime);
- pthread_cond_timedwait(&ipcpi.alloc_cond,
- &ipcpi.alloc_lock,
- &abstime);
- }
-
- if (ipcp_get_state() != IPCP_OPERATIONAL) {
- pthread_mutex_unlock(&ipcpi.alloc_lock);
- return -1;
- }
-
- ipcpi.alloc_id = -1;
- pthread_cond_broadcast(&ipcpi.alloc_cond);
-
- pthread_mutex_unlock(&ipcpi.alloc_lock);
-
- pthread_rwlock_rdlock(&udp_data.flows_lock);
-
- skfd = udp_data.fd_to_uf[fd].skfd;
- d_eid = udp_data.fd_to_uf[fd].d_eid;
-
- pthread_rwlock_unlock(&udp_data.flows_lock);
-
- if (ipcp_udp_port_alloc_resp(skfd, d_eid, fd, resp, data, len) < 0) {
- log_err("Failed to respond to flow request.");
- return -1;
- }
-
- fset_add(udp_data.np1_flows, fd);
-
- log_dbg("Accepted flow, fd %d on eid %d.",
- fd, d_eid);
-
- return 0;
-}
-
-static int ipcp_udp_flow_dealloc(int fd)
-{
- int skfd = -1;
-
- ipcp_flow_fini(fd);
-
- fset_del(udp_data.np1_flows, fd);
-
- pthread_rwlock_wrlock(&udp_data.flows_lock);
-
- skfd = udp_data.fd_to_uf[fd].skfd;
-
- udp_data.fd_to_uf[fd].d_eid = -1;
- udp_data.fd_to_uf[fd].skfd = -1;
-
- pthread_rwlock_unlock(&udp_data.flows_lock);
-
- close(skfd);
-
- flow_dealloc(fd);
-
- log_dbg("Flow with fd %d deallocated.", fd);
-
- return 0;
-}
-
-static struct ipcp_ops udp_ops = {
- .ipcp_bootstrap = ipcp_udp_bootstrap,
- .ipcp_enroll = NULL,
- .ipcp_connect = NULL,
- .ipcp_disconnect = NULL,
- .ipcp_reg = ipcp_udp_reg,
- .ipcp_unreg = ipcp_udp_unreg,
- .ipcp_query = ipcp_udp_query,
- .ipcp_flow_alloc = ipcp_udp_flow_alloc,
- .ipcp_flow_join = NULL,
- .ipcp_flow_alloc_resp = ipcp_udp_flow_alloc_resp,
- .ipcp_flow_dealloc = ipcp_udp_flow_dealloc
-};
-
-int main(int argc,
- char * argv[])
-{
- int i;
-
- if (ipcp_init(argc, argv, &udp_ops) < 0)
- goto fail_init;
-
- if (udp_data_init() < 0) {
- log_err("Failed to init udp data.");
- goto fail_data_init;
- }
-
- if (ipcp_boot() < 0) {
- log_err("Failed to boot IPCP.");
- goto fail_boot;
- }
-
- if (ipcp_create_r(0)) {
- log_err("Failed to notify IRMd we are initialized.");
- goto fail_create_r;
- }
-
- ipcp_shutdown();
-
- if (ipcp_get_state() == IPCP_SHUTDOWN) {
- for (i = 0; i < IPCP_UDP_RD_THR; ++i)
- pthread_cancel(udp_data.packet_reader[i]);
- for (i = 0; i < IPCP_UDP_WR_THR; ++i)
- pthread_cancel(udp_data.packet_writer[i]);
- pthread_cancel(udp_data.mgmt_handler);
-
- for (i = 0; i < IPCP_UDP_RD_THR; ++i)
- pthread_join(udp_data.packet_reader[i], NULL);
- for (i = 0; i < IPCP_UDP_WR_THR; ++i)
- pthread_join(udp_data.packet_writer[i], NULL);
- pthread_join(udp_data.mgmt_handler, NULL);
- }
-
- udp_data_fini();
-
- ipcp_fini();
-
- exit(EXIT_SUCCESS);
- fail_create_r:
- ipcp_set_state(IPCP_NULL);
- ipcp_shutdown();
- fail_boot:
- udp_data_fini();
- fail_data_init:
- ipcp_fini();
- fail_init:
- ipcp_create_r(-1);
- exit(EXIT_FAILURE);
-}
diff --git a/src/ipcpd/udp/udp.c b/src/ipcpd/udp/udp.c
new file mode 100644
index 00000000..db57e2f4
--- /dev/null
+++ b/src/ipcpd/udp/udp.c
@@ -0,0 +1,1333 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * IPC process over UDP
+ *
+ * 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 <ouroboros/bitmap.h>
+#include <ouroboros/endian.h>
+#include <ouroboros/hash.h>
+#include <ouroboros/list.h>
+#include <ouroboros/utils.h>
+#include <ouroboros/dev.h>
+#include <ouroboros/ipcp-dev.h>
+#include <ouroboros/np1_flow.h>
+#include <ouroboros/fqueue.h>
+#include <ouroboros/errno.h>
+#include <ouroboros/logs.h>
+#include <ouroboros/pthread.h>
+
+#include "ipcp.h"
+#include "np1.h"
+#include "shim-data.h"
+
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <unistd.h>
+#if defined(__linux__)
+#include <netinet/ip.h>
+#endif
+
+#define FLOW_REQ 1
+#define FLOW_REPLY 2
+#define FLOW_IRM_UPDATE 3
+
+#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;
+ int32_t response;
+ uint64_t bandwidth;
+ uint32_t delay;
+ uint32_t loss;
+ uint32_t ber;
+ uint32_t max_gap;
+ uint32_t timeout;
+ uint8_t code;
+ /* QoS parameters from spec */
+ uint8_t availability;
+ uint8_t service;
+} __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);
+}
+
+#if defined(BUILD_IPCP_UDP4)
+#define UDP_MTU_FALLBACK IPCP_UDP4_MTU
+#define UDP_IP_OVERHEAD 28U /* IPv4 + UDP */
+#else
+#define UDP_MTU_FALLBACK IPCP_UDP6_MTU
+#define UDP_IP_OVERHEAD 48U /* IPv6 + UDP */
+#endif
+
+static uint32_t udp_query_mtu(const struct __SOCKADDR * saddr)
+{
+#if defined(__linux__) && (defined(IP_MTU) || defined(IPV6_MTU))
+ int sock;
+ int mtu = 0;
+ socklen_t len = sizeof(mtu);
+
+ sock = socket(__AF, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock < 0)
+ return UDP_MTU_FALLBACK;
+
+ if (connect(sock, (const struct sockaddr *) saddr,
+ sizeof(*saddr)) < 0)
+ goto fallback;
+
+#if defined(BUILD_IPCP_UDP4) && defined(IP_MTU)
+ if (getsockopt(sock, IPPROTO_IP, IP_MTU, &mtu, &len) < 0)
+ goto fallback;
+#elif defined(BUILD_IPCP_UDP6) && defined(IPV6_MTU)
+ if (getsockopt(sock, IPPROTO_IPV6, IPV6_MTU, &mtu, &len) < 0)
+ goto fallback;
+#else
+ goto fallback;
+#endif
+ close(sock);
+
+ if (mtu <= (int) UDP_IP_OVERHEAD)
+ return UDP_MTU_FALLBACK;
+
+ return (uint32_t) mtu - UDP_IP_OVERHEAD;
+
+ fallback:
+ close(sock);
+#else
+ (void) saddr;
+#endif
+ return UDP_MTU_FALLBACK;
+}
+
+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;
+
+ memset(buf, 0, len + data->len);
+
+ 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->service = qs.service;
+ msg->max_gap = hton32(qs.max_gap);
+ 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,
+ int32_t response,
+ const buffer_t * data)
+{
+ struct mgmt_msg * msg;
+
+ msg = malloc(sizeof(*msg) + data->len);
+ if (msg == NULL)
+ return -1;
+
+ memset(msg, 0, sizeof(*msg) + data->len);
+
+ msg->eid = hton32(MGMT_EID);
+ msg->code = FLOW_REPLY;
+ msg->s_eid = hton32(s_eid);
+ msg->d_eid = hton32(d_eid);
+ msg->response = hton32(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_flow_update(int fd,
+ const buffer_t * data)
+{
+ struct mgmt_msg * msg;
+ struct __SOCKADDR r_saddr;
+ uint32_t d_eid;
+
+ msg = malloc(sizeof(*msg) + data->len);
+ if (msg == NULL)
+ return -1;
+
+ memset(msg, 0, sizeof(*msg) + data->len);
+
+ pthread_rwlock_rdlock(&udp_data.flows_lock);
+
+ r_saddr = udp_data.fd_to_uf[fd].r_saddr;
+ d_eid = (uint32_t) udp_data.fd_to_uf[fd].d_eid;
+
+ pthread_rwlock_unlock(&udp_data.flows_lock);
+
+ msg->eid = hton32(MGMT_EID);
+ msg->code = FLOW_IRM_UPDATE;
+ msg->s_eid = hton32(d_eid);
+ msg->d_eid = hton32((uint32_t) fd);
+
+ 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) {
+ log_err("Failed to send flow update: %s.", strerror(errno));
+ 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,
+ udp_query_mtu(c_saddr), 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,
+ int32_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,
+ udp_query_mtu(saddr), 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_flow_update_arr(const uint8_t * buf,
+ size_t len)
+{
+ struct mgmt_msg * msg;
+ buffer_t data;
+ int fd;
+ int flow_id;
+
+ msg = (struct mgmt_msg *) buf;
+
+ fd = (int) ntoh32(msg->s_eid);
+ if (fd < 0 || fd >= SYS_MAX_FLOWS) {
+ log_err("Flow update for invalid eid %d.", fd);
+ return -1;
+ }
+
+ data.len = len - sizeof(*msg);
+ data.data = (uint8_t *) buf + sizeof(*msg);
+
+ flow_id = np1_flow_id(fd);
+ if (flow_id < 0)
+ return -1;
+
+ if (ipcp_flow_update_arr(flow_id, &data) < 0) {
+ log_err("Failed to relay flow update on fd %d.", fd);
+ return -1;
+ }
+
+ 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;
+
+ /* Defence against malformed/corrupted wire input. */
+ if (len < sizeof(*msg))
+ return -1;
+
+ msg = (struct mgmt_msg *) buf;
+
+ switch (msg->code) {
+ case FLOW_REQ:
+ msg_len = sizeof(*msg) + ipcp_dir_hash_len();
+
+ if (len < msg_len)
+ return -1;
+
+ 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.service = msg->service;
+ qs.max_gap = ntoh32(msg->max_gap);
+ 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:
+ 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),
+ ntoh32(msg->response),
+ &data);
+ case FLOW_IRM_UPDATE:
+ return udp_ipcp_flow_update_arr(buf, len);
+ 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 ssm_pk_buff * spb;
+ 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_spb_reserve(&spb, n))
+ continue;
+
+ head = ssm_pk_buff_head(spb);
+ memcpy(head, data, n);
+ if (np1_flow_write(eid, spb, NP1_GET_POOL(eid)) < 0)
+ ipcp_spb_release(spb);
+ }
+
+ return (void *) 0;
+}
+
+static void cleanup_fqueue(void * fq)
+{
+ fqueue_destroy((fqueue_t *) fq);
+}
+
+static void cleanup_spb(void * spb)
+{
+ ipcp_spb_release((struct ssm_pk_buff *) spb);
+}
+
+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 ssm_pk_buff * spb;
+ uint8_t * buf;
+ uint16_t len;
+
+ if (fqueue_type(fq) != FLOW_PKT)
+ continue;
+
+ if (np1_flow_read(fd, &spb, NP1_GET_POOL(fd))) {
+ log_dbg("Bad read from fd %d.", fd);
+ continue;
+ }
+
+ len = ssm_pk_buff_len(spb);
+ if (len > IPCP_UDP_MAX_PACKET_SIZE) {
+ log_dbg("Packet length exceeds MTU.");
+ ipcp_spb_release(spb);
+ continue;
+ }
+
+ buf = ssm_pk_buff_push(spb, OUR_HEADER_LEN);
+ if (buf == NULL) {
+ log_dbg("Failed to allocate header.");
+ ipcp_spb_release(spb);
+ 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_spb, spb);
+
+ 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,
+ .ipcp_flow_update = udp_ipcp_flow_update
+};
+
+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..ff57bc09
--- /dev/null
+++ b/src/ipcpd/udp/udp4.c
@@ -0,0 +1,42 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * IPC process over UDP/IPv4
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#if defined(__linux__) || defined(__CYGWIN__)
+#define _DEFAULT_SOURCE
+#else
+#define _POSIX_C_SOURCE 200112L
+#endif
+
+#include <ouroboros/ipcp-dev.h>
+
+#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..2ceb95f0
--- /dev/null
+++ b/src/ipcpd/udp/udp6.c
@@ -0,0 +1,42 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * IPC process over UDP/IPv6
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#if defined(__linux__) || defined(__CYGWIN__)
+#define _DEFAULT_SOURCE
+#else
+#define _POSIX_C_SOURCE 200112L
+#endif
+
+#include <ouroboros/ipcp-dev.h>
+
+#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"