From e5cc7fbcf165ef9c205b6c136f239f645a8d7830 Mon Sep 17 00:00:00 2001 From: Dimitri Staessens Date: Mon, 18 Dec 2017 20:50:04 +0100 Subject: ipcpd: Use the term "layer" and deprecate "shim" This changes the terminology to use layer instead of DIF and deprecate the word "shim" for the IPCPs that attach to Ethernet LLC and UDP .The terminology has not yet been changed in the variable names etc. This reflects the design choices in Ouroboros to make IPCPs pure resource allocators instead of also providing an "IPC service". The Ouroboros IPCPs that attach to Ethernet and UDP implement the allocator and are thus not really shims. Signed-off-by: Dimitri Staessens Signed-off-by: Sander Vrijders --- src/ipcpd/CMakeLists.txt | 4 +- src/ipcpd/config.h.in | 4 +- src/ipcpd/eth-llc/CMakeLists.txt | 106 +++ src/ipcpd/eth-llc/main.c | 1420 +++++++++++++++++++++++++++++++++ src/ipcpd/ipcp.c | 8 +- src/ipcpd/local/CMakeLists.txt | 4 +- src/ipcpd/normal/main.c | 4 +- src/ipcpd/normal/pol/flat.c | 1 - src/ipcpd/shim-eth-llc/CMakeLists.txt | 106 --- src/ipcpd/shim-eth-llc/main.c | 1420 --------------------------------- src/ipcpd/shim-udp/CMakeLists.txt | 60 -- src/ipcpd/shim-udp/main.c | 1167 --------------------------- src/ipcpd/udp/CMakeLists.txt | 60 ++ src/ipcpd/udp/main.c | 1167 +++++++++++++++++++++++++++ 14 files changed, 2765 insertions(+), 2766 deletions(-) create mode 100644 src/ipcpd/eth-llc/CMakeLists.txt create mode 100644 src/ipcpd/eth-llc/main.c delete mode 100644 src/ipcpd/shim-eth-llc/CMakeLists.txt delete mode 100644 src/ipcpd/shim-eth-llc/main.c delete mode 100644 src/ipcpd/shim-udp/CMakeLists.txt delete mode 100644 src/ipcpd/shim-udp/main.c create mode 100644 src/ipcpd/udp/CMakeLists.txt create mode 100644 src/ipcpd/udp/main.c (limited to 'src/ipcpd') diff --git a/src/ipcpd/CMakeLists.txt b/src/ipcpd/CMakeLists.txt index f43dba65..67d9fccf 100644 --- a/src/ipcpd/CMakeLists.txt +++ b/src/ipcpd/CMakeLists.txt @@ -34,8 +34,8 @@ set(IPCP_SOURCES add_subdirectory(local) add_subdirectory(normal) add_subdirectory(raptor) -add_subdirectory(shim-udp) -add_subdirectory(shim-eth-llc) +add_subdirectory(udp) +add_subdirectory(eth-llc) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/config.h" @ONLY) diff --git a/src/ipcpd/config.h.in b/src/ipcpd/config.h.in index 322c6856..0c0e2922 100644 --- a/src/ipcpd/config.h.in +++ b/src/ipcpd/config.h.in @@ -45,12 +45,12 @@ #define IPCP_SCHED_THR_MUL @IPCP_SCHED_THR_MUL@ #define PFT_SIZE @PFT_SIZE@ -/* shim-udp */ +/* udp */ #cmakedefine HAVE_DDNS #define NSUPDATE_EXEC "@NSUPDATE_EXECUTABLE@" #define NSLOOKUP_EXEC "@NSLOOKUP_EXECUTABLE@" -/* shim-eth-llc */ +/* eth-llc */ #cmakedefine HAVE_NETMAP #cmakedefine HAVE_BPF #cmakedefine HAVE_RAW_SOCKETS diff --git a/src/ipcpd/eth-llc/CMakeLists.txt b/src/ipcpd/eth-llc/CMakeLists.txt new file mode 100644 index 00000000..11a631b3 --- /dev/null +++ b/src/ipcpd/eth-llc/CMakeLists.txt @@ -0,0 +1,106 @@ +get_filename_component(CURRENT_SOURCE_PARENT_DIR + ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY) +get_filename_component(CURRENT_BINARY_PARENT_DIR + ${CMAKE_CURRENT_BINARY_DIR} DIRECTORY) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +include_directories(${CURRENT_SOURCE_PARENT_DIR}) +include_directories(${CURRENT_BINARY_PARENT_DIR}) + +include_directories(${CMAKE_SOURCE_DIR}/include) +include_directories(${CMAKE_BINARY_DIR}/include) + +find_path(NETMAP_C_INCLUDE_DIR + net/netmap_user.h + HINTS /usr/include /usr/local/include +) + +mark_as_advanced(NETMAP_C_INCLUDE_DIR) + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(DISABLE_RAW_SOCKETS FALSE CACHE BOOL + "Disable raw socket support for LLC IPCP") + if (NOT DISABLE_RAW_SOCKETS) + message(STATUS "Raw socket support for eth-llc enabled") + set(HAVE_RAW_SOCKETS TRUE PARENT_SCOPE) + set(HAVE_LLC TRUE) + else () + message(STATUS "Raw socket support for eth-llc disabled by user") + unset(HAVE_RAW_SOCKETS) + unset(HAVE_LLC) + endif () +endif () + +if (NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") + find_path(BPF_C_INCLUDE_DIR + net/bpf.h + HINTS /usr/include /usr/local/include + ) + + mark_as_advanced(BPF_C_INCLUDE_DIR) + + if (BPF_C_INCLUDE_DIR) + set(DISABLE_BPF FALSE CACHE BOOL + "Disable Berkeley Packet Filter support for LLC IPCP") + if (NOT DISABLE_BPF) + message(STATUS "Berkeley Packet Filter support " + "for eth-llc enabled") + set(HAVE_BPF TRUE PARENT_SCOPE) + set(HAVE_LLC TRUE) + else () + message(STATUS "Berkeley Packet Filter support " + "for eth-llc disabled by user") + unset(HAVE_BPF) + unset(HAVE_LLC) + endif () + endif () +endif () + +if (NETMAP_C_INCLUDE_DIR) + set(DISABLE_NETMAP FALSE CACHE BOOL + "Disable netmap support for LLC IPCP") + if (NOT DISABLE_NETMAP) + message(STATUS "Netmap support for eth-llc enabled") + set(HAVE_NETMAP TRUE PARENT_SCOPE) + test_and_set_c_compiler_flag_global(-std=c99) + set(HAVE_LLC TRUE) + else () + message(STATUS "Netmap support for eth-llc disabled by user") + unset(HAVE_NETMAP) + unset(HAVE_LLC) + unset(IPCP_ETH_LLC_TARGET CACHE) + endif () +endif () + +if (HAVE_LLC) + message(STATUS "Supported raw Ethernet API found, building eth-llc") + + set(ETH_LLC_SOURCES + # Add source files here + ${CMAKE_CURRENT_SOURCE_DIR}/main.c + ) + + set(IPCP_ETH_LLC_TARGET ipcpd-eth-llc CACHE INTERNAL "") + + add_executable(ipcpd-eth-llc ${ETH_LLC_SOURCES} ${IPCP_SOURCES}) + + if (HAVE_BPF AND NOT APPLE) + target_include_directories(ipcpd-eth-llc PUBLIC ${BPF_C_INCLUDE_DIR}) + endif () + + if (HAVE_NETMAP AND NOT APPLE) + target_include_directories(ipcpd-eth-llc PUBLIC + ${NETMAP_C_INCLUDE_DIR}) + endif () + + target_link_libraries(ipcpd-eth-llc LINK_PUBLIC ouroboros-dev) + + include(AddCompileFlags) + if (CMAKE_BUILD_TYPE MATCHES "Debug*") + add_compile_flags(ipcpd-eth-llc -DCONFIG_OUROBOROS_DEBUG) + endif () + + install(TARGETS ipcpd-eth-llc RUNTIME DESTINATION sbin) +endif () diff --git a/src/ipcpd/eth-llc/main.c b/src/ipcpd/eth-llc/main.c new file mode 100644 index 00000000..105058d3 --- /dev/null +++ b/src/ipcpd/eth-llc/main.c @@ -0,0 +1,1420 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2017 + * + * IPC process over Ethernet with LLC + * + * Dimitri Staessens + * Sander Vrijders + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#if defined(__APPLE__) +#define _BSD_SOURCE +#define _DARWIN_C_SOURCE +#elif defined(__FreeBSD__) +#define __BSD_VISIBLE 1 +#else +#define _POSIX_C_SOURCE 200112L +#endif + +#include "config.h" + +#define OUROBOROS_PREFIX "ipcpd/eth-llc" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipcp.h" +#include "shim-data.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef __linux__ +#include +#include +#include +#include +#endif + +#ifdef __FreeBSD__ +#include +#include +#include +#endif + +#ifdef __APPLE__ +#include +#include +#endif + +#include +#include + +#if defined(HAVE_NETMAP) +#define NETMAP_WITH_LIBS +#include +#elif defined(HAVE_BPF) +#define BPF_DEV_MAX 256 +#define BPF_BLEN sysconf(_SC_PAGESIZE) +#include +#endif + +#define THIS_TYPE IPCP_ETH_LLC + +#define MGMT_SAP 0x01 +#define MAC_SIZE 6 +#define LLC_HEADER_SIZE 3 +#define MAX_SAPS 64 +#define ETH_HEADER_SIZE (2 * MAC_SIZE + 2) +#define ETH_FRAME_SIZE (ETH_HEADER_SIZE + LLC_HEADER_SIZE \ + + SHIM_ETH_LLC_MAX_SDU_SIZE) +#define SHIM_ETH_LLC_MAX_SDU_SIZE (1500 - LLC_HEADER_SIZE) +#define ALLOC_TIMEO 10 /* ms */ +#define NAME_QUERY_TIMEO 2000 /* ms */ +#define MGMT_TIMEO 100 /* ms */ + +#define FLOW_REQ 0 +#define FLOW_REPLY 1 +#define NAME_QUERY_REQ 2 +#define NAME_QUERY_REPLY 3 + +struct mgmt_msg { + uint8_t code; + uint8_t ssap; + uint8_t dsap; + uint8_t qoscube; + int8_t response; +} __attribute__((packed)); + +struct eth_llc_frame { + uint8_t dst_hwaddr[MAC_SIZE]; + uint8_t src_hwaddr[MAC_SIZE]; + uint8_t length[2]; + uint8_t dsap; + uint8_t ssap; + uint8_t cf; + uint8_t payload; +} __attribute__((packed)); + +struct ef { + int8_t sap; + int8_t r_sap; + uint8_t r_addr[MAC_SIZE]; +}; + +struct mgmt_frame { + struct list_head next; + uint8_t r_addr[MAC_SIZE]; + uint8_t buf[ETH_FRAME_SIZE]; + size_t len; +}; + +struct { + struct shim_data * shim_data; + +#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 */ + + struct bmp * saps; + fset_t * np1_flows; + fqueue_t * fq; + int * ef_to_fd; + struct ef * fd_to_ef; + pthread_rwlock_t flows_lock; + + pthread_t sdu_writer; + pthread_t sdu_reader; + +#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_llc_data; + +static int eth_llc_data_init(void) +{ + int i; + int ret = -ENOMEM; + pthread_condattr_t cattr; + + eth_llc_data.fd_to_ef = + malloc(sizeof(*eth_llc_data.fd_to_ef) * SYS_MAX_FLOWS); + if (eth_llc_data.fd_to_ef == NULL) + goto fail_fd_to_ef; + + eth_llc_data.ef_to_fd = + malloc(sizeof(*eth_llc_data.ef_to_fd) * MAX_SAPS); + if (eth_llc_data.ef_to_fd == NULL) + goto fail_ef_to_fd; + + eth_llc_data.saps = bmp_create(MAX_SAPS, 2); + if (eth_llc_data.saps == NULL) + goto fail_saps; + + eth_llc_data.np1_flows = fset_create(); + if (eth_llc_data.np1_flows == NULL) + goto fail_np1_flows; + + eth_llc_data.fq = fqueue_create(); + if (eth_llc_data.fq == NULL) + goto fail_fq; + + for (i = 0; i < MAX_SAPS; ++i) + eth_llc_data.ef_to_fd[i] = -1; + + for (i = 0; i < SYS_MAX_FLOWS; ++i) { + eth_llc_data.fd_to_ef[i].sap = -1; + eth_llc_data.fd_to_ef[i].r_sap = -1; + memset(ð_llc_data.fd_to_ef[i].r_addr, 0, MAC_SIZE); + } + + eth_llc_data.shim_data = shim_data_create(); + if (eth_llc_data.shim_data == NULL) + goto fail_shim_data; + + ret = -1; + + if (pthread_rwlock_init(ð_llc_data.flows_lock, NULL)) + goto fail_flows_lock; + + if (pthread_mutex_init(ð_llc_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(ð_llc_data.mgmt_cond, &cattr)) + goto fail_mgmt_cond; + + pthread_condattr_destroy(&cattr); + + list_head_init(ð_llc_data.mgmt_frames); + + return 0; + + fail_mgmt_cond: + pthread_condattr_destroy(&cattr); + fail_condattr: + pthread_mutex_destroy(ð_llc_data.mgmt_lock); + fail_mgmt_lock: + pthread_rwlock_destroy(ð_llc_data.flows_lock); + fail_flows_lock: + shim_data_destroy(eth_llc_data.shim_data); + fail_shim_data: + fqueue_destroy(eth_llc_data.fq); + fail_fq: + fset_destroy(eth_llc_data.np1_flows); + fail_np1_flows: + bmp_destroy(eth_llc_data.saps); + fail_saps: + free(eth_llc_data.ef_to_fd); + fail_ef_to_fd: + free(eth_llc_data.fd_to_ef); + fail_fd_to_ef: + return ret; +} + +static void eth_llc_data_fini(void) +{ +#if defined(HAVE_NETMAP) + nm_close(eth_llc_data.nmd); +#elif defined(HAVE_BPF) + close(eth_llc_data.bpf); +#elif defined(HAVE_RAW_SOCKETS) + close(eth_llc_data.s_fd); +#endif + pthread_cond_destroy(ð_llc_data.mgmt_cond); + pthread_mutex_destroy(ð_llc_data.mgmt_lock); + pthread_rwlock_destroy(ð_llc_data.flows_lock); + shim_data_destroy(eth_llc_data.shim_data); + fqueue_destroy(eth_llc_data.fq); + fset_destroy(eth_llc_data.np1_flows); + bmp_destroy(eth_llc_data.saps); + free(eth_llc_data.fd_to_ef); + free(eth_llc_data.ef_to_fd); +} + +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; +} + +static int eth_llc_ipcp_send_frame(const uint8_t * dst_addr, + uint8_t dsap, + uint8_t ssap, + const uint8_t * payload, + size_t len) +{ + uint32_t frame_len = 0; + uint8_t cf = 0x03; + uint16_t length; + uint8_t frame[SHIM_ETH_LLC_MAX_SDU_SIZE]; + struct eth_llc_frame * llc_frame; + + if (payload == NULL) { + log_err("Payload was NULL."); + return -1; + } + + if (len > SHIM_ETH_LLC_MAX_SDU_SIZE) + return -1; + + llc_frame = (struct eth_llc_frame *) frame; + + memcpy(llc_frame->dst_hwaddr, dst_addr, MAC_SIZE); + memcpy(llc_frame->src_hwaddr, +#if defined(HAVE_NETMAP) || defined(HAVE_BPF) + eth_llc_data.hw_addr, +#elif defined(HAVE_RAW_SOCKETS) + eth_llc_data.device.sll_addr, +#endif /* HAVE_NETMAP */ + MAC_SIZE); + length = htons(LLC_HEADER_SIZE + len); + memcpy(&llc_frame->length, &length, sizeof(length)); + llc_frame->dsap = dsap; + llc_frame->ssap = ssap; + llc_frame->cf = cf; + memcpy(&llc_frame->payload, payload, len); + + frame_len = ETH_HEADER_SIZE + LLC_HEADER_SIZE + len; +#if defined(HAVE_NETMAP) + if (poll(ð_llc_data.poll_out, 1, -1) < 0) + return -1; + + if (nm_inject(eth_llc_data.nmd, frame, frame_len) != (int) frame_len) { + log_dbg("Failed to send message."); + return -1; + } +#elif defined(HAVE_BPF) + if (write(eth_llc_data.bpf, frame, frame_len) < 0) { + log_dbg("Failed to send message."); + return -1; + } + +#elif defined(HAVE_RAW_SOCKETS) + if (sendto(eth_llc_data.s_fd, + frame, + frame_len, + 0, + (struct sockaddr *) ð_llc_data.device, + sizeof(eth_llc_data.device)) <= 0) { + log_dbg("Failed to send message."); + return -1; + } +#endif /* HAVE_NETMAP */ + return 0; +} + +static int eth_llc_ipcp_sap_alloc(const uint8_t * dst_addr, + uint8_t ssap, + const uint8_t * hash, + qoscube_t cube) +{ + uint8_t * buf; + struct mgmt_msg * msg; + size_t len; + int ret; + + len = sizeof(*msg) + ipcp_dir_hash_len(); + + buf = malloc(len); + if (buf == NULL) + return -1; + + msg = (struct mgmt_msg *) buf; + msg->code = FLOW_REQ; + msg->ssap = ssap; + msg->qoscube = cube; + + memcpy(msg + 1, hash, ipcp_dir_hash_len()); + + ret = eth_llc_ipcp_send_frame(dst_addr, reverse_bits(MGMT_SAP), + reverse_bits(MGMT_SAP), buf, len); + + free(buf); + + return ret; +} + +static int eth_llc_ipcp_sap_alloc_resp(uint8_t * dst_addr, + uint8_t ssap, + uint8_t dsap, + int response) +{ + struct mgmt_msg msg; + + msg.code = FLOW_REPLY; + msg.ssap = ssap; + msg.dsap = dsap; + msg.response = response; + + return eth_llc_ipcp_send_frame(dst_addr, reverse_bits(MGMT_SAP), + reverse_bits(MGMT_SAP), + (uint8_t *) &msg, sizeof(msg)); +} + +static int eth_llc_ipcp_sap_req(uint8_t r_sap, + uint8_t * r_addr, + const uint8_t * dst, + qoscube_t cube) +{ + struct timespec ts = {0, ALLOC_TIMEO * MILLION}; + struct timespec abstime; + int fd; + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + + pthread_mutex_lock(&ipcpi.alloc_lock); + + while (ipcpi.alloc_id != -1 && ipcp_get_state() == IPCP_OPERATIONAL) { + ts_add(&abstime, &ts, &abstime); + pthread_cond_timedwait(&ipcpi.alloc_cond, + &ipcpi.alloc_lock, + &abstime); + } + + if (ipcp_get_state() != IPCP_OPERATIONAL) { + log_dbg("Won't allocate over non-operational IPCP."); + pthread_mutex_unlock(&ipcpi.alloc_lock); + return -1; + } + + /* reply to IRM, called under lock to prevent race */ + fd = ipcp_flow_req_arr(getpid(), dst, ipcp_dir_hash_len(), cube); + if (fd < 0) { + pthread_mutex_unlock(&ipcpi.alloc_lock); + log_err("Could not get new flow from IRMd."); + return -1; + } + + pthread_rwlock_wrlock(ð_llc_data.flows_lock); + + eth_llc_data.fd_to_ef[fd].r_sap = r_sap; + memcpy(eth_llc_data.fd_to_ef[fd].r_addr, r_addr, MAC_SIZE); + + ipcpi.alloc_id = fd; + pthread_cond_broadcast(&ipcpi.alloc_cond); + + pthread_rwlock_unlock(ð_llc_data.flows_lock); + pthread_mutex_unlock(&ipcpi.alloc_lock); + + log_dbg("New flow request, fd %d, remote SAP %d.", fd, r_sap); + + return 0; +} + +static int eth_llc_ipcp_sap_alloc_reply(uint8_t ssap, + uint8_t * r_addr, + int dsap, + int response) +{ + int ret = 0; + int fd = -1; + + pthread_rwlock_wrlock(ð_llc_data.flows_lock); + + fd = eth_llc_data.ef_to_fd[dsap]; + if (fd < 0) { + pthread_rwlock_unlock(& eth_llc_data.flows_lock); + log_err("No flow found with that SAP."); + return -1; /* -EFLOWNOTFOUND */ + } + + if (response) { + bmp_release(eth_llc_data.saps, eth_llc_data.fd_to_ef[fd].sap); + } else { + eth_llc_data.fd_to_ef[fd].r_sap = ssap; + memcpy(eth_llc_data.fd_to_ef[fd].r_addr, r_addr, MAC_SIZE); + } + + pthread_rwlock_unlock(ð_llc_data.flows_lock); + + log_dbg("Flow reply, fd %d, SSAP %d, DSAP %d.", fd, ssap, dsap); + + if ((ret = ipcp_flow_alloc_reply(fd, response)) < 0) + return -1; + + return ret; + +} + +static int eth_llc_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_llc_data.shim_data, hash)) { + len = sizeof(*msg) + ipcp_dir_hash_len(); + + buf = malloc(len); + if (buf == NULL) + return -1; + + msg = (struct mgmt_msg *) buf; + msg->code = NAME_QUERY_REPLY; + + memcpy(msg + 1, hash, ipcp_dir_hash_len()); + + if (eth_llc_ipcp_send_frame(r_addr, reverse_bits(MGMT_SAP), + reverse_bits(MGMT_SAP), buf, len)) { + log_err("Failed to send management frame."); + free(buf); + return -1; + } + + free(buf); + } + + return 0; +} + +static int eth_llc_ipcp_name_query_reply(const uint8_t * hash, + uint8_t * r_addr) +{ + uint64_t address = 0; + + memcpy(&address, r_addr, MAC_SIZE); + + shim_data_dir_add_entry(eth_llc_data.shim_data, hash, address); + + shim_data_dir_query_respond(eth_llc_data.shim_data, hash); + + return 0; +} + +static int eth_llc_ipcp_mgmt_frame(const uint8_t * buf, + uint8_t * r_addr) +{ + struct mgmt_msg * msg; + + msg = (struct mgmt_msg *) buf; + + switch (msg->code) { + case FLOW_REQ: + if (shim_data_reg_has(eth_llc_data.shim_data, + buf + sizeof(*msg))) { + eth_llc_ipcp_sap_req(msg->ssap, + r_addr, + buf + sizeof(*msg), + msg->qoscube); + } + break; + case FLOW_REPLY: + eth_llc_ipcp_sap_alloc_reply(msg->ssap, + r_addr, + msg->dsap, + msg->response); + break; + case NAME_QUERY_REQ: + eth_llc_ipcp_name_query_req(buf + sizeof(*msg), r_addr); + break; + case NAME_QUERY_REPLY: + eth_llc_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_llc_ipcp_mgmt_handler(void * o) +{ + int ret; + struct timespec timeout = {(MGMT_TIMEO / 1000), + (MGMT_TIMEO % 1000) * MILLION}; + struct timespec abstime; + struct mgmt_frame * frame; + + (void) o; + + pthread_cleanup_push((void (*)(void *)) pthread_mutex_unlock, + (void *) ð_llc_data.mgmt_lock); + + while (true) { + ret = 0; + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeout, &abstime); + + pthread_mutex_lock(ð_llc_data.mgmt_lock); + + while (list_is_empty(ð_llc_data.mgmt_frames) && + ret != -ETIMEDOUT) + ret = -pthread_cond_timedwait(ð_llc_data.mgmt_cond, + ð_llc_data.mgmt_lock, + &abstime); + + if (ret == -ETIMEDOUT) { + pthread_mutex_unlock(ð_llc_data.mgmt_lock); + continue; + } + + frame = list_first_entry((ð_llc_data.mgmt_frames), + struct mgmt_frame, next); + if (frame == NULL) { + pthread_mutex_unlock(ð_llc_data.mgmt_lock); + continue; + } + + list_del(&frame->next); + pthread_mutex_unlock(ð_llc_data.mgmt_lock); + + eth_llc_ipcp_mgmt_frame(frame->buf, frame->r_addr); + free(frame); + } + + pthread_cleanup_pop(false); + + return (void *) 0; +} + +static void * eth_llc_ipcp_sdu_reader(void * o) +{ + uint8_t br_addr[MAC_SIZE]; + uint16_t length; + uint8_t dsap; + uint8_t ssap; + int fd; +#if defined(HAVE_NETMAP) + uint8_t * buf; + struct nm_pkthdr hdr; +#elif defined(HAVE_BPF) + uint8_t buf[BPF_BLEN]; +#elif defined(HAVE_RAW_SOCKETS) + uint8_t buf[ETH_FRAME_SIZE]; +#endif + int frame_len = 0; + struct eth_llc_frame * llc_frame; + struct mgmt_frame * frame; + + (void) o; + + memset(br_addr, 0xff, MAC_SIZE * sizeof(uint8_t)); + + while (true) { +#if defined(HAVE_NETMAP) + if (poll(ð_llc_data.poll_in, 1, -1) < 0) + continue; + if (eth_llc_data.poll_in.revents == 0) /* TIMED OUT */ + continue; + + buf = nm_nextpkt(eth_llc_data.nmd, &hdr); + if (buf == NULL) { + log_err("Bad read from netmap device."); + continue; + } +#elif defined(HAVE_BPF) + frame_len = read(eth_llc_data.bpf, buf, BPF_BLEN); +#elif defined(HAVE_RAW_SOCKETS) + frame_len = recv(eth_llc_data.s_fd, buf, + SHIM_ETH_LLC_MAX_SDU_SIZE, 0); +#endif + if (frame_len <= 0) + continue; + +#if defined(HAVE_BPF) && !defined(HAVE_NETMAP) + llc_frame = (struct eth_llc_frame *) + (buf + ((struct bpf_hdr *) buf)->bh_hdrlen); +#else + llc_frame = (struct eth_llc_frame *) buf; +#endif + assert(llc_frame->dst_hwaddr); + +#if !defined(HAVE_BPF) + #if defined(HAVE_NETMAP) + if (memcmp(eth_llc_data.hw_addr, + #elif defined(HAVE_RAW_SOCKETS) + if (memcmp(eth_llc_data.device.sll_addr, + #endif /* HAVE_NETMAP */ + llc_frame->dst_hwaddr, + MAC_SIZE) && + memcmp(br_addr, llc_frame->dst_hwaddr, MAC_SIZE)) { + } +#endif + memcpy(&length, &llc_frame->length, sizeof(length)); + length = ntohs(length); + + if (length > 0x05FF) /* DIX */ + continue; + + length -= LLC_HEADER_SIZE; + + dsap = reverse_bits(llc_frame->dsap); + ssap = reverse_bits(llc_frame->ssap); + + if (ssap == MGMT_SAP && dsap == MGMT_SAP) { + pthread_mutex_lock(ð_llc_data.mgmt_lock); + + frame = malloc(sizeof(*frame)); + if (frame == NULL) { + pthread_mutex_unlock(ð_llc_data.mgmt_lock); + continue; + } + + memcpy(frame->buf, &llc_frame->payload, length); + memcpy(frame->r_addr, llc_frame->src_hwaddr, MAC_SIZE); + frame->len = length; + list_add(&frame->next, ð_llc_data.mgmt_frames); + pthread_cond_signal(ð_llc_data.mgmt_cond); + pthread_mutex_unlock(ð_llc_data.mgmt_lock); + } else { + pthread_rwlock_rdlock(ð_llc_data.flows_lock); + + fd = eth_llc_data.ef_to_fd[dsap]; + if (fd < 0) { + pthread_rwlock_unlock(ð_llc_data.flows_lock); + continue; + } + + if (eth_llc_data.fd_to_ef[fd].r_sap != ssap + || memcmp(eth_llc_data.fd_to_ef[fd].r_addr, + llc_frame->src_hwaddr, MAC_SIZE)) { + pthread_rwlock_unlock(ð_llc_data.flows_lock); + continue; + } + + pthread_rwlock_unlock(ð_llc_data.flows_lock); + + flow_write(fd, &llc_frame->payload, length); + } + } + + return (void *) 0; +} + +static void * eth_llc_ipcp_sdu_writer(void * o) +{ + int fd; + struct shm_du_buff * sdb; + uint8_t ssap; + uint8_t dsap; + uint8_t r_addr[MAC_SIZE]; + + (void) o; + + while (true) { + fevent(eth_llc_data.np1_flows, eth_llc_data.fq, NULL); + + pthread_rwlock_rdlock(ð_llc_data.flows_lock); + while ((fd = fqueue_next(eth_llc_data.fq)) >= 0) { + if (ipcp_flow_read(fd, &sdb)) { + log_err("Bad read from fd %d.", fd); + continue; + } + + ssap = reverse_bits(eth_llc_data.fd_to_ef[fd].sap); + dsap = reverse_bits(eth_llc_data.fd_to_ef[fd].r_sap); + memcpy(r_addr, + eth_llc_data.fd_to_ef[fd].r_addr, + MAC_SIZE); + + eth_llc_ipcp_send_frame(r_addr, dsap, ssap, + shm_du_buff_head(sdb), + shm_du_buff_tail(sdb) + - shm_du_buff_head(sdb)); + ipcp_sdb_release(sdb); + } + pthread_rwlock_unlock(ð_llc_data.flows_lock); + } + + 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(ð_llc_data.flows_lock); + + for (i = 0; i < MAX_SAPS; i++) { + if (eth_llc_data.ef_to_fd[i] != -1) { + fccntl(i, FLOWGFLAGS, &flags); + if (up) + fccntl(eth_llc_data.ef_to_fd[i], + FLOWSFLAGS, flags & ~FLOWFDOWN); + else + fccntl(eth_llc_data.ef_to_fd[i], + FLOWSFLAGS, flags | FLOWFDOWN); + } + } + + pthread_rwlock_unlock(ð_llc_data.flows_lock); +} + +static void close_ptr(void * o) +{ + close(*((int *) o)); +} + + +static void * eth_llc_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(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_llc_data.device.sll_ifindex) + 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 + +static int eth_llc_ipcp_bootstrap(const struct ipcp_config * conf) +{ + int idx; + struct ifreq ifr; +#if defined(HAVE_NETMAP) + char ifn[IFNAMSIZ]; +#elif defined(HAVE_BPF) + int enable = 1; + int disable = 0; + int blen; +#endif /* HAVE_NETMAP */ + +#if defined(__FreeBSD__) || defined(__APPLE__) + struct ifaddrs * ifaddr; + struct ifaddrs * ifa; +#elif defined(__linux__) + int skfd; +#endif + assert(conf); + assert(conf->type == THIS_TYPE); + + if (conf->if_name == NULL) { + log_err("Interface name is NULL."); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + memcpy(ifr.ifr_name, conf->if_name, strlen(conf->if_name)); + +#if defined(__FreeBSD__) || defined(__APPLE__) + if (getifaddrs(&ifaddr) < 0) { + log_err("Could not get interfaces."); + return -1; + } + + for (ifa = ifaddr, idx = 0; ifa != NULL; ifa = ifa->ifa_next, ++idx) { + if (strcmp(ifa->ifa_name, conf->if_name)) + continue; + log_dbg("Interface %s found.", conf->if_name); + + #if defined(HAVE_NETMAP) || defined(HAVE_BPF) + memcpy(eth_llc_data.hw_addr, + LLADDR((struct sockaddr_dl *) (ifa)->ifa_addr), + MAC_SIZE); + #elif defined (HAVE_RAW_SOCKETS) + memcpy(&ifr.ifr_addr, ifa->ifa_addr, sizeof(*ifa->ifa_addr)); + #endif + break; + } + + freeifaddrs(ifaddr); + + if (ifa == NULL) { + log_err("Interface not found."); + return -1; + } + +#elif defined(__linux__) + skfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (skfd < 0) { + log_err("Failed to open socket."); + return -1; + } + + if (ioctl(skfd, SIOCGIFHWADDR, &ifr)) { + log_err("Failed to ioctl."); + close(skfd); + return -1; + } + + close(skfd); + + idx = if_nametoindex(conf->if_name); + if (idx == 0) { + log_err("Failed to retrieve interface index."); + close(skfd); + return -1; + } +#endif /* __FreeBSD__ */ + +#if defined(HAVE_NETMAP) + strcpy(ifn, "netmap:"); + strcat(ifn, conf->if_name); + + eth_llc_data.nmd = nm_open(ifn, NULL, 0, NULL); + if (eth_llc_data.nmd == NULL) { + log_err("Failed to open netmap device."); + return -1; + } + + memset(ð_llc_data.poll_in, 0, sizeof(eth_llc_data.poll_in)); + memset(ð_llc_data.poll_out, 0, sizeof(eth_llc_data.poll_out)); + + eth_llc_data.poll_in.fd = NETMAP_FD(eth_llc_data.nmd); + eth_llc_data.poll_in.events = POLLIN; + eth_llc_data.poll_out.fd = NETMAP_FD(eth_llc_data.nmd); + eth_llc_data.poll_out.events = POLLOUT; + + log_info("Using netmap device."); +#elif defined(HAVE_BPF) /* !HAVE_NETMAP */ + eth_llc_data.bpf = open_bpf_device(); + if (eth_llc_data.bpf < 0) { + log_err("Failed to open bpf device."); + return -1; + } + + ioctl(eth_llc_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_llc_data.bpf, BIOCSETIF, &ifr) < 0) { + log_err("Failed to set interface."); + goto fail_device; + } + + if (ioctl(eth_llc_data.bpf, BIOCSHDRCMPLT, &enable) < 0) { + log_err("Failed to set BIOCSHDRCMPLT."); + goto fail_device; + } + + if (ioctl(eth_llc_data.bpf, BIOCSSEESENT, &disable) < 0) { + log_err("Failed to set BIOCSSEESENT."); + goto fail_device; + } + + if (ioctl(eth_llc_data.bpf, BIOCIMMEDIATE, &enable) < 0) { + log_err("Failed to set BIOCIMMEDIATE."); + goto fail_device; + } + + log_info("Using Berkeley Packet Filter."); +#elif defined(HAVE_RAW_SOCKETS) + memset(&(eth_llc_data.device), 0, sizeof(eth_llc_data.device)); + eth_llc_data.device.sll_ifindex = idx; + eth_llc_data.device.sll_family = AF_PACKET; + memcpy(eth_llc_data.device.sll_addr, ifr.ifr_hwaddr.sa_data, MAC_SIZE); + eth_llc_data.device.sll_halen = MAC_SIZE; + eth_llc_data.device.sll_protocol = htons(ETH_P_ALL); + eth_llc_data.s_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_802_2)); + + log_info("Using raw socket device."); + + if (eth_llc_data.s_fd < 0) { + log_err("Failed to create socket."); + return -1; + } + + if (bind(eth_llc_data.s_fd, (struct sockaddr *) ð_llc_data.device, + sizeof(eth_llc_data.device))) { + log_err("Failed to bind socket to interface"); + goto fail_device; + } + +#endif /* HAVE_NETMAP */ + ipcp_set_state(IPCP_OPERATIONAL); + +#ifdef __linux__ + if (pthread_create(ð_llc_data.if_monitor, + NULL, + eth_llc_ipcp_if_monitor, + NULL)) { + ipcp_set_state(IPCP_INIT); + goto fail_device; + } +#endif + + if (pthread_create(ð_llc_data.mgmt_handler, + NULL, + eth_llc_ipcp_mgmt_handler, + NULL)) { + ipcp_set_state(IPCP_INIT); + goto fail_mgmt_handler; + } + + if (pthread_create(ð_llc_data.sdu_reader, + NULL, + eth_llc_ipcp_sdu_reader, + NULL)) { + ipcp_set_state(IPCP_INIT); + goto fail_sdu_reader; + } + + if (pthread_create(ð_llc_data.sdu_writer, + NULL, + eth_llc_ipcp_sdu_writer, + NULL)) { + ipcp_set_state(IPCP_INIT); + goto fail_sdu_writer; + } + + log_dbg("Bootstrapped IPCP over Ethernet with LLC with pid %d.", + getpid()); + + return 0; + + fail_sdu_writer: + pthread_cancel(eth_llc_data.sdu_reader); + pthread_join(eth_llc_data.sdu_reader, NULL); + fail_sdu_reader: + pthread_cancel(eth_llc_data.mgmt_handler); + pthread_join(eth_llc_data.mgmt_handler, NULL); + fail_mgmt_handler: +#if defined(__linux__) + pthread_cancel(eth_llc_data.if_monitor); + pthread_join(eth_llc_data.if_monitor, NULL); +#endif +#if !defined(HAVE_NETMAP) + fail_device: +#endif +#if defined(HAVE_NETMAP) + nm_close(eth_llc_data.nmd); +#elif defined(HAVE_BPF) + close(eth_llc_data.bpf); +#elif defined(HAVE_RAW_SOCKETS) + close(eth_llc_data.s_fd); +#endif + return -1; +} + +static int eth_llc_ipcp_reg(const uint8_t * hash) +{ + if (shim_data_reg_add_entry(eth_llc_data.shim_data, hash)) { + log_err("Failed to add " HASH_FMT " to local registry.", + HASH_VAL(hash)); + return -1; + } + + log_dbg("Registered " HASH_FMT ".", HASH_VAL(hash)); + + return 0; +} + +static int eth_llc_ipcp_unreg(const uint8_t * hash) +{ + shim_data_reg_del_entry(eth_llc_data.shim_data, hash); + + return 0; +} + +static int eth_llc_ipcp_query(const uint8_t * hash) +{ + uint8_t r_addr[MAC_SIZE]; + struct timespec timeout = {(NAME_QUERY_TIMEO / 1000), + (NAME_QUERY_TIMEO % 1000) * MILLION}; + struct dir_query * query; + int ret; + uint8_t * buf; + struct mgmt_msg * msg; + size_t len; + + if (shim_data_dir_has(eth_llc_data.shim_data, hash)) + return 0; + + len = sizeof(*msg) + ipcp_dir_hash_len(); + + buf = malloc(len); + if (buf == NULL) + return -1; + + msg = (struct mgmt_msg *) buf; + msg->code = NAME_QUERY_REQ; + + memcpy(buf + sizeof(*msg), hash, ipcp_dir_hash_len()); + + memset(r_addr, 0xff, MAC_SIZE); + + query = shim_data_dir_query_create(eth_llc_data.shim_data, hash); + if (query == NULL) { + free(buf); + return -1; + } + + if (eth_llc_ipcp_send_frame(r_addr, reverse_bits(MGMT_SAP), + reverse_bits(MGMT_SAP), buf, len)) { + log_err("Failed to send management frame."); + shim_data_dir_query_destroy(eth_llc_data.shim_data, query); + free(buf); + return -1; + } + + free(buf); + + ret = shim_data_dir_query_wait(query, &timeout); + + shim_data_dir_query_destroy(eth_llc_data.shim_data, query); + + return ret; +} + +static int eth_llc_ipcp_flow_alloc(int fd, + const uint8_t * hash, + qoscube_t cube) +{ + uint8_t ssap = 0; + uint8_t r_addr[MAC_SIZE]; + uint64_t addr = 0; + + log_dbg("Allocating flow to " HASH_FMT ".", HASH_VAL(hash)); + + assert(hash); + + if (cube != QOS_CUBE_BE) { + log_dbg("Unsupported QoS requested."); + return -1; + } + + if (!shim_data_dir_has(eth_llc_data.shim_data, hash)) { + log_err("Destination unreachable."); + return -1; + } + addr = shim_data_dir_get_addr(eth_llc_data.shim_data, hash); + + pthread_rwlock_wrlock(ð_llc_data.flows_lock); + + ssap = bmp_allocate(eth_llc_data.saps); + if (!bmp_is_id_valid(eth_llc_data.saps, ssap)) { + pthread_rwlock_unlock(ð_llc_data.flows_lock); + return -1; + } + + eth_llc_data.fd_to_ef[fd].sap = ssap; + eth_llc_data.ef_to_fd[ssap] = fd; + + pthread_rwlock_unlock(ð_llc_data.flows_lock); + + memcpy(r_addr, &addr, MAC_SIZE); + + if (eth_llc_ipcp_sap_alloc(r_addr, ssap, hash, cube) < 0) { + pthread_rwlock_wrlock(ð_llc_data.flows_lock); + bmp_release(eth_llc_data.saps, eth_llc_data.fd_to_ef[fd].sap); + eth_llc_data.fd_to_ef[fd].sap = -1; + eth_llc_data.ef_to_fd[ssap] = -1; + pthread_rwlock_unlock(ð_llc_data.flows_lock); + return -1; + } + + fset_add(eth_llc_data.np1_flows, fd); + + log_dbg("Pending flow with fd %d on SAP %d.", fd, ssap); + + return 0; +} + +static int eth_llc_ipcp_flow_alloc_resp(int fd, + int response) +{ + struct timespec ts = {0, ALLOC_TIMEO * MILLION}; + struct timespec abstime; + uint8_t ssap = 0; + uint8_t r_sap = 0; + uint8_t r_addr[MAC_SIZE]; + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + + pthread_mutex_lock(&ipcpi.alloc_lock); + + while (ipcpi.alloc_id != fd && ipcp_get_state() == IPCP_OPERATIONAL) { + ts_add(&abstime, &ts, &abstime); + pthread_cond_timedwait(&ipcpi.alloc_cond, + &ipcpi.alloc_lock, + &abstime); + } + + if (ipcp_get_state() != IPCP_OPERATIONAL) { + pthread_mutex_unlock(&ipcpi.alloc_lock); + return -1; + } + + ipcpi.alloc_id = -1; + pthread_cond_broadcast(&ipcpi.alloc_cond); + + pthread_mutex_unlock(&ipcpi.alloc_lock); + + pthread_rwlock_wrlock(ð_llc_data.flows_lock); + + ssap = bmp_allocate(eth_llc_data.saps); + if (!bmp_is_id_valid(eth_llc_data.saps, ssap)) { + pthread_rwlock_unlock(ð_llc_data.flows_lock); + return -1; + } + + eth_llc_data.fd_to_ef[fd].sap = ssap; + memcpy(r_addr, eth_llc_data.fd_to_ef[fd].r_addr, MAC_SIZE); + r_sap = eth_llc_data.fd_to_ef[fd].r_sap; + eth_llc_data.ef_to_fd[ssap] = fd; + + pthread_rwlock_unlock(ð_llc_data.flows_lock); + + if (eth_llc_ipcp_sap_alloc_resp(r_addr, ssap, r_sap, response) < 0) { + pthread_rwlock_wrlock(ð_llc_data.flows_lock); + bmp_release(eth_llc_data.saps, eth_llc_data.fd_to_ef[fd].sap); + pthread_rwlock_unlock(ð_llc_data.flows_lock); + return -1; + } + + fset_add(eth_llc_data.np1_flows, fd); + + log_dbg("Accepted flow, fd %d, SAP %d.", fd, (uint8_t)ssap); + + return 0; +} + +static int eth_llc_ipcp_flow_dealloc(int fd) +{ + uint8_t sap; + uint8_t addr[MAC_SIZE]; + + ipcp_flow_fini(fd); + + pthread_rwlock_wrlock(ð_llc_data.flows_lock); + + fset_del(eth_llc_data.np1_flows, fd); + + sap = eth_llc_data.fd_to_ef[fd].sap; + memcpy(addr, eth_llc_data.fd_to_ef[fd].r_addr, MAC_SIZE); + bmp_release(eth_llc_data.saps, sap); + eth_llc_data.fd_to_ef[fd].sap = -1; + eth_llc_data.fd_to_ef[fd].r_sap = -1; + memset(ð_llc_data.fd_to_ef[fd].r_addr, 0, MAC_SIZE); + + eth_llc_data.ef_to_fd[sap] = -1; + + pthread_rwlock_unlock(ð_llc_data.flows_lock); + + flow_dealloc(fd); + + log_dbg("Flow with fd %d deallocated.", fd); + + return 0; +} + +static struct ipcp_ops eth_llc_ops = { + .ipcp_bootstrap = eth_llc_ipcp_bootstrap, + .ipcp_enroll = NULL, + .ipcp_connect = NULL, + .ipcp_disconnect = NULL, + .ipcp_reg = eth_llc_ipcp_reg, + .ipcp_unreg = eth_llc_ipcp_unreg, + .ipcp_query = eth_llc_ipcp_query, + .ipcp_flow_alloc = eth_llc_ipcp_flow_alloc, + .ipcp_flow_alloc_resp = eth_llc_ipcp_flow_alloc_resp, + .ipcp_flow_dealloc = eth_llc_ipcp_flow_dealloc +}; + +int main(int argc, + char * argv[]) +{ + if (ipcp_init(argc, argv, ð_llc_ops) < 0) + goto fail_init; + + if (eth_llc_data_init() < 0) { + log_err("Failed to init eth-llc data."); + goto fail_data_init; + } + + if (ipcp_boot() < 0) { + log_err("Failed to boot IPCP."); + goto fail_boot; + } + + if (ipcp_create_r(getpid(), 0)) { + log_err("Failed to notify IRMd we are initialized."); + ipcp_set_state(IPCP_NULL); + goto fail_create_r; + } + + ipcp_shutdown(); + + if (ipcp_get_state() == IPCP_SHUTDOWN) { + pthread_cancel(eth_llc_data.sdu_writer); + pthread_cancel(eth_llc_data.sdu_reader); + pthread_cancel(eth_llc_data.mgmt_handler); +#ifdef __linux__ + pthread_cancel(eth_llc_data.if_monitor); +#endif + pthread_join(eth_llc_data.sdu_writer, NULL); + pthread_join(eth_llc_data.sdu_reader, NULL); + pthread_join(eth_llc_data.mgmt_handler, NULL); +#ifdef __linux__ + pthread_join(eth_llc_data.if_monitor, NULL); +#endif + } + + eth_llc_data_fini(); + + ipcp_fini(); + + exit(EXIT_SUCCESS); + + fail_create_r: + ipcp_shutdown(); + fail_boot: + eth_llc_data_fini(); + fail_data_init: + ipcp_fini(); + fail_init: + ipcp_create_r(getpid(), -1); + exit(EXIT_FAILURE); +} diff --git a/src/ipcpd/ipcp.c b/src/ipcpd/ipcp.c index 84c42b98..c6e819bb 100644 --- a/src/ipcpd/ipcp.c +++ b/src/ipcpd/ipcp.c @@ -245,10 +245,10 @@ static void * mainloop(void * o) conf.pff_type = conf_msg->pff_type; } - if (conf_msg->ipcp_type == IPCP_SHIM_ETH_LLC) + if (conf_msg->ipcp_type == IPCP_ETH_LLC) conf.if_name = conf_msg->if_name; - if (conf_msg->ipcp_type == IPCP_SHIM_UDP) { + if (conf_msg->ipcp_type == IPCP_UDP) { conf.ip_addr = conf_msg->ip_addr; conf.dns_addr = conf_msg->dns_addr; @@ -256,8 +256,8 @@ static void * mainloop(void * o) dif_info.dir_hash_algo = HASH_MD5; } - /* Only shim-udp needs a fixed hash algorithm */ - if (conf_msg->ipcp_type != IPCP_SHIM_UDP) { + /* Only udp needs a fixed hash algorithm */ + if (conf_msg->ipcp_type != IPCP_UDP) { switch(conf_msg->dif_info->dir_hash_algo) { case DIR_HASH_SHA3_224: conf.dif_info.dir_hash_algo = diff --git a/src/ipcpd/local/CMakeLists.txt b/src/ipcpd/local/CMakeLists.txt index 422fa709..ab416c61 100644 --- a/src/ipcpd/local/CMakeLists.txt +++ b/src/ipcpd/local/CMakeLists.txt @@ -14,11 +14,11 @@ include_directories(${CMAKE_BINARY_DIR}/include) set(IPCP_LOCAL_TARGET ipcpd-local CACHE INTERNAL "") -set(SHIM_LOCAL_SOURCES +set(LOCAL_SOURCES # Add source files here ${CMAKE_CURRENT_SOURCE_DIR}/main.c) -add_executable(ipcpd-local ${SHIM_LOCAL_SOURCES} ${IPCP_SOURCES}) +add_executable(ipcpd-local ${LOCAL_SOURCES} ${IPCP_SOURCES}) target_link_libraries(ipcpd-local LINK_PUBLIC ouroboros-common ouroboros-dev) include(AddCompileFlags) diff --git a/src/ipcpd/normal/main.c b/src/ipcpd/normal/main.c index 772b5792..401c1578 100644 --- a/src/ipcpd/normal/main.c +++ b/src/ipcpd/normal/main.c @@ -55,7 +55,7 @@ static int initialize_components(const struct ipcp_config * conf) { ipcpi.dif_name = strdup(conf->dif_info.dif_name); if (ipcpi.dif_name == NULL) { - log_err("Failed to set DIF name."); + log_err("Failed to set layer name."); goto fail_dif_name; } @@ -263,7 +263,7 @@ static int normal_ipcp_bootstrap(const struct ipcp_config * conf) goto fail_bootstrap; } - log_dbg("Bootstrapped in DIF %s.", conf->dif_info.dif_name); + log_dbg("Bootstrapped in layer %s.", conf->dif_info.dif_name); return 0; diff --git a/src/ipcpd/normal/pol/flat.c b/src/ipcpd/normal/pol/flat.c index c52ebe38..49525994 100644 --- a/src/ipcpd/normal/pol/flat.c +++ b/src/ipcpd/normal/pol/flat.c @@ -39,7 +39,6 @@ #include #define NAME_LEN 8 -#define REC_DIF_SIZE 10000 struct { uint8_t addr_size; diff --git a/src/ipcpd/shim-eth-llc/CMakeLists.txt b/src/ipcpd/shim-eth-llc/CMakeLists.txt deleted file mode 100644 index e1eea2b5..00000000 --- a/src/ipcpd/shim-eth-llc/CMakeLists.txt +++ /dev/null @@ -1,106 +0,0 @@ -get_filename_component(CURRENT_SOURCE_PARENT_DIR - ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY) -get_filename_component(CURRENT_BINARY_PARENT_DIR - ${CMAKE_CURRENT_BINARY_DIR} DIRECTORY) - -include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -include_directories(${CMAKE_CURRENT_BINARY_DIR}) - -include_directories(${CURRENT_SOURCE_PARENT_DIR}) -include_directories(${CURRENT_BINARY_PARENT_DIR}) - -include_directories(${CMAKE_SOURCE_DIR}/include) -include_directories(${CMAKE_BINARY_DIR}/include) - -find_path(NETMAP_C_INCLUDE_DIR - net/netmap_user.h - HINTS /usr/include /usr/local/include -) - -mark_as_advanced(NETMAP_C_INCLUDE_DIR) - -if (CMAKE_SYSTEM_NAME STREQUAL "Linux") - set(DISABLE_RAW_SOCKETS FALSE CACHE BOOL - "Disable raw socket support for LLC shim") - if (NOT DISABLE_RAW_SOCKETS) - message(STATUS "Raw socket support for shim-eth-llc enabled") - set(HAVE_RAW_SOCKETS TRUE PARENT_SCOPE) - set(HAVE_LLC TRUE) - else () - message(STATUS "Raw socket support for shim-eth-llc disabled by user") - unset(HAVE_RAW_SOCKETS) - unset(HAVE_LLC) - endif () -endif () - -if (NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") - find_path(BPF_C_INCLUDE_DIR - net/bpf.h - HINTS /usr/include /usr/local/include - ) - - mark_as_advanced(BPF_C_INCLUDE_DIR) - - if (BPF_C_INCLUDE_DIR) - set(DISABLE_BPF FALSE CACHE BOOL - "Disable Berkeley Packet Filter support for LLC shim") - if (NOT DISABLE_BPF) - message(STATUS "Berkeley Packet Filter support " - "for shim-eth-llc enabled") - set(HAVE_BPF TRUE PARENT_SCOPE) - set(HAVE_LLC TRUE) - else () - message(STATUS "Berkeley Packet Filter support " - "for shim-eth-llc disabled by user") - unset(HAVE_BPF) - unset(HAVE_LLC) - endif () - endif () -endif () - -if (NETMAP_C_INCLUDE_DIR) - set(DISABLE_NETMAP FALSE CACHE BOOL - "Disable netmap support for LLC shim") - if (NOT DISABLE_NETMAP) - message(STATUS "Netmap support for shim-eth-llc enabled") - set(HAVE_NETMAP TRUE PARENT_SCOPE) - test_and_set_c_compiler_flag_global(-std=c99) - set(HAVE_LLC TRUE) - else () - message(STATUS "Netmap support for shim-eth-llc disabled by user") - unset(HAVE_NETMAP) - unset(HAVE_LLC) - unset(IPCP_SHIM_ETH_LLC_TARGET CACHE) - endif () -endif () - -if (HAVE_LLC) - message(STATUS "Supported raw Ethernet API found, building shim-eth-llc") - - set(SHIM_ETH_LLC_SOURCES - # Add source files here - ${CMAKE_CURRENT_SOURCE_DIR}/main.c - ) - - set(IPCP_SHIM_ETH_LLC_TARGET ipcpd-shim-eth-llc CACHE INTERNAL "") - - add_executable(ipcpd-shim-eth-llc ${SHIM_ETH_LLC_SOURCES} ${IPCP_SOURCES}) - - if (HAVE_BPF AND NOT APPLE) - target_include_directories(ipcpd-shim-eth-llc PUBLIC ${BPF_C_INCLUDE_DIR}) - endif () - - if (HAVE_NETMAP AND NOT APPLE) - target_include_directories(ipcpd-shim-eth-llc PUBLIC - ${NETMAP_C_INCLUDE_DIR}) - endif () - - target_link_libraries(ipcpd-shim-eth-llc LINK_PUBLIC ouroboros-dev) - - include(AddCompileFlags) - if (CMAKE_BUILD_TYPE MATCHES "Debug*") - add_compile_flags(ipcpd-shim-eth-llc -DCONFIG_OUROBOROS_DEBUG) - endif () - - install(TARGETS ipcpd-shim-eth-llc RUNTIME DESTINATION sbin) -endif () diff --git a/src/ipcpd/shim-eth-llc/main.c b/src/ipcpd/shim-eth-llc/main.c deleted file mode 100644 index 3fec7c3d..00000000 --- a/src/ipcpd/shim-eth-llc/main.c +++ /dev/null @@ -1,1420 +0,0 @@ -/* - * Ouroboros - Copyright (C) 2016 - 2017 - * - * Shim IPC process over Ethernet with LLC - * - * Dimitri Staessens - * Sander Vrijders - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., http://www.fsf.org/about/contact/. - */ - -#if defined(__APPLE__) -#define _BSD_SOURCE -#define _DARWIN_C_SOURCE -#elif defined(__FreeBSD__) -#define __BSD_VISIBLE 1 -#else -#define _POSIX_C_SOURCE 200112L -#endif - -#include "config.h" - -#define OUROBOROS_PREFIX "ipcpd/shim-eth-llc" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ipcp.h" -#include "shim-data.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#ifdef __linux__ -#include -#include -#include -#include -#endif - -#ifdef __FreeBSD__ -#include -#include -#include -#endif - -#ifdef __APPLE__ -#include -#include -#endif - -#include -#include - -#if defined(HAVE_NETMAP) -#define NETMAP_WITH_LIBS -#include -#elif defined(HAVE_BPF) -#define BPF_DEV_MAX 256 -#define BPF_BLEN sysconf(_SC_PAGESIZE) -#include -#endif - -#define THIS_TYPE IPCP_SHIM_ETH_LLC - -#define MGMT_SAP 0x01 -#define MAC_SIZE 6 -#define LLC_HEADER_SIZE 3 -#define MAX_SAPS 64 -#define ETH_HEADER_SIZE (2 * MAC_SIZE + 2) -#define ETH_FRAME_SIZE (ETH_HEADER_SIZE + LLC_HEADER_SIZE \ - + SHIM_ETH_LLC_MAX_SDU_SIZE) -#define SHIM_ETH_LLC_MAX_SDU_SIZE (1500 - LLC_HEADER_SIZE) -#define ALLOC_TIMEO 10 /* ms */ -#define NAME_QUERY_TIMEO 2000 /* ms */ -#define MGMT_TIMEO 100 /* ms */ - -#define FLOW_REQ 0 -#define FLOW_REPLY 1 -#define NAME_QUERY_REQ 2 -#define NAME_QUERY_REPLY 3 - -struct mgmt_msg { - uint8_t code; - uint8_t ssap; - uint8_t dsap; - uint8_t qoscube; - int8_t response; -} __attribute__((packed)); - -struct eth_llc_frame { - uint8_t dst_hwaddr[MAC_SIZE]; - uint8_t src_hwaddr[MAC_SIZE]; - uint8_t length[2]; - uint8_t dsap; - uint8_t ssap; - uint8_t cf; - uint8_t payload; -} __attribute__((packed)); - -struct ef { - int8_t sap; - int8_t r_sap; - uint8_t r_addr[MAC_SIZE]; -}; - -struct mgmt_frame { - struct list_head next; - uint8_t r_addr[MAC_SIZE]; - uint8_t buf[ETH_FRAME_SIZE]; - size_t len; -}; - -struct { - struct shim_data * shim_data; - -#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 */ - - struct bmp * saps; - fset_t * np1_flows; - fqueue_t * fq; - int * ef_to_fd; - struct ef * fd_to_ef; - pthread_rwlock_t flows_lock; - - pthread_t sdu_writer; - pthread_t sdu_reader; - -#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_llc_data; - -static int eth_llc_data_init(void) -{ - int i; - int ret = -ENOMEM; - pthread_condattr_t cattr; - - eth_llc_data.fd_to_ef = - malloc(sizeof(*eth_llc_data.fd_to_ef) * SYS_MAX_FLOWS); - if (eth_llc_data.fd_to_ef == NULL) - goto fail_fd_to_ef; - - eth_llc_data.ef_to_fd = - malloc(sizeof(*eth_llc_data.ef_to_fd) * MAX_SAPS); - if (eth_llc_data.ef_to_fd == NULL) - goto fail_ef_to_fd; - - eth_llc_data.saps = bmp_create(MAX_SAPS, 2); - if (eth_llc_data.saps == NULL) - goto fail_saps; - - eth_llc_data.np1_flows = fset_create(); - if (eth_llc_data.np1_flows == NULL) - goto fail_np1_flows; - - eth_llc_data.fq = fqueue_create(); - if (eth_llc_data.fq == NULL) - goto fail_fq; - - for (i = 0; i < MAX_SAPS; ++i) - eth_llc_data.ef_to_fd[i] = -1; - - for (i = 0; i < SYS_MAX_FLOWS; ++i) { - eth_llc_data.fd_to_ef[i].sap = -1; - eth_llc_data.fd_to_ef[i].r_sap = -1; - memset(ð_llc_data.fd_to_ef[i].r_addr, 0, MAC_SIZE); - } - - eth_llc_data.shim_data = shim_data_create(); - if (eth_llc_data.shim_data == NULL) - goto fail_shim_data; - - ret = -1; - - if (pthread_rwlock_init(ð_llc_data.flows_lock, NULL)) - goto fail_flows_lock; - - if (pthread_mutex_init(ð_llc_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(ð_llc_data.mgmt_cond, &cattr)) - goto fail_mgmt_cond; - - pthread_condattr_destroy(&cattr); - - list_head_init(ð_llc_data.mgmt_frames); - - return 0; - - fail_mgmt_cond: - pthread_condattr_destroy(&cattr); - fail_condattr: - pthread_mutex_destroy(ð_llc_data.mgmt_lock); - fail_mgmt_lock: - pthread_rwlock_destroy(ð_llc_data.flows_lock); - fail_flows_lock: - shim_data_destroy(eth_llc_data.shim_data); - fail_shim_data: - fqueue_destroy(eth_llc_data.fq); - fail_fq: - fset_destroy(eth_llc_data.np1_flows); - fail_np1_flows: - bmp_destroy(eth_llc_data.saps); - fail_saps: - free(eth_llc_data.ef_to_fd); - fail_ef_to_fd: - free(eth_llc_data.fd_to_ef); - fail_fd_to_ef: - return ret; -} - -static void eth_llc_data_fini(void) -{ -#if defined(HAVE_NETMAP) - nm_close(eth_llc_data.nmd); -#elif defined(HAVE_BPF) - close(eth_llc_data.bpf); -#elif defined(HAVE_RAW_SOCKETS) - close(eth_llc_data.s_fd); -#endif - pthread_cond_destroy(ð_llc_data.mgmt_cond); - pthread_mutex_destroy(ð_llc_data.mgmt_lock); - pthread_rwlock_destroy(ð_llc_data.flows_lock); - shim_data_destroy(eth_llc_data.shim_data); - fqueue_destroy(eth_llc_data.fq); - fset_destroy(eth_llc_data.np1_flows); - bmp_destroy(eth_llc_data.saps); - free(eth_llc_data.fd_to_ef); - free(eth_llc_data.ef_to_fd); -} - -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; -} - -static int eth_llc_ipcp_send_frame(const uint8_t * dst_addr, - uint8_t dsap, - uint8_t ssap, - const uint8_t * payload, - size_t len) -{ - uint32_t frame_len = 0; - uint8_t cf = 0x03; - uint16_t length; - uint8_t frame[SHIM_ETH_LLC_MAX_SDU_SIZE]; - struct eth_llc_frame * llc_frame; - - if (payload == NULL) { - log_err("Payload was NULL."); - return -1; - } - - if (len > SHIM_ETH_LLC_MAX_SDU_SIZE) - return -1; - - llc_frame = (struct eth_llc_frame *) frame; - - memcpy(llc_frame->dst_hwaddr, dst_addr, MAC_SIZE); - memcpy(llc_frame->src_hwaddr, -#if defined(HAVE_NETMAP) || defined(HAVE_BPF) - eth_llc_data.hw_addr, -#elif defined(HAVE_RAW_SOCKETS) - eth_llc_data.device.sll_addr, -#endif /* HAVE_NETMAP */ - MAC_SIZE); - length = htons(LLC_HEADER_SIZE + len); - memcpy(&llc_frame->length, &length, sizeof(length)); - llc_frame->dsap = dsap; - llc_frame->ssap = ssap; - llc_frame->cf = cf; - memcpy(&llc_frame->payload, payload, len); - - frame_len = ETH_HEADER_SIZE + LLC_HEADER_SIZE + len; -#if defined(HAVE_NETMAP) - if (poll(ð_llc_data.poll_out, 1, -1) < 0) - return -1; - - if (nm_inject(eth_llc_data.nmd, frame, frame_len) != (int) frame_len) { - log_dbg("Failed to send message."); - return -1; - } -#elif defined(HAVE_BPF) - if (write(eth_llc_data.bpf, frame, frame_len) < 0) { - log_dbg("Failed to send message."); - return -1; - } - -#elif defined(HAVE_RAW_SOCKETS) - if (sendto(eth_llc_data.s_fd, - frame, - frame_len, - 0, - (struct sockaddr *) ð_llc_data.device, - sizeof(eth_llc_data.device)) <= 0) { - log_dbg("Failed to send message."); - return -1; - } -#endif /* HAVE_NETMAP */ - return 0; -} - -static int eth_llc_ipcp_sap_alloc(const uint8_t * dst_addr, - uint8_t ssap, - const uint8_t * hash, - qoscube_t cube) -{ - uint8_t * buf; - struct mgmt_msg * msg; - size_t len; - int ret; - - len = sizeof(*msg) + ipcp_dir_hash_len(); - - buf = malloc(len); - if (buf == NULL) - return -1; - - msg = (struct mgmt_msg *) buf; - msg->code = FLOW_REQ; - msg->ssap = ssap; - msg->qoscube = cube; - - memcpy(msg + 1, hash, ipcp_dir_hash_len()); - - ret = eth_llc_ipcp_send_frame(dst_addr, reverse_bits(MGMT_SAP), - reverse_bits(MGMT_SAP), buf, len); - - free(buf); - - return ret; -} - -static int eth_llc_ipcp_sap_alloc_resp(uint8_t * dst_addr, - uint8_t ssap, - uint8_t dsap, - int response) -{ - struct mgmt_msg msg; - - msg.code = FLOW_REPLY; - msg.ssap = ssap; - msg.dsap = dsap; - msg.response = response; - - return eth_llc_ipcp_send_frame(dst_addr, reverse_bits(MGMT_SAP), - reverse_bits(MGMT_SAP), - (uint8_t *) &msg, sizeof(msg)); -} - -static int eth_llc_ipcp_sap_req(uint8_t r_sap, - uint8_t * r_addr, - const uint8_t * dst, - qoscube_t cube) -{ - struct timespec ts = {0, ALLOC_TIMEO * MILLION}; - struct timespec abstime; - int fd; - - clock_gettime(PTHREAD_COND_CLOCK, &abstime); - - pthread_mutex_lock(&ipcpi.alloc_lock); - - while (ipcpi.alloc_id != -1 && ipcp_get_state() == IPCP_OPERATIONAL) { - ts_add(&abstime, &ts, &abstime); - pthread_cond_timedwait(&ipcpi.alloc_cond, - &ipcpi.alloc_lock, - &abstime); - } - - if (ipcp_get_state() != IPCP_OPERATIONAL) { - log_dbg("Won't allocate over non-operational IPCP."); - pthread_mutex_unlock(&ipcpi.alloc_lock); - return -1; - } - - /* reply to IRM, called under lock to prevent race */ - fd = ipcp_flow_req_arr(getpid(), dst, ipcp_dir_hash_len(), cube); - if (fd < 0) { - pthread_mutex_unlock(&ipcpi.alloc_lock); - log_err("Could not get new flow from IRMd."); - return -1; - } - - pthread_rwlock_wrlock(ð_llc_data.flows_lock); - - eth_llc_data.fd_to_ef[fd].r_sap = r_sap; - memcpy(eth_llc_data.fd_to_ef[fd].r_addr, r_addr, MAC_SIZE); - - ipcpi.alloc_id = fd; - pthread_cond_broadcast(&ipcpi.alloc_cond); - - pthread_rwlock_unlock(ð_llc_data.flows_lock); - pthread_mutex_unlock(&ipcpi.alloc_lock); - - log_dbg("New flow request, fd %d, remote SAP %d.", fd, r_sap); - - return 0; -} - -static int eth_llc_ipcp_sap_alloc_reply(uint8_t ssap, - uint8_t * r_addr, - int dsap, - int response) -{ - int ret = 0; - int fd = -1; - - pthread_rwlock_wrlock(ð_llc_data.flows_lock); - - fd = eth_llc_data.ef_to_fd[dsap]; - if (fd < 0) { - pthread_rwlock_unlock(& eth_llc_data.flows_lock); - log_err("No flow found with that SAP."); - return -1; /* -EFLOWNOTFOUND */ - } - - if (response) { - bmp_release(eth_llc_data.saps, eth_llc_data.fd_to_ef[fd].sap); - } else { - eth_llc_data.fd_to_ef[fd].r_sap = ssap; - memcpy(eth_llc_data.fd_to_ef[fd].r_addr, r_addr, MAC_SIZE); - } - - pthread_rwlock_unlock(ð_llc_data.flows_lock); - - log_dbg("Flow reply, fd %d, SSAP %d, DSAP %d.", fd, ssap, dsap); - - if ((ret = ipcp_flow_alloc_reply(fd, response)) < 0) - return -1; - - return ret; - -} - -static int eth_llc_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_llc_data.shim_data, hash)) { - len = sizeof(*msg) + ipcp_dir_hash_len(); - - buf = malloc(len); - if (buf == NULL) - return -1; - - msg = (struct mgmt_msg *) buf; - msg->code = NAME_QUERY_REPLY; - - memcpy(msg + 1, hash, ipcp_dir_hash_len()); - - if (eth_llc_ipcp_send_frame(r_addr, reverse_bits(MGMT_SAP), - reverse_bits(MGMT_SAP), buf, len)) { - log_err("Failed to send management frame."); - free(buf); - return -1; - } - - free(buf); - } - - return 0; -} - -static int eth_llc_ipcp_name_query_reply(const uint8_t * hash, - uint8_t * r_addr) -{ - uint64_t address = 0; - - memcpy(&address, r_addr, MAC_SIZE); - - shim_data_dir_add_entry(eth_llc_data.shim_data, hash, address); - - shim_data_dir_query_respond(eth_llc_data.shim_data, hash); - - return 0; -} - -static int eth_llc_ipcp_mgmt_frame(const uint8_t * buf, - uint8_t * r_addr) -{ - struct mgmt_msg * msg; - - msg = (struct mgmt_msg *) buf; - - switch (msg->code) { - case FLOW_REQ: - if (shim_data_reg_has(eth_llc_data.shim_data, - buf + sizeof(*msg))) { - eth_llc_ipcp_sap_req(msg->ssap, - r_addr, - buf + sizeof(*msg), - msg->qoscube); - } - break; - case FLOW_REPLY: - eth_llc_ipcp_sap_alloc_reply(msg->ssap, - r_addr, - msg->dsap, - msg->response); - break; - case NAME_QUERY_REQ: - eth_llc_ipcp_name_query_req(buf + sizeof(*msg), r_addr); - break; - case NAME_QUERY_REPLY: - eth_llc_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_llc_ipcp_mgmt_handler(void * o) -{ - int ret; - struct timespec timeout = {(MGMT_TIMEO / 1000), - (MGMT_TIMEO % 1000) * MILLION}; - struct timespec abstime; - struct mgmt_frame * frame; - - (void) o; - - pthread_cleanup_push((void (*)(void *)) pthread_mutex_unlock, - (void *) ð_llc_data.mgmt_lock); - - while (true) { - ret = 0; - - clock_gettime(PTHREAD_COND_CLOCK, &abstime); - ts_add(&abstime, &timeout, &abstime); - - pthread_mutex_lock(ð_llc_data.mgmt_lock); - - while (list_is_empty(ð_llc_data.mgmt_frames) && - ret != -ETIMEDOUT) - ret = -pthread_cond_timedwait(ð_llc_data.mgmt_cond, - ð_llc_data.mgmt_lock, - &abstime); - - if (ret == -ETIMEDOUT) { - pthread_mutex_unlock(ð_llc_data.mgmt_lock); - continue; - } - - frame = list_first_entry((ð_llc_data.mgmt_frames), - struct mgmt_frame, next); - if (frame == NULL) { - pthread_mutex_unlock(ð_llc_data.mgmt_lock); - continue; - } - - list_del(&frame->next); - pthread_mutex_unlock(ð_llc_data.mgmt_lock); - - eth_llc_ipcp_mgmt_frame(frame->buf, frame->r_addr); - free(frame); - } - - pthread_cleanup_pop(false); - - return (void *) 0; -} - -static void * eth_llc_ipcp_sdu_reader(void * o) -{ - uint8_t br_addr[MAC_SIZE]; - uint16_t length; - uint8_t dsap; - uint8_t ssap; - int fd; -#if defined(HAVE_NETMAP) - uint8_t * buf; - struct nm_pkthdr hdr; -#elif defined(HAVE_BPF) - uint8_t buf[BPF_BLEN]; -#elif defined(HAVE_RAW_SOCKETS) - uint8_t buf[ETH_FRAME_SIZE]; -#endif - int frame_len = 0; - struct eth_llc_frame * llc_frame; - struct mgmt_frame * frame; - - (void) o; - - memset(br_addr, 0xff, MAC_SIZE * sizeof(uint8_t)); - - while (true) { -#if defined(HAVE_NETMAP) - if (poll(ð_llc_data.poll_in, 1, -1) < 0) - continue; - if (eth_llc_data.poll_in.revents == 0) /* TIMED OUT */ - continue; - - buf = nm_nextpkt(eth_llc_data.nmd, &hdr); - if (buf == NULL) { - log_err("Bad read from netmap device."); - continue; - } -#elif defined(HAVE_BPF) - frame_len = read(eth_llc_data.bpf, buf, BPF_BLEN); -#elif defined(HAVE_RAW_SOCKETS) - frame_len = recv(eth_llc_data.s_fd, buf, - SHIM_ETH_LLC_MAX_SDU_SIZE, 0); -#endif - if (frame_len <= 0) - continue; - -#if defined(HAVE_BPF) && !defined(HAVE_NETMAP) - llc_frame = (struct eth_llc_frame *) - (buf + ((struct bpf_hdr *) buf)->bh_hdrlen); -#else - llc_frame = (struct eth_llc_frame *) buf; -#endif - assert(llc_frame->dst_hwaddr); - -#if !defined(HAVE_BPF) - #if defined(HAVE_NETMAP) - if (memcmp(eth_llc_data.hw_addr, - #elif defined(HAVE_RAW_SOCKETS) - if (memcmp(eth_llc_data.device.sll_addr, - #endif /* HAVE_NETMAP */ - llc_frame->dst_hwaddr, - MAC_SIZE) && - memcmp(br_addr, llc_frame->dst_hwaddr, MAC_SIZE)) { - } -#endif - memcpy(&length, &llc_frame->length, sizeof(length)); - length = ntohs(length); - - if (length > 0x05FF) /* DIX */ - continue; - - length -= LLC_HEADER_SIZE; - - dsap = reverse_bits(llc_frame->dsap); - ssap = reverse_bits(llc_frame->ssap); - - if (ssap == MGMT_SAP && dsap == MGMT_SAP) { - pthread_mutex_lock(ð_llc_data.mgmt_lock); - - frame = malloc(sizeof(*frame)); - if (frame == NULL) { - pthread_mutex_unlock(ð_llc_data.mgmt_lock); - continue; - } - - memcpy(frame->buf, &llc_frame->payload, length); - memcpy(frame->r_addr, llc_frame->src_hwaddr, MAC_SIZE); - frame->len = length; - list_add(&frame->next, ð_llc_data.mgmt_frames); - pthread_cond_signal(ð_llc_data.mgmt_cond); - pthread_mutex_unlock(ð_llc_data.mgmt_lock); - } else { - pthread_rwlock_rdlock(ð_llc_data.flows_lock); - - fd = eth_llc_data.ef_to_fd[dsap]; - if (fd < 0) { - pthread_rwlock_unlock(ð_llc_data.flows_lock); - continue; - } - - if (eth_llc_data.fd_to_ef[fd].r_sap != ssap - || memcmp(eth_llc_data.fd_to_ef[fd].r_addr, - llc_frame->src_hwaddr, MAC_SIZE)) { - pthread_rwlock_unlock(ð_llc_data.flows_lock); - continue; - } - - pthread_rwlock_unlock(ð_llc_data.flows_lock); - - flow_write(fd, &llc_frame->payload, length); - } - } - - return (void *) 0; -} - -static void * eth_llc_ipcp_sdu_writer(void * o) -{ - int fd; - struct shm_du_buff * sdb; - uint8_t ssap; - uint8_t dsap; - uint8_t r_addr[MAC_SIZE]; - - (void) o; - - while (true) { - fevent(eth_llc_data.np1_flows, eth_llc_data.fq, NULL); - - pthread_rwlock_rdlock(ð_llc_data.flows_lock); - while ((fd = fqueue_next(eth_llc_data.fq)) >= 0) { - if (ipcp_flow_read(fd, &sdb)) { - log_err("Bad read from fd %d.", fd); - continue; - } - - ssap = reverse_bits(eth_llc_data.fd_to_ef[fd].sap); - dsap = reverse_bits(eth_llc_data.fd_to_ef[fd].r_sap); - memcpy(r_addr, - eth_llc_data.fd_to_ef[fd].r_addr, - MAC_SIZE); - - eth_llc_ipcp_send_frame(r_addr, dsap, ssap, - shm_du_buff_head(sdb), - shm_du_buff_tail(sdb) - - shm_du_buff_head(sdb)); - ipcp_sdb_release(sdb); - } - pthread_rwlock_unlock(ð_llc_data.flows_lock); - } - - 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(ð_llc_data.flows_lock); - - for (i = 0; i < MAX_SAPS; i++) { - if (eth_llc_data.ef_to_fd[i] != -1) { - fccntl(i, FLOWGFLAGS, &flags); - if (up) - fccntl(eth_llc_data.ef_to_fd[i], - FLOWSFLAGS, flags & ~FLOWFDOWN); - else - fccntl(eth_llc_data.ef_to_fd[i], - FLOWSFLAGS, flags | FLOWFDOWN); - } - } - - pthread_rwlock_unlock(ð_llc_data.flows_lock); -} - -static void close_ptr(void * o) -{ - close(*((int *) o)); -} - - -static void * eth_llc_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(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_llc_data.device.sll_ifindex) - 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 - -static int eth_llc_ipcp_bootstrap(const struct ipcp_config * conf) -{ - int idx; - struct ifreq ifr; -#if defined(HAVE_NETMAP) - char ifn[IFNAMSIZ]; -#elif defined(HAVE_BPF) - int enable = 1; - int disable = 0; - int blen; -#endif /* HAVE_NETMAP */ - -#if defined(__FreeBSD__) || defined(__APPLE__) - struct ifaddrs * ifaddr; - struct ifaddrs * ifa; -#elif defined(__linux__) - int skfd; -#endif - assert(conf); - assert(conf->type == THIS_TYPE); - - if (conf->if_name == NULL) { - log_err("Interface name is NULL."); - return -1; - } - - memset(&ifr, 0, sizeof(ifr)); - memcpy(ifr.ifr_name, conf->if_name, strlen(conf->if_name)); - -#if defined(__FreeBSD__) || defined(__APPLE__) - if (getifaddrs(&ifaddr) < 0) { - log_err("Could not get interfaces."); - return -1; - } - - for (ifa = ifaddr, idx = 0; ifa != NULL; ifa = ifa->ifa_next, ++idx) { - if (strcmp(ifa->ifa_name, conf->if_name)) - continue; - log_dbg("Interface %s found.", conf->if_name); - - #if defined(HAVE_NETMAP) || defined(HAVE_BPF) - memcpy(eth_llc_data.hw_addr, - LLADDR((struct sockaddr_dl *) (ifa)->ifa_addr), - MAC_SIZE); - #elif defined (HAVE_RAW_SOCKETS) - memcpy(&ifr.ifr_addr, ifa->ifa_addr, sizeof(*ifa->ifa_addr)); - #endif - break; - } - - freeifaddrs(ifaddr); - - if (ifa == NULL) { - log_err("Interface not found."); - return -1; - } - -#elif defined(__linux__) - skfd = socket(AF_UNIX, SOCK_STREAM, 0); - if (skfd < 0) { - log_err("Failed to open socket."); - return -1; - } - - if (ioctl(skfd, SIOCGIFHWADDR, &ifr)) { - log_err("Failed to ioctl."); - close(skfd); - return -1; - } - - close(skfd); - - idx = if_nametoindex(conf->if_name); - if (idx == 0) { - log_err("Failed to retrieve interface index."); - close(skfd); - return -1; - } -#endif /* __FreeBSD__ */ - -#if defined(HAVE_NETMAP) - strcpy(ifn, "netmap:"); - strcat(ifn, conf->if_name); - - eth_llc_data.nmd = nm_open(ifn, NULL, 0, NULL); - if (eth_llc_data.nmd == NULL) { - log_err("Failed to open netmap device."); - return -1; - } - - memset(ð_llc_data.poll_in, 0, sizeof(eth_llc_data.poll_in)); - memset(ð_llc_data.poll_out, 0, sizeof(eth_llc_data.poll_out)); - - eth_llc_data.poll_in.fd = NETMAP_FD(eth_llc_data.nmd); - eth_llc_data.poll_in.events = POLLIN; - eth_llc_data.poll_out.fd = NETMAP_FD(eth_llc_data.nmd); - eth_llc_data.poll_out.events = POLLOUT; - - log_info("Using netmap device."); -#elif defined(HAVE_BPF) /* !HAVE_NETMAP */ - eth_llc_data.bpf = open_bpf_device(); - if (eth_llc_data.bpf < 0) { - log_err("Failed to open bpf device."); - return -1; - } - - ioctl(eth_llc_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_llc_data.bpf, BIOCSETIF, &ifr) < 0) { - log_err("Failed to set interface."); - goto fail_device; - } - - if (ioctl(eth_llc_data.bpf, BIOCSHDRCMPLT, &enable) < 0) { - log_err("Failed to set BIOCSHDRCMPLT."); - goto fail_device; - } - - if (ioctl(eth_llc_data.bpf, BIOCSSEESENT, &disable) < 0) { - log_err("Failed to set BIOCSSEESENT."); - goto fail_device; - } - - if (ioctl(eth_llc_data.bpf, BIOCIMMEDIATE, &enable) < 0) { - log_err("Failed to set BIOCIMMEDIATE."); - goto fail_device; - } - - log_info("Using Berkeley Packet Filter."); -#elif defined(HAVE_RAW_SOCKETS) - memset(&(eth_llc_data.device), 0, sizeof(eth_llc_data.device)); - eth_llc_data.device.sll_ifindex = idx; - eth_llc_data.device.sll_family = AF_PACKET; - memcpy(eth_llc_data.device.sll_addr, ifr.ifr_hwaddr.sa_data, MAC_SIZE); - eth_llc_data.device.sll_halen = MAC_SIZE; - eth_llc_data.device.sll_protocol = htons(ETH_P_ALL); - eth_llc_data.s_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_802_2)); - - log_info("Using raw socket device."); - - if (eth_llc_data.s_fd < 0) { - log_err("Failed to create socket."); - return -1; - } - - if (bind(eth_llc_data.s_fd, (struct sockaddr *) ð_llc_data.device, - sizeof(eth_llc_data.device))) { - log_err("Failed to bind socket to interface"); - goto fail_device; - } - -#endif /* HAVE_NETMAP */ - ipcp_set_state(IPCP_OPERATIONAL); - -#ifdef __linux__ - if (pthread_create(ð_llc_data.if_monitor, - NULL, - eth_llc_ipcp_if_monitor, - NULL)) { - ipcp_set_state(IPCP_INIT); - goto fail_device; - } -#endif - - if (pthread_create(ð_llc_data.mgmt_handler, - NULL, - eth_llc_ipcp_mgmt_handler, - NULL)) { - ipcp_set_state(IPCP_INIT); - goto fail_mgmt_handler; - } - - if (pthread_create(ð_llc_data.sdu_reader, - NULL, - eth_llc_ipcp_sdu_reader, - NULL)) { - ipcp_set_state(IPCP_INIT); - goto fail_sdu_reader; - } - - if (pthread_create(ð_llc_data.sdu_writer, - NULL, - eth_llc_ipcp_sdu_writer, - NULL)) { - ipcp_set_state(IPCP_INIT); - goto fail_sdu_writer; - } - - log_dbg("Bootstrapped shim IPCP over Ethernet with LLC with pid %d.", - getpid()); - - return 0; - - fail_sdu_writer: - pthread_cancel(eth_llc_data.sdu_reader); - pthread_join(eth_llc_data.sdu_reader, NULL); - fail_sdu_reader: - pthread_cancel(eth_llc_data.mgmt_handler); - pthread_join(eth_llc_data.mgmt_handler, NULL); - fail_mgmt_handler: -#if defined(__linux__) - pthread_cancel(eth_llc_data.if_monitor); - pthread_join(eth_llc_data.if_monitor, NULL); -#endif -#if !defined(HAVE_NETMAP) - fail_device: -#endif -#if defined(HAVE_NETMAP) - nm_close(eth_llc_data.nmd); -#elif defined(HAVE_BPF) - close(eth_llc_data.bpf); -#elif defined(HAVE_RAW_SOCKETS) - close(eth_llc_data.s_fd); -#endif - return -1; -} - -static int eth_llc_ipcp_reg(const uint8_t * hash) -{ - if (shim_data_reg_add_entry(eth_llc_data.shim_data, hash)) { - log_err("Failed to add " HASH_FMT " to local registry.", - HASH_VAL(hash)); - return -1; - } - - log_dbg("Registered " HASH_FMT ".", HASH_VAL(hash)); - - return 0; -} - -static int eth_llc_ipcp_unreg(const uint8_t * hash) -{ - shim_data_reg_del_entry(eth_llc_data.shim_data, hash); - - return 0; -} - -static int eth_llc_ipcp_query(const uint8_t * hash) -{ - uint8_t r_addr[MAC_SIZE]; - struct timespec timeout = {(NAME_QUERY_TIMEO / 1000), - (NAME_QUERY_TIMEO % 1000) * MILLION}; - struct dir_query * query; - int ret; - uint8_t * buf; - struct mgmt_msg * msg; - size_t len; - - if (shim_data_dir_has(eth_llc_data.shim_data, hash)) - return 0; - - len = sizeof(*msg) + ipcp_dir_hash_len(); - - buf = malloc(len); - if (buf == NULL) - return -1; - - msg = (struct mgmt_msg *) buf; - msg->code = NAME_QUERY_REQ; - - memcpy(buf + sizeof(*msg), hash, ipcp_dir_hash_len()); - - memset(r_addr, 0xff, MAC_SIZE); - - query = shim_data_dir_query_create(eth_llc_data.shim_data, hash); - if (query == NULL) { - free(buf); - return -1; - } - - if (eth_llc_ipcp_send_frame(r_addr, reverse_bits(MGMT_SAP), - reverse_bits(MGMT_SAP), buf, len)) { - log_err("Failed to send management frame."); - shim_data_dir_query_destroy(eth_llc_data.shim_data, query); - free(buf); - return -1; - } - - free(buf); - - ret = shim_data_dir_query_wait(query, &timeout); - - shim_data_dir_query_destroy(eth_llc_data.shim_data, query); - - return ret; -} - -static int eth_llc_ipcp_flow_alloc(int fd, - const uint8_t * hash, - qoscube_t cube) -{ - uint8_t ssap = 0; - uint8_t r_addr[MAC_SIZE]; - uint64_t addr = 0; - - log_dbg("Allocating flow to " HASH_FMT ".", HASH_VAL(hash)); - - assert(hash); - - if (cube != QOS_CUBE_BE) { - log_dbg("Unsupported QoS requested."); - return -1; - } - - if (!shim_data_dir_has(eth_llc_data.shim_data, hash)) { - log_err("Destination unreachable."); - return -1; - } - addr = shim_data_dir_get_addr(eth_llc_data.shim_data, hash); - - pthread_rwlock_wrlock(ð_llc_data.flows_lock); - - ssap = bmp_allocate(eth_llc_data.saps); - if (!bmp_is_id_valid(eth_llc_data.saps, ssap)) { - pthread_rwlock_unlock(ð_llc_data.flows_lock); - return -1; - } - - eth_llc_data.fd_to_ef[fd].sap = ssap; - eth_llc_data.ef_to_fd[ssap] = fd; - - pthread_rwlock_unlock(ð_llc_data.flows_lock); - - memcpy(r_addr, &addr, MAC_SIZE); - - if (eth_llc_ipcp_sap_alloc(r_addr, ssap, hash, cube) < 0) { - pthread_rwlock_wrlock(ð_llc_data.flows_lock); - bmp_release(eth_llc_data.saps, eth_llc_data.fd_to_ef[fd].sap); - eth_llc_data.fd_to_ef[fd].sap = -1; - eth_llc_data.ef_to_fd[ssap] = -1; - pthread_rwlock_unlock(ð_llc_data.flows_lock); - return -1; - } - - fset_add(eth_llc_data.np1_flows, fd); - - log_dbg("Pending flow with fd %d on SAP %d.", fd, ssap); - - return 0; -} - -static int eth_llc_ipcp_flow_alloc_resp(int fd, - int response) -{ - struct timespec ts = {0, ALLOC_TIMEO * MILLION}; - struct timespec abstime; - uint8_t ssap = 0; - uint8_t r_sap = 0; - uint8_t r_addr[MAC_SIZE]; - - clock_gettime(PTHREAD_COND_CLOCK, &abstime); - - pthread_mutex_lock(&ipcpi.alloc_lock); - - while (ipcpi.alloc_id != fd && ipcp_get_state() == IPCP_OPERATIONAL) { - ts_add(&abstime, &ts, &abstime); - pthread_cond_timedwait(&ipcpi.alloc_cond, - &ipcpi.alloc_lock, - &abstime); - } - - if (ipcp_get_state() != IPCP_OPERATIONAL) { - pthread_mutex_unlock(&ipcpi.alloc_lock); - return -1; - } - - ipcpi.alloc_id = -1; - pthread_cond_broadcast(&ipcpi.alloc_cond); - - pthread_mutex_unlock(&ipcpi.alloc_lock); - - pthread_rwlock_wrlock(ð_llc_data.flows_lock); - - ssap = bmp_allocate(eth_llc_data.saps); - if (!bmp_is_id_valid(eth_llc_data.saps, ssap)) { - pthread_rwlock_unlock(ð_llc_data.flows_lock); - return -1; - } - - eth_llc_data.fd_to_ef[fd].sap = ssap; - memcpy(r_addr, eth_llc_data.fd_to_ef[fd].r_addr, MAC_SIZE); - r_sap = eth_llc_data.fd_to_ef[fd].r_sap; - eth_llc_data.ef_to_fd[ssap] = fd; - - pthread_rwlock_unlock(ð_llc_data.flows_lock); - - if (eth_llc_ipcp_sap_alloc_resp(r_addr, ssap, r_sap, response) < 0) { - pthread_rwlock_wrlock(ð_llc_data.flows_lock); - bmp_release(eth_llc_data.saps, eth_llc_data.fd_to_ef[fd].sap); - pthread_rwlock_unlock(ð_llc_data.flows_lock); - return -1; - } - - fset_add(eth_llc_data.np1_flows, fd); - - log_dbg("Accepted flow, fd %d, SAP %d.", fd, (uint8_t)ssap); - - return 0; -} - -static int eth_llc_ipcp_flow_dealloc(int fd) -{ - uint8_t sap; - uint8_t addr[MAC_SIZE]; - - ipcp_flow_fini(fd); - - pthread_rwlock_wrlock(ð_llc_data.flows_lock); - - fset_del(eth_llc_data.np1_flows, fd); - - sap = eth_llc_data.fd_to_ef[fd].sap; - memcpy(addr, eth_llc_data.fd_to_ef[fd].r_addr, MAC_SIZE); - bmp_release(eth_llc_data.saps, sap); - eth_llc_data.fd_to_ef[fd].sap = -1; - eth_llc_data.fd_to_ef[fd].r_sap = -1; - memset(ð_llc_data.fd_to_ef[fd].r_addr, 0, MAC_SIZE); - - eth_llc_data.ef_to_fd[sap] = -1; - - pthread_rwlock_unlock(ð_llc_data.flows_lock); - - flow_dealloc(fd); - - log_dbg("Flow with fd %d deallocated.", fd); - - return 0; -} - -static struct ipcp_ops eth_llc_ops = { - .ipcp_bootstrap = eth_llc_ipcp_bootstrap, - .ipcp_enroll = NULL, - .ipcp_connect = NULL, - .ipcp_disconnect = NULL, - .ipcp_reg = eth_llc_ipcp_reg, - .ipcp_unreg = eth_llc_ipcp_unreg, - .ipcp_query = eth_llc_ipcp_query, - .ipcp_flow_alloc = eth_llc_ipcp_flow_alloc, - .ipcp_flow_alloc_resp = eth_llc_ipcp_flow_alloc_resp, - .ipcp_flow_dealloc = eth_llc_ipcp_flow_dealloc -}; - -int main(int argc, - char * argv[]) -{ - if (ipcp_init(argc, argv, ð_llc_ops) < 0) - goto fail_init; - - if (eth_llc_data_init() < 0) { - log_err("Failed to init shim-eth-llc data."); - goto fail_data_init; - } - - if (ipcp_boot() < 0) { - log_err("Failed to boot IPCP."); - goto fail_boot; - } - - if (ipcp_create_r(getpid(), 0)) { - log_err("Failed to notify IRMd we are initialized."); - ipcp_set_state(IPCP_NULL); - goto fail_create_r; - } - - ipcp_shutdown(); - - if (ipcp_get_state() == IPCP_SHUTDOWN) { - pthread_cancel(eth_llc_data.sdu_writer); - pthread_cancel(eth_llc_data.sdu_reader); - pthread_cancel(eth_llc_data.mgmt_handler); -#ifdef __linux__ - pthread_cancel(eth_llc_data.if_monitor); -#endif - pthread_join(eth_llc_data.sdu_writer, NULL); - pthread_join(eth_llc_data.sdu_reader, NULL); - pthread_join(eth_llc_data.mgmt_handler, NULL); -#ifdef __linux__ - pthread_join(eth_llc_data.if_monitor, NULL); -#endif - } - - eth_llc_data_fini(); - - ipcp_fini(); - - exit(EXIT_SUCCESS); - - fail_create_r: - ipcp_shutdown(); - fail_boot: - eth_llc_data_fini(); - fail_data_init: - ipcp_fini(); - fail_init: - ipcp_create_r(getpid(), -1); - exit(EXIT_FAILURE); -} diff --git a/src/ipcpd/shim-udp/CMakeLists.txt b/src/ipcpd/shim-udp/CMakeLists.txt deleted file mode 100644 index aaf7e83b..00000000 --- a/src/ipcpd/shim-udp/CMakeLists.txt +++ /dev/null @@ -1,60 +0,0 @@ -get_filename_component(CURRENT_SOURCE_PARENT_DIR - ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY) -get_filename_component(CURRENT_BINARY_PARENT_DIR - ${CMAKE_CURRENT_BINARY_DIR} DIRECTORY) - -include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -include_directories(${CMAKE_CURRENT_BINARY_DIR}) - -include_directories(${CURRENT_SOURCE_PARENT_DIR}) -include_directories(${CURRENT_BINARY_PARENT_DIR}) - -include_directories(${CMAKE_SOURCE_DIR}/include) -include_directories(${CMAKE_BINARY_DIR}/include) - -set(IPCP_SHIM_UDP_TARGET ipcpd-shim-udp CACHE INTERNAL "") - -set(SHIM_UDP_SOURCES - # Add source files here - ${CMAKE_CURRENT_SOURCE_DIR}/main.c) - -add_executable(ipcpd-shim-udp ${SHIM_UDP_SOURCES} ${IPCP_SOURCES}) -target_link_libraries(ipcpd-shim-udp LINK_PUBLIC ouroboros-dev) - -# Find the nsupdate executable -find_program(NSUPDATE_EXECUTABLE - NAMES nsupdate - DOC "The nsupdate tool that enables DDNS") - -# Find the nslookup executable -find_program(NSLOOKUP_EXECUTABLE - NAMES nslookup - DOC "The nslookup tool that resolves DNS names") - -mark_as_advanced(NSLOOKUP_EXECUTABLE NSUPDATE_EXECUTABLE) - -if (NSLOOKUP_EXECUTABLE AND NSUPDATE_EXECUTABLE) - set(DISABLE_DDNS FALSE CACHE BOOL "Disable DDNS support") - if (NOT DISABLE_DNS) - message(STATUS "DDNS support enabled") - set(HAVE_DDNS TRUE CACHE INTERNAL "") - else () - message(STATUS "DDNS support disabled by user") - unset(HAVE_DDNS CACHE) - endif () -else () - if (NSLOOKUP_EXECUTABLE) - message(STATUS "Install nsupdate to enable DDNS support") - elseif (NSUPDATE_EXECUTABLE) - message(STATUS "Install nslookup to enable DDNS support") - else () - message(STATUS "Install nslookup and nsupdate to enable DDNS support") - endif () -endif () - -include(AddCompileFlags) -if (CMAKE_BUILD_TYPE MATCHES "Debug*") - add_compile_flags(ipcpd-shim-udp -DCONFIG_OUROBOROS_DEBUG) -endif () - -install(TARGETS ipcpd-shim-udp RUNTIME DESTINATION sbin) diff --git a/src/ipcpd/shim-udp/main.c b/src/ipcpd/shim-udp/main.c deleted file mode 100644 index 79d5ea83..00000000 --- a/src/ipcpd/shim-udp/main.c +++ /dev/null @@ -1,1167 +0,0 @@ -/* - * Ouroboros - Copyright (C) 2016 - 2017 - * - * Shim IPC process over UDP - * - * Dimitri Staessens - * Sander Vrijders - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., http://www.fsf.org/about/contact/. - */ - -#define _POSIX_C_SOURCE 200112L - -#include "config.h" - -#define OUROBOROS_PREFIX "ipcpd/shim-udp" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ipcp.h" -#include "shim-data.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define FLOW_REQ 1 -#define FLOW_REPLY 2 - -#define THIS_TYPE IPCP_SHIM_UDP -#define LISTEN_PORT htons(0x0D1F) -#define SHIM_UDP_BUF_SIZE 256 -#define SHIM_UDP_MSG_SIZE 256 -#define SHIM_UDP_MAX_SDU_SIZE 8980 -#define DNS_TTL 86400 -#define FD_UPDATE_TIMEOUT 100 /* microseconds */ - -#define local_ip (udp_data.s_saddr.sin_addr.s_addr) - -#define UDP_MAX_PORTS 0xFFFF - -struct mgmt_msg { - uint16_t src_udp_port; - uint16_t dst_udp_port; - uint8_t code; - uint8_t qoscube; - uint8_t response; -} __attribute__((packed)); - -struct uf { - int udp; - int skfd; -}; - -struct { - struct shim_data * shim_data; - - uint32_t ip_addr; - uint32_t dns_addr; - /* listen server */ - struct sockaddr_in s_saddr; - int s_fd; - - fset_t * np1_flows; - fqueue_t * fq; - fd_set flow_fd_s; - /* bidir mappings of (n - 1) file descriptor to (n) flow descriptor */ - int uf_to_fd[FD_SETSIZE]; - struct uf fd_to_uf[SYS_MAX_FLOWS]; - pthread_rwlock_t flows_lock; - - pthread_t sduloop; - pthread_t handler; - pthread_t sdu_reader; - - bool fd_set_mod; - pthread_cond_t fd_set_cond; - pthread_mutex_t fd_set_lock; -} udp_data; - -static int udp_data_init(void) -{ - int i; - - for (i = 0; i < FD_SETSIZE; ++i) - udp_data.uf_to_fd[i] = -1; - - for (i = 0; i < SYS_MAX_FLOWS; ++i) { - udp_data.fd_to_uf[i].skfd = -1; - udp_data.fd_to_uf[i].udp = -1; - } - - FD_ZERO(&udp_data.flow_fd_s); - - udp_data.np1_flows = fset_create(); - if (udp_data.np1_flows == NULL) - return -ENOMEM; - - udp_data.fq = fqueue_create(); - if (udp_data.fq == NULL) { - fset_destroy(udp_data.np1_flows); - return -ENOMEM; - } - - udp_data.shim_data = shim_data_create(); - if (udp_data.shim_data == NULL) { - fqueue_destroy(udp_data.fq); - fset_destroy(udp_data.np1_flows); - return -ENOMEM; - } - - pthread_rwlock_init(&udp_data.flows_lock, NULL); - pthread_cond_init(&udp_data.fd_set_cond, NULL); - pthread_mutex_init(&udp_data.fd_set_lock, NULL); - - return 0; -} - -static void udp_data_fini(void) -{ - fset_destroy(udp_data.np1_flows); - fqueue_destroy(udp_data.fq); - - shim_data_destroy(udp_data.shim_data); - - pthread_rwlock_destroy(&udp_data.flows_lock); - pthread_mutex_destroy(&udp_data.fd_set_lock); - pthread_cond_destroy(&udp_data.fd_set_cond); -} - -static void set_fd(int fd) -{ - pthread_mutex_lock(&udp_data.fd_set_lock); - - udp_data.fd_set_mod = true; - FD_SET(fd, &udp_data.flow_fd_s); - - while (udp_data.fd_set_mod) - pthread_cond_wait(&udp_data.fd_set_cond, &udp_data.fd_set_lock); - - pthread_mutex_unlock(&udp_data.fd_set_lock); -} - -static void clr_fd(int fd) -{ - pthread_mutex_lock(&udp_data.fd_set_lock); - - udp_data.fd_set_mod = true; - FD_CLR(fd, &udp_data.flow_fd_s); - - while (udp_data.fd_set_mod) - pthread_cond_wait(&udp_data.fd_set_cond, &udp_data.fd_set_lock); - - pthread_mutex_unlock(&udp_data.fd_set_lock); -} - -static int send_shim_udp_msg(uint8_t * buf, - size_t len, - uint32_t dst_ip_addr) -{ - struct sockaddr_in r_saddr; - - memset((char *)&r_saddr, 0, sizeof(r_saddr)); - r_saddr.sin_family = AF_INET; - r_saddr.sin_addr.s_addr = dst_ip_addr; - r_saddr.sin_port = LISTEN_PORT; - - if (sendto(udp_data.s_fd, buf, len, 0, - (struct sockaddr *) &r_saddr, - sizeof(r_saddr)) == -1) { - log_err("Failed to send message."); - return -1; - } - - return 0; -} - -static int ipcp_udp_port_alloc(uint32_t dst_ip_addr, - uint16_t src_udp_port, - const uint8_t * dst, - qoscube_t cube) -{ - uint8_t * buf; - struct mgmt_msg * msg; - size_t len; - int ret; - - len = sizeof(*msg) + ipcp_dir_hash_len(); - - buf = malloc(len); - if (buf == NULL) - return -1; - - msg = (struct mgmt_msg *) buf; - msg->code = FLOW_REQ; - msg->src_udp_port = src_udp_port; - msg->qoscube = cube; - - memcpy(msg + 1, dst, ipcp_dir_hash_len()); - - ret = send_shim_udp_msg(buf, len, dst_ip_addr); - - free(buf); - - return ret; -} - -static int ipcp_udp_port_alloc_resp(uint32_t dst_ip_addr, - uint16_t src_udp_port, - uint16_t dst_udp_port, - int response) -{ - uint8_t * buf; - struct mgmt_msg * msg; - int ret; - - buf = malloc(sizeof(*msg)); - if (buf == NULL) - return -1; - - msg = (struct mgmt_msg *) buf; - msg->code = FLOW_REPLY; - msg->src_udp_port = src_udp_port; - msg->dst_udp_port = dst_udp_port; - msg->response = response; - - ret = send_shim_udp_msg(buf, sizeof(*msg), dst_ip_addr); - - free(buf); - - return ret; -} - -static int ipcp_udp_port_req(struct sockaddr_in * c_saddr, - const uint8_t * dst, - qoscube_t cube) -{ - struct timespec ts = {0, FD_UPDATE_TIMEOUT * 1000}; - struct timespec abstime; - struct sockaddr_in f_saddr; - socklen_t f_saddr_len = sizeof(f_saddr); - int skfd; - int fd; - - log_dbg("Port request arrived from UDP port %d", - ntohs(c_saddr->sin_port)); - - if ((skfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { - log_err("Could not create UDP socket."); - return -1; - } - - memset((char *) &f_saddr, 0, sizeof(f_saddr)); - f_saddr.sin_family = AF_INET; - f_saddr.sin_addr.s_addr = local_ip; - f_saddr.sin_port = 0; - - if (bind(skfd, (struct sockaddr *) &f_saddr, sizeof(f_saddr)) < 0) { - log_err("Could not bind to socket."); - close(skfd); - return -1; - } - - if (getsockname(skfd, (struct sockaddr *) &f_saddr, &f_saddr_len) < 0) { - log_err("Could not get address from fd."); - return -1; - } - - /* connect stores the remote address in the file descriptor */ - if (connect(skfd, (struct sockaddr *) c_saddr, sizeof(*c_saddr)) < 0) { - log_err("Could not connect to remote UDP client."); - close(skfd); - return -1; - } - - clock_gettime(PTHREAD_COND_CLOCK, &abstime); - - pthread_mutex_lock(&ipcpi.alloc_lock); - - while (ipcpi.alloc_id != -1 && ipcp_get_state() == IPCP_OPERATIONAL) { - ts_add(&abstime, &ts, &abstime); - pthread_cond_timedwait(&ipcpi.alloc_cond, - &ipcpi.alloc_lock, - &abstime); - } - - if (ipcp_get_state() != IPCP_OPERATIONAL) { - log_dbg("Won't allocate over non-operational IPCP."); - pthread_mutex_unlock(&ipcpi.alloc_lock); - return -1; - } - - /* reply to IRM */ - fd = ipcp_flow_req_arr(getpid(), dst, ipcp_dir_hash_len(), cube); - if (fd < 0) { - pthread_mutex_unlock(&ipcpi.alloc_lock); - log_err("Could not get new flow from IRMd."); - close(skfd); - return -1; - } - - pthread_rwlock_wrlock(&udp_data.flows_lock); - - udp_data.uf_to_fd[skfd] = fd; - udp_data.fd_to_uf[fd].skfd = skfd; - udp_data.fd_to_uf[fd].udp = f_saddr.sin_port; - - pthread_rwlock_unlock(&udp_data.flows_lock); - - ipcpi.alloc_id = fd; - pthread_cond_broadcast(&ipcpi.alloc_cond); - - pthread_mutex_unlock(&ipcpi.alloc_lock); - - log_dbg("Pending allocation request, fd %d, UDP port (%d, %d).", - fd, ntohs(f_saddr.sin_port), ntohs(c_saddr->sin_port)); - - return 0; -} - -/* returns the n flow descriptor */ -static int udp_port_to_fd(int udp_port) -{ - int i; - - for (i = 0; i < SYS_MAX_FLOWS; ++i) - if (udp_data.fd_to_uf[i].udp == udp_port) - return i; - - return -1; -} - -static int ipcp_udp_port_alloc_reply(uint16_t src_udp_port, - uint16_t dst_udp_port, - int response) -{ - int fd = -1; - int ret = 0; - int skfd = -1; - - struct sockaddr_in t_saddr; - socklen_t t_saddr_len = sizeof(t_saddr); - - log_dbg("Received reply for flow on udp port %d.", - ntohs(dst_udp_port)); - - pthread_rwlock_rdlock(&udp_data.flows_lock); - - fd = udp_port_to_fd(dst_udp_port); - skfd = udp_data.fd_to_uf[fd].skfd; - - pthread_rwlock_unlock(&udp_data.flows_lock); - - /* get the original address with the LISTEN PORT */ - if (getpeername(skfd, (struct sockaddr *) &t_saddr, &t_saddr_len) < 0) { - log_dbg("Flow with fd %d has no peer.", fd); - return -1; - } - - /* connect to the flow udp port */ - t_saddr.sin_port = src_udp_port; - - if (connect(skfd, (struct sockaddr *) &t_saddr, sizeof(t_saddr)) < 0) { - close(skfd); - return -1; - } - - pthread_rwlock_rdlock(&udp_data.flows_lock); - - set_fd(skfd); - - pthread_rwlock_unlock(&udp_data.flows_lock); - - if (ipcp_flow_alloc_reply(fd, response) < 0) - return -1; - - log_dbg("Flow allocation completed, UDP ports: (%d, %d).", - ntohs(dst_udp_port), ntohs(src_udp_port)); - - return ret; -} - -static void * ipcp_udp_listener(void * o) -{ - uint8_t buf[SHIM_UDP_MSG_SIZE]; - ssize_t n = 0; - struct sockaddr_in c_saddr; - int sfd = udp_data.s_fd; - - (void) o; - - while (true) { - struct mgmt_msg * msg = NULL; - - memset(&buf, 0, SHIM_UDP_MSG_SIZE); - n = sizeof(c_saddr); - n = recvfrom(sfd, buf, SHIM_UDP_MSG_SIZE, 0, - (struct sockaddr *) &c_saddr, (unsigned *) &n); - if (n < 0) - continue; - - /* flow alloc request from other host */ - if (gethostbyaddr((const char *) &c_saddr.sin_addr.s_addr, - sizeof(c_saddr.sin_addr.s_addr), AF_INET) - == NULL) - continue; - - msg = (struct mgmt_msg *) buf; - - switch (msg->code) { - case FLOW_REQ: - c_saddr.sin_port = msg->src_udp_port; - ipcp_udp_port_req(&c_saddr, - (uint8_t *) (msg + 1), - msg->qoscube); - break; - case FLOW_REPLY: - ipcp_udp_port_alloc_reply(msg->src_udp_port, - msg->dst_udp_port, - msg->response); - break; - default: - log_err("Unknown message received %d.", msg->code); - continue; - } - - c_saddr.sin_port = LISTEN_PORT; - } - - return 0; -} - -static void * ipcp_udp_sdu_reader(void * o) -{ - ssize_t n; - int skfd; - int fd; - /* FIXME: avoid this copy */ - char buf[SHIM_UDP_MAX_SDU_SIZE]; - struct sockaddr_in r_saddr; - struct timeval tv = {0, FD_UPDATE_TIMEOUT}; - fd_set read_fds; - int flags; - - (void) o; - - while (true) { - pthread_rwlock_rdlock(&udp_data.flows_lock); - pthread_mutex_lock(&udp_data.fd_set_lock); - - read_fds = udp_data.flow_fd_s; - udp_data.fd_set_mod = false; - pthread_cond_broadcast(&udp_data.fd_set_cond); - - pthread_mutex_unlock(&udp_data.fd_set_lock); - pthread_rwlock_unlock(&udp_data.flows_lock); - - if (select(FD_SETSIZE, &read_fds, NULL, NULL, &tv) <= 0) - continue; - - for (skfd = 0; skfd < FD_SETSIZE; ++skfd) { - if (!FD_ISSET(skfd, &read_fds)) - continue; - flags = fcntl(skfd, F_GETFL, 0); - fcntl(skfd, F_SETFL, flags | O_NONBLOCK); - n = sizeof(r_saddr); - if ((n = recvfrom(skfd, - &buf, - SHIM_UDP_MAX_SDU_SIZE, - 0, - (struct sockaddr *) &r_saddr, - (unsigned *) &n)) <= 0) - continue; - - pthread_rwlock_rdlock(&udp_data.flows_lock); - - fd = udp_data.uf_to_fd[skfd]; - - pthread_rwlock_unlock(&udp_data.flows_lock); - - flow_write(fd, buf, n); - } - } - - return (void *) 0; -} - -static void * ipcp_udp_sdu_loop(void * o) -{ - int fd; - struct shm_du_buff * sdb; - - (void) o; - - while (true) { - fevent(udp_data.np1_flows, udp_data.fq, NULL); - while ((fd = fqueue_next(udp_data.fq)) >= 0) { - if (ipcp_flow_read(fd, &sdb)) { - log_err("Bad read from fd %d.", fd); - continue; - } - - pthread_rwlock_rdlock(&udp_data.flows_lock); - - fd = udp_data.fd_to_uf[fd].skfd; - - pthread_rwlock_unlock(&udp_data.flows_lock); - - pthread_cleanup_push((void (*)(void *)) ipcp_sdb_release, - (void *) sdb); - - if (send(fd, shm_du_buff_head(sdb), - shm_du_buff_tail(sdb) - shm_du_buff_head(sdb), - 0) < 0) - log_err("Failed to send SDU."); - - pthread_cleanup_pop(true); - } - } - - return (void *) 1; -} - -static int ipcp_udp_bootstrap(const struct ipcp_config * conf) -{ - struct sockaddr_in s_saddr; - char ipstr[INET_ADDRSTRLEN]; - char dnsstr[INET_ADDRSTRLEN]; - int enable = 1; - int fd = -1; - - assert(conf); - assert(conf->type == THIS_TYPE); - - if (inet_ntop(AF_INET, - &conf->ip_addr, - ipstr, - INET_ADDRSTRLEN) == NULL) { - log_err("Failed to convert IP address"); - return -1; - } - - if (conf->dns_addr != 0) { - if (inet_ntop(AF_INET, - &conf->dns_addr, - dnsstr, - INET_ADDRSTRLEN) == NULL) { - log_err("Failed to convert DNS address"); - return -1; - } -#ifndef HAVE_DDNS - log_warn("DNS disabled at compile time, address ignored"); -#endif - } else { - strcpy(dnsstr, "not set"); - } - - /* UDP listen server */ - if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { - log_err("Can't create socket."); - goto fail_socket; - } - - if (setsockopt(fd, - SOL_SOCKET, - SO_REUSEADDR, - &enable, - sizeof(int)) < 0) - log_warn("Failed to set SO_REUSEADDR."); - - memset((char *) &s_saddr, 0, sizeof(s_saddr)); - udp_data.s_saddr.sin_family = AF_INET; - udp_data.s_saddr.sin_addr.s_addr = conf->ip_addr; - udp_data.s_saddr.sin_port = LISTEN_PORT; - - if (bind(fd, - (struct sockaddr *) &udp_data.s_saddr, - sizeof(udp_data.s_saddr)) < 0) { - log_err("Couldn't bind to %s.", ipstr); - goto fail_bind; - } - - udp_data.s_fd = fd; - udp_data.ip_addr = conf->ip_addr; - udp_data.dns_addr = conf->dns_addr; - - FD_CLR(udp_data.s_fd, &udp_data.flow_fd_s); - - ipcp_set_state(IPCP_OPERATIONAL); - - if (pthread_create(&udp_data.handler, - NULL, - ipcp_udp_listener, - NULL)) { - ipcp_set_state(IPCP_INIT); - goto fail_bind; - } - - if (pthread_create(&udp_data.sdu_reader, - NULL, - ipcp_udp_sdu_reader, - NULL)) { - ipcp_set_state(IPCP_INIT); - goto fail_sdu_reader; - } - - if (pthread_create(&udp_data.sduloop, - NULL, - ipcp_udp_sdu_loop, - NULL)) { - ipcp_set_state(IPCP_INIT); - goto fail_sduloop; - } - - log_dbg("Bootstrapped shim IPCP over UDP with pid %d.", getpid()); - log_dbg("Bound to IP address %s.", ipstr); - log_dbg("DNS server address is %s.", dnsstr); - - return 0; - - fail_sduloop: - pthread_cancel(udp_data.sdu_reader); - pthread_join(udp_data.sdu_reader, NULL); - fail_sdu_reader: - pthread_cancel(udp_data.handler); - pthread_join(udp_data.handler, NULL); - fail_bind: - close(fd); - fail_socket: - return -1; -} - -#ifdef HAVE_DDNS -/* FIXME: Dependency on nsupdate to be removed in the end */ -/* NOTE: Disgusted with this crap */ -static int ddns_send(char * cmd) -{ - pid_t pid = -1; - int wstatus; - int pipe_fd[2]; - char * argv[] = {NSUPDATE_EXEC, 0}; - char * envp[] = {0}; - - if (pipe(pipe_fd)) { - log_err("Failed to create pipe."); - return -1; - } - - pid = fork(); - if (pid == -1) { - log_err("Failed to fork."); - return -1; - } - - if (pid == 0) { - close(pipe_fd[1]); - dup2(pipe_fd[0], 0); - execve(argv[0], &argv[0], envp); - } - - close(pipe_fd[0]); - - if (write(pipe_fd[1], cmd, strlen(cmd)) == -1) { - log_err("Failed to communicate with nsupdate."); - close(pipe_fd[1]); - return -1; - } - - waitpid(pid, &wstatus, 0); - if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) - log_dbg("Succesfully communicated with DNS server."); - else - log_err("Failed to register with DNS server."); - - close(pipe_fd[1]); - return 0; -} - -static uint32_t ddns_resolve(char * name, - uint32_t dns_addr) -{ - pid_t pid = -1; - int wstatus; - int pipe_fd[2]; - char dnsstr[INET_ADDRSTRLEN]; - char buf[SHIM_UDP_BUF_SIZE]; - ssize_t count = 0; - char * substr = NULL; - char * substr2 = NULL; - char * addr_str = "Address:"; - uint32_t ip_addr = 0; - - if (inet_ntop(AF_INET, &dns_addr, dnsstr, INET_ADDRSTRLEN) == NULL) - return 0; - - if (pipe(pipe_fd)) { - log_err("Failed to create pipe."); - return 0; - } - - pid = fork(); - if (pid == -1) { - log_err("Failed to fork."); - return 0; - } - - if (pid == 0) { - char * argv[] = {NSLOOKUP_EXEC, name, dnsstr, 0}; - char * envp[] = {0}; - - close(pipe_fd[0]); - dup2(pipe_fd[1], 1); - execve(argv[0], &argv[0], envp); - } - - close(pipe_fd[1]); - - count = read(pipe_fd[0], buf, SHIM_UDP_BUF_SIZE); - if (count <= 0) { - log_err("Failed to communicate with nslookup."); - close(pipe_fd[0]); - return 0; - } - - close(pipe_fd[0]); - - waitpid(pid, &wstatus, 0); - if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) - log_dbg("Succesfully communicated with nslookup."); - else - log_err("Failed to resolve DNS address."); - - buf[count] = '\0'; - substr = strtok(buf, "\n"); - while (substr != NULL) { - substr2 = substr; - substr = strtok(NULL, "\n"); - } - - if (substr2 == NULL || strstr(substr2, addr_str) == NULL) { - log_err("Failed to resolve DNS address."); - return 0; - } - - if (inet_pton(AF_INET, substr2 + strlen(addr_str) + 1, &ip_addr) != 1) { - log_err("Failed to resolve DNS address."); - return 0; - } - - return ip_addr; -} -#endif - -static int ipcp_udp_reg(const uint8_t * hash) -{ -#ifdef HAVE_DDNS - char ipstr[INET_ADDRSTRLEN]; - char dnsstr[INET_ADDRSTRLEN]; - char cmd[1000]; - uint32_t dns_addr; - uint32_t ip_addr; -#endif - char hashstr[ipcp_dir_hash_strlen() + 1]; - - assert(hash); - - ipcp_hash_str(hashstr, hash); - - if (shim_data_reg_add_entry(udp_data.shim_data, hash)) { - log_err("Failed to add " HASH_FMT " to local registry.", - HASH_VAL(hash)); - return -1; - } - -#ifdef HAVE_DDNS - /* register application with DNS server */ - - dns_addr = udp_data.dns_addr; - - if (dns_addr != 0) { - ip_addr = udp_data.ip_addr; - - if (inet_ntop(AF_INET, &ip_addr, - ipstr, INET_ADDRSTRLEN) == NULL) { - return -1; - } - - if (inet_ntop(AF_INET, &dns_addr, - dnsstr, INET_ADDRSTRLEN) == NULL) { - return -1; - } - - sprintf(cmd, "server %s\nupdate add %s %d A %s\nsend\nquit\n", - dnsstr, hashstr, DNS_TTL, ipstr); - - if (ddns_send(cmd)) { - shim_data_reg_del_entry(udp_data.shim_data, hash); - return -1; - } - } -#endif - log_dbg("Registered " HASH_FMT ".", HASH_VAL(hash)); - - return 0; -} - -static int ipcp_udp_unreg(const uint8_t * hash) -{ -#ifdef HAVE_DDNS - char dnsstr[INET_ADDRSTRLEN]; - /* max DNS name length + max IP length + max command length */ - char cmd[100]; - uint32_t dns_addr; -#endif - char hashstr[ipcp_dir_hash_strlen() + 1]; - - assert(hash); - - ipcp_hash_str(hashstr, hash); - -#ifdef HAVE_DDNS - /* unregister application with DNS server */ - - dns_addr = udp_data.dns_addr; - - if (dns_addr != 0) { - if (inet_ntop(AF_INET, &dns_addr, dnsstr, INET_ADDRSTRLEN) - == NULL) { - return -1; - } - sprintf(cmd, "server %s\nupdate delete %s A\nsend\nquit\n", - dnsstr, hashstr); - - ddns_send(cmd); - } -#endif - - shim_data_reg_del_entry(udp_data.shim_data, hash); - - log_dbg("Unregistered " HASH_FMT ".", HASH_VAL(hash)); - - return 0; -} - -static int ipcp_udp_query(const uint8_t * hash) -{ - uint32_t ip_addr = 0; - struct hostent * h; -#ifdef HAVE_DDNS - uint32_t dns_addr = 0; -#endif - char hashstr[ipcp_dir_hash_strlen() + 1]; - - assert(hash); - - ipcp_hash_str(hashstr, hash); - - if (shim_data_dir_has(udp_data.shim_data, hash)) - return 0; - -#ifdef HAVE_DDNS - dns_addr = udp_data.dns_addr; - - if (dns_addr != 0) { - ip_addr = ddns_resolve(hashstr, dns_addr); - if (ip_addr == 0) { - log_dbg("Could not resolve %s.", hashstr); - return -1; - } - } else { -#endif - h = gethostbyname(hashstr); - if (h == NULL) { - log_dbg("Could not resolve %s.", hashstr); - return -1; - } - - ip_addr = *((uint32_t *) (h->h_addr_list[0])); -#ifdef HAVE_DDNS - } -#endif - - if (shim_data_dir_add_entry(udp_data.shim_data, hash, ip_addr)) { - log_err("Failed to add directory entry."); - return -1; - } - - return 0; -} - -static int ipcp_udp_flow_alloc(int fd, - const uint8_t * dst, - qoscube_t cube) -{ - struct sockaddr_in r_saddr; /* server address */ - struct sockaddr_in f_saddr; /* flow */ - socklen_t f_saddr_len = sizeof(f_saddr); - int skfd; - uint32_t ip_addr = 0; - - log_dbg("Allocating flow to " HASH_FMT ".", HASH_VAL(dst)); - - assert(dst); - - if (cube != QOS_CUBE_BE) { - log_dbg("Unsupported QoS requested."); - return -1; - } - - skfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - - /* this socket is for the flow */ - memset((char *) &f_saddr, 0, sizeof(f_saddr)); - f_saddr.sin_family = AF_INET; - f_saddr.sin_addr.s_addr = local_ip; - f_saddr.sin_port = 0; - - if (bind(skfd, (struct sockaddr *) &f_saddr, sizeof(f_saddr)) < 0) { - close(skfd); - return -1; - } - - if (getsockname(skfd, (struct sockaddr *) &f_saddr, &f_saddr_len) < 0) { - log_err("Could not get address from fd."); - close(skfd); - return -1; - } - - if (!shim_data_dir_has(udp_data.shim_data, dst)) { - log_dbg("Could not resolve destination."); - close(skfd); - return -1; - } - ip_addr = (uint32_t) shim_data_dir_get_addr(udp_data.shim_data, dst); - - /* connect to server (store the remote IP address in the fd) */ - memset((char *) &r_saddr, 0, sizeof(r_saddr)); - r_saddr.sin_family = AF_INET; - r_saddr.sin_addr.s_addr = ip_addr; - r_saddr.sin_port = LISTEN_PORT; - - if (connect(skfd, (struct sockaddr *) &r_saddr, sizeof(r_saddr)) < 0) { - close(skfd); - return -1; - } - - pthread_rwlock_wrlock(&udp_data.flows_lock); - - udp_data.fd_to_uf[fd].udp = f_saddr.sin_port; - udp_data.fd_to_uf[fd].skfd = skfd; - udp_data.uf_to_fd[skfd] = fd; - - fset_add(udp_data.np1_flows, fd); - - pthread_rwlock_unlock(&udp_data.flows_lock); - - if (ipcp_udp_port_alloc(ip_addr, f_saddr.sin_port, dst, cube) < 0) { - pthread_rwlock_wrlock(&udp_data.flows_lock); - - udp_data.fd_to_uf[fd].udp = -1; - udp_data.fd_to_uf[fd].skfd = -1; - udp_data.uf_to_fd[skfd] = -1; - - pthread_rwlock_unlock(&udp_data.flows_lock); - close(skfd); - return -1; - } - - log_dbg("Flow pending on fd %d, UDP port %d.", - fd, ntohs(f_saddr.sin_port)); - - return 0; -} - -static int ipcp_udp_flow_alloc_resp(int fd, - int response) -{ - struct timespec ts = {0, FD_UPDATE_TIMEOUT * 1000}; - struct timespec abstime; - int skfd = -1; - struct sockaddr_in f_saddr; - struct sockaddr_in r_saddr; - socklen_t len = sizeof(r_saddr); - - if (response) - return 0; - - clock_gettime(PTHREAD_COND_CLOCK, &abstime); - - pthread_mutex_lock(&ipcpi.alloc_lock); - - while (ipcpi.alloc_id != fd && ipcp_get_state() == IPCP_OPERATIONAL) { - ts_add(&abstime, &ts, &abstime); - pthread_cond_timedwait(&ipcpi.alloc_cond, - &ipcpi.alloc_lock, - &abstime); - } - - if (ipcp_get_state() != IPCP_OPERATIONAL) { - pthread_mutex_unlock(&ipcpi.alloc_lock); - return -1; - } - - ipcpi.alloc_id = -1; - pthread_cond_broadcast(&ipcpi.alloc_cond); - - pthread_mutex_unlock(&ipcpi.alloc_lock); - - pthread_rwlock_rdlock(&udp_data.flows_lock); - - skfd = udp_data.fd_to_uf[fd].skfd; - - pthread_rwlock_unlock(&udp_data.flows_lock); - - if (getsockname(skfd, (struct sockaddr *) &f_saddr, &len) < 0) { - log_dbg("Socket with fd %d has no address.", skfd); - return -1; - } - - if (getpeername(skfd, (struct sockaddr *) &r_saddr, &len) < 0) { - log_dbg("Socket with fd %d has no peer.", skfd); - return -1; - } - - pthread_rwlock_rdlock(&udp_data.flows_lock); - - set_fd(skfd); - - fset_add(udp_data.np1_flows, fd); - - pthread_rwlock_unlock(&udp_data.flows_lock); - - if (ipcp_udp_port_alloc_resp(r_saddr.sin_addr.s_addr, f_saddr.sin_port, - r_saddr.sin_port, response) < 0) { - pthread_rwlock_rdlock(&udp_data.flows_lock); - clr_fd(skfd); - pthread_rwlock_unlock(&udp_data.flows_lock); - return -1; - } - - log_dbg("Accepted flow, fd %d on UDP port %d.", - fd, ntohs(f_saddr.sin_port)); - - return 0; -} - -static int ipcp_udp_flow_dealloc(int fd) -{ - int skfd = -1; - - ipcp_flow_fini(fd); - - pthread_rwlock_wrlock(&udp_data.flows_lock); - - fset_del(udp_data.np1_flows, fd); - - skfd = udp_data.fd_to_uf[fd].skfd; - - udp_data.uf_to_fd[skfd] = -1; - udp_data.fd_to_uf[fd].udp = -1; - udp_data.fd_to_uf[fd].skfd = -1; - - close(skfd); - - pthread_rwlock_unlock(&udp_data.flows_lock); - pthread_rwlock_rdlock(&udp_data.flows_lock); - - clr_fd(skfd); - - pthread_rwlock_unlock(&udp_data.flows_lock); - - flow_dealloc(fd); - - log_dbg("Flow with fd %d deallocated.", fd); - - return 0; -} - -static struct ipcp_ops udp_ops = { - .ipcp_bootstrap = ipcp_udp_bootstrap, - .ipcp_enroll = NULL, - .ipcp_connect = NULL, - .ipcp_disconnect = NULL, - .ipcp_reg = ipcp_udp_reg, - .ipcp_unreg = ipcp_udp_unreg, - .ipcp_query = ipcp_udp_query, - .ipcp_flow_alloc = ipcp_udp_flow_alloc, - .ipcp_flow_alloc_resp = ipcp_udp_flow_alloc_resp, - .ipcp_flow_dealloc = ipcp_udp_flow_dealloc -}; - -int main(int argc, - char * argv[]) -{ - if (ipcp_init(argc, argv, &udp_ops) < 0) { - ipcp_create_r(getpid(), -1); - exit(EXIT_FAILURE); - } - - if (udp_data_init() < 0) { - log_err("Failed to init shim-udp data."); - ipcp_create_r(getpid(), -1); - ipcp_fini(); - exit(EXIT_FAILURE); - } - - if (ipcp_boot() < 0) { - log_err("Failed to boot IPCP."); - ipcp_create_r(getpid(), -1); - udp_data_fini(); - ipcp_fini(); - exit(EXIT_FAILURE); - } - - if (ipcp_create_r(getpid(), 0)) { - log_err("Failed to notify IRMd we are initialized."); - ipcp_set_state(IPCP_NULL); - ipcp_shutdown(); - udp_data_fini(); - ipcp_fini(); - exit(EXIT_FAILURE); - } - - ipcp_shutdown(); - - if (ipcp_get_state() == IPCP_SHUTDOWN) { - pthread_cancel(udp_data.sduloop); - pthread_cancel(udp_data.handler); - pthread_cancel(udp_data.sdu_reader); - - pthread_join(udp_data.sduloop, NULL); - pthread_join(udp_data.handler, NULL); - pthread_join(udp_data.sdu_reader, NULL); - } - - udp_data_fini(); - - ipcp_fini(); - - exit(EXIT_SUCCESS); -} diff --git a/src/ipcpd/udp/CMakeLists.txt b/src/ipcpd/udp/CMakeLists.txt new file mode 100644 index 00000000..20c1b58e --- /dev/null +++ b/src/ipcpd/udp/CMakeLists.txt @@ -0,0 +1,60 @@ +get_filename_component(CURRENT_SOURCE_PARENT_DIR + ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY) +get_filename_component(CURRENT_BINARY_PARENT_DIR + ${CMAKE_CURRENT_BINARY_DIR} DIRECTORY) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +include_directories(${CURRENT_SOURCE_PARENT_DIR}) +include_directories(${CURRENT_BINARY_PARENT_DIR}) + +include_directories(${CMAKE_SOURCE_DIR}/include) +include_directories(${CMAKE_BINARY_DIR}/include) + +set(IPCP_UDP_TARGET ipcpd-udp CACHE INTERNAL "") + +set(UDP_SOURCES + # Add source files here + ${CMAKE_CURRENT_SOURCE_DIR}/main.c) + +add_executable(ipcpd-udp ${UDP_SOURCES} ${IPCP_SOURCES}) +target_link_libraries(ipcpd-udp LINK_PUBLIC ouroboros-dev) + +# Find the nsupdate executable +find_program(NSUPDATE_EXECUTABLE + NAMES nsupdate + DOC "The nsupdate tool that enables DDNS") + +# Find the nslookup executable +find_program(NSLOOKUP_EXECUTABLE + NAMES nslookup + DOC "The nslookup tool that resolves DNS names") + +mark_as_advanced(NSLOOKUP_EXECUTABLE NSUPDATE_EXECUTABLE) + +if (NSLOOKUP_EXECUTABLE AND NSUPDATE_EXECUTABLE) + set(DISABLE_DDNS FALSE CACHE BOOL "Disable DDNS support") + if (NOT DISABLE_DNS) + message(STATUS "DDNS support enabled") + set(HAVE_DDNS TRUE CACHE INTERNAL "") + else () + message(STATUS "DDNS support disabled by user") + unset(HAVE_DDNS CACHE) + endif () +else () + if (NSLOOKUP_EXECUTABLE) + message(STATUS "Install nsupdate to enable DDNS support") + elseif (NSUPDATE_EXECUTABLE) + message(STATUS "Install nslookup to enable DDNS support") + else () + message(STATUS "Install nslookup and nsupdate to enable DDNS support") + endif () +endif () + +include(AddCompileFlags) +if (CMAKE_BUILD_TYPE MATCHES "Debug*") + add_compile_flags(ipcpd-udp -DCONFIG_OUROBOROS_DEBUG) +endif () + +install(TARGETS ipcpd-udp RUNTIME DESTINATION sbin) diff --git a/src/ipcpd/udp/main.c b/src/ipcpd/udp/main.c new file mode 100644 index 00000000..c54e6624 --- /dev/null +++ b/src/ipcpd/udp/main.c @@ -0,0 +1,1167 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2017 + * + * IPC process over UDP + * + * Dimitri Staessens + * Sander Vrijders + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#define _POSIX_C_SOURCE 200112L + +#include "config.h" + +#define OUROBOROS_PREFIX "ipcpd/udp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipcp.h" +#include "shim-data.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FLOW_REQ 1 +#define FLOW_REPLY 2 + +#define THIS_TYPE IPCP_UDP +#define LISTEN_PORT htons(0x0D1F) +#define SHIM_UDP_BUF_SIZE 256 +#define SHIM_UDP_MSG_SIZE 256 +#define SHIM_UDP_MAX_SDU_SIZE 8980 +#define DNS_TTL 86400 +#define FD_UPDATE_TIMEOUT 100 /* microseconds */ + +#define local_ip (udp_data.s_saddr.sin_addr.s_addr) + +#define UDP_MAX_PORTS 0xFFFF + +struct mgmt_msg { + uint16_t src_udp_port; + uint16_t dst_udp_port; + uint8_t code; + uint8_t qoscube; + uint8_t response; +} __attribute__((packed)); + +struct uf { + int udp; + int skfd; +}; + +struct { + struct shim_data * shim_data; + + uint32_t ip_addr; + uint32_t dns_addr; + /* listen server */ + struct sockaddr_in s_saddr; + int s_fd; + + fset_t * np1_flows; + fqueue_t * fq; + fd_set flow_fd_s; + /* bidir mappings of (n - 1) file descriptor to (n) flow descriptor */ + int uf_to_fd[FD_SETSIZE]; + struct uf fd_to_uf[SYS_MAX_FLOWS]; + pthread_rwlock_t flows_lock; + + pthread_t sduloop; + pthread_t handler; + pthread_t sdu_reader; + + bool fd_set_mod; + pthread_cond_t fd_set_cond; + pthread_mutex_t fd_set_lock; +} udp_data; + +static int udp_data_init(void) +{ + int i; + + for (i = 0; i < FD_SETSIZE; ++i) + udp_data.uf_to_fd[i] = -1; + + for (i = 0; i < SYS_MAX_FLOWS; ++i) { + udp_data.fd_to_uf[i].skfd = -1; + udp_data.fd_to_uf[i].udp = -1; + } + + FD_ZERO(&udp_data.flow_fd_s); + + udp_data.np1_flows = fset_create(); + if (udp_data.np1_flows == NULL) + return -ENOMEM; + + udp_data.fq = fqueue_create(); + if (udp_data.fq == NULL) { + fset_destroy(udp_data.np1_flows); + return -ENOMEM; + } + + udp_data.shim_data = shim_data_create(); + if (udp_data.shim_data == NULL) { + fqueue_destroy(udp_data.fq); + fset_destroy(udp_data.np1_flows); + return -ENOMEM; + } + + pthread_rwlock_init(&udp_data.flows_lock, NULL); + pthread_cond_init(&udp_data.fd_set_cond, NULL); + pthread_mutex_init(&udp_data.fd_set_lock, NULL); + + return 0; +} + +static void udp_data_fini(void) +{ + fset_destroy(udp_data.np1_flows); + fqueue_destroy(udp_data.fq); + + shim_data_destroy(udp_data.shim_data); + + pthread_rwlock_destroy(&udp_data.flows_lock); + pthread_mutex_destroy(&udp_data.fd_set_lock); + pthread_cond_destroy(&udp_data.fd_set_cond); +} + +static void set_fd(int fd) +{ + pthread_mutex_lock(&udp_data.fd_set_lock); + + udp_data.fd_set_mod = true; + FD_SET(fd, &udp_data.flow_fd_s); + + while (udp_data.fd_set_mod) + pthread_cond_wait(&udp_data.fd_set_cond, &udp_data.fd_set_lock); + + pthread_mutex_unlock(&udp_data.fd_set_lock); +} + +static void clr_fd(int fd) +{ + pthread_mutex_lock(&udp_data.fd_set_lock); + + udp_data.fd_set_mod = true; + FD_CLR(fd, &udp_data.flow_fd_s); + + while (udp_data.fd_set_mod) + pthread_cond_wait(&udp_data.fd_set_cond, &udp_data.fd_set_lock); + + pthread_mutex_unlock(&udp_data.fd_set_lock); +} + +static int send_shim_udp_msg(uint8_t * buf, + size_t len, + uint32_t dst_ip_addr) +{ + struct sockaddr_in r_saddr; + + memset((char *)&r_saddr, 0, sizeof(r_saddr)); + r_saddr.sin_family = AF_INET; + r_saddr.sin_addr.s_addr = dst_ip_addr; + r_saddr.sin_port = LISTEN_PORT; + + if (sendto(udp_data.s_fd, buf, len, 0, + (struct sockaddr *) &r_saddr, + sizeof(r_saddr)) == -1) { + log_err("Failed to send message."); + return -1; + } + + return 0; +} + +static int ipcp_udp_port_alloc(uint32_t dst_ip_addr, + uint16_t src_udp_port, + const uint8_t * dst, + qoscube_t cube) +{ + uint8_t * buf; + struct mgmt_msg * msg; + size_t len; + int ret; + + len = sizeof(*msg) + ipcp_dir_hash_len(); + + buf = malloc(len); + if (buf == NULL) + return -1; + + msg = (struct mgmt_msg *) buf; + msg->code = FLOW_REQ; + msg->src_udp_port = src_udp_port; + msg->qoscube = cube; + + memcpy(msg + 1, dst, ipcp_dir_hash_len()); + + ret = send_shim_udp_msg(buf, len, dst_ip_addr); + + free(buf); + + return ret; +} + +static int ipcp_udp_port_alloc_resp(uint32_t dst_ip_addr, + uint16_t src_udp_port, + uint16_t dst_udp_port, + int response) +{ + uint8_t * buf; + struct mgmt_msg * msg; + int ret; + + buf = malloc(sizeof(*msg)); + if (buf == NULL) + return -1; + + msg = (struct mgmt_msg *) buf; + msg->code = FLOW_REPLY; + msg->src_udp_port = src_udp_port; + msg->dst_udp_port = dst_udp_port; + msg->response = response; + + ret = send_shim_udp_msg(buf, sizeof(*msg), dst_ip_addr); + + free(buf); + + return ret; +} + +static int ipcp_udp_port_req(struct sockaddr_in * c_saddr, + const uint8_t * dst, + qoscube_t cube) +{ + struct timespec ts = {0, FD_UPDATE_TIMEOUT * 1000}; + struct timespec abstime; + struct sockaddr_in f_saddr; + socklen_t f_saddr_len = sizeof(f_saddr); + int skfd; + int fd; + + log_dbg("Port request arrived from UDP port %d", + ntohs(c_saddr->sin_port)); + + if ((skfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + log_err("Could not create UDP socket."); + return -1; + } + + memset((char *) &f_saddr, 0, sizeof(f_saddr)); + f_saddr.sin_family = AF_INET; + f_saddr.sin_addr.s_addr = local_ip; + f_saddr.sin_port = 0; + + if (bind(skfd, (struct sockaddr *) &f_saddr, sizeof(f_saddr)) < 0) { + log_err("Could not bind to socket."); + close(skfd); + return -1; + } + + if (getsockname(skfd, (struct sockaddr *) &f_saddr, &f_saddr_len) < 0) { + log_err("Could not get address from fd."); + return -1; + } + + /* connect stores the remote address in the file descriptor */ + if (connect(skfd, (struct sockaddr *) c_saddr, sizeof(*c_saddr)) < 0) { + log_err("Could not connect to remote UDP client."); + close(skfd); + return -1; + } + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + + pthread_mutex_lock(&ipcpi.alloc_lock); + + while (ipcpi.alloc_id != -1 && ipcp_get_state() == IPCP_OPERATIONAL) { + ts_add(&abstime, &ts, &abstime); + pthread_cond_timedwait(&ipcpi.alloc_cond, + &ipcpi.alloc_lock, + &abstime); + } + + if (ipcp_get_state() != IPCP_OPERATIONAL) { + log_dbg("Won't allocate over non-operational IPCP."); + pthread_mutex_unlock(&ipcpi.alloc_lock); + return -1; + } + + /* reply to IRM */ + fd = ipcp_flow_req_arr(getpid(), dst, ipcp_dir_hash_len(), cube); + if (fd < 0) { + pthread_mutex_unlock(&ipcpi.alloc_lock); + log_err("Could not get new flow from IRMd."); + close(skfd); + return -1; + } + + pthread_rwlock_wrlock(&udp_data.flows_lock); + + udp_data.uf_to_fd[skfd] = fd; + udp_data.fd_to_uf[fd].skfd = skfd; + udp_data.fd_to_uf[fd].udp = f_saddr.sin_port; + + pthread_rwlock_unlock(&udp_data.flows_lock); + + ipcpi.alloc_id = fd; + pthread_cond_broadcast(&ipcpi.alloc_cond); + + pthread_mutex_unlock(&ipcpi.alloc_lock); + + log_dbg("Pending allocation request, fd %d, UDP port (%d, %d).", + fd, ntohs(f_saddr.sin_port), ntohs(c_saddr->sin_port)); + + return 0; +} + +/* returns the n flow descriptor */ +static int udp_port_to_fd(int udp_port) +{ + int i; + + for (i = 0; i < SYS_MAX_FLOWS; ++i) + if (udp_data.fd_to_uf[i].udp == udp_port) + return i; + + return -1; +} + +static int ipcp_udp_port_alloc_reply(uint16_t src_udp_port, + uint16_t dst_udp_port, + int response) +{ + int fd = -1; + int ret = 0; + int skfd = -1; + + struct sockaddr_in t_saddr; + socklen_t t_saddr_len = sizeof(t_saddr); + + log_dbg("Received reply for flow on udp port %d.", + ntohs(dst_udp_port)); + + pthread_rwlock_rdlock(&udp_data.flows_lock); + + fd = udp_port_to_fd(dst_udp_port); + skfd = udp_data.fd_to_uf[fd].skfd; + + pthread_rwlock_unlock(&udp_data.flows_lock); + + /* get the original address with the LISTEN PORT */ + if (getpeername(skfd, (struct sockaddr *) &t_saddr, &t_saddr_len) < 0) { + log_dbg("Flow with fd %d has no peer.", fd); + return -1; + } + + /* connect to the flow udp port */ + t_saddr.sin_port = src_udp_port; + + if (connect(skfd, (struct sockaddr *) &t_saddr, sizeof(t_saddr)) < 0) { + close(skfd); + return -1; + } + + pthread_rwlock_rdlock(&udp_data.flows_lock); + + set_fd(skfd); + + pthread_rwlock_unlock(&udp_data.flows_lock); + + if (ipcp_flow_alloc_reply(fd, response) < 0) + return -1; + + log_dbg("Flow allocation completed, UDP ports: (%d, %d).", + ntohs(dst_udp_port), ntohs(src_udp_port)); + + return ret; +} + +static void * ipcp_udp_listener(void * o) +{ + uint8_t buf[SHIM_UDP_MSG_SIZE]; + ssize_t n = 0; + struct sockaddr_in c_saddr; + int sfd = udp_data.s_fd; + + (void) o; + + while (true) { + struct mgmt_msg * msg = NULL; + + memset(&buf, 0, SHIM_UDP_MSG_SIZE); + n = sizeof(c_saddr); + n = recvfrom(sfd, buf, SHIM_UDP_MSG_SIZE, 0, + (struct sockaddr *) &c_saddr, (unsigned *) &n); + if (n < 0) + continue; + + /* flow alloc request from other host */ + if (gethostbyaddr((const char *) &c_saddr.sin_addr.s_addr, + sizeof(c_saddr.sin_addr.s_addr), AF_INET) + == NULL) + continue; + + msg = (struct mgmt_msg *) buf; + + switch (msg->code) { + case FLOW_REQ: + c_saddr.sin_port = msg->src_udp_port; + ipcp_udp_port_req(&c_saddr, + (uint8_t *) (msg + 1), + msg->qoscube); + break; + case FLOW_REPLY: + ipcp_udp_port_alloc_reply(msg->src_udp_port, + msg->dst_udp_port, + msg->response); + break; + default: + log_err("Unknown message received %d.", msg->code); + continue; + } + + c_saddr.sin_port = LISTEN_PORT; + } + + return 0; +} + +static void * ipcp_udp_sdu_reader(void * o) +{ + ssize_t n; + int skfd; + int fd; + /* FIXME: avoid this copy */ + char buf[SHIM_UDP_MAX_SDU_SIZE]; + struct sockaddr_in r_saddr; + struct timeval tv = {0, FD_UPDATE_TIMEOUT}; + fd_set read_fds; + int flags; + + (void) o; + + while (true) { + pthread_rwlock_rdlock(&udp_data.flows_lock); + pthread_mutex_lock(&udp_data.fd_set_lock); + + read_fds = udp_data.flow_fd_s; + udp_data.fd_set_mod = false; + pthread_cond_broadcast(&udp_data.fd_set_cond); + + pthread_mutex_unlock(&udp_data.fd_set_lock); + pthread_rwlock_unlock(&udp_data.flows_lock); + + if (select(FD_SETSIZE, &read_fds, NULL, NULL, &tv) <= 0) + continue; + + for (skfd = 0; skfd < FD_SETSIZE; ++skfd) { + if (!FD_ISSET(skfd, &read_fds)) + continue; + flags = fcntl(skfd, F_GETFL, 0); + fcntl(skfd, F_SETFL, flags | O_NONBLOCK); + n = sizeof(r_saddr); + if ((n = recvfrom(skfd, + &buf, + SHIM_UDP_MAX_SDU_SIZE, + 0, + (struct sockaddr *) &r_saddr, + (unsigned *) &n)) <= 0) + continue; + + pthread_rwlock_rdlock(&udp_data.flows_lock); + + fd = udp_data.uf_to_fd[skfd]; + + pthread_rwlock_unlock(&udp_data.flows_lock); + + flow_write(fd, buf, n); + } + } + + return (void *) 0; +} + +static void * ipcp_udp_sdu_loop(void * o) +{ + int fd; + struct shm_du_buff * sdb; + + (void) o; + + while (true) { + fevent(udp_data.np1_flows, udp_data.fq, NULL); + while ((fd = fqueue_next(udp_data.fq)) >= 0) { + if (ipcp_flow_read(fd, &sdb)) { + log_err("Bad read from fd %d.", fd); + continue; + } + + pthread_rwlock_rdlock(&udp_data.flows_lock); + + fd = udp_data.fd_to_uf[fd].skfd; + + pthread_rwlock_unlock(&udp_data.flows_lock); + + pthread_cleanup_push((void (*)(void *)) ipcp_sdb_release, + (void *) sdb); + + if (send(fd, shm_du_buff_head(sdb), + shm_du_buff_tail(sdb) - shm_du_buff_head(sdb), + 0) < 0) + log_err("Failed to send SDU."); + + pthread_cleanup_pop(true); + } + } + + return (void *) 1; +} + +static int ipcp_udp_bootstrap(const struct ipcp_config * conf) +{ + struct sockaddr_in s_saddr; + char ipstr[INET_ADDRSTRLEN]; + char dnsstr[INET_ADDRSTRLEN]; + int enable = 1; + int fd = -1; + + assert(conf); + assert(conf->type == THIS_TYPE); + + if (inet_ntop(AF_INET, + &conf->ip_addr, + ipstr, + INET_ADDRSTRLEN) == NULL) { + log_err("Failed to convert IP address"); + return -1; + } + + if (conf->dns_addr != 0) { + if (inet_ntop(AF_INET, + &conf->dns_addr, + dnsstr, + INET_ADDRSTRLEN) == NULL) { + log_err("Failed to convert DNS address"); + return -1; + } +#ifndef HAVE_DDNS + log_warn("DNS disabled at compile time, address ignored"); +#endif + } else { + strcpy(dnsstr, "not set"); + } + + /* UDP listen server */ + if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { + log_err("Can't create socket."); + goto fail_socket; + } + + if (setsockopt(fd, + SOL_SOCKET, + SO_REUSEADDR, + &enable, + sizeof(int)) < 0) + log_warn("Failed to set SO_REUSEADDR."); + + memset((char *) &s_saddr, 0, sizeof(s_saddr)); + udp_data.s_saddr.sin_family = AF_INET; + udp_data.s_saddr.sin_addr.s_addr = conf->ip_addr; + udp_data.s_saddr.sin_port = LISTEN_PORT; + + if (bind(fd, + (struct sockaddr *) &udp_data.s_saddr, + sizeof(udp_data.s_saddr)) < 0) { + log_err("Couldn't bind to %s.", ipstr); + goto fail_bind; + } + + udp_data.s_fd = fd; + udp_data.ip_addr = conf->ip_addr; + udp_data.dns_addr = conf->dns_addr; + + FD_CLR(udp_data.s_fd, &udp_data.flow_fd_s); + + ipcp_set_state(IPCP_OPERATIONAL); + + if (pthread_create(&udp_data.handler, + NULL, + ipcp_udp_listener, + NULL)) { + ipcp_set_state(IPCP_INIT); + goto fail_bind; + } + + if (pthread_create(&udp_data.sdu_reader, + NULL, + ipcp_udp_sdu_reader, + NULL)) { + ipcp_set_state(IPCP_INIT); + goto fail_sdu_reader; + } + + if (pthread_create(&udp_data.sduloop, + NULL, + ipcp_udp_sdu_loop, + NULL)) { + ipcp_set_state(IPCP_INIT); + goto fail_sduloop; + } + + log_dbg("Bootstrapped IPCP over UDP with pid %d.", getpid()); + log_dbg("Bound to IP address %s.", ipstr); + log_dbg("DNS server address is %s.", dnsstr); + + return 0; + + fail_sduloop: + pthread_cancel(udp_data.sdu_reader); + pthread_join(udp_data.sdu_reader, NULL); + fail_sdu_reader: + pthread_cancel(udp_data.handler); + pthread_join(udp_data.handler, NULL); + fail_bind: + close(fd); + fail_socket: + return -1; +} + +#ifdef HAVE_DDNS +/* FIXME: Dependency on nsupdate to be removed in the end */ +/* NOTE: Disgusted with this crap */ +static int ddns_send(char * cmd) +{ + pid_t pid = -1; + int wstatus; + int pipe_fd[2]; + char * argv[] = {NSUPDATE_EXEC, 0}; + char * envp[] = {0}; + + if (pipe(pipe_fd)) { + log_err("Failed to create pipe."); + return -1; + } + + pid = fork(); + if (pid == -1) { + log_err("Failed to fork."); + return -1; + } + + if (pid == 0) { + close(pipe_fd[1]); + dup2(pipe_fd[0], 0); + execve(argv[0], &argv[0], envp); + } + + close(pipe_fd[0]); + + if (write(pipe_fd[1], cmd, strlen(cmd)) == -1) { + log_err("Failed to communicate with nsupdate."); + close(pipe_fd[1]); + return -1; + } + + waitpid(pid, &wstatus, 0); + if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) + log_dbg("Succesfully communicated with DNS server."); + else + log_err("Failed to register with DNS server."); + + close(pipe_fd[1]); + return 0; +} + +static uint32_t ddns_resolve(char * name, + uint32_t dns_addr) +{ + pid_t pid = -1; + int wstatus; + int pipe_fd[2]; + char dnsstr[INET_ADDRSTRLEN]; + char buf[SHIM_UDP_BUF_SIZE]; + ssize_t count = 0; + char * substr = NULL; + char * substr2 = NULL; + char * addr_str = "Address:"; + uint32_t ip_addr = 0; + + if (inet_ntop(AF_INET, &dns_addr, dnsstr, INET_ADDRSTRLEN) == NULL) + return 0; + + if (pipe(pipe_fd)) { + log_err("Failed to create pipe."); + return 0; + } + + pid = fork(); + if (pid == -1) { + log_err("Failed to fork."); + return 0; + } + + if (pid == 0) { + char * argv[] = {NSLOOKUP_EXEC, name, dnsstr, 0}; + char * envp[] = {0}; + + close(pipe_fd[0]); + dup2(pipe_fd[1], 1); + execve(argv[0], &argv[0], envp); + } + + close(pipe_fd[1]); + + count = read(pipe_fd[0], buf, SHIM_UDP_BUF_SIZE); + if (count <= 0) { + log_err("Failed to communicate with nslookup."); + close(pipe_fd[0]); + return 0; + } + + close(pipe_fd[0]); + + waitpid(pid, &wstatus, 0); + if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) + log_dbg("Succesfully communicated with nslookup."); + else + log_err("Failed to resolve DNS address."); + + buf[count] = '\0'; + substr = strtok(buf, "\n"); + while (substr != NULL) { + substr2 = substr; + substr = strtok(NULL, "\n"); + } + + if (substr2 == NULL || strstr(substr2, addr_str) == NULL) { + log_err("Failed to resolve DNS address."); + return 0; + } + + if (inet_pton(AF_INET, substr2 + strlen(addr_str) + 1, &ip_addr) != 1) { + log_err("Failed to resolve DNS address."); + return 0; + } + + return ip_addr; +} +#endif + +static int ipcp_udp_reg(const uint8_t * hash) +{ +#ifdef HAVE_DDNS + char ipstr[INET_ADDRSTRLEN]; + char dnsstr[INET_ADDRSTRLEN]; + char cmd[1000]; + uint32_t dns_addr; + uint32_t ip_addr; +#endif + char hashstr[ipcp_dir_hash_strlen() + 1]; + + assert(hash); + + ipcp_hash_str(hashstr, hash); + + if (shim_data_reg_add_entry(udp_data.shim_data, hash)) { + log_err("Failed to add " HASH_FMT " to local registry.", + HASH_VAL(hash)); + return -1; + } + +#ifdef HAVE_DDNS + /* register application with DNS server */ + + dns_addr = udp_data.dns_addr; + + if (dns_addr != 0) { + ip_addr = udp_data.ip_addr; + + if (inet_ntop(AF_INET, &ip_addr, + ipstr, INET_ADDRSTRLEN) == NULL) { + return -1; + } + + if (inet_ntop(AF_INET, &dns_addr, + dnsstr, INET_ADDRSTRLEN) == NULL) { + return -1; + } + + sprintf(cmd, "server %s\nupdate add %s %d A %s\nsend\nquit\n", + dnsstr, hashstr, DNS_TTL, ipstr); + + if (ddns_send(cmd)) { + shim_data_reg_del_entry(udp_data.shim_data, hash); + return -1; + } + } +#endif + log_dbg("Registered " HASH_FMT ".", HASH_VAL(hash)); + + return 0; +} + +static int ipcp_udp_unreg(const uint8_t * hash) +{ +#ifdef HAVE_DDNS + char dnsstr[INET_ADDRSTRLEN]; + /* max DNS name length + max IP length + max command length */ + char cmd[100]; + uint32_t dns_addr; +#endif + char hashstr[ipcp_dir_hash_strlen() + 1]; + + assert(hash); + + ipcp_hash_str(hashstr, hash); + +#ifdef HAVE_DDNS + /* unregister application with DNS server */ + + dns_addr = udp_data.dns_addr; + + if (dns_addr != 0) { + if (inet_ntop(AF_INET, &dns_addr, dnsstr, INET_ADDRSTRLEN) + == NULL) { + return -1; + } + sprintf(cmd, "server %s\nupdate delete %s A\nsend\nquit\n", + dnsstr, hashstr); + + ddns_send(cmd); + } +#endif + + shim_data_reg_del_entry(udp_data.shim_data, hash); + + log_dbg("Unregistered " HASH_FMT ".", HASH_VAL(hash)); + + return 0; +} + +static int ipcp_udp_query(const uint8_t * hash) +{ + uint32_t ip_addr = 0; + struct hostent * h; +#ifdef HAVE_DDNS + uint32_t dns_addr = 0; +#endif + char hashstr[ipcp_dir_hash_strlen() + 1]; + + assert(hash); + + ipcp_hash_str(hashstr, hash); + + if (shim_data_dir_has(udp_data.shim_data, hash)) + return 0; + +#ifdef HAVE_DDNS + dns_addr = udp_data.dns_addr; + + if (dns_addr != 0) { + ip_addr = ddns_resolve(hashstr, dns_addr); + if (ip_addr == 0) { + log_dbg("Could not resolve %s.", hashstr); + return -1; + } + } else { +#endif + h = gethostbyname(hashstr); + if (h == NULL) { + log_dbg("Could not resolve %s.", hashstr); + return -1; + } + + ip_addr = *((uint32_t *) (h->h_addr_list[0])); +#ifdef HAVE_DDNS + } +#endif + + if (shim_data_dir_add_entry(udp_data.shim_data, hash, ip_addr)) { + log_err("Failed to add directory entry."); + return -1; + } + + return 0; +} + +static int ipcp_udp_flow_alloc(int fd, + const uint8_t * dst, + qoscube_t cube) +{ + struct sockaddr_in r_saddr; /* server address */ + struct sockaddr_in f_saddr; /* flow */ + socklen_t f_saddr_len = sizeof(f_saddr); + int skfd; + uint32_t ip_addr = 0; + + log_dbg("Allocating flow to " HASH_FMT ".", HASH_VAL(dst)); + + assert(dst); + + if (cube != QOS_CUBE_BE) { + log_dbg("Unsupported QoS requested."); + return -1; + } + + skfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + /* this socket is for the flow */ + memset((char *) &f_saddr, 0, sizeof(f_saddr)); + f_saddr.sin_family = AF_INET; + f_saddr.sin_addr.s_addr = local_ip; + f_saddr.sin_port = 0; + + if (bind(skfd, (struct sockaddr *) &f_saddr, sizeof(f_saddr)) < 0) { + close(skfd); + return -1; + } + + if (getsockname(skfd, (struct sockaddr *) &f_saddr, &f_saddr_len) < 0) { + log_err("Could not get address from fd."); + close(skfd); + return -1; + } + + if (!shim_data_dir_has(udp_data.shim_data, dst)) { + log_dbg("Could not resolve destination."); + close(skfd); + return -1; + } + ip_addr = (uint32_t) shim_data_dir_get_addr(udp_data.shim_data, dst); + + /* connect to server (store the remote IP address in the fd) */ + memset((char *) &r_saddr, 0, sizeof(r_saddr)); + r_saddr.sin_family = AF_INET; + r_saddr.sin_addr.s_addr = ip_addr; + r_saddr.sin_port = LISTEN_PORT; + + if (connect(skfd, (struct sockaddr *) &r_saddr, sizeof(r_saddr)) < 0) { + close(skfd); + return -1; + } + + pthread_rwlock_wrlock(&udp_data.flows_lock); + + udp_data.fd_to_uf[fd].udp = f_saddr.sin_port; + udp_data.fd_to_uf[fd].skfd = skfd; + udp_data.uf_to_fd[skfd] = fd; + + fset_add(udp_data.np1_flows, fd); + + pthread_rwlock_unlock(&udp_data.flows_lock); + + if (ipcp_udp_port_alloc(ip_addr, f_saddr.sin_port, dst, cube) < 0) { + pthread_rwlock_wrlock(&udp_data.flows_lock); + + udp_data.fd_to_uf[fd].udp = -1; + udp_data.fd_to_uf[fd].skfd = -1; + udp_data.uf_to_fd[skfd] = -1; + + pthread_rwlock_unlock(&udp_data.flows_lock); + close(skfd); + return -1; + } + + log_dbg("Flow pending on fd %d, UDP port %d.", + fd, ntohs(f_saddr.sin_port)); + + return 0; +} + +static int ipcp_udp_flow_alloc_resp(int fd, + int response) +{ + struct timespec ts = {0, FD_UPDATE_TIMEOUT * 1000}; + struct timespec abstime; + int skfd = -1; + struct sockaddr_in f_saddr; + struct sockaddr_in r_saddr; + socklen_t len = sizeof(r_saddr); + + if (response) + return 0; + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + + pthread_mutex_lock(&ipcpi.alloc_lock); + + while (ipcpi.alloc_id != fd && ipcp_get_state() == IPCP_OPERATIONAL) { + ts_add(&abstime, &ts, &abstime); + pthread_cond_timedwait(&ipcpi.alloc_cond, + &ipcpi.alloc_lock, + &abstime); + } + + if (ipcp_get_state() != IPCP_OPERATIONAL) { + pthread_mutex_unlock(&ipcpi.alloc_lock); + return -1; + } + + ipcpi.alloc_id = -1; + pthread_cond_broadcast(&ipcpi.alloc_cond); + + pthread_mutex_unlock(&ipcpi.alloc_lock); + + pthread_rwlock_rdlock(&udp_data.flows_lock); + + skfd = udp_data.fd_to_uf[fd].skfd; + + pthread_rwlock_unlock(&udp_data.flows_lock); + + if (getsockname(skfd, (struct sockaddr *) &f_saddr, &len) < 0) { + log_dbg("Socket with fd %d has no address.", skfd); + return -1; + } + + if (getpeername(skfd, (struct sockaddr *) &r_saddr, &len) < 0) { + log_dbg("Socket with fd %d has no peer.", skfd); + return -1; + } + + pthread_rwlock_rdlock(&udp_data.flows_lock); + + set_fd(skfd); + + fset_add(udp_data.np1_flows, fd); + + pthread_rwlock_unlock(&udp_data.flows_lock); + + if (ipcp_udp_port_alloc_resp(r_saddr.sin_addr.s_addr, f_saddr.sin_port, + r_saddr.sin_port, response) < 0) { + pthread_rwlock_rdlock(&udp_data.flows_lock); + clr_fd(skfd); + pthread_rwlock_unlock(&udp_data.flows_lock); + return -1; + } + + log_dbg("Accepted flow, fd %d on UDP port %d.", + fd, ntohs(f_saddr.sin_port)); + + return 0; +} + +static int ipcp_udp_flow_dealloc(int fd) +{ + int skfd = -1; + + ipcp_flow_fini(fd); + + pthread_rwlock_wrlock(&udp_data.flows_lock); + + fset_del(udp_data.np1_flows, fd); + + skfd = udp_data.fd_to_uf[fd].skfd; + + udp_data.uf_to_fd[skfd] = -1; + udp_data.fd_to_uf[fd].udp = -1; + udp_data.fd_to_uf[fd].skfd = -1; + + close(skfd); + + pthread_rwlock_unlock(&udp_data.flows_lock); + pthread_rwlock_rdlock(&udp_data.flows_lock); + + clr_fd(skfd); + + pthread_rwlock_unlock(&udp_data.flows_lock); + + flow_dealloc(fd); + + log_dbg("Flow with fd %d deallocated.", fd); + + return 0; +} + +static struct ipcp_ops udp_ops = { + .ipcp_bootstrap = ipcp_udp_bootstrap, + .ipcp_enroll = NULL, + .ipcp_connect = NULL, + .ipcp_disconnect = NULL, + .ipcp_reg = ipcp_udp_reg, + .ipcp_unreg = ipcp_udp_unreg, + .ipcp_query = ipcp_udp_query, + .ipcp_flow_alloc = ipcp_udp_flow_alloc, + .ipcp_flow_alloc_resp = ipcp_udp_flow_alloc_resp, + .ipcp_flow_dealloc = ipcp_udp_flow_dealloc +}; + +int main(int argc, + char * argv[]) +{ + if (ipcp_init(argc, argv, &udp_ops) < 0) { + ipcp_create_r(getpid(), -1); + exit(EXIT_FAILURE); + } + + if (udp_data_init() < 0) { + log_err("Failed to init udp data."); + ipcp_create_r(getpid(), -1); + ipcp_fini(); + exit(EXIT_FAILURE); + } + + if (ipcp_boot() < 0) { + log_err("Failed to boot IPCP."); + ipcp_create_r(getpid(), -1); + udp_data_fini(); + ipcp_fini(); + exit(EXIT_FAILURE); + } + + if (ipcp_create_r(getpid(), 0)) { + log_err("Failed to notify IRMd we are initialized."); + ipcp_set_state(IPCP_NULL); + ipcp_shutdown(); + udp_data_fini(); + ipcp_fini(); + exit(EXIT_FAILURE); + } + + ipcp_shutdown(); + + if (ipcp_get_state() == IPCP_SHUTDOWN) { + pthread_cancel(udp_data.sduloop); + pthread_cancel(udp_data.handler); + pthread_cancel(udp_data.sdu_reader); + + pthread_join(udp_data.sduloop, NULL); + pthread_join(udp_data.handler, NULL); + pthread_join(udp_data.sdu_reader, NULL); + } + + udp_data_fini(); + + ipcp_fini(); + + exit(EXIT_SUCCESS); +} -- cgit v1.2.3