diff options
Diffstat (limited to 'src/ipcpd')
| -rw-r--r-- | src/ipcpd/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | src/ipcpd/broadcast/main.c | 13 | ||||
| -rw-r--r-- | src/ipcpd/common/connmgr.c | 93 | ||||
| -rw-r--r-- | src/ipcpd/common/connmgr.h | 17 | ||||
| -rw-r--r-- | src/ipcpd/config.h.in | 25 | ||||
| -rw-r--r-- | src/ipcpd/eth/CMakeLists.txt | 21 | ||||
| -rw-r--r-- | src/ipcpd/eth/eth.c | 2468 | ||||
| -rw-r--r-- | src/ipcpd/eth/llc.c | 26 | ||||
| -rw-r--r-- | src/ipcpd/ipcp.c | 305 | ||||
| -rw-r--r-- | src/ipcpd/ipcp.h | 13 | ||||
| -rw-r--r-- | src/ipcpd/local/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/ipcpd/local/main.c | 32 | ||||
| -rw-r--r-- | src/ipcpd/local/reg.c | 217 | ||||
| -rw-r--r-- | src/ipcpd/local/reg.h (renamed from src/ipcpd/eth/dix.c) | 27 | ||||
| -rw-r--r-- | src/ipcpd/shim-data.c | 582 | ||||
| -rw-r--r-- | src/ipcpd/shim-data.h | 109 | ||||
| -rw-r--r-- | src/ipcpd/udp/CMakeLists.txt | 14 | ||||
| -rw-r--r-- | src/ipcpd/udp/udp.c | 1333 | ||||
| -rw-r--r-- | src/ipcpd/udp/udp4.c | 42 | ||||
| -rw-r--r-- | src/ipcpd/udp/udp6.c | 42 | ||||
| -rw-r--r-- | src/ipcpd/unicast/fa.c | 8 | ||||
| -rw-r--r-- | src/ipcpd/unicast/main.c | 9 |
22 files changed, 631 insertions, 4771 deletions
diff --git a/src/ipcpd/CMakeLists.txt b/src/ipcpd/CMakeLists.txt index 609da54a..a84e5369 100644 --- a/src/ipcpd/CMakeLists.txt +++ b/src/ipcpd/CMakeLists.txt @@ -4,7 +4,6 @@ # Common sources shared by all IPCPs (absolute paths for subdirectories) set(IPCP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/ipcp.c - ${CMAKE_CURRENT_SOURCE_DIR}/shim-data.c ) set(COMMON_SOURCES @@ -24,7 +23,3 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" add_subdirectory(local) add_subdirectory(broadcast) add_subdirectory(unicast) -if(HAVE_ETH) - add_subdirectory(eth) -endif() -add_subdirectory(udp) diff --git a/src/ipcpd/broadcast/main.c b/src/ipcpd/broadcast/main.c index d18cac82..22b34a76 100644 --- a/src/ipcpd/broadcast/main.c +++ b/src/ipcpd/broadcast/main.c @@ -37,6 +37,7 @@ #include <ouroboros/logs.h> #include <ouroboros/notifier.h> #include <ouroboros/np1_flow.h> +#include <ouroboros/qos.h> #include <ouroboros/random.h> #include <ouroboros/rib.h> #include <ouroboros/time.h> @@ -100,12 +101,14 @@ static void stop_components(void) enroll_stop(); } -static int broadcast_ipcp_enroll(const char * dst, - struct layer_info * info) +static int broadcast_ipcp_enroll(const char * dst, + const struct poa_addr * addr, + struct layer_info * info) { struct ipcp_config * conf; - struct conn conn; - uint8_t id[ENROLL_ID_LEN]; + struct conn conn; + uint8_t id[ENROLL_ID_LEN]; + qosspec_t qs = qos_msg; if (random_buffer(id, ENROLL_ID_LEN) < 0) { log_err("Failed to generate enrollment ID."); @@ -114,7 +117,7 @@ static int broadcast_ipcp_enroll(const char * dst, log_info_id(id, "Requesting enrollment."); - if (connmgr_alloc(COMPID_ENROLL, dst, NULL, &conn) < 0) { + if (connmgr_alloc(COMPID_ENROLL, dst, &qs, addr, &conn) < 0) { log_err_id(id, "Failed to get connection."); goto fail_id; } diff --git a/src/ipcpd/common/connmgr.c b/src/ipcpd/common/connmgr.c index 6384a45c..e0ad80cb 100644 --- a/src/ipcpd/common/connmgr.c +++ b/src/ipcpd/common/connmgr.c @@ -26,6 +26,7 @@ #include <ouroboros/dev.h> #include <ouroboros/errno.h> #include <ouroboros/fccntl.h> +#include <ouroboros/ipcp-dev.h> #include <ouroboros/list.h> #include <ouroboros/logs.h> #include <ouroboros/notifier.h> @@ -39,6 +40,10 @@ #include <stdlib.h> #include <string.h> +#define CONNMGR_ETH_PROBE_TIMEO 20 /* ms, one query attempt */ +#define CONNMGR_ETH_RETRY_TIMEO 1500 /* ms, the remaining tries */ +#define CONNMGR_DHT_TIMEO 1000 /* ms, bounded lower-layer */ + struct conn_el { struct list_head next; struct conn conn; @@ -60,6 +65,14 @@ struct { pthread_t acceptor; } connmgr; +static bool is_eth_query(const struct poa_addr * addr) +{ + static const uint8_t zero[POA_MAC_SIZE] = { 0 }; + + return addr->type == POA_ETH && + memcmp(addr->eth.dst.mac, zero, POA_MAC_SIZE) == 0; +} + static int get_id_by_name(const char * name) { enum comp_id i; @@ -121,6 +134,7 @@ static int add_comp_conn(enum comp_id id, return 0; } +/* qs is also an in-parameter, and flow_accept writes it back. */ static void * flow_acceptor(void * o) { int fd; @@ -323,9 +337,10 @@ void connmgr_comp_fini(enum comp_id id) memset(&connmgr.comps[id].info, 0, sizeof(connmgr.comps[id].info)); } -int connmgr_ipcp_connect(const char * dst, - const char * component, - qosspec_t qs) +int connmgr_ipcp_connect(const char * dst, + const char * component, + qosspec_t qs, + const struct poa_addr * addr) { struct conn_el * ce; int id; @@ -334,6 +349,11 @@ int connmgr_ipcp_connect(const char * dst, assert(dst); assert(component); + if (qs.service == SVC_STREAM) { + log_err("No stream service on component flows."); + return -ENOTSUP; + } + ce = malloc(sizeof(*ce)); if (ce == NULL) { log_err("Out of memory."); @@ -348,7 +368,7 @@ int connmgr_ipcp_connect(const char * dst, pthread_cleanup_push(free, ce); - ret = connmgr_alloc(id, dst, &qs, &ce->conn); + ret = connmgr_alloc(id, dst, &qs, addr, &ce->conn); pthread_cleanup_pop(false); @@ -414,10 +434,46 @@ int connmgr_ipcp_disconnect(const char * dst, return 0; } -int connmgr_alloc(enum comp_id id, - const char * dst, - qosspec_t * qs, - struct conn * conn) +/* + * Without an address, a peer may be on the wire or reachable through + * the layer below. A PoA query is cheap and creates no flow, so it + * goes first; the layer below gets a bounded try before the query + * retries, and the last try is unbounded. + */ +static int alloc_any(const char * dst, + qosspec_t * qs) +{ + struct timespec probe = TIMESPEC_INIT_MS(CONNMGR_ETH_PROBE_TIMEO); + struct timespec retry = TIMESPEC_INIT_MS(CONNMGR_ETH_RETRY_TIMEO); + struct timespec below = TIMESPEC_INIT_MS(CONNMGR_DHT_TIMEO); + struct poa_addr addr; + int fd; + + if (poa_query(dst, &probe, &addr) == 0) { + fd = poa_flow_alloc(dst, &addr, qs, NULL); + if (fd >= 0) + return fd; + } + + fd = flow_alloc(dst, qs, &below); + if (fd >= 0) + return fd; + + if (poa_query(dst, &retry, &addr) == 0) { + fd = poa_flow_alloc(dst, &addr, qs, NULL); + if (fd >= 0) + return fd; + } + + return flow_alloc(dst, qs, NULL); +} + +/* A literal peer address bypasses the layer below. */ +int connmgr_alloc(enum comp_id id, + const char * dst, + qosspec_t * qs, + const struct poa_addr * addr, + struct conn * conn) { struct comp * comp; int fd; @@ -428,7 +484,26 @@ int connmgr_alloc(enum comp_id id, comp = connmgr.comps + id; - fd = flow_alloc(dst, qs, NULL); + if (addr != NULL) + fd = poa_flow_alloc(dst, addr, qs, NULL); + else + fd = alloc_any(dst, qs); + + if (fd == -EPERM && addr != NULL) { + log_err("No PoA attached to reach %s.", dst); + goto fail_alloc; + } + + if (fd == -EINVAL && addr != NULL) { + log_err("More than one PoA could reach %s", dst); + goto fail_alloc; + } + + if (fd == -ETIMEDOUT && addr != NULL && is_eth_query(addr)) { + log_err("No answer to name query for %s.", dst); + goto fail_alloc; + } + if (fd < 0) { log_err("Failed to allocate flow to %s.", dst); goto fail_alloc; diff --git a/src/ipcpd/common/connmgr.h b/src/ipcpd/common/connmgr.h index f48ecd1b..86f80fb0 100644 --- a/src/ipcpd/common/connmgr.h +++ b/src/ipcpd/common/connmgr.h @@ -24,6 +24,7 @@ #define OUROBOROS_IPCPD_COMMON_CONNMGR_H #include <ouroboros/cep.h> +#include <ouroboros/ipcp.h> #include <ouroboros/qos.h> #include "comp.h" @@ -53,17 +54,19 @@ int connmgr_comp_init(enum comp_id id, void connmgr_comp_fini(enum comp_id id); -int connmgr_ipcp_connect(const char * dst, - const char * component, - qosspec_t qs); +int connmgr_ipcp_connect(const char * dst, + const char * component, + qosspec_t qs, + const struct poa_addr * addr); int connmgr_ipcp_disconnect(const char * dst, const char * component); -int connmgr_alloc(enum comp_id id, - const char * dst, - qosspec_t * qs, - struct conn * conn); +int connmgr_alloc(enum comp_id id, + const char * dst, + qosspec_t * qs, + const struct poa_addr * addr, + struct conn * conn); int connmgr_dealloc(enum comp_id id, struct conn * conn); diff --git a/src/ipcpd/config.h.in b/src/ipcpd/config.h.in index 517016cc..55b0120c 100644 --- a/src/ipcpd/config.h.in +++ b/src/ipcpd/config.h.in @@ -25,6 +25,7 @@ #define SYS_MAX_FLOWS @SYS_MAX_FLOWS@ #define PROC_RES_FDS @PROC_RES_FDS@ #define PROC_MAX_FLOWS @PROC_MAX_FLOWS@ +#define POA_MAX_POAS @POA_MAX_POAS@ #define SOCKET_TIMEOUT @SOCKET_TIMEOUT@ #define CONNECT_TIMEOUT @CONNECT_TIMEOUT@ @@ -54,7 +55,6 @@ #cmakedefine DISABLE_CORE_LOCK #cmakedefine BUILD_CONTAINER #cmakedefine IPCP_FLOW_STATS -#cmakedefine IPCP_ETH_FLOW_STATS #cmakedefine IPCP_DEBUG_LOCAL #ifdef CONFIG_OUROBOROS_DEBUG #cmakedefine DEBUG_PROTO_DHT @@ -62,29 +62,6 @@ #cmakedefine DEBUG_PROTO_LS #endif -/* udp */ -#cmakedefine HAVE_DDNS -#define NSUPDATE_EXEC "@NSUPDATE_EXECUTABLE@" -#define NSLOOKUP_EXEC "@NSLOOKUP_EXECUTABLE@" -#define IPCP_UDP_RD_THR @IPCP_UDP_RD_THR@ -#define IPCP_UDP_WR_THR @IPCP_UDP_WR_THR@ -#define IPCP_UDP_MPL @IPCP_UDP_MPL@ -#define IPCP_UDP4_MTU @IPCP_UDP4_MTU@ -#define IPCP_UDP6_MTU @IPCP_UDP6_MTU@ - -/* eth */ -#cmakedefine HAVE_NETMAP -#cmakedefine HAVE_BPF -#cmakedefine HAVE_RAW_SOCKETS -#cmakedefine IPCP_ETH_QDISC_BYPASS -#define IPCP_ETH_RD_THR @IPCP_ETH_RD_THR@ -#define IPCP_ETH_WR_THR @IPCP_ETH_WR_THR@ -#define IPCP_ETH_LO_MTU @IPCP_ETH_LO_MTU@ -#define IPCP_ETH_MGMT_FRAME_SIZE @IPCP_ETH_MGMT_FRAME_SIZE@ -#define IPCP_ETH_MPL @IPCP_ETH_MPL@ -#define IPCP_ETH_SNDBUF @IPCP_ETH_SNDBUF@ -#define IPCP_ETH_RCVBUF @IPCP_ETH_RCVBUF@ - /* local */ #define IPCP_LOCAL_MPL @IPCP_LOCAL_MPL@ #define IPCP_LOCAL_MTU @IPCP_LOCAL_MTU@ diff --git a/src/ipcpd/eth/CMakeLists.txt b/src/ipcpd/eth/CMakeLists.txt deleted file mode 100644 index 5a36352d..00000000 --- a/src/ipcpd/eth/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -# Ethernet IPCPs build configuration (LLC and DIX) -# HAVE_ETH detection is in cmake/dependencies.cmake - -add_executable(${IPCP_ETH_LLC_TARGET} llc.c ${IPCP_SOURCES}) -add_executable(${IPCP_ETH_DIX_TARGET} dix.c ${IPCP_SOURCES}) - -foreach(target ${IPCP_ETH_LLC_TARGET} ${IPCP_ETH_DIX_TARGET}) - target_include_directories(${target} PRIVATE ${IPCP_INCLUDE_DIRS}) - if(HAVE_BPF AND NOT APPLE) - target_include_directories(${target} PRIVATE ${BPF_C_INCLUDE_DIR}) - endif() - if(HAVE_NETMAP AND NOT APPLE) - target_compile_options(${target} PRIVATE -std=c99) - target_include_directories(${target} PRIVATE ${NETMAP_C_INCLUDE_DIR}) - endif() - target_link_libraries(${target} PRIVATE ouroboros-dev) - ouroboros_target_debug_definitions(${target}) -endforeach() - -install(TARGETS ${IPCP_ETH_LLC_TARGET} ${IPCP_ETH_DIX_TARGET} - RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}) diff --git a/src/ipcpd/eth/eth.c b/src/ipcpd/eth/eth.c deleted file mode 100644 index 2d3bd249..00000000 --- a/src/ipcpd/eth/eth.c +++ /dev/null @@ -1,2468 +0,0 @@ -/* - * Ouroboros - Copyright (C) 2016 - 2026 - * - * IPC processes over Ethernet - * - * 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(BUILD_ETH_DIX) && !defined(BUILD_ETH_LLC) -#error Define BUILD_ETH_DIX or BUILD_ETH_LLC to build an Ethernet IPCP -#endif - -#if defined(__APPLE__) -#define _BSD_SOURCE -#define _DARWIN_C_SOURCE -#elif defined(__FreeBSD__) -#define __BSD_VISIBLE 1 -#elif defined (__linux__) || defined (__CYGWIN__) -#define _DEFAULT_SOURCE -#else -#define _POSIX_C_SOURCE 200112L -#endif - -#include "config.h" - -#include <ouroboros/atomics.h> -#include <ouroboros/endian.h> -#include <ouroboros/hash.h> -#include <ouroboros/errno.h> -#include <ouroboros/list.h> -#include <ouroboros/utils.h> -#include <ouroboros/bitmap.h> -#include <ouroboros/crc8.h> -#include <ouroboros/dev.h> -#include <ouroboros/ipcp-dev.h> -#include <ouroboros/fqueue.h> -#include <ouroboros/logs.h> -#include <ouroboros/np1_flow.h> -#include <ouroboros/time.h> -#include <ouroboros/fccntl.h> -#include <ouroboros/pthread.h> -#include <ouroboros/rib.h> - -#ifndef IPCP_ETH_FLOW_STATS -#undef FETCH_ADD_RELAXED -#define FETCH_ADD_RELAXED(p, v) ((void) 0) -#undef FETCH_SUB_RELAXED -#define FETCH_SUB_RELAXED(p, v) ((void) 0) -#endif - -#include "ipcp.h" -#include "np1.h" -#include "shim-data.h" - -#include <signal.h> -#include <stdlib.h> -#include <fcntl.h> -#include <unistd.h> -#include <string.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/ioctl.h> - -#include <net/if.h> -#include <netinet/in.h> - -#ifdef __linux__ -#include <linux/if_packet.h> -#include <linux/if_ether.h> -#include <linux/netlink.h> -#include <linux/rtnetlink.h> -#include <linux/sockios.h> -#endif - -#ifdef __FreeBSD__ -#include <net/if_dl.h> -#include <netinet/if_ether.h> -#include <ifaddrs.h> -#endif - -#ifdef __APPLE__ -#include <net/if_dl.h> -#include <ifaddrs.h> -#endif - -#include <poll.h> -#include <sys/mman.h> - -#if defined(HAVE_NETMAP) - #define NETMAP_WITH_LIBS - #include <net/netmap_user.h> -#elif defined(HAVE_BPF) - #define BPF_DEV_MAX 256 - #define BPF_BLEN sysconf(_SC_PAGESIZE) - #include <net/bpf.h> -#endif - -#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x" -#define MAC_VAL(a) \ - (uint8_t)(a)[0], (uint8_t)(a)[1], (uint8_t)(a)[2], \ - (uint8_t)(a)[3], (uint8_t)(a)[4], (uint8_t)(a)[5] - - -#ifndef ETH_MAX_MTU /* In if_ether.h as of Linux 4.10. */ - #define ETH_MAX_MTU 0xFFFFU -#endif /* ETH_MAX_MTU */ -#ifdef BUILD_ETH_DIX - #define ETH_MTU eth_data.mtu - #define ETH_MTU_MAX ETH_MAX_MTU -#else - #define ETH_MTU eth_data.mtu - #define ETH_MTU_MAX 1500 -#endif /* BUILD_ETH_DIX */ - -#define ETH_TYPE_LENGTH_SIZE sizeof(uint16_t) -#define ETH_HEADER_SIZE (2 * MAC_SIZE + ETH_TYPE_LENGTH_SIZE) - -#if defined(BUILD_ETH_DIX) -#define THIS_TYPE IPCP_ETH_DIX -#define MGMT_EID 0 -#define DIX_EID_SIZE sizeof(uint16_t) -#define DIX_LENGTH_SIZE sizeof(uint16_t) -#define DIX_HCS_SIZE CRC8_HASH_LEN -#define DIX_HEADER_SIZE (DIX_EID_SIZE + DIX_LENGTH_SIZE + DIX_HCS_SIZE) -#define ETH_HEADER_TOT_SIZE (ETH_HEADER_SIZE + DIX_HEADER_SIZE) -#define MAX_EIDS (1 << (8 * DIX_EID_SIZE)) -#define ETH_MAX_PACKET_SIZE (ETH_MTU - DIX_HEADER_SIZE) -#define ETH_FRAME_SIZE (ETH_HEADER_SIZE + ETH_MTU_MAX) -#elif defined(BUILD_ETH_LLC) -#define THIS_TYPE IPCP_ETH_LLC -#define MGMT_SAP 0x01 -#define LLC_FIELDS_SIZE 3 -#define LLC_HCS_SIZE CRC8_HASH_LEN -#define LLC_HEADER_SIZE (LLC_FIELDS_SIZE + LLC_HCS_SIZE) -#define ETH_HEADER_TOT_SIZE (ETH_HEADER_SIZE + LLC_HEADER_SIZE) -#define MAX_SAPS 64 -#define ETH_MAX_PACKET_SIZE (ETH_MTU - LLC_HEADER_SIZE) -#define ETH_FRAME_SIZE (ETH_HEADER_SIZE + ETH_MTU_MAX) -#endif - -#define NAME_QUERY_TIMEO 1900 /* ms total budget */ -#define NAME_QUERY_RETRIES 3 /* retransmits, 4 attempts total */ -#define MGMT_TIMEO 100 /* ms */ -#define MGMT_FRAME_SIZE IPCP_ETH_MGMT_FRAME_SIZE -#define ETH_RIB_PATH "eth" - -#define FLOW_REQ 0 -#define FLOW_REPLY 1 -#define NAME_QUERY_REQ 2 -#define NAME_QUERY_REPLY 3 -#define FLOW_IRM_UPDATE 4 - -struct mgmt_msg { -#if defined(BUILD_ETH_DIX) - uint16_t seid; - uint16_t deid; -#elif defined(BUILD_ETH_LLC) - uint8_t ssap; - uint8_t dsap; - /* QoS here for alignment */ - uint8_t code; - uint8_t availability; -#endif - /* QoS parameters from spec, aligned */ - uint32_t loss; - uint64_t bandwidth; - uint32_t ber; - uint32_t max_gap; - uint32_t delay; - uint32_t timeout; - int32_t response; - uint8_t service; -#if defined (BUILD_ETH_DIX) - uint8_t code; - uint8_t availability; -#endif -} __attribute__((packed)); - -struct eth_frame { - uint8_t dst_hwaddr[MAC_SIZE]; - uint8_t src_hwaddr[MAC_SIZE]; -#if defined(BUILD_ETH_DIX) - uint16_t ethertype; - uint16_t eid; - uint16_t length; -#elif defined(BUILD_ETH_LLC) - uint16_t length; - uint8_t dsap; - uint8_t ssap; - uint8_t cf; -#endif - uint8_t hcs; - uint8_t payload; -} __attribute__((packed)); - -struct ef { -#if defined(BUILD_ETH_DIX) - int32_t r_eid; -#elif defined(BUILD_ETH_LLC) - int8_t sap; - int8_t r_sap; -#endif - uint8_t r_addr[MAC_SIZE]; -#ifdef IPCP_ETH_FLOW_STATS - struct { - time_t stamp; - size_t p_rcv; - size_t b_rcv; - size_t p_dlv_f; - size_t p_snd; - size_t b_snd; - size_t p_snd_f; - } stat; -#endif -}; - -struct mgmt_frame { - struct list_head next; - uint8_t r_addr[MAC_SIZE]; - uint8_t buf[MGMT_FRAME_SIZE]; - size_t len; -}; - -struct { - struct shim_data * shim_data; - - int mtu; -#ifdef __linux__ - int if_idx; -#endif -#if defined(HAVE_NETMAP) - struct nm_desc * nmd; - uint8_t hw_addr[MAC_SIZE]; - struct pollfd poll_in; - struct pollfd poll_out; -#elif defined(HAVE_BPF) - int bpf; - uint8_t hw_addr[MAC_SIZE]; -#elif defined(HAVE_RAW_SOCKETS) - int s_fd; - struct sockaddr_ll device; -#endif /* HAVE_NETMAP */ -#if defined (BUILD_ETH_DIX) - uint16_t ethertype; -#elif defined(BUILD_ETH_LLC) - struct bmp * saps; - int * ef_to_fd; -#endif - struct ef * fd_to_ef; - fset_t * np1_flows; - pthread_rwlock_t flows_lock; -#ifdef IPCP_ETH_FLOW_STATS - struct { - size_t n_flows; - size_t n_rcv; - size_t n_snd; - size_t n_mgmt_rcv; - size_t n_mgmt_snd; - size_t n_bad_id; - size_t n_dlv_f; - size_t n_buf_f; - size_t n_rcv_f; - size_t n_snd_f; - size_t kern_rcv; - size_t kern_drp; - } stat; -#endif - - pthread_t packet_writer[IPCP_ETH_WR_THR]; - pthread_t packet_reader[IPCP_ETH_RD_THR]; - -#ifdef __linux__ - pthread_t if_monitor; -#endif - - /* 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; -} eth_data; - -static int eth_data_init(void) -{ - int i; - int ret = -ENOMEM; - pthread_condattr_t cattr; - - eth_data.fd_to_ef = - malloc(sizeof(*eth_data.fd_to_ef) * SYS_MAX_FLOWS); - if (eth_data.fd_to_ef == NULL) - goto fail_fd_to_ef; - -#ifdef BUILD_ETH_LLC - eth_data.ef_to_fd = - malloc(sizeof(*eth_data.ef_to_fd) * MAX_SAPS); - if (eth_data.ef_to_fd == NULL) - goto fail_ef_to_fd; - - for (i = 0; i < MAX_SAPS; ++i) - eth_data.ef_to_fd[i] = -1; - - eth_data.saps = bmp_create(MAX_SAPS, 2); - if (eth_data.saps == NULL) - goto fail_saps; -#endif - eth_data.np1_flows = fset_create(); - if (eth_data.np1_flows == NULL) - goto fail_np1_flows; - - for (i = 0; i < SYS_MAX_FLOWS; ++i) { -#if defined(BUILD_ETH_DIX) - eth_data.fd_to_ef[i].r_eid = -1; -#elif defined(BUILD_ETH_LLC) - eth_data.fd_to_ef[i].sap = -1; - eth_data.fd_to_ef[i].r_sap = -1; -#endif - memset(ð_data.fd_to_ef[i].r_addr, 0, MAC_SIZE); -#ifdef IPCP_ETH_FLOW_STATS - memset(ð_data.fd_to_ef[i].stat, 0, - sizeof(eth_data.fd_to_ef[i].stat)); -#endif - } -#ifdef IPCP_ETH_FLOW_STATS - memset(ð_data.stat, 0, sizeof(eth_data.stat)); -#endif - - eth_data.shim_data = shim_data_create(); - if (eth_data.shim_data == NULL) - goto fail_shim_data; - - ret = -1; - - if (pthread_rwlock_init(ð_data.flows_lock, NULL)) - goto fail_flows_lock; - - if (pthread_mutex_init(ð_data.mgmt_lock, NULL)) - goto fail_mgmt_lock; - - if (pthread_condattr_init(&cattr)) - goto fail_condattr; - -#ifndef __APPLE__ - pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK); -#endif - - if (pthread_cond_init(ð_data.mgmt_cond, &cattr)) - goto fail_mgmt_cond; - - pthread_condattr_destroy(&cattr); - - list_head_init(ð_data.mgmt_frames); - - return 0; - - fail_mgmt_cond: - pthread_condattr_destroy(&cattr); - fail_condattr: - pthread_mutex_destroy(ð_data.mgmt_lock); - fail_mgmt_lock: - pthread_rwlock_destroy(ð_data.flows_lock); - fail_flows_lock: - shim_data_destroy(eth_data.shim_data); - fail_shim_data: - fset_destroy(eth_data.np1_flows); - fail_np1_flows: -#ifdef BUILD_ETH_LLC - bmp_destroy(eth_data.saps); - fail_saps: - free(eth_data.ef_to_fd); - fail_ef_to_fd: -#endif - free(eth_data.fd_to_ef); - fail_fd_to_ef: - return ret; -} - -static void eth_data_fini(void) -{ -#if defined(HAVE_NETMAP) - nm_close(eth_data.nmd); -#elif defined(HAVE_BPF) - close(eth_data.bpf); -#elif defined(HAVE_RAW_SOCKETS) - close(eth_data.s_fd); -#endif - pthread_cond_destroy(ð_data.mgmt_cond); - pthread_mutex_destroy(ð_data.mgmt_lock); - pthread_rwlock_destroy(ð_data.flows_lock); - shim_data_destroy(eth_data.shim_data); - fset_destroy(eth_data.np1_flows); -#ifdef BUILD_ETH_LLC - bmp_destroy(eth_data.saps); - free(eth_data.ef_to_fd); -#endif - free(eth_data.fd_to_ef); -} - -#ifdef IPCP_ETH_FLOW_STATS -static int eth_rib_read(const char * path, - char * buf, - size_t len) -{ - struct ef * flow; - int fd; - char tmstr[RIB_TM_STRLEN]; - struct tm * tm; - time_t stamp; - char * entry; - - entry = strstr(path, RIB_SEPARATOR) + 1; - assert(entry); - - if (len < 2048) - return 0; - - buf[0] = '\0'; - - if (strcmp(entry, "summary") == 0) { - int n; -#if defined(HAVE_RAW_SOCKETS) - int rcvbuf = 0; - int sndbuf = 0; - int queued = 0; - socklen_t optlen = sizeof(rcvbuf); -# if defined(__linux__) - int outq = 0; - struct tpacket_stats tp_stats; - socklen_t tp_len = sizeof(tp_stats); -# endif - - getsockopt(eth_data.s_fd, SOL_SOCKET, - SO_RCVBUF, &rcvbuf, &optlen); - optlen = sizeof(sndbuf); - getsockopt(eth_data.s_fd, SOL_SOCKET, - SO_SNDBUF, &sndbuf, &optlen); - ioctl(eth_data.s_fd, FIONREAD, &queued); -# if defined(__linux__) - ioctl(eth_data.s_fd, SIOCOUTQ, &outq); - if (getsockopt(eth_data.s_fd, SOL_PACKET, - PACKET_STATISTICS, - &tp_stats, &tp_len) == 0) { - FETCH_ADD_RELAXED(ð_data.stat.kern_rcv, - tp_stats.tp_packets); - FETCH_ADD_RELAXED(ð_data.stat.kern_drp, - tp_stats.tp_drops); - } -# endif -#endif - n = sprintf(buf, - "Active flows: %20zu\n" - "Total frames received: %20zu\n" - "Total frames sent: %20zu\n" - "Management frames received: %20zu\n" - "Management frames sent: %20zu\n" - "Bad EID/SAP frames: %20zu\n" - "Delivery (N+1) failures: %20zu\n" - "Buffer alloc failures: %20zu\n" - "Frame read failures: %20zu\n" - "Frame send failures: %20zu\n", - LOAD_RELAXED(ð_data.stat.n_flows), - LOAD_RELAXED(ð_data.stat.n_rcv), - LOAD_RELAXED(ð_data.stat.n_snd), - LOAD_RELAXED(ð_data.stat.n_mgmt_rcv), - LOAD_RELAXED(ð_data.stat.n_mgmt_snd), - LOAD_RELAXED(ð_data.stat.n_bad_id), - LOAD_RELAXED(ð_data.stat.n_dlv_f), - LOAD_RELAXED(ð_data.stat.n_buf_f), - LOAD_RELAXED(ð_data.stat.n_rcv_f), - LOAD_RELAXED(ð_data.stat.n_snd_f)); -#if defined(HAVE_RAW_SOCKETS) - n += sprintf(buf + n, - "Socket rcvbuf (bytes): %20d\n" - "Socket sndbuf (bytes): %20d\n" - "Socket ingress (bytes): %20d\n", - rcvbuf, sndbuf, queued); -# if defined(__linux__) - n += sprintf(buf + n, - "Socket egress (bytes): %20d\n" - "Kernel frames received: %20zu\n" - "Kernel frames dropped: %20zu\n", - outq, - LOAD_RELAXED(ð_data.stat.kern_rcv), - LOAD_RELAXED(ð_data.stat.kern_drp)); -# endif -#endif - return n; - } - - fd = atoi(entry); - - if (fd < 0 || fd >= SYS_MAX_FLOWS) - return -1; - - flow = ð_data.fd_to_ef[fd]; - - pthread_rwlock_rdlock(ð_data.flows_lock); - - stamp = flow->stat.stamp; - if (stamp == 0) { - pthread_rwlock_unlock(ð_data.flows_lock); - return 0; - } - - pthread_rwlock_unlock(ð_data.flows_lock); - - tm = gmtime(&stamp); - strftime(tmstr, sizeof(tmstr), RIB_TM_FORMAT, tm); - - sprintf(buf, - "Flow established at: %20s\n" - "Sent (packets): %20zu\n" - "Sent (bytes): %20zu\n" - "Send failed (packets): %20zu\n" - "Received (packets): %20zu\n" - "Received (bytes): %20zu\n" - "Delivery (N+1) failures: %20zu\n", - tmstr, - LOAD_RELAXED(&flow->stat.p_snd), - LOAD_RELAXED(&flow->stat.b_snd), - LOAD_RELAXED(&flow->stat.p_snd_f), - LOAD_RELAXED(&flow->stat.p_rcv), - LOAD_RELAXED(&flow->stat.b_rcv), - LOAD_RELAXED(&flow->stat.p_dlv_f)); - - return strlen(buf); -} - -static int eth_rib_readdir(char *** buf) -{ - char entry[RIB_PATH_LEN + 1]; - size_t i; - int idx = 0; - int n_entries; - - pthread_rwlock_rdlock(ð_data.flows_lock); - - n_entries = (int) LOAD_RELAXED(ð_data.stat.n_flows) + 1; - - *buf = malloc(sizeof(**buf) * n_entries); - if (*buf == NULL) - goto fail_entries; - - (*buf)[idx] = malloc(strlen("summary") + 1); - if ((*buf)[idx] == NULL) - goto fail_entry; - - strcpy((*buf)[idx++], "summary"); - - for (i = 0; i < SYS_MAX_FLOWS && idx < n_entries; ++i) { - if (eth_data.fd_to_ef[i].stat.stamp == 0) - continue; - - sprintf(entry, "%zu", i); - - (*buf)[idx] = malloc(strlen(entry) + 1); - if ((*buf)[idx] == NULL) - goto fail_entry; - - strcpy((*buf)[idx++], entry); - } - - pthread_rwlock_unlock(ð_data.flows_lock); - - return idx; - - fail_entry: - while (idx-- > 0) - free((*buf)[idx]); - - free(*buf); - fail_entries: - pthread_rwlock_unlock(ð_data.flows_lock); - return -ENOMEM; -} - -static int eth_rib_getattr(const char * path, - struct rib_attr * attr) -{ - int fd; - char * entry; - struct ef * flow; - - entry = strstr(path, RIB_SEPARATOR) + 1; - assert(entry); - - if (strcmp(entry, "summary") == 0) { - attr->size = 2048; - attr->mtime = 0; - return 0; - } - - fd = atoi(entry); - - if (fd < 0 || fd >= SYS_MAX_FLOWS) { - attr->size = 0; - attr->mtime = 0; - return 0; - } - - flow = ð_data.fd_to_ef[fd]; - - pthread_rwlock_rdlock(ð_data.flows_lock); - - if (flow->stat.stamp != 0) { - attr->size = 2048; - attr->mtime = flow->stat.stamp; - } else { - attr->size = 0; - attr->mtime = 0; - } - - pthread_rwlock_unlock(ð_data.flows_lock); - - return 0; -} - -static struct rib_ops eth_r_ops = { - .read = eth_rib_read, - .readdir = eth_rib_readdir, - .getattr = eth_rib_getattr -}; -#endif /* IPCP_ETH_FLOW_STATS */ - -#ifdef BUILD_ETH_LLC -static uint8_t reverse_bits(uint8_t b) -{ - b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; - b = (b & 0xCC) >> 2 | (b & 0x33) << 2; - b = (b & 0xAA) >> 1 | (b & 0x55) << 1; - - return b; -} -#endif - -/* Pass a buffer that contains space for the header. */ -static int eth_ipcp_send_frame(const uint8_t * dst_addr, -#if defined(BUILD_ETH_DIX) - uint16_t deid, -#elif defined(BUILD_ETH_LLC) - uint8_t dsap, - uint8_t ssap, -#endif - const uint8_t * frame, - size_t len) -{ - uint32_t frame_len = 0; -#ifdef BUILD_ETH_LLC - uint8_t cf = 0x03; -#endif - struct eth_frame * e_frame; -#ifdef HAVE_RAW_SOCKETS - fd_set fds; - - FD_ZERO(&fds); -#endif - - assert(frame); - - if (len > (size_t) ETH_MAX_PACKET_SIZE) - return -1; - - e_frame = (struct eth_frame *) frame; - - memcpy(e_frame->dst_hwaddr, dst_addr, MAC_SIZE); - memcpy(e_frame->src_hwaddr, -#if defined(HAVE_NETMAP) || defined(HAVE_BPF) - eth_data.hw_addr, -#elif defined(HAVE_RAW_SOCKETS) - eth_data.device.sll_addr, -#endif /* HAVE_NETMAP */ - MAC_SIZE); -#if defined(BUILD_ETH_DIX) - e_frame->ethertype = eth_data.ethertype; - e_frame->eid = htons(deid); - e_frame->length = htons(len); - mem_hash(HASH_CRC8, &e_frame->hcs, - (uint8_t *) &e_frame->eid, - DIX_EID_SIZE + DIX_LENGTH_SIZE); - frame_len = ETH_HEADER_TOT_SIZE + len; -#elif defined(BUILD_ETH_LLC) - e_frame->length = htons(LLC_HEADER_SIZE + len); - e_frame->dsap = dsap; - e_frame->ssap = ssap; - e_frame->cf = cf; - mem_hash(HASH_CRC8, &e_frame->hcs, - (uint8_t *) &e_frame->dsap, - LLC_FIELDS_SIZE); - frame_len = ETH_HEADER_TOT_SIZE + len; -#endif - -#if defined(HAVE_NETMAP) - if (poll(ð_data.poll_out, 1, -1) < 0) - return -1; - - if (nm_inject(eth_data.nmd, frame, frame_len) != (int) frame_len) { - log_dbg("Failed to send message."); - return -1; - } -#elif defined(HAVE_BPF) - if (write(eth_data.bpf, frame, frame_len) < 0) { - log_dbg("Failed to send message."); - return -1; - } - -#elif defined(HAVE_RAW_SOCKETS) - FD_SET(eth_data.s_fd, &fds); - if (select(eth_data.s_fd + 1, NULL, &fds, NULL, NULL) < 0) { - log_dbg("Select() failed: %s.", strerror(errno)); - return -1; - } - assert(FD_ISSET(eth_data.s_fd, &fds)); - - if (sendto(eth_data.s_fd, frame, frame_len, 0, - (struct sockaddr *) ð_data.device, - sizeof(eth_data.device)) <= 0) { - log_dbg("Failed to send message: %s.", strerror(errno)); - return -1; - } -#endif /* HAVE_NETMAP */ - - FETCH_ADD_RELAXED(ð_data.stat.n_snd, 1); - - return 0; -} - -static int eth_ipcp_alloc(const uint8_t * dst_addr, -#if defined(BUILD_ETH_DIX) - uint16_t eid, -#elif defined(BUILD_ETH_LLC) - uint8_t ssap, -#endif - const uint8_t * hash, - qosspec_t qs, - const buffer_t * data) -{ - uint8_t * buf; - struct mgmt_msg * msg; - size_t len; - int ret; - - len = sizeof(*msg) + ipcp_dir_hash_len(); - - buf = malloc(len + ETH_HEADER_TOT_SIZE + data->len); - if (buf == NULL) - return -1; - - memset(buf, 0, len + ETH_HEADER_TOT_SIZE + data->len); - - msg = (struct mgmt_msg *) (buf + ETH_HEADER_TOT_SIZE); - msg->code = FLOW_REQ; -#if defined(BUILD_ETH_DIX) - msg->seid = htons(eid); -#elif defined(BUILD_ETH_LLC) - msg->ssap = ssap; -#endif - - 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, hash, ipcp_dir_hash_len()); - if (data->len > 0) - memcpy(buf + len + ETH_HEADER_TOT_SIZE, data->data, data->len); - - ret = eth_ipcp_send_frame(dst_addr, -#if defined(BUILD_ETH_DIX) - MGMT_EID, -#elif defined(BUILD_ETH_LLC) - reverse_bits(MGMT_SAP), - reverse_bits(MGMT_SAP), -#endif - buf, len + data->len); - free(buf); - - if (ret == 0) - FETCH_ADD_RELAXED(ð_data.stat.n_mgmt_snd, 1); - - return ret; -} - -static int eth_ipcp_alloc_resp(uint8_t * dst_addr, -#if defined(BUILD_ETH_DIX) - uint16_t seid, - uint16_t deid, -#elif defined(BUILD_ETH_LLC) - uint8_t ssap, - uint8_t dsap, -#endif - int response, - const buffer_t * data) -{ - struct mgmt_msg * msg; - uint8_t * buf; - - buf = malloc(sizeof(*msg) + ETH_HEADER_TOT_SIZE + data->len); - if (buf == NULL) - return -1; - - memset(buf, 0, sizeof(*msg) + ETH_HEADER_TOT_SIZE + data->len); - - msg = (struct mgmt_msg *) (buf + ETH_HEADER_TOT_SIZE); - - msg->code = FLOW_REPLY; -#if defined(BUILD_ETH_DIX) - msg->seid = htons(seid); - msg->deid = htons(deid); -#elif defined(BUILD_ETH_LLC) - msg->ssap = ssap; - msg->dsap = dsap; -#endif - msg->response = hton32(response); - - if (data->len > 0) - memcpy(msg + 1, data->data, data->len); - - if (eth_ipcp_send_frame(dst_addr, -#if defined(BUILD_ETH_DIX) - MGMT_EID, -#elif defined(BUILD_ETH_LLC) - reverse_bits(MGMT_SAP), - reverse_bits(MGMT_SAP), -#endif - buf, sizeof(*msg) + data->len)) { - free(buf); - return -1; - } - - FETCH_ADD_RELAXED(ð_data.stat.n_mgmt_snd, 1); - - free(buf); - - return 0; -} - -static int eth_ipcp_flow_update(int fd, - const buffer_t * data) -{ - struct mgmt_msg * msg; - struct ef * flow; - uint8_t * buf; - uint8_t r_addr[MAC_SIZE]; - int ret; - - buf = malloc(sizeof(*msg) + ETH_HEADER_TOT_SIZE + data->len); - if (buf == NULL) - return -1; - - memset(buf, 0, sizeof(*msg) + ETH_HEADER_TOT_SIZE + data->len); - - msg = (struct mgmt_msg *) (buf + ETH_HEADER_TOT_SIZE); - - msg->code = FLOW_IRM_UPDATE; - - pthread_rwlock_rdlock(ð_data.flows_lock); - - flow = ð_data.fd_to_ef[fd]; -#if defined(BUILD_ETH_DIX) - msg->seid = htons((uint16_t) fd); - msg->deid = htons((uint16_t) flow->r_eid); -#elif defined(BUILD_ETH_LLC) - msg->ssap = flow->sap; - msg->dsap = (uint8_t) flow->r_sap; -#endif - memcpy(r_addr, flow->r_addr, MAC_SIZE); - - pthread_rwlock_unlock(ð_data.flows_lock); - - if (data->len > 0) - memcpy(msg + 1, data->data, data->len); - - ret = eth_ipcp_send_frame(r_addr, -#if defined(BUILD_ETH_DIX) - MGMT_EID, -#elif defined(BUILD_ETH_LLC) - reverse_bits(MGMT_SAP), - reverse_bits(MGMT_SAP), -#endif - buf, sizeof(*msg) + data->len); - free(buf); - - if (ret == 0) - FETCH_ADD_RELAXED(ð_data.stat.n_mgmt_snd, 1); - - return ret; -} - -static int eth_ipcp_req(uint8_t * r_addr, -#if defined(BUILD_ETH_DIX) - uint16_t r_eid, -#elif defined(BUILD_ETH_LLC) - uint8_t r_sap, -#endif - const uint8_t * dst, - qosspec_t qs, - const buffer_t * data) -{ - int fd; - - fd = ipcp_wait_flow_req_arr(dst, qs, IPCP_ETH_MPL, - ETH_MAX_PACKET_SIZE, data); - if (fd < 0) { - log_err("Could not get new flow from IRMd."); - return -1; - } - - pthread_rwlock_wrlock(ð_data.flows_lock); -#if defined(BUILD_ETH_DIX) - eth_data.fd_to_ef[fd].r_eid = r_eid; -#elif defined(BUILD_ETH_LLC) - eth_data.fd_to_ef[fd].r_sap = r_sap; -#endif - memcpy(eth_data.fd_to_ef[fd].r_addr, r_addr, MAC_SIZE); - - pthread_rwlock_unlock(ð_data.flows_lock); - -#if defined(BUILD_ETH_DIX) - log_dbg("New flow request, fd %d, remote endpoint %d.", fd, r_eid); -#elif defined(BUILD_ETH_LLC) - log_dbg("New flow request, fd %d, remote SAP %d.", fd, r_sap); -#endif - return 0; -} - -static int eth_ipcp_alloc_reply(uint8_t * r_addr, -#if defined(BUILD_ETH_DIX) - uint16_t seid, - uint16_t deid, -#elif defined(BUILD_ETH_LLC) - uint8_t ssap, - int dsap, -#endif - int response, - const buffer_t * data) -{ - int ret = 0; - int fd = -1; - time_t mpl = IPCP_ETH_MPL; - - pthread_rwlock_wrlock(ð_data.flows_lock); - -#if defined(BUILD_ETH_DIX) - fd = deid; -#elif defined(BUILD_ETH_LLC) - fd = eth_data.ef_to_fd[dsap]; -#endif - if (fd < 0) { - pthread_rwlock_unlock(ð_data.flows_lock); - log_err("No flow found with that SAP."); - return -1; /* -EFLOWNOTFOUND */ - } - - if (response) { -#ifdef BUILD_ETH_LLC - bmp_release(eth_data.saps, eth_data.fd_to_ef[fd].sap); -#endif - } else { -#if defined(BUILD_ETH_DIX) - eth_data.fd_to_ef[fd].r_eid = seid; -#elif defined(BUILD_ETH_LLC) - eth_data.fd_to_ef[fd].r_sap = ssap; -#endif - memcpy(eth_data.fd_to_ef[fd].r_addr, r_addr, MAC_SIZE); - } - - pthread_rwlock_unlock(ð_data.flows_lock); - -#if defined(BUILD_ETH_DIX) - log_dbg("Flow reply, fd %d, src eid %d, dst eid %d.", fd, seid, deid); -#elif defined(BUILD_ETH_LLC) - log_dbg("Flow reply, fd %d, SSAP %d, DSAP %d.", fd, ssap, dsap); -#endif - if ((ret = ipcp_flow_alloc_reply(fd, response, mpl, - ETH_MAX_PACKET_SIZE, data)) < 0) { - log_err("Failed to reply to flow allocation."); - return -1; - } - - return ret; -} - -static int eth_ipcp_name_query_req(const uint8_t * hash, - uint8_t * r_addr) -{ - uint8_t * buf; - struct mgmt_msg * msg; - size_t len; - - if (shim_data_reg_has(eth_data.shim_data, hash)) { - len = sizeof(*msg) + ipcp_dir_hash_len(); - - buf = malloc(len + ETH_HEADER_TOT_SIZE); - if (buf == NULL) - return -1; - - memset(buf, 0, len + ETH_HEADER_TOT_SIZE); - - msg = (struct mgmt_msg *) (buf + ETH_HEADER_TOT_SIZE); - msg->code = NAME_QUERY_REPLY; - - memcpy(msg + 1, hash, ipcp_dir_hash_len()); - - if (eth_ipcp_send_frame(r_addr, -#if defined(BUILD_ETH_DIX) - MGMT_EID, -#elif defined(BUILD_ETH_LLC) - reverse_bits(MGMT_SAP), - reverse_bits(MGMT_SAP), -#endif - buf, len)) { - log_err("Failed to send management frame."); - free(buf); - return -1; - } - - FETCH_ADD_RELAXED(ð_data.stat.n_mgmt_snd, 1); - - free(buf); - } - - return 0; -} - -static int eth_ipcp_name_query_reply(const uint8_t * hash, - uint8_t * r_addr) -{ - struct addr addr; - - memcpy(&addr.mac, r_addr, MAC_SIZE); - - shim_data_dir_add_entry(eth_data.shim_data, hash, addr); - - shim_data_dir_query_respond(eth_data.shim_data, hash); - - return 0; -} - -static int eth_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; - - data.data = (uint8_t *) buf + sizeof(*msg); - data.len = len - sizeof(*msg); - - pthread_rwlock_rdlock(ð_data.flows_lock); -#if defined(BUILD_ETH_DIX) - fd = ntohs(msg->deid); -#elif defined(BUILD_ETH_LLC) - fd = eth_data.ef_to_fd[msg->dsap]; -#endif - pthread_rwlock_unlock(ð_data.flows_lock); - - if (fd < 0 || fd >= SYS_MAX_FLOWS) { - log_err("Flow update for unknown endpoint."); - return -1; - } - - 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 eth_ipcp_mgmt_frame(const uint8_t * buf, - size_t len, - uint8_t * r_addr) -{ - struct mgmt_msg * msg; - size_t msg_len; - qosspec_t qs; - buffer_t data; - - 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; - - 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); - - data.data = (uint8_t *) buf + msg_len; - data.len = len - msg_len; - - if (shim_data_reg_has(eth_data.shim_data, - buf + sizeof(*msg))) { - eth_ipcp_req(r_addr, -#if defined(BUILD_ETH_DIX) - ntohs(msg->seid), -#elif defined(BUILD_ETH_LLC) - msg->ssap, -#endif - buf + sizeof(*msg), - qs, - &data); - } - break; - case FLOW_REPLY: - data.data = (uint8_t *) buf + sizeof(*msg); - data.len = len - sizeof(*msg); - - eth_ipcp_alloc_reply(r_addr, -#if defined(BUILD_ETH_DIX) - ntohs(msg->seid), - ntohs(msg->deid), -#elif defined(BUILD_ETH_LLC) - msg->ssap, - msg->dsap, -#endif - ntoh32(msg->response), - &data); - break; - case FLOW_IRM_UPDATE: - eth_ipcp_flow_update_arr(buf, len); - break; - case NAME_QUERY_REQ: - if (len < sizeof(*msg) + ipcp_dir_hash_len()) - return -1; - eth_ipcp_name_query_req(buf + sizeof(*msg), r_addr); - break; - case NAME_QUERY_REPLY: - if (len < sizeof(*msg) + ipcp_dir_hash_len()) - return -1; - eth_ipcp_name_query_reply(buf + sizeof(*msg), r_addr); - break; - default: - log_err("Unknown message received %d.", msg->code); - return -1; - } - - return 0; -} - -static void * eth_ipcp_mgmt_handler(void * o) -{ - (void) o; - - pthread_cleanup_push(__cleanup_mutex_unlock, ð_data.mgmt_lock); - - while (true) { - int ret = 0; - struct timespec timeout = TIMESPEC_INIT_MS(MGMT_TIMEO); - struct timespec abstime; - struct mgmt_frame * frame = NULL; - - clock_gettime(PTHREAD_COND_CLOCK, &abstime); - ts_add(&abstime, &timeout, &abstime); - - pthread_mutex_lock(ð_data.mgmt_lock); - - while (list_is_empty(ð_data.mgmt_frames) && - ret != -ETIMEDOUT) - ret = -pthread_cond_timedwait(ð_data.mgmt_cond, - ð_data.mgmt_lock, - &abstime); - if (ret != -ETIMEDOUT) - frame = list_first_entry((ð_data.mgmt_frames), - struct mgmt_frame, next); - if (frame != NULL) - list_del(&frame->next); - - pthread_mutex_unlock(ð_data.mgmt_lock); - - if (frame == NULL) - continue; - - eth_ipcp_mgmt_frame(frame->buf, frame->len, frame->r_addr); - - free(frame); - } - - pthread_cleanup_pop(false); - - return (void *) 0; -} - -static void * eth_ipcp_packet_reader(void * o) -{ - uint8_t br_addr[MAC_SIZE]; -#if defined(BUILD_ETH_DIX) - uint16_t deid; -#elif defined(BUILD_ETH_LLC) - uint8_t dsap; - uint8_t ssap; -#endif - uint16_t length; - int fd; - uint8_t * buf; -#if defined(HAVE_NETMAP) - struct nm_pkthdr hdr; -#else - struct ssm_pk_buff * spb; - fd_set fds; - int frame_len; -#endif -#if defined(HAVE_RAW_SOCKETS) - struct sockaddr_ll src; - socklen_t slen; -#endif - size_t eth_len; - uint8_t hcs; - struct eth_frame * e_frame; - struct mgmt_frame * frame; - - (void) o; - - ipcp_lock_to_core(); - - memset(br_addr, 0xff, MAC_SIZE * sizeof(uint8_t)); - - while (true) { -#if defined(HAVE_NETMAP) - if (poll(ð_data.poll_in, 1, -1) < 0) - continue; - if (eth_data.poll_in.revents == 0) /* TIMED OUT */ - continue; - - buf = nm_nextpkt(eth_data.nmd, &hdr); - if (buf == NULL) { - log_dbg("Bad read from netmap device."); - continue; - } -#else - FD_ZERO(&fds); - #if defined(HAVE_BPF) - FD_SET(eth_data.bpf, &fds); - if (select(eth_data.bpf + 1, &fds, NULL, NULL, NULL)) - continue; - assert(FD_ISSET(eth_data.bpf, &fds)); - if (ipcp_spb_reserve(&spb, BPF_LEN)) - continue; - buf = ssm_pk_buff_head(spb); - frame_len = read(eth_data.bpf, buf, BPF_BLEN); - #elif defined(HAVE_RAW_SOCKETS) - FD_SET(eth_data.s_fd, &fds); - if (select(eth_data.s_fd + 1, &fds, NULL, NULL, NULL) < 0) - continue; - assert(FD_ISSET(eth_data.s_fd, &fds)); - if (ipcp_spb_reserve(&spb, ETH_MTU)) { - FETCH_ADD_RELAXED(ð_data.stat.n_buf_f, 1); - continue; - } - buf = ssm_pk_buff_push(spb, ETH_HEADER_TOT_SIZE); - if (buf == NULL) { - log_dbg("Failed to allocate header."); - ipcp_spb_release(spb); - FETCH_ADD_RELAXED(ð_data.stat.n_buf_f, 1); - continue; - } - slen = sizeof(src); - /* MSG_DONTWAIT: RD_THR>1 race-loser bails with EAGAIN. */ - frame_len = recvfrom(eth_data.s_fd, buf, - ETH_MTU + ETH_HEADER_TOT_SIZE, - MSG_DONTWAIT, - (struct sockaddr *) &src, &slen); - #endif - if (frame_len == 0) { - ipcp_spb_release(spb); - continue; /* Spurious */ - } - - if (frame_len < 0) { - ipcp_spb_release(spb); - - if (errno == EAGAIN || errno == EWOULDBLOCK) - continue; - - log_dbg("Failed to rcv frame: %s.", strerror(errno)); - FETCH_ADD_RELAXED(ð_data.stat.n_rcv_f, 1); - continue; - } -#endif - -#if defined(HAVE_NETMAP) - eth_len = hdr.len; -#elif defined(HAVE_BPF) - eth_len = ((struct bpf_hdr *) buf)->bh_caplen; -#else - eth_len = (size_t) frame_len; -#endif - /* Defense in depth: reject before parsing dereferences. */ - if (eth_len < ETH_HEADER_TOT_SIZE) - goto fail_frame; - -#if defined(HAVE_RAW_SOCKETS) - /* Drop our own egress. */ - if (src.sll_pkttype == PACKET_OUTGOING) - goto fail_frame; -#endif - -#if defined(HAVE_BPF) && !defined(HAVE_NETMAP) - e_frame = (struct eth_frame *) - (buf + ((struct bpf_hdr *) buf)->bh_hdrlen); -#else - e_frame = (struct eth_frame *) buf; -#endif - assert(e_frame->dst_hwaddr); - -#if !defined(HAVE_BPF) - #if defined(HAVE_NETMAP) - if (memcmp(eth_data.hw_addr, - #elif defined(HAVE_RAW_SOCKETS) - if (memcmp(eth_data.device.sll_addr, - #endif /* HAVE_NETMAP */ - e_frame->dst_hwaddr, - MAC_SIZE) && - memcmp(br_addr, e_frame->dst_hwaddr, MAC_SIZE)) { - FETCH_ADD_RELAXED(ð_data.stat.n_bad_id, 1); - goto fail_frame; - } -#endif - length = ntohs(e_frame->length); -#if defined(BUILD_ETH_DIX) - if (e_frame->ethertype != eth_data.ethertype) - goto fail_frame; - - if (length > ETH_MTU) - goto fail_frame; - - deid = ntohs(e_frame->eid); -#elif defined (BUILD_ETH_LLC) - if (length > 0x05FF) /* DIX */ - goto fail_frame; - - if (length < LLC_HEADER_SIZE || length > ETH_MTU) - goto fail_frame; - - length -= LLC_HEADER_SIZE; - - dsap = reverse_bits(e_frame->dsap); - ssap = reverse_bits(e_frame->ssap); -#endif - - if (eth_len < ETH_HEADER_TOT_SIZE + (size_t) length) - goto fail_frame; - -#if defined(BUILD_ETH_DIX) - mem_hash(HASH_CRC8, &hcs, - (uint8_t *) &e_frame->eid, - DIX_EID_SIZE + DIX_LENGTH_SIZE); -#elif defined(BUILD_ETH_LLC) - mem_hash(HASH_CRC8, &hcs, - (uint8_t *) &e_frame->dsap, - LLC_FIELDS_SIZE); -#endif - if (hcs != e_frame->hcs) - goto fail_frame; - -#if defined(BUILD_ETH_DIX) - if (deid == MGMT_EID) { -#elif defined (BUILD_ETH_LLC) - if (ssap == MGMT_SAP && dsap == MGMT_SAP) { -#endif - ipcp_spb_release(spb); /* No need for the N+1 buffer. */ - - if (length > MGMT_FRAME_SIZE) { - log_warn("Management frame size %u exceeds %u.", - length, MGMT_FRAME_SIZE); - continue; - } - - frame = malloc(sizeof(*frame)); - if (frame == NULL) { - log_err("Failed to allocate frame."); - continue; - } - - memcpy(frame->buf, &e_frame->payload, length); - memcpy(frame->r_addr, e_frame->src_hwaddr, MAC_SIZE); - frame->len = length; - - pthread_mutex_lock(ð_data.mgmt_lock); - list_add(&frame->next, ð_data.mgmt_frames); - pthread_cond_signal(ð_data.mgmt_cond); - pthread_mutex_unlock(ð_data.mgmt_lock); - FETCH_ADD_RELAXED(ð_data.stat.n_rcv, 1); - FETCH_ADD_RELAXED(ð_data.stat.n_mgmt_rcv, 1); - } else { - pthread_rwlock_rdlock(ð_data.flows_lock); - -#if defined(BUILD_ETH_DIX) - fd = deid; -#elif defined(BUILD_ETH_LLC) - fd = eth_data.ef_to_fd[dsap]; -#endif - if (fd < 0) { - pthread_rwlock_unlock(ð_data.flows_lock); - FETCH_ADD_RELAXED(ð_data.stat.n_bad_id, 1); - goto fail_frame; - } - -#ifdef BUILD_ETH_LLC - if (eth_data.fd_to_ef[fd].r_sap != ssap - || memcmp(eth_data.fd_to_ef[fd].r_addr, - e_frame->src_hwaddr, MAC_SIZE)) { - pthread_rwlock_unlock(ð_data.flows_lock); - FETCH_ADD_RELAXED(ð_data.stat.n_bad_id, 1); - goto fail_frame; - } -#endif - FETCH_ADD_RELAXED(ð_data.fd_to_ef[fd].stat.p_rcv, 1); - FETCH_ADD_RELAXED(ð_data.fd_to_ef[fd].stat.b_rcv, - length); - FETCH_ADD_RELAXED(ð_data.stat.n_rcv, 1); - pthread_rwlock_unlock(ð_data.flows_lock); - -#ifndef HAVE_NETMAP - ssm_pk_buff_pop(spb, ETH_HEADER_TOT_SIZE); - ssm_pk_buff_truncate(spb, length); -#else - if (ipcp_spb_reserve(&spb, length)) - continue; - - buf = ssm_pk_buff_head(spb); - memcpy(buf, &e_frame->payload, length); -#endif - if (np1_flow_write(fd, spb, NP1_GET_POOL(fd)) < 0) { - ipcp_spb_release(spb); - FETCH_ADD_RELAXED( - ð_data.fd_to_ef[fd].stat.p_dlv_f, - 1); - FETCH_ADD_RELAXED(ð_data.stat.n_dlv_f, 1); - } - - continue; - fail_frame: -#ifndef HAVE_NETMAP - ipcp_spb_release(spb); -#endif - } - } - - return (void *) 0; -} - -static void cleanup_writer(void * o) -{ - fqueue_destroy((fqueue_t *) o); -} - -static void * eth_ipcp_packet_writer(void * o) -{ - int fd; - struct ssm_pk_buff * spb; - size_t len; -#if defined(BUILD_ETH_DIX) - uint16_t deid; -#elif defined(BUILD_ETH_LLC) - uint8_t dsap; - uint8_t ssap; -#endif - uint8_t r_addr[MAC_SIZE]; - - 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) { - fevent(eth_data.np1_flows, fq, NULL); - while ((fd = fqueue_next(fq)) >= 0) { - 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 (ssm_pk_buff_push(spb, ETH_HEADER_TOT_SIZE) - == NULL) { - log_dbg("Failed to allocate header."); - ipcp_spb_release(spb); - FETCH_ADD_RELAXED(ð_data.stat.n_buf_f, 1); - continue; - } - - pthread_rwlock_rdlock(ð_data.flows_lock); -#if defined(BUILD_ETH_DIX) - deid = eth_data.fd_to_ef[fd].r_eid; -#elif defined(BUILD_ETH_LLC) - dsap = reverse_bits(eth_data.fd_to_ef[fd].r_sap); - ssap = reverse_bits(eth_data.fd_to_ef[fd].sap); -#endif - memcpy(r_addr, - eth_data.fd_to_ef[fd].r_addr, - MAC_SIZE); - - pthread_rwlock_unlock(ð_data.flows_lock); - - if (eth_ipcp_send_frame(r_addr, -#if defined(BUILD_ETH_DIX) - deid, -#elif defined(BUILD_ETH_LLC) - dsap, ssap, -#endif - ssm_pk_buff_head(spb), - len)) { - log_dbg("Failed to send frame."); - FETCH_ADD_RELAXED( - ð_data.fd_to_ef[fd].stat.p_snd_f, - 1); - FETCH_ADD_RELAXED(ð_data.stat.n_snd_f, 1); - } else { - FETCH_ADD_RELAXED( - ð_data.fd_to_ef[fd].stat.p_snd, - 1); - FETCH_ADD_RELAXED( - ð_data.fd_to_ef[fd].stat.b_snd, - len); - } - ipcp_spb_release(spb); - } - } - - pthread_cleanup_pop(true); - - return (void *) 1; -} - -#ifdef __linux__ -static int open_netlink_socket(void) -{ - struct sockaddr_nl sa; - int fd; - - memset(&sa, 0, sizeof(sa)); - sa.nl_family = AF_NETLINK; - sa.nl_pid = getpid(); - sa.nl_groups = RTMGRP_LINK; - - fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (fd < 0) - return -1; - - if (bind(fd, (struct sockaddr *) &sa, sizeof(sa))) { - close(fd); - return -1; - } - - return fd; -} - -static void change_flows_state(bool up) -{ - int i; - uint32_t flags; - - pthread_rwlock_rdlock(ð_data.flows_lock); - -#if defined(BUILD_ETH_DIX) - for (i = 0; i < SYS_MAX_FLOWS; ++i) - if (eth_data.fd_to_ef[i].r_eid != -1) { - fccntl(i, FLOWGFLAGS, &flags); - if (up) - fccntl(i, FLOWSFLAGS, flags & ~FLOWFDOWN); - else - fccntl(i, FLOWSFLAGS, flags | FLOWFDOWN); - } -#elif defined(BUILD_ETH_LLC) - for (i = 0; i < MAX_SAPS; i++) - if (eth_data.ef_to_fd[i] != -1) { - fccntl(eth_data.ef_to_fd[i], FLOWGFLAGS, &flags); - if (up) - fccntl(eth_data.ef_to_fd[i], - FLOWSFLAGS, flags & ~FLOWFDOWN); - else - fccntl(eth_data.ef_to_fd[i], - FLOWSFLAGS, flags | FLOWFDOWN); - } -#endif - - pthread_rwlock_unlock(ð_data.flows_lock); -} - -static void * eth_ipcp_if_monitor(void * o) -{ - int fd; - int status; - char buf[4096]; - struct iovec iov = {buf, sizeof(buf)}; - struct sockaddr_nl snl; - struct msghdr msg = {(void *) &snl, sizeof(snl), - &iov, 1, NULL, 0, 0}; - struct nlmsghdr * h; - struct ifinfomsg * ifi; - - (void ) o; - - fd = open_netlink_socket(); - if (fd < 0) { - log_err("Failed to open socket."); - return (void *) -1; - } - - pthread_cleanup_push(__cleanup_close_ptr, &fd); - - while (true) { - status = recvmsg(fd, &msg, 0); - if (status < 0) - continue; - - for (h = (struct nlmsghdr *) buf; - NLMSG_OK(h, (unsigned int) status); - h = NLMSG_NEXT(h, status)) { - - /* Finish reading */ - if (h->nlmsg_type == NLMSG_DONE) - break; - - /* Message is some kind of error */ - if (h->nlmsg_type == NLMSG_ERROR) - continue; - - /* Only interested in link up/down */ - if (h->nlmsg_type != RTM_NEWLINK) - continue; - - ifi = NLMSG_DATA(h); - - /* Not our interface */ - if (ifi->ifi_index != eth_data.if_idx) - continue; - - if (ifi->ifi_flags & IFF_UP) { - change_flows_state(true); - log_dbg("Interface up."); - } else { - change_flows_state(false); - log_dbg("Interface down."); - } - } - } - - pthread_cleanup_pop(true); - - return (void *) 0; -} -#endif - -#if defined (HAVE_BPF) && !defined(HAVE_NETMAP) -static int open_bpf_device(void) -{ - char dev[32]; - size_t i = 0; - - for (i = 0; i < BPF_DEV_MAX; i++) { - int fd = -1; - - snprintf(dev, sizeof(dev), "/dev/bpf%zu", i); - - fd = open(dev, O_RDWR); - if (fd > -1) - return fd; - } - - return -1; -} -#endif - -#if defined(__FreeBSD__) || defined(__APPLE__) -static int ifr_hwaddr_from_ifaddrs(struct ifreq * ifr) -{ - struct ifaddrs * ifaddr; - struct ifaddrs * ifa; - int idx; - - if (getifaddrs(&ifaddr) < 0) { - log_err("Could not get interfaces."); - goto fail_ifaddrs; - } - - for (ifa = ifaddr, idx = 0; ifa != NULL; ifa = ifa->ifa_next, ++idx) { - if (strcmp(ifa->ifa_name, ifr->ifr_name) == 0) - break; - } - - if (ifa == NULL) { - log_err("Interface not found."); - goto fail_ifa; - } - - memcpy(&ifr->ifr_addr, ifa->ifa_addr, sizeof(*ifa->ifa_addr)); - - log_dbg("Interface %s hwaddr " MAC_FMT ".", ifr->ifr_name, - MAC_VAL(ifr->ifr_addr.sa_data)); - - freeifaddrs(ifaddr); - - return 0; - fail_ifa: - freeifaddrs(ifaddr); - fail_ifaddrs: - return -1; - -} -#elif defined(__linux__) -static int ifr_hwaddr_from_socket(struct ifreq * ifr) -{ - int skfd; - - skfd = socket(AF_UNIX, SOCK_STREAM, 0); - if (skfd < 0) { - log_err("Failed to open socket."); - goto fail_socket; - } - - if (ioctl(skfd, SIOCGIFHWADDR, ifr)) { - log_err("Failed to get hwaddr."); - goto fail_ifr; - } - - log_dbg("Interface %s hwaddr " MAC_FMT ".", ifr->ifr_name, - MAC_VAL(ifr->ifr_hwaddr.sa_data)); - - close(skfd); - - return 0; - - fail_ifr: - close(skfd); - fail_socket: - return -1; -} -#endif - -static int eth_ifr_hwaddr(struct ifreq * ifr) -{ -#if defined(__FreeBSD__) || defined(__APPLE__) - return ifr_hwaddr_from_ifaddrs(ifr); -#elif defined(__linux__) - return ifr_hwaddr_from_socket(ifr); -#else - return -1; -#endif -} - -static int eth_ifr_mtu(struct ifreq * ifr) -{ - int skfd; - - skfd = socket(AF_UNIX, SOCK_STREAM, 0); - if (skfd < 0) { - log_err("Failed to open socket."); - goto fail_socket; - } - - if (ioctl(skfd, SIOCGIFMTU, ifr) < 0) { - log_err("Failed to get MTU."); - goto fail_mtu; - } - close(skfd); - - return 0; - - fail_mtu: - close(skfd); - fail_socket: - return -1; -} - -static int eth_set_mtu(struct ifreq * ifr) -{ - if (eth_ifr_mtu(ifr) < 0) { - log_err("Failed to get interface MTU."); - return -1; - } - - log_dbg("Device MTU is %d.", ifr->ifr_mtu); - - eth_data.mtu = MIN((int) ETH_MTU_MAX, ifr->ifr_mtu); - if (memcmp(ifr->ifr_name, "lo", 2) == 0 && - eth_data.mtu > IPCP_ETH_LO_MTU) { - log_dbg("Using loopback interface. MTU restricted to %d.", - IPCP_ETH_LO_MTU); - eth_data.mtu = IPCP_ETH_LO_MTU; - } - - log_dbg("Layer MTU is %d.", eth_data.mtu); - - return 0; -} -#if defined(HAVE_NETMAP) -static int eth_init_nmd(struct ifreq * ifr) -{ - strcpy(ifn, "netmap:"); - strcat(ifn, ifr->ifr_name); - - eth_data.nmd = nm_open(ifn, NULL, 0, NULL); - if (eth_data.nmd == NULL) { - log_err("Failed to open netmap device."); - goto fail_nmd; - } - - memset(ð_data.poll_in, 0, sizeof(eth_data.poll_in)); - memset(ð_data.poll_out, 0, sizeof(eth_data.poll_out)); - - eth_data.poll_in.fd = NETMAP_FD(eth_data.nmd); - eth_data.poll_in.events = POLLIN; - eth_data.poll_out.fd = NETMAP_FD(eth_data.nmd); - eth_data.poll_out.events = POLLOUT; - - log_info("Using netmap device."); - - return 0; - fail_nmd: - return -1; -} -#elif defined (HAVE_BPF) -static int eth_init_bpf(struct ifreq * ifr) -{ - int enable = 1; - int disable = 0; - int blen; - - eth_data.bpf = open_bpf_device(); - if (eth_data.bpf < 0) { - log_err("Failed to open bpf device."); - goto fail_bpf; - } - - ioctl(eth_data.bpf, BIOCGBLEN, &blen); - if (BPF_BLEN < blen) { - log_err("BPF buffer too small (is: %ld must be: %d).", - BPF_BLEN, blen); - goto fail_device; - } - - if (ioctl(eth_data.bpf, BIOCSETIF, ifr) < 0) { - log_err("Failed to set interface."); - goto fail_device; - } - - if (ioctl(eth_data.bpf, BIOCSHDRCMPLT, &enable) < 0) { - log_err("Failed to set BIOCSHDRCMPLT."); - goto fail_device; - } - - if (ioctl(eth_data.bpf, BIOCSSEESENT, &disable) < 0) { - log_err("Failed to set BIOCSSEESENT."); - goto fail_device; - } - - if (ioctl(eth_data.bpf, BIOCIMMEDIATE, &enable) < 0) { - log_err("Failed to set BIOCIMMEDIATE."); - goto fail_device; - } - - log_info("Using Berkeley Packet Filter."); - - return 0; - - fail_device: - close(eth_data.bpf); - fail_bpf: - return -1; -} -#elif defined(HAVE_RAW_SOCKETS) -#define SOCKOPT() -static int eth_init_raw_socket(struct ifreq * ifr) -{ - int idx; - int sndbuf; - int rcvbuf; -#if defined(IPCP_ETH_QDISC_BYPASS) - int qdisc_bypass = 1; -#endif /* ENABLE_QDISC_BYPASS */ - - idx = if_nametoindex(ifr->ifr_name); - if (idx == 0) { - log_err("Failed to retrieve interface index."); - return -1; - } - - memset(&(eth_data.device), 0, sizeof(eth_data.device)); - eth_data.device.sll_ifindex = idx; - eth_data.device.sll_family = AF_PACKET; - memcpy(eth_data.device.sll_addr, ifr->ifr_hwaddr.sa_data, MAC_SIZE); - eth_data.device.sll_halen = MAC_SIZE; - eth_data.device.sll_protocol = htons(ETH_P_ALL); -#if defined (BUILD_ETH_DIX) - eth_data.s_fd = socket(AF_PACKET, SOCK_RAW, eth_data.ethertype); -#elif defined (BUILD_ETH_LLC) - eth_data.s_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_802_2)); -#endif - if (eth_data.s_fd < 0) { - log_err("Failed to create socket."); - goto fail_socket; - } - -#if defined(IPCP_ETH_QDISC_BYPASS) - if (setsockopt(eth_data.s_fd, SOL_PACKET, PACKET_QDISC_BYPASS, - &qdisc_bypass, sizeof(qdisc_bypass))) { - log_info("Qdisc bypass not supported."); - } -#endif - - sndbuf = IPCP_ETH_SNDBUF; - if (sndbuf > 0) { - /* Never below one frame or sendto() cannot fit an SDU. */ - sndbuf = MAX(sndbuf, (int) (ETH_HEADER_TOT_SIZE + ETH_MTU)); - if (setsockopt(eth_data.s_fd, SOL_SOCKET, SO_SNDBUF, - &sndbuf, sizeof(sndbuf))) - log_info("Failed to set SO_SNDBUF to %d.", sndbuf); - } - - rcvbuf = IPCP_ETH_RCVBUF; - if (rcvbuf > 0) { - /* SO_RCVBUFFORCE bypasses rmem_max; SO_RCVBUF is fallback. */ - if (setsockopt(eth_data.s_fd, SOL_SOCKET, SO_RCVBUFFORCE, - &rcvbuf, sizeof(rcvbuf)) - && setsockopt(eth_data.s_fd, SOL_SOCKET, SO_RCVBUF, - &rcvbuf, sizeof(rcvbuf))) - log_info("Failed to set SO_RCVBUF to %d.", rcvbuf); - } - - if (bind(eth_data.s_fd, (struct sockaddr *) ð_data.device, - sizeof(eth_data.device)) < 0) { - log_err("Failed to bind socket to interface."); - goto fail_device; - } -#ifdef __linux__ - eth_data.if_idx = idx; -#endif - log_info("Using raw socket device."); - - return 0; - fail_device: - close(eth_data.s_fd); - fail_socket: - return -1; -} -#endif - -static int eth_ipcp_bootstrap(struct ipcp_config * conf) -{ - struct ifreq ifr; - int i; -#if defined(HAVE_NETMAP) - char ifn[IFNAMSIZ]; -#endif /* HAVE_NETMAP */ - - assert(conf); - assert(conf->type == THIS_TYPE); - - memset(&ifr, 0, sizeof(ifr)); - strcpy(ifr.ifr_name, conf->eth.dev); - - if (strlen(conf->eth.dev) >= IFNAMSIZ) { - log_err("Invalid device name: %s.", conf->eth.dev); - return -1; - } -#ifdef BUILD_ETH_DIX - if (conf->eth.ethertype < 0x0600 || conf->eth.ethertype == 0xFFFF) { - log_err("Invalid Ethertype: %d.", conf->eth.ethertype); - return -1; - } - eth_data.ethertype = htons(conf->eth.ethertype); -#endif - if (eth_set_mtu(&ifr) < 0) { - log_err("Failed to set MTU."); - return -1; - } - - if (eth_ifr_hwaddr(&ifr) < 0) { - log_err("Failed to get hardware addr."); - return -1; - } -#if defined(HAVE_NETMAP) || defined(HAVE_BPF) - memcpy(eth_data.hw_addr, LLADDR((struct sockaddr_dl *) &ifr.ifr_addr), - MAC_SIZE); -#endif -#if defined(HAVE_NETMAP) - if (eth_init_nmd(&ifr) < 0) { - log_err("Failed to initialize netmap device."); - return -1; - } -#elif defined(HAVE_BPF) /* !HAVE_NETMAP */ - if (eth_init_bpf(&ifr) < 0) { - log_err("Failed to initialize BPF device."); - return -1; - } -#elif defined(HAVE_RAW_SOCKETS) - if (eth_init_raw_socket(&ifr) < 0) { - log_err("Failed to initialize raw socket device."); - return -1; - } -#endif /* HAVE_NETMAP */ -#ifdef IPCP_ETH_FLOW_STATS - if (rib_reg(ETH_RIB_PATH, ð_r_ops)) { - log_err("Failed to register RIB."); - goto fail_rib_reg; - } -#endif -#if defined(__linux__) - if (pthread_create(ð_data.if_monitor, NULL, - eth_ipcp_if_monitor, NULL)) { - log_err("Failed to create monitor thread: %s.", - strerror(errno)); - goto fail_monitor; - } -#endif - if (pthread_create(ð_data.mgmt_handler, NULL, - eth_ipcp_mgmt_handler, NULL)) { - log_err("Failed to create mgmt handler thread: %s.", - strerror(errno)); - goto fail_mgmt_handler; - } - - for (i = 0; i < IPCP_ETH_RD_THR; i++) { - if (pthread_create(ð_data.packet_reader[i], NULL, - eth_ipcp_packet_reader, NULL)) { - log_err("Failed to create packet reader thread: %s", - strerror(errno)); - goto fail_packet_reader; - } - } - - for (i = 0; i < IPCP_ETH_WR_THR; i++) { - if (pthread_create(ð_data.packet_writer[i], NULL, - eth_ipcp_packet_writer, NULL)) { - log_err("Failed to create packet writer thread: %s", - strerror(errno)); - goto fail_packet_writer; - } - } - -#if defined(BUILD_ETH_DIX) - log_dbg("Bootstrapped IPCP over DIX Ethernet with pid %d " - "and Ethertype 0x%X.", getpid(), conf->eth.ethertype); -#elif defined(BUILD_ETH_LLC) - log_dbg("Bootstrapped IPCP over Ethernet with LLC with pid %d.", - getpid()); -#endif - return 0; - - fail_packet_writer: - while (i-- > 0) { - pthread_cancel(eth_data.packet_writer[i]); - pthread_join(eth_data.packet_writer[i], NULL); - } - i = IPCP_ETH_RD_THR; - fail_packet_reader: - while (i-- > 0) { - pthread_cancel(eth_data.packet_reader[i]); - pthread_join(eth_data.packet_reader[i], NULL); - } - pthread_cancel(eth_data.mgmt_handler); - pthread_join(eth_data.mgmt_handler, NULL); - fail_mgmt_handler: -#if defined(__linux__) - pthread_cancel(eth_data.if_monitor); - pthread_join(eth_data.if_monitor, NULL); -#endif -#if defined(__linux__) - fail_monitor: -#endif -#ifdef IPCP_ETH_FLOW_STATS - rib_unreg(ETH_RIB_PATH); - fail_rib_reg: -#endif -#if defined(HAVE_NETMAP) - nm_close(eth_data.nmd); -#elif defined(HAVE_BPF) - close(eth_data.bpf); -#elif defined(HAVE_RAW_SOCKETS) - close(eth_data.s_fd); -#endif - return -1; -} - -static int eth_ipcp_reg(const uint8_t * hash) -{ - if (shim_data_reg_add_entry(eth_data.shim_data, hash)) { - log_err("Failed to add " HASH_FMT32 " to local registry.", - HASH_VAL32(hash)); - return -1; - } - - return 0; -} - -static int eth_ipcp_unreg(const uint8_t * hash) -{ - shim_data_reg_del_entry(eth_data.shim_data, hash); - - return 0; -} - -static int eth_ipcp_query(const uint8_t * hash) -{ - uint8_t r_addr[MAC_SIZE]; - struct timespec timeout; - struct dir_query * query; - int ret; - int attempt; - uint8_t * buf; - struct mgmt_msg * msg; - size_t len; - long per_ms; - - if (shim_data_dir_has(eth_data.shim_data, hash)) - return 0; - - len = sizeof(*msg) + ipcp_dir_hash_len(); - - buf = malloc(len + ETH_HEADER_TOT_SIZE); - if (buf == NULL) - return -1; - - memset(buf, 0, len + ETH_HEADER_TOT_SIZE); - - msg = (struct mgmt_msg *) (buf + ETH_HEADER_TOT_SIZE); - msg->code = NAME_QUERY_REQ; - - memcpy(msg + 1, hash, ipcp_dir_hash_len()); - - memset(r_addr, 0xff, MAC_SIZE); - - per_ms = NAME_QUERY_TIMEO / (NAME_QUERY_RETRIES + 1); - - ret = -1; - for (attempt = 0; attempt <= NAME_QUERY_RETRIES; ++attempt) { - query = shim_data_dir_query_create(eth_data.shim_data, hash); - if (query == NULL) { - ret = -1; - break; - } - - if (eth_ipcp_send_frame(r_addr, -#if defined(BUILD_ETH_DIX) - MGMT_EID, -#elif defined(BUILD_ETH_LLC) - reverse_bits(MGMT_SAP), - reverse_bits(MGMT_SAP), -#endif - buf, len)) { - log_err("Failed to send management frame."); - shim_data_dir_query_destroy(eth_data.shim_data, - query); - ret = -1; - break; - } - - FETCH_ADD_RELAXED(ð_data.stat.n_mgmt_snd, 1); - - timeout.tv_sec = per_ms / 1000; - timeout.tv_nsec = (per_ms % 1000) * 1000000L; - - ret = shim_data_dir_query_wait(query, &timeout); - - shim_data_dir_query_destroy(eth_data.shim_data, query); - - if (ret != -ETIMEDOUT) - break; - } - - free(buf); - - return ret; -} - -static int eth_ipcp_flow_alloc(int fd, - const uint8_t * hash, - qosspec_t qs, - const buffer_t * data) -{ -#ifdef BUILD_ETH_LLC - uint8_t ssap = 0; -#endif - uint8_t r_addr[MAC_SIZE]; - struct addr addr; - - assert(hash); - - if (!shim_data_dir_has(eth_data.shim_data, hash)) { - log_err("Destination "HASH_FMT32 "unreachable.", - HASH_VAL32(hash)); - return -1; - } - - addr = shim_data_dir_get_addr(eth_data.shim_data, hash); - memcpy(r_addr, &addr.mac, MAC_SIZE); - -#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); - log_err("Failed to allocate SSAP."); - return -1; - } - - eth_data.fd_to_ef[fd].sap = ssap; - eth_data.ef_to_fd[ssap] = fd; - pthread_rwlock_unlock(ð_data.flows_lock); -#endif - - if (eth_ipcp_alloc(r_addr, -#if defined(BUILD_ETH_DIX) - fd, -#elif defined(BUILD_ETH_LLC) - ssap, -#endif - hash, - qs, - data) < 0) { -#ifdef BUILD_ETH_LLC - pthread_rwlock_wrlock(ð_data.flows_lock); - bmp_release(eth_data.saps, eth_data.fd_to_ef[fd].sap); - eth_data.fd_to_ef[fd].sap = -1; - eth_data.ef_to_fd[ssap] = -1; - pthread_rwlock_unlock(ð_data.flows_lock); - log_err("Failed to allocate with peer."); -#endif - return -1; - } - - fset_add(eth_data.np1_flows, fd); -#ifdef IPCP_ETH_FLOW_STATS - pthread_rwlock_wrlock(ð_data.flows_lock); - memset(ð_data.fd_to_ef[fd].stat, 0, - sizeof(eth_data.fd_to_ef[fd].stat)); - eth_data.fd_to_ef[fd].stat.stamp = time(NULL); - FETCH_ADD_RELAXED(ð_data.stat.n_flows, 1); - pthread_rwlock_unlock(ð_data.flows_lock); -#endif -#if defined(BUILD_ETH_LLC) - log_dbg("Assigned SAP %d for fd %d.", ssap, fd); -#endif - return 0; -} - -static int eth_ipcp_flow_alloc_resp(int fd, - int response, - const buffer_t * data) -{ -#if defined(BUILD_ETH_DIX) - uint16_t r_eid; -#elif defined(BUILD_ETH_LLC) - uint8_t ssap; - uint8_t r_sap; -#endif - uint8_t r_addr[MAC_SIZE]; - - if (ipcp_wait_flow_resp(fd) < 0) { - log_err("Failed to wait for flow response."); - return -1; - } - - pthread_rwlock_wrlock(ð_data.flows_lock); -#if defined(BUILD_ETH_DIX) - r_eid = eth_data.fd_to_ef[fd].r_eid; -#elif defined(BUILD_ETH_LLC) - ssap = bmp_allocate(eth_data.saps); - if (!bmp_is_id_valid(eth_data.saps, ssap)) { - pthread_rwlock_unlock(ð_data.flows_lock); - log_err("Failed to allocate SSAP."); - return -1; - } - - eth_data.fd_to_ef[fd].sap = ssap; - r_sap = eth_data.fd_to_ef[fd].r_sap; - eth_data.ef_to_fd[ssap] = fd; -#endif - memcpy(r_addr, eth_data.fd_to_ef[fd].r_addr, MAC_SIZE); - - pthread_rwlock_unlock(ð_data.flows_lock); - - if (eth_ipcp_alloc_resp(r_addr, -#if defined(BUILD_ETH_DIX) - fd, r_eid, -#elif defined(BUILD_ETH_LLC) - ssap, r_sap, -#endif - response, - data) < 0) { -#ifdef BUILD_ETH_LLC - pthread_rwlock_wrlock(ð_data.flows_lock); - bmp_release(eth_data.saps, eth_data.fd_to_ef[fd].sap); - pthread_rwlock_unlock(ð_data.flows_lock); -#endif - log_err("Failed to respond to peer."); - return -1; - } - - fset_add(eth_data.np1_flows, fd); -#ifdef IPCP_ETH_FLOW_STATS - pthread_rwlock_wrlock(ð_data.flows_lock); - memset(ð_data.fd_to_ef[fd].stat, 0, - sizeof(eth_data.fd_to_ef[fd].stat)); - eth_data.fd_to_ef[fd].stat.stamp = time(NULL); - FETCH_ADD_RELAXED(ð_data.stat.n_flows, 1); - pthread_rwlock_unlock(ð_data.flows_lock); -#endif -#if defined(BUILD_ETH_LLC) - log_dbg("Assigned SAP %d for fd %d.", ssap, fd); -#endif - return 0; -} - -static int eth_ipcp_flow_dealloc(int fd) -{ -#ifdef BUILD_ETH_LLC - uint8_t sap; -#endif - ipcp_flow_fini(fd); - - fset_del(eth_data.np1_flows, fd); - - pthread_rwlock_wrlock(ð_data.flows_lock); - -#if defined(BUILD_ETH_DIX) - eth_data.fd_to_ef[fd].r_eid = -1; -#elif defined BUILD_ETH_LLC - sap = eth_data.fd_to_ef[fd].sap; - bmp_release(eth_data.saps, sap); - eth_data.fd_to_ef[fd].sap = -1; - eth_data.fd_to_ef[fd].r_sap = -1; - eth_data.ef_to_fd[sap] = -1; -#endif - memset(ð_data.fd_to_ef[fd].r_addr, 0, MAC_SIZE); - -#ifdef IPCP_ETH_FLOW_STATS - memset(ð_data.fd_to_ef[fd].stat, 0, - sizeof(eth_data.fd_to_ef[fd].stat)); - FETCH_SUB_RELAXED(ð_data.stat.n_flows, 1); -#endif - - pthread_rwlock_unlock(ð_data.flows_lock); - - ipcp_flow_dealloc(fd); - - return 0; -} - -static struct ipcp_ops eth_ops = { - .ipcp_bootstrap = eth_ipcp_bootstrap, - .ipcp_enroll = NULL, - .ipcp_connect = NULL, - .ipcp_disconnect = NULL, - .ipcp_reg = eth_ipcp_reg, - .ipcp_unreg = eth_ipcp_unreg, - .ipcp_query = eth_ipcp_query, - .ipcp_flow_alloc = eth_ipcp_flow_alloc, - .ipcp_flow_join = NULL, - .ipcp_flow_alloc_resp = eth_ipcp_flow_alloc_resp, - .ipcp_flow_dealloc = eth_ipcp_flow_dealloc, - .ipcp_flow_update = eth_ipcp_flow_update -}; - -int main(int argc, - char * argv[]) -{ - int i; - - if (eth_data_init() < 0) { -#if defined(BUILD_ETH_DIX) - log_err("Failed to init eth-llc data."); -#elif defined(BUILD_ETH_LLC) - log_err("Failed to init eth-dix data."); -#endif - goto fail_data_init; - } - - if (ipcp_init(argc, argv, ð_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_ETH_WR_THR; ++i) - pthread_cancel(eth_data.packet_writer[i]); - for (i = 0; i < IPCP_ETH_RD_THR; ++i) - pthread_cancel(eth_data.packet_reader[i]); - - pthread_cancel(eth_data.mgmt_handler); -#ifdef __linux__ - pthread_cancel(eth_data.if_monitor); -#endif - for (i = 0; i < IPCP_ETH_WR_THR; ++i) - pthread_join(eth_data.packet_writer[i], NULL); - for (i = 0; i < IPCP_ETH_RD_THR; ++i) - pthread_join(eth_data.packet_reader[i], NULL); - - pthread_join(eth_data.mgmt_handler, NULL); -#ifdef __linux__ - pthread_join(eth_data.if_monitor, NULL); -#endif -#ifdef IPCP_ETH_FLOW_STATS - rib_unreg(ETH_RIB_PATH); -#endif - } - - ipcp_stop(); - - ipcp_fini(); - - eth_data_fini(); - - exit(EXIT_SUCCESS); - - fail_start: - ipcp_fini(); - fail_init: - eth_data_fini(); - fail_data_init: - exit(EXIT_FAILURE); -} diff --git a/src/ipcpd/eth/llc.c b/src/ipcpd/eth/llc.c deleted file mode 100644 index a772e86e..00000000 --- a/src/ipcpd/eth/llc.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Ouroboros - Copyright (C) 2016 - 2026 - * - * IPC processes over Ethernet - LLC - * - * Dimitri Staessens <dimitri@ouroboros.rocks> - * Sander Vrijders <sander@ouroboros.rocks> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., http://www.fsf.org/about/contact/. - */ - -#define BUILD_ETH_LLC -#define OUROBOROS_PREFIX "ipcpd/eth-llc" - -#include "eth.c" diff --git a/src/ipcpd/ipcp.c b/src/ipcpd/ipcp.c index dcee4b9c..b25b7d04 100644 --- a/src/ipcpd/ipcp.c +++ b/src/ipcpd/ipcp.c @@ -36,6 +36,7 @@ #define OUROBOROS_PREFIX "ipcpd/ipcp" #define IPCP_INFO "info" #define ALLOC_TIMEOUT 50 /* ms */ +#define HAS_POA (ipcpd.type != IPCP_LOCAL) #include <ouroboros/bitmap.h> #include <ouroboros/dev.h> @@ -54,6 +55,7 @@ #include "ipcp.h" #include "np1.h" +#include <arpa/inet.h> #include <signal.h> #include <string.h> #include <sys/socket.h> @@ -69,14 +71,14 @@ #define CLOCK_REALTIME_COARSE CLOCK_REALTIME #endif +/* Fits "eth <dev> 0x<type>", the longest PoA rendering. */ +/* Matches src/tools/irm/irm_utils.h; keep in sync. */ +#define POA_STRLEN (DEV_NAME_SIZE + 11) + static char * ipcp_type_str[] = { "local", "unicast", - "broadcast", - "eth-llc", - "eth-dix", - "udp4", - "udp6" + "broadcast" }; static char * dir_hash_str[] = { @@ -237,14 +239,6 @@ static int ipcp_rib_read(const char * path, strcpy(buf, "unicast\n"); else if (ipcpd.type == IPCP_BROADCAST) strcpy(buf, "broadcast\n"); - else if (ipcpd.type == IPCP_ETH_LLC) - strcpy(buf, "eth-llc\n"); - else if (ipcpd.type == IPCP_ETH_DIX) - strcpy(buf, "eth-dix\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"); } @@ -316,8 +310,7 @@ static void * acceptloop(void * o) (void) o; - while (ipcp_get_state() != IPCP_SHUTDOWN && - ipcp_get_state() != IPCP_INIT) { + while (ipcp_get_state() != IPCP_SHUTDOWN) { struct cmd * cmd; csockfd = accept(ipcpd.sockfd, 0, 0); @@ -471,11 +464,6 @@ 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_UDP4: - /* FALLTHRU */ - case IPCP_UDP6: - conf.layer_info.dir_hash_algo = (enum pol_dir_hash) HASH_MD5; - break; case IPCP_BROADCAST: conf.layer_info.dir_hash_algo = DIR_HASH_SHA3_256; break; @@ -493,6 +481,8 @@ static void do_bootstrap(ipcp_config_msg_t * conf_msg, strcpy(ipcpd.layer_name, info->name); ipcpd.dir_hash_algo = (enum hash_algo) info->dir_hash_algo; + if (poa_set_layer(info->name) < 0) + log_warn("Failed to set layer name for PoA queries."); ret_msg->layer_info = layer_info_s_to_msg(info); ipcp_set_state(IPCP_OPERATIONAL); @@ -503,10 +493,13 @@ static void do_bootstrap(ipcp_config_msg_t * conf_msg, ipcp_dir_hash_len()); } -static void do_enroll(const char * dst, - ipcp_msg_t * ret_msg) +static void do_enroll(const char * dst, + const poa_addr_msg_t * peer, + ipcp_msg_t * ret_msg) { struct layer_info info; + struct poa_addr addr; + struct poa_addr * pa = NULL; log_info("Enrolling with %s...", dst); @@ -524,7 +517,16 @@ static void do_enroll(const char * dst, return; } - ret_msg->result = ipcpd.ops->ipcp_enroll(dst, &info); + if (peer != NULL) { + addr = poa_addr_msg_to_s(peer); + if (addr.type == POA_INVALID || addr.type == POA_UDP) { + ret_msg->result = -EINVAL; + return; + } + pa = &addr; + } + + ret_msg->result = ipcpd.ops->ipcp_enroll(dst, pa, &info); if (ret_msg->result < 0) { log_err("Failed to bootstrap IPCP."); return; @@ -532,6 +534,8 @@ static void do_enroll(const char * dst, strcpy(ipcpd.layer_name, info.name); ipcpd.dir_hash_algo = (enum hash_algo) info.dir_hash_algo; + if (poa_set_layer(info.name) < 0) + log_warn("Failed to set layer name for PoA queries."); ret_msg->layer_info = layer_info_s_to_msg(&info); ipcp_set_state(IPCP_OPERATIONAL); @@ -542,11 +546,136 @@ static void do_enroll(const char * dst, ipcp_dir_hash_len()); } -static void do_connect(const char * dst, - const char * comp, - qosspec_t qs, - ipcp_msg_t * ret_msg) +/* Bounded so one oversized reply cannot be built; 64 is generous. */ +#define POA_LIST_MAX 64 + +static void do_list_poas(ipcp_msg_t * ret_msg) +{ + struct poa_spec eps[POA_LIST_MAX]; + ssize_t n; + ssize_t i; + + if (ipcpd.type != IPCP_UNICAST && ipcpd.type != IPCP_BROADCAST) { + ret_msg->result = -ENOTSUP; + return; + } + + n = poa_list(eps, POA_LIST_MAX); + if (n < 0) { + ret_msg->result = (int) n; + return; + } + + if (n > POA_LIST_MAX) { + log_warn("Listing %d of %zd PoAs.", POA_LIST_MAX, n); + + n = POA_LIST_MAX; + } + + if (n == 0) { + ret_msg->result = 0; + return; + } + + ret_msg->poas = malloc(n * sizeof(*ret_msg->poas)); + if (ret_msg->poas == NULL) { + ret_msg->result = -ENOMEM; + return; + } + + for (i = 0; i < n; i++) { + ret_msg->poas[i] = poa_spec_s_to_msg(&eps[i]); + if (ret_msg->poas[i] == NULL) { + ret_msg->result = -ENOMEM; + return; + } + ret_msg->n_poas++; + } + + ret_msg->result = 0; +} + +/* Names a PoA the way "irm ipcp poa list" prints it. */ +/* Matches src/tools/irm/irm_utils.c; keep in sync. */ +static void poa_spec_str(const struct poa_spec * poa, + char * buf, + size_t len) +{ + char addr[INET6_ADDRSTRLEN]; + + switch (poa->type) { + case POA_UDP4: + if (inet_ntop(AF_INET, &poa->udp4.ip_addr, + addr, sizeof(addr)) == NULL) + break; + + snprintf(buf, len, "udp4 %s:%u", addr, poa->udp4.port); + return; + case POA_UDP6: + if (inet_ntop(AF_INET6, &poa->udp6.ip_addr, + addr, sizeof(addr)) == NULL) + break; + + snprintf(buf, len, "udp6 [%s]:%u", addr, poa->udp6.port); + return; + case POA_ETH: + snprintf(buf, len, "eth %s 0x%04X", poa->eth.dev, + poa->eth.ethertype); + return; + default: + break; + } + + snprintf(buf, len, "(unknown)"); +} + +static void do_attach(poa_spec_msg_t * msg, + ipcp_msg_t * ret_msg) { + struct poa_spec poa; + char str[POA_STRLEN + 1]; + + poa = poa_spec_msg_to_s(msg); + + poa_spec_str(&poa, str, sizeof(str)); + + ret_msg->result = poa_attach(&poa); + if (ret_msg->result < 0) { + log_err("Failed to attach %s.", str); + return; + } + + log_info("Attached %s.", str); +} + +static void do_detach(poa_spec_msg_t * msg, + ipcp_msg_t * ret_msg) +{ + struct poa_spec poa; + char str[POA_STRLEN + 1]; + + poa = poa_spec_msg_to_s(msg); + + poa_spec_str(&poa, str, sizeof(str)); + + ret_msg->result = poa_detach(&poa); + if (ret_msg->result < 0) { + log_err("Failed to detach %s.", str); + return; + } + + log_info("Detached %s.", str); +} + +static void do_connect(const char * dst, + const char * comp, + qosspec_t qs, + const poa_addr_msg_t * peer, + ipcp_msg_t * ret_msg) +{ + struct poa_addr addr; + struct poa_addr * pa = NULL; + log_info("Connecting %s to %s...", comp, dst); if (ipcpd.ops->ipcp_connect == NULL) { @@ -555,7 +684,16 @@ static void do_connect(const char * dst, return; } - ret_msg->result = ipcpd.ops->ipcp_connect(dst, comp, qs); + if (peer != NULL) { + addr = poa_addr_msg_to_s(peer); + if (addr.type == POA_INVALID || addr.type == POA_UDP) { + ret_msg->result = -EINVAL; + return; + } + pa = &addr; + } + + ret_msg->result = ipcpd.ops->ipcp_connect(dst, comp, qs, pa); log_info("Finished connecting."); } @@ -734,9 +872,14 @@ static void do_flow_join(pid_t pid, log_info("Finished joining layer " HASH_FMT32 ".", HASH_VAL32(dst)); } +/* + * The IRMd says whether the flow is on a PoA, as it may not be known + * here yet. PoA flows answer without the IPCP type's flow machinery. + */ static void do_flow_alloc_resp(int resp, int flow_id, uid_t uid, + bool is_poa, const buffer_t * data, ipcp_msg_t * ret_msg) { @@ -745,13 +888,6 @@ static void do_flow_alloc_resp(int resp, log_info("Responding %d to alloc on flow_id %d.", resp, flow_id); - if (ipcpd.ops->ipcp_flow_alloc_resp == NULL) { - log_err("Failed to respond on flow %d: operation unsupported.", - flow_id); - ret_msg->result = -ENOTSUP; - return; - } - if (ipcp_get_state() != IPCP_OPERATIONAL) { log_err("Failed to respond to flow %d:" "IPCP in state <%s>, need <%s>.", @@ -762,6 +898,20 @@ static void do_flow_alloc_resp(int resp, return; } + if (is_poa) { + ret_msg->result = poa_flow_alloc_resp(flow_id, resp, data); + log_info("Finished responding %d on PoA flow %d.", + ret_msg->result, flow_id); + return; + } + + if (ipcpd.ops->ipcp_flow_alloc_resp == NULL) { + log_err("Failed to respond on flow %d: operation unsupported.", + flow_id); + ret_msg->result = -ENOTSUP; + return; + } + fd = np1_flow_resp(flow_id, resp); if (fd < 0) { log_warn("Flow_id %d is not known.", flow_id); @@ -788,18 +938,13 @@ static void do_flow_alloc_resp(int resp, static void do_flow_dealloc(int flow_id, int timeo_sec, + bool is_poa, ipcp_msg_t * ret_msg) { int fd; log_info("Deallocating flow %d.", flow_id); - if (ipcpd.ops->ipcp_flow_dealloc == NULL) { - log_err("Failed to dealloc: operation unsupported."); - ret_msg->result = -ENOTSUP; - return; - } - if (ipcp_get_state() != IPCP_OPERATIONAL) { log_err("Failed to enroll: IPCP in state <%s>, need <%s>.", ipcp_state_str[ipcp_get_state()], @@ -808,6 +953,20 @@ static void do_flow_dealloc(int flow_id, return; } + if (is_poa) { + ret_msg->result = poa_flow_dealloc(flow_id); + + log_info("Finished deallocating PoA flow %d.", flow_id); + return; + } + + if (ipcpd.ops->ipcp_flow_dealloc == NULL) { + log_err("Failed to dealloc: operation unsupported."); + + ret_msg->result = -ENOTSUP; + return; + } + fd = np1_flow_dealloc(flow_id, timeo_sec); if (fd < 0) { log_warn("Could not deallocate flow_id %d.", flow_id); @@ -822,18 +981,25 @@ static void do_flow_dealloc(int flow_id, static void do_flow_update(int flow_id, const buffer_t * data, + bool is_poa, ipcp_msg_t * ret_msg) { int fd; - if (ipcpd.ops->ipcp_flow_update == NULL) { - log_err("Failed to update flow: operation unsupported."); - ret_msg->result = -ENOTSUP; + if (ipcp_get_state() != IPCP_OPERATIONAL) { + ret_msg->result = -EIPCPSTATE; return; } - if (ipcp_get_state() != IPCP_OPERATIONAL) { - ret_msg->result = -EIPCPSTATE; + if (is_poa) { + ret_msg->result = poa_flow_update(flow_id, data); + return; + } + + if (ipcpd.ops->ipcp_flow_update == NULL) { + log_err("Failed to update flow: operation unsupported."); + + ret_msg->result = -ENOTSUP; return; } @@ -881,6 +1047,7 @@ static void * mainloop(void * o) free(cmd); if (msg == NULL) { + log_err("Failed to unpack command message."); close(sfd); continue; } @@ -897,11 +1064,23 @@ static void * mainloop(void * o) do_bootstrap(msg->conf, &ret_msg); break; case IPCP_MSG_CODE__IPCP_ENROLL: - do_enroll(msg->dst, &ret_msg); + do_enroll(msg->dst, msg->peer, &ret_msg); + break; + case IPCP_MSG_CODE__IPCP_ATTACH: + assert(HAS_POA); + do_attach(msg->poa, &ret_msg); + break; + case IPCP_MSG_CODE__IPCP_DETACH: + assert(HAS_POA); + do_detach(msg->poa, &ret_msg); + break; + case IPCP_MSG_CODE__IPCP_LIST_POAS: + do_list_poas(&ret_msg); break; case IPCP_MSG_CODE__IPCP_CONNECT: qs = qos_spec_msg_to_s(msg->qosspec); - do_connect(msg->dst, msg->comp, qs, &ret_msg); + do_connect(msg->dst, msg->comp, qs, msg->peer, + &ret_msg); break; case IPCP_MSG_CODE__IPCP_DISCONNECT: do_disconnect(msg->dst, msg->comp, &ret_msg); @@ -940,17 +1119,20 @@ static void * mainloop(void * o) data.len = msg->pk.len; data.data = msg->pk.data; do_flow_alloc_resp(msg->response, msg->flow_id, - msg->uid, &data, &ret_msg); + msg->uid, msg->is_poa, + &data, &ret_msg); break; case IPCP_MSG_CODE__IPCP_FLOW_DEALLOC: - do_flow_dealloc(msg->flow_id, msg->timeo_sec, &ret_msg); + do_flow_dealloc(msg->flow_id, msg->timeo_sec, + msg->is_poa, &ret_msg); break; case IPCP_MSG_CODE__IPCP_FLOW_UPDATE: assert(msg->pk.len > 0 ? msg->pk.data != NULL : msg->pk.data == NULL); data.len = msg->pk.len; data.data = msg->pk.data; - do_flow_update(msg->flow_id, &data, &ret_msg); + do_flow_update(msg->flow_id, &data, msg->is_poa, + &ret_msg); break; default: ret_msg.result = -1; @@ -988,7 +1170,7 @@ static void * mainloop(void * o) if (write(sfd, buffer.data, buffer.len) == -1) log_warn("Failed to send reply message"); - pthread_cleanup_pop(true); /* close sfd */ + pthread_cleanup_pop(true); /* close sfd */ pthread_cleanup_pop(true); /* free buffer.data */ tpm_end_work(ipcpd.tpm); @@ -1101,6 +1283,11 @@ int ipcp_init(int argc, goto fail_rib_reg; } + if (poa_init(ipcpd.name) < 0) { + log_err("Failed to initialize PoAs."); + goto fail_poa_init; + } + list_head_init(&ipcpd.cmds); ipcpd.tpm = tpm_create(IPCP_MIN_THREADS, IPCP_ADD_THREADS, @@ -1124,6 +1311,8 @@ int ipcp_init(int argc, return 0; fail_tpm_create: + poa_fini(); + fail_poa_init: rib_unreg(IPCP_INFO); fail_rib_reg: rib_fini(); @@ -1149,6 +1338,7 @@ int ipcp_init(int argc, return -1; } +/* Enrolment runs over a PoA, so poa_start() precedes any RPC. */ int ipcp_start(void) { sigset_t sigset; @@ -1169,6 +1359,11 @@ int ipcp_start(void) ipcp_set_state(IPCP_BOOT); + if (poa_start() < 0) { + log_err("Failed to start PoAs."); + goto fail_poa_start; + } + if (tpm_start(ipcpd.tpm)) { log_err("Failed to start threadpool manager."); goto fail_tpm_start; @@ -1192,6 +1387,8 @@ int ipcp_start(void) fail_acceptor: tpm_stop(ipcpd.tpm); fail_tpm_start: + poa_stop(); + fail_poa_start: tpm_destroy(ipcpd.tpm); ipcp_set_state(IPCP_INIT); ipcp_create_r(&info); @@ -1263,6 +1460,8 @@ void ipcp_stop(void) tpm_stop(ipcpd.tpm); + poa_stop(); + ipcp_set_state(IPCP_INIT); } @@ -1271,6 +1470,8 @@ void ipcp_fini(void) tpm_destroy(ipcpd.tpm); + poa_fini(); + rib_unreg(IPCP_INFO); rib_fini(); diff --git a/src/ipcpd/ipcp.h b/src/ipcpd/ipcp.h index 210157ec..e0aab291 100644 --- a/src/ipcpd/ipcp.h +++ b/src/ipcpd/ipcp.h @@ -36,15 +36,18 @@ #define ipcp_dir_hash_strlen() (ipcp_dir_hash_len() * 2) +/* Attach or release one flow PoA on a unicast or broadcast IPCP. */ struct ipcp_ops { int (* ipcp_bootstrap)(struct ipcp_config * conf); - int (* ipcp_enroll)(const char * dst, - struct layer_info * info); + int (* ipcp_enroll)(const char * dst, + const struct poa_addr * addr, + struct layer_info * info); - int (* ipcp_connect)(const char * dst, - const char * component, - qosspec_t qs); + int (* ipcp_connect)(const char * dst, + const char * component, + qosspec_t qs, + const struct poa_addr * addr); int (* ipcp_disconnect)(const char * dst, const char * component); diff --git a/src/ipcpd/local/CMakeLists.txt b/src/ipcpd/local/CMakeLists.txt index 91f300a3..af433d01 100644 --- a/src/ipcpd/local/CMakeLists.txt +++ b/src/ipcpd/local/CMakeLists.txt @@ -2,6 +2,7 @@ add_executable(${IPCP_LOCAL_TARGET} main.c + reg.c ${IPCP_SOURCES} ) diff --git a/src/ipcpd/local/main.c b/src/ipcpd/local/main.c index c0aeb51e..69eac8a6 100644 --- a/src/ipcpd/local/main.c +++ b/src/ipcpd/local/main.c @@ -42,7 +42,7 @@ #include "ipcp.h" #include "np1.h" -#include "shim-data.h" +#include "reg.h" #include <string.h> #include <stdlib.h> @@ -53,14 +53,14 @@ #define THIS_TYPE IPCP_LOCAL struct { - struct shim_data * shim_data; + struct reg * reg; - int in_out[SYS_MAX_FLOWS]; - fset_t * flows; - fqueue_t * fq; + int in_out[SYS_MAX_FLOWS]; + fset_t * flows; + fqueue_t * fq; - pthread_rwlock_t lock; - pthread_t packet_loop; + pthread_rwlock_t lock; + pthread_t packet_loop; } local_data; static int local_data_init(void) @@ -77,9 +77,9 @@ static int local_data_init(void) if (local_data.fq == NULL) goto fail_fqueue; - local_data.shim_data = shim_data_create(); - if (local_data.shim_data == NULL) - goto fail_shim_data; + local_data.reg = reg_create(); + if (local_data.reg == NULL) + goto fail_reg; if (pthread_rwlock_init(&local_data.lock, NULL) < 0) goto fail_rwlock_init; @@ -87,8 +87,8 @@ static int local_data_init(void) return 0; fail_rwlock_init: - shim_data_destroy(local_data.shim_data); - fail_shim_data: + reg_destroy(local_data.reg); + fail_reg: fqueue_destroy(local_data.fq); fail_fqueue: fset_destroy(local_data.flows); @@ -98,7 +98,7 @@ static int local_data_init(void) static void local_data_fini(void){ pthread_rwlock_destroy(&local_data.lock); - shim_data_destroy(local_data.shim_data); + reg_destroy(local_data.reg); fqueue_destroy(local_data.fq); fset_destroy(local_data.flows); } @@ -166,7 +166,7 @@ static int local_ipcp_bootstrap(struct ipcp_config * conf) static int local_ipcp_reg(const uint8_t * hash) { - if (shim_data_reg_add_entry(local_data.shim_data, hash)) { + if (reg_add(local_data.reg, hash) < 0) { log_err("Failed to add " HASH_FMT32 " to local registry.", HASH_VAL32(hash)); return -1; @@ -177,7 +177,7 @@ static int local_ipcp_reg(const uint8_t * hash) static int local_ipcp_unreg(const uint8_t * hash) { - shim_data_reg_del_entry(local_data.shim_data, hash); + reg_del(local_data.reg, hash); log_info("Unregistered " HASH_FMT32 ".", HASH_VAL32(hash)); @@ -188,7 +188,7 @@ static int local_ipcp_query(const uint8_t * hash) { int ret; - ret = (shim_data_reg_has(local_data.shim_data, hash) ? 0 : -1); + ret = (reg_has(local_data.reg, hash) ? 0 : -1); return ret; } diff --git a/src/ipcpd/local/reg.c b/src/ipcpd/local/reg.c new file mode 100644 index 00000000..36f19b16 --- /dev/null +++ b/src/ipcpd/local/reg.c @@ -0,0 +1,217 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2026 + * + * Names registered with the local IPCP + * + * 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 + +#define OUROBOROS_PREFIX "local-reg" + +#include <ouroboros/hash.h> +#include <ouroboros/list.h> +#include <ouroboros/logs.h> + +#include "reg.h" +#include "ipcp.h" + +#include <assert.h> +#include <pthread.h> +#include <stdlib.h> +#include <string.h> + +struct reg_entry { + struct list_head list; + uint8_t * hash; +}; + +struct reg { + struct list_head names; + pthread_rwlock_t lock; +}; + +static struct reg_entry * reg_entry_create(uint8_t * hash) +{ + struct reg_entry * entry; + + entry = malloc(sizeof(*entry)); + if (entry == NULL) + return NULL; + + list_head_init(&entry->list); + + entry->hash = hash; + + return entry; +} + +static void reg_entry_destroy(struct reg_entry * entry) +{ + assert(entry); + + free(entry->hash); + free(entry); +} + +/* Call with the lock held. */ +static struct reg_entry * reg_find(struct reg * reg, + const uint8_t * hash) +{ + struct list_head * p; + + list_for_each(p, ®->names) { + struct reg_entry * e; + + e = list_entry(p, struct reg_entry, list); + if (memcmp(e->hash, hash, ipcp_dir_hash_len()) == 0) + return e; + } + + return NULL; +} + +struct reg * reg_create(void) +{ + struct reg * reg; + + reg = malloc(sizeof(*reg)); + if (reg == NULL) + goto fail_malloc; + + list_head_init(®->names); + + if (pthread_rwlock_init(®->lock, NULL) < 0) + goto fail_lock; + + return reg; + + fail_lock: + free(reg); + fail_malloc: + return NULL; +} + +void reg_destroy(struct reg * reg) +{ + if (reg == NULL) + return; + + pthread_rwlock_wrlock(®->lock); + + while (!list_is_empty(®->names)) { + struct reg_entry * e; + + e = list_first_entry(®->names, struct reg_entry, list); + + list_del(&e->list); + + reg_entry_destroy(e); + } + + pthread_rwlock_unlock(®->lock); + + pthread_rwlock_destroy(®->lock); + + free(reg); +} + +int reg_add(struct reg * reg, + const uint8_t * hash) +{ + struct reg_entry * entry; + uint8_t * dup; + + assert(reg); + assert(hash); + + pthread_rwlock_wrlock(®->lock); + + if (reg_find(reg, hash) != NULL) { + pthread_rwlock_unlock(®->lock); + log_dbg(HASH_FMT32 " was already registered.", + HASH_VAL32(hash)); + return 0; + } + + dup = ipcp_hash_dup(hash); + if (dup == NULL) + goto fail; + + entry = reg_entry_create(dup); + if (entry == NULL) { + free(dup); + goto fail; + } + + list_add(&entry->list, ®->names); + + pthread_rwlock_unlock(®->lock); + + return 0; + + fail: + pthread_rwlock_unlock(®->lock); + return -1; +} + +int reg_del(struct reg * reg, + const uint8_t * hash) +{ + struct reg_entry * e; + + if (reg == NULL) + return -1; + + pthread_rwlock_wrlock(®->lock); + + e = reg_find(reg, hash); + if (e == NULL) { + pthread_rwlock_unlock(®->lock); + return 0; /* nothing to do */ + } + + list_del(&e->list); + + pthread_rwlock_unlock(®->lock); + + reg_entry_destroy(e); + + return 0; +} + +bool reg_has(struct reg * reg, + const uint8_t * hash) +{ + bool ret; + + assert(reg); + assert(hash); + + pthread_rwlock_rdlock(®->lock); + + ret = reg_find(reg, hash) != NULL; + + pthread_rwlock_unlock(®->lock); + + return ret; +} diff --git a/src/ipcpd/eth/dix.c b/src/ipcpd/local/reg.h index cf8253bd..2c6142bb 100644 --- a/src/ipcpd/eth/dix.c +++ b/src/ipcpd/local/reg.h @@ -1,7 +1,7 @@ /* * Ouroboros - Copyright (C) 2016 - 2026 * - * IPC processes over Ethernet - DIX + * Names registered with the local IPCP * * Dimitri Staessens <dimitri@ouroboros.rocks> * Sander Vrijders <sander@ouroboros.rocks> @@ -20,7 +20,26 @@ * Foundation, Inc., http://www.fsf.org/about/contact/. */ -#define BUILD_ETH_DIX -#define OUROBOROS_PREFIX "ipcpd/eth-dix" +#ifndef OUROBOROS_IPCPD_LOCAL_REG_H +#define OUROBOROS_IPCPD_LOCAL_REG_H -#include "eth.c" +#include <stdbool.h> +#include <stdint.h> + +/* The hashes of the names registered with this IPCP. */ +struct reg; + +struct reg * reg_create(void); + +void reg_destroy(struct reg * reg); + +int reg_add(struct reg * reg, + const uint8_t * hash); + +int reg_del(struct reg * reg, + const uint8_t * hash); + +bool reg_has(struct reg * reg, + const uint8_t * hash); + +#endif /* OUROBOROS_IPCPD_LOCAL_REG_H */ diff --git a/src/ipcpd/shim-data.c b/src/ipcpd/shim-data.c deleted file mode 100644 index 90a676da..00000000 --- a/src/ipcpd/shim-data.c +++ /dev/null @@ -1,582 +0,0 @@ -/* - * Ouroboros - Copyright (C) 2016 - 2026 - * - * IPC process utilities - * - * Dimitri Staessens <dimitri@ouroboros.rocks> - * Sander Vrijders <sander@ouroboros.rocks> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., http://www.fsf.org/about/contact/. - */ - -#if defined(__linux__) || defined(__CYGWIN__) -#define _DEFAULT_SOURCE -#else -#define _POSIX_C_SOURCE 200112L -#endif - -#include "config.h" - -#define OUROBOROS_PREFIX "shim-data" - -#include <ouroboros/errno.h> -#include <ouroboros/hash.h> -#include <ouroboros/list.h> -#include <ouroboros/logs.h> -#include <ouroboros/time.h> - -#include "shim-data.h" -#include "ipcp.h" - -#include <assert.h> -#include <stdlib.h> -#include <string.h> - -struct reg_entry { - struct list_head list; - uint8_t * hash; -}; - -struct dir_entry { - struct list_head list; - uint8_t * hash; - struct addr addr; -}; - -static void destroy_dir_query(struct dir_query * query) -{ - assert(query); - - pthread_mutex_lock(&query->lock); - - switch (query->state) { - case QUERY_INIT: - query->state = QUERY_DONE; - break; - case QUERY_PENDING: - query->state = QUERY_DESTROY; - pthread_cond_broadcast(&query->cond); - break; - case QUERY_RESPONSE: - case QUERY_DONE: - break; - case QUERY_DESTROY: - pthread_mutex_unlock(&query->lock); - return; - default: - assert(false); - return; - } - - while (query->state != QUERY_DONE) - pthread_cond_wait(&query->cond, &query->lock); - - pthread_mutex_unlock(&query->lock); - - pthread_cond_destroy(&query->cond); - pthread_mutex_destroy(&query->lock); - - free(query->hash); - free(query); -} - -static struct reg_entry * reg_entry_create(uint8_t * hash) -{ - struct reg_entry * entry = malloc(sizeof(*entry)); - if (entry == NULL) - return NULL; - - assert(hash); - - entry->hash = hash; - - return entry; -} - -static void reg_entry_destroy(struct reg_entry * entry) -{ - assert(entry); - - free(entry->hash); - free(entry); -} - -static struct dir_entry * dir_entry_create(uint8_t * hash, - struct addr addr) -{ - struct dir_entry * entry = malloc(sizeof(*entry)); - if (entry == NULL) - return NULL; - - assert(hash); - - entry->addr = addr; - entry->hash = hash; - - return entry; -} - -static void dir_entry_destroy(struct dir_entry * entry) -{ - assert(entry); - - free(entry->hash); - free(entry); -} - -struct shim_data * shim_data_create(void) -{ - struct shim_data * sd; - - sd = malloc(sizeof(*sd)); - if (sd == NULL) - return NULL; - - /* init the lists */ - list_head_init(&sd->registry); - list_head_init(&sd->directory); - list_head_init(&sd->dir_queries); - - /* init the locks */ - if (pthread_rwlock_init(&sd->reg_lock, NULL) < 0) - goto fail_reg_lock_init; - - if (pthread_rwlock_init(&sd->dir_lock, NULL) < 0) - goto fail_dir_lock_init; - - if (pthread_mutex_init(&sd->dir_queries_lock, NULL) < 0) - goto fail_mutex_init; - - return sd; - - fail_mutex_init: - pthread_rwlock_destroy(&sd->dir_lock); - fail_dir_lock_init: - pthread_rwlock_destroy(&sd->reg_lock); - fail_reg_lock_init: - return NULL; -} - -static void clear_registry(struct shim_data * data) -{ - struct list_head * h; - struct list_head * t; - - assert(data); - - list_for_each_safe(h, t, &data->registry) { - struct reg_entry * e = list_entry(h, struct reg_entry, list); - list_del(&e->list); - reg_entry_destroy(e); - } -} - -static void clear_directory(struct shim_data * data) -{ - struct list_head * h; - struct list_head * t; - - assert(data); - - list_for_each_safe(h, t, &data->directory) { - struct dir_entry * e = list_entry(h, struct dir_entry, list); - list_del(&e->list); - dir_entry_destroy(e); - } -} - -static void clear_dir_queries(struct shim_data * data) -{ - struct list_head * h; - struct list_head * t; - - assert(data); - - list_for_each_safe(h, t, &data->dir_queries) { - struct dir_query * e = list_entry(h, struct dir_query, next); - list_del(&e->next); - destroy_dir_query(e); - } -} - -void shim_data_destroy(struct shim_data * data) -{ - if (data == NULL) - return; - - /* clear the lists */ - pthread_rwlock_wrlock(&data->reg_lock); - clear_registry(data); - pthread_rwlock_unlock(&data->reg_lock); - - pthread_rwlock_wrlock(&data->dir_lock); - clear_directory(data); - pthread_rwlock_unlock(&data->dir_lock); - - pthread_mutex_lock(&data->dir_queries_lock); - clear_dir_queries(data); - pthread_mutex_unlock(&data->dir_queries_lock); - - pthread_rwlock_destroy(&data->dir_lock); - pthread_rwlock_destroy(&data->reg_lock); - pthread_mutex_destroy(&data->dir_queries_lock); - - free(data); -} - -static struct reg_entry * find_reg_entry_by_hash(struct shim_data * data, - const uint8_t * hash) -{ - struct list_head * h; - - assert(data); - assert(hash); - - list_for_each(h, &data->registry) { - struct reg_entry * e = list_entry(h, struct reg_entry, list); - if (!memcmp(e->hash, hash, ipcp_dir_hash_len())) - return e; - } - - return NULL; -} - -static struct dir_entry * find_dir_entry(struct shim_data * data, - const uint8_t * hash, - 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 (memcmp(&e->addr, &addr, sizeof(addr)) != 0) - continue; - - if (memcmp(e->hash, hash, ipcp_dir_hash_len()) == 0) - return e; - } - - return NULL; -} - -static struct dir_entry * find_dir_entry_any(struct shim_data * data, - const uint8_t * hash) -{ - struct list_head * h; - list_for_each(h, &data->directory) { - struct dir_entry * e = list_entry(h, struct dir_entry, list); - if (!memcmp(e->hash, hash, ipcp_dir_hash_len())) - return e; - } - - return NULL; -} - -int shim_data_reg_add_entry(struct shim_data * data, - const uint8_t * hash) -{ - struct reg_entry * entry; - uint8_t * hash_dup; - - assert(data); - assert(hash); - - pthread_rwlock_wrlock(&data->reg_lock); - - if (find_reg_entry_by_hash(data, hash)) { - pthread_rwlock_unlock(&data->reg_lock); - log_dbg(HASH_FMT32 " was already in the directory.", - HASH_VAL32(hash)); - return 0; - } - - hash_dup = ipcp_hash_dup(hash); - if (hash_dup == NULL) { - pthread_rwlock_unlock(&data->reg_lock); - return -1; - } - - entry = reg_entry_create(hash_dup); - if (entry == NULL) { - pthread_rwlock_unlock(&data->reg_lock); - return -1; - } - - list_add(&entry->list, &data->registry); - - pthread_rwlock_unlock(&data->reg_lock); - - return 0; -} - -int shim_data_reg_del_entry(struct shim_data * data, - const uint8_t * hash) -{ - struct reg_entry * e; - if (data == NULL) - return -1; - - pthread_rwlock_wrlock(&data->reg_lock); - - e = find_reg_entry_by_hash(data, hash); - if (e == NULL) { - pthread_rwlock_unlock(&data->reg_lock); - return 0; /* nothing to do */ - } - - list_del(&e->list); - - pthread_rwlock_unlock(&data->reg_lock); - - reg_entry_destroy(e); - - return 0; -} - -bool shim_data_reg_has(struct shim_data * data, - const uint8_t * hash) -{ - bool ret = false; - - assert(data); - assert(hash); - - pthread_rwlock_rdlock(&data->reg_lock); - - ret = (find_reg_entry_by_hash(data, hash) != NULL); - - pthread_rwlock_unlock(&data->reg_lock); - - return ret; -} - -int shim_data_dir_add_entry(struct shim_data * data, - const uint8_t * hash, - struct addr addr) -{ - struct dir_entry * entry; - uint8_t * entry_hash; - - assert(data); - assert(hash); - - pthread_rwlock_wrlock(&data->dir_lock); - - if (find_dir_entry(data, hash, addr) != NULL) { - pthread_rwlock_unlock(&data->dir_lock); - return -1; - } - - entry_hash = ipcp_hash_dup(hash); - if (entry_hash == NULL) { - pthread_rwlock_unlock(&data->dir_lock); - return -1; - } - - entry = dir_entry_create(entry_hash, addr); - if (entry == NULL) { - pthread_rwlock_unlock(&data->dir_lock); - return -1; - } - - list_add(&entry->list,&data->directory); - - pthread_rwlock_unlock(&data->dir_lock); - - return 0; -} - -int shim_data_dir_del_entry(struct shim_data * data, - const uint8_t * hash, - struct addr addr) -{ - struct dir_entry * e; - if (data == NULL) - return -1; - - pthread_rwlock_wrlock(&data->dir_lock); - - e = find_dir_entry(data, hash, addr); - if (e == NULL) { - pthread_rwlock_unlock(&data->dir_lock); - return 0; /* nothing to do */ - } - - list_del(&e->list); - - pthread_rwlock_unlock(&data->dir_lock); - - dir_entry_destroy(e); - - return 0; -} - -bool shim_data_dir_has(struct shim_data * data, - const uint8_t * hash) -{ - bool ret = false; - - pthread_rwlock_rdlock(&data->dir_lock); - - ret = (find_dir_entry_any(data, hash) != NULL); - - pthread_rwlock_unlock(&data->dir_lock); - - return ret; -} - -struct addr shim_data_dir_get_addr(struct shim_data * data, - const uint8_t * hash) -{ - struct dir_entry * entry; - struct addr addr = {0}; - - pthread_rwlock_rdlock(&data->dir_lock); - - entry = find_dir_entry_any(data, hash); - if (entry == NULL) { - pthread_rwlock_unlock(&data->dir_lock); - log_warn("No address for " HASH_FMT32 ".", HASH_VAL32(hash)); - return addr; /* undefined behaviour, 0 may be a valid address */ - } - - addr = entry->addr; - - pthread_rwlock_unlock(&data->dir_lock); - - return addr; -} - -struct dir_query * shim_data_dir_query_create(struct shim_data * data, - const uint8_t * hash) -{ - struct dir_query * query; - pthread_condattr_t cattr; - - query = malloc(sizeof(*query)); - if (query == NULL) - return NULL; - - query->hash = ipcp_hash_dup(hash); - if (query->hash == NULL) { - free(query); - return NULL; - } - - query->state = QUERY_INIT; - - pthread_condattr_init(&cattr); -#ifndef __APPLE__ - pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK); -#endif - pthread_cond_init(&query->cond, &cattr); - pthread_mutex_init(&query->lock, NULL); - - list_head_init(&query->next); - - pthread_mutex_lock(&data->dir_queries_lock); - list_add(&query->next, &data->dir_queries); - pthread_mutex_unlock(&data->dir_queries_lock); - - return query; -} - -void shim_data_dir_query_respond(struct shim_data * data, - const uint8_t * hash) -{ - struct dir_query * e = NULL; - struct list_head * pos; - bool found = false; - - pthread_mutex_lock(&data->dir_queries_lock); - - list_for_each(pos, &data->dir_queries) { - e = list_entry(pos, struct dir_query, next); - - if (memcmp(e->hash, hash, ipcp_dir_hash_len()) == 0) { - found = true; - break; - } - } - - if (!found) { - pthread_mutex_unlock(&data->dir_queries_lock); - return; - } - - pthread_mutex_lock(&e->lock); - - if (e->state != QUERY_PENDING) { - pthread_mutex_unlock(&e->lock); - pthread_mutex_unlock(&data->dir_queries_lock); - return; - } - - e->state = QUERY_RESPONSE; - pthread_cond_broadcast(&e->cond); - - while (e->state == QUERY_RESPONSE) - pthread_cond_wait(&e->cond, &e->lock); - - pthread_mutex_unlock(&e->lock); - - pthread_mutex_unlock(&data->dir_queries_lock); -} - -void shim_data_dir_query_destroy(struct shim_data * data, - struct dir_query * query) -{ - pthread_mutex_lock(&data->dir_queries_lock); - - list_del(&query->next); - destroy_dir_query(query); - - pthread_mutex_unlock(&data->dir_queries_lock); -} - -int shim_data_dir_query_wait(struct dir_query * query, - const struct timespec * timeout) -{ - struct timespec abstime; - int ret = 0; - - assert(query); - assert(timeout); - - clock_gettime(PTHREAD_COND_CLOCK, &abstime); - ts_add(&abstime, timeout, &abstime); - - pthread_mutex_lock(&query->lock); - - if (query->state != QUERY_INIT) { - pthread_mutex_unlock(&query->lock); - return -EINVAL; - } - - query->state = QUERY_PENDING; - - while (query->state == QUERY_PENDING && ret != -ETIMEDOUT) - ret = -pthread_cond_timedwait(&query->cond, - &query->lock, - &abstime); - - if (query->state == QUERY_DESTROY) - ret = -1; - - query->state = QUERY_DONE; - pthread_cond_broadcast(&query->cond); - - pthread_mutex_unlock(&query->lock); - - return ret; -} diff --git a/src/ipcpd/shim-data.h b/src/ipcpd/shim-data.h deleted file mode 100644 index fbadb4d4..00000000 --- a/src/ipcpd/shim-data.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Ouroboros - Copyright (C) 2016 - 2026 - * - * Utitilies for building IPC processes - * - * Dimitri Staessens <dimitri@ouroboros.rocks> - * Sander Vrijders <sander@ouroboros.rocks> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., http://www.fsf.org/about/contact/. - */ - -#ifndef OUROBOROS_IPCPD_IPCP_DATA_H -#define OUROBOROS_IPCPD_IPCP_DATA_H - -#include <ouroboros/list.h> - -#include <pthread.h> -#include <stdint.h> -#include <netinet/in.h> -#include <sys/types.h> - -#define MAC_SIZE 6 - -enum dir_query_state { - QUERY_INIT = 0, - QUERY_PENDING, - QUERY_RESPONSE, - QUERY_DONE, - QUERY_DESTROY -}; - -struct dir_query { - struct list_head next; - uint8_t * hash; - enum dir_query_state state; - - pthread_mutex_t lock; - 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; - - struct list_head directory; - pthread_rwlock_t dir_lock; - - struct list_head dir_queries; - pthread_mutex_t dir_queries_lock; -}; - -struct shim_data * shim_data_create(void); - -void shim_data_destroy(struct shim_data * data); - -int shim_data_reg_add_entry(struct shim_data * data, - const uint8_t * hash); - -int shim_data_reg_del_entry(struct shim_data * data, - const uint8_t * hash); - -bool shim_data_reg_has(struct shim_data * data, - const uint8_t * hash); - -int shim_data_dir_add_entry(struct shim_data * data, - const uint8_t * hash, - struct addr addr); - -int shim_data_dir_del_entry(struct shim_data * data, - const uint8_t * hash, - struct addr addr); - -bool shim_data_dir_has(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_query * shim_data_dir_query_create(struct shim_data * data, - const uint8_t * hash); - -void shim_data_dir_query_destroy(struct shim_data * data, - struct dir_query * query); - -void shim_data_dir_query_respond(struct shim_data * data, - const uint8_t * hash); - -int shim_data_dir_query_wait(struct dir_query * query, - const struct timespec * timeout); -#endif /* OUROBOROS_IPCPD_SHIM_DATA_H */ diff --git a/src/ipcpd/udp/CMakeLists.txt b/src/ipcpd/udp/CMakeLists.txt deleted file mode 100644 index a98f0919..00000000 --- a/src/ipcpd/udp/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -# UDP IPCPs build configuration (UDP4 and UDP6) -# DDNS detection is in cmake/dependencies/udp/ddns.cmake - -add_executable(${IPCP_UDP4_TARGET} udp4.c ${IPCP_SOURCES}) -add_executable(${IPCP_UDP6_TARGET} udp6.c ${IPCP_SOURCES}) - -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() - -install(TARGETS ${IPCP_UDP4_TARGET} ${IPCP_UDP6_TARGET} - RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}) diff --git a/src/ipcpd/udp/udp.c b/src/ipcpd/udp/udp.c deleted file mode 100644 index db57e2f4..00000000 --- a/src/ipcpd/udp/udp.c +++ /dev/null @@ -1,1333 +0,0 @@ -/* - * 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 deleted file mode 100644 index ff57bc09..00000000 --- a/src/ipcpd/udp/udp4.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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 deleted file mode 100644 index 2ceb95f0..00000000 --- a/src/ipcpd/udp/udp6.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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" diff --git a/src/ipcpd/unicast/fa.c b/src/ipcpd/unicast/fa.c index 596b101b..744eb316 100644 --- a/src/ipcpd/unicast/fa.c +++ b/src/ipcpd/unicast/fa.c @@ -130,8 +130,8 @@ struct fa_flow { size_t u_snd; /* Flow updates sent */ size_t u_rcv; /* Flow updates received */ #endif - uint64_t s_eid; /* Local endpoint id */ - uint64_t r_eid; /* Remote endpoint id */ + uint64_t s_eid; /* Local PoA id */ + uint64_t r_eid; /* Remote PoA id */ uint64_t r_addr; /* Remote address */ void * ctx; /* Congestion avoidance context */ uint64_t fair; /* SFQ virtual finish tag (bytes) */ @@ -208,8 +208,8 @@ static int fa_rib_read(const char * path, sprintf(buf, "Flow established at: %20s\n" "Remote address: %20s\n" - "Local endpoint ID: %20s\n" - "Remote endpoint ID: %20s\n" + "Local PoA ID: %20s\n" + "Remote PoA ID: %20s\n" "Sent (packets): %20zu\n" "Sent (bytes): %20zu\n" "Send failed (packets): %20zu\n" diff --git a/src/ipcpd/unicast/main.c b/src/ipcpd/unicast/main.c index 86cb1f06..3ee53401 100644 --- a/src/ipcpd/unicast/main.c +++ b/src/ipcpd/unicast/main.c @@ -35,6 +35,7 @@ #include <ouroboros/ipcp-dev.h> #include <ouroboros/logs.h> #include <ouroboros/notifier.h> +#include <ouroboros/qos.h> #include <ouroboros/random.h> #include <ouroboros/rib.h> #include <ouroboros/time.h> @@ -175,12 +176,14 @@ static void stop_components(void) ipcp_set_state(IPCP_BOOT); } -static int unicast_ipcp_enroll(const char * dst, - struct layer_info * info) +static int unicast_ipcp_enroll(const char * dst, + const struct poa_addr * addr, + struct layer_info * info) { struct ipcp_config * conf; struct conn conn; uint8_t id[ENROLL_ID_LEN]; + qosspec_t qs = qos_msg; if (random_buffer(id, ENROLL_ID_LEN) < 0) { log_err("Failed to generate enrollment ID."); @@ -189,7 +192,7 @@ static int unicast_ipcp_enroll(const char * dst, log_info_id(id, "Requesting enrollment."); - if (connmgr_alloc(COMPID_ENROLL, dst, NULL, &conn) < 0) { + if (connmgr_alloc(COMPID_ENROLL, dst, &qs, addr, &conn) < 0) { log_err_id(id, "Failed to get connection."); goto fail_id; } |
