diff options
Diffstat (limited to 'src/ipcpd/eth')
| -rw-r--r-- | src/ipcpd/eth/CMakeLists.txt | 119 | ||||
| -rw-r--r-- | src/ipcpd/eth/dix.c | 26 | ||||
| -rw-r--r-- | src/ipcpd/eth/eth.c | 1640 | ||||
| -rw-r--r-- | src/ipcpd/eth/llc.c | 26 | 
4 files changed, 1811 insertions, 0 deletions
| diff --git a/src/ipcpd/eth/CMakeLists.txt b/src/ipcpd/eth/CMakeLists.txt new file mode 100644 index 00000000..6b8d1a77 --- /dev/null +++ b/src/ipcpd/eth/CMakeLists.txt @@ -0,0 +1,119 @@ +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 Ethernet IPCPs") +  if (NOT DISABLE_RAW_SOCKETS) +    message(STATUS "Raw socket support for Ethernet IPCPs enabled") +    set(HAVE_RAW_SOCKETS TRUE PARENT_SCOPE) +    set(HAVE_ETH TRUE) +  else () +    message(STATUS "Raw socket support for Ethernet IPCPs disabled by user") +    unset(HAVE_RAW_SOCKETS) +    unset(HAVE_ETH) +  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 Ethernet IPCPs") +    if (NOT DISABLE_BPF) +      message(STATUS "Berkeley Packet Filter support " +                     "for Ethernet IPCPs enabled") +      set(HAVE_BPF TRUE PARENT_SCOPE) +      set(HAVE_ETH TRUE) +    else () +      message(STATUS "Berkeley Packet Filter support " +                     "for Ethernet IPCPs disabled by user") +      unset(HAVE_BPF) +      unset(HAVE_ETH) +    endif () +  endif () +endif () + +if (NETMAP_C_INCLUDE_DIR) +  set(DISABLE_NETMAP FALSE CACHE BOOL +      "Disable netmap support for ETH IPCPs") +  if (NOT DISABLE_NETMAP) +    message(STATUS "Netmap support for Ethernet IPCPs enabled") +    set(HAVE_NETMAP TRUE PARENT_SCOPE) +    test_and_set_c_compiler_flag_global(-std=c99) +    set(HAVE_ETH TRUE) +  else () +    message(STATUS "Netmap support for Ethernet IPCPs disabled by user") +    unset(HAVE_NETMAP) +    unset(HAVE_ETH) +    unset(IPCP_ETH_TARGET CACHE) +  endif () +endif () + +if (HAVE_ETH) +  message(STATUS "Supported raw packet API found, building eth-llc and eth-dix") + +  set(ETH_LLC_SOURCES +    # Add source files here +    ${CMAKE_CURRENT_SOURCE_DIR}/llc.c +  ) + +  set(ETH_DIX_SOURCES +    # Add source files here +    ${CMAKE_CURRENT_SOURCE_DIR}/dix.c +  ) + +  set(IPCP_ETH_LLC_TARGET ipcpd-eth-llc CACHE INTERNAL "") +  set(IPCP_ETH_DIX_TARGET ipcpd-eth-dix CACHE INTERNAL "") + +  add_executable(ipcpd-eth-llc ${ETH_LLC_SOURCES} ${IPCP_SOURCES}) +  add_executable(ipcpd-eth-dix ${ETH_DIX_SOURCES} ${IPCP_SOURCES}) + +  if (HAVE_BPF AND NOT APPLE) +    target_include_directories(ipcpd-eth-llc PUBLIC ${BPF_C_INCLUDE_DIR}) +    target_include_directories(ipcpd-eth-dix PUBLIC ${BPF_C_INCLUDE_DIR}) +  endif () + +  if (HAVE_NETMAP AND NOT APPLE) +    target_include_directories(ipcpd-eth-llc PUBLIC +      ${NETMAP_C_INCLUDE_DIR}) +    target_include_directories(ipcpd-eth-dix PUBLIC +      ${NETMAP_C_INCLUDE_DIR}) +  endif () + +  target_link_libraries(ipcpd-eth-llc LINK_PUBLIC ouroboros-dev) +  target_link_libraries(ipcpd-eth-dix LINK_PUBLIC ouroboros-dev) + +  include(AddCompileFlags) +  if (CMAKE_BUILD_TYPE MATCHES "Debug*") +    add_compile_flags(ipcpd-eth-llc -DCONFIG_OUROBOROS_DEBUG) +    add_compile_flags(ipcpd-eth-dix -DCONFIG_OUROBOROS_DEBUG) +  endif () + +  install(TARGETS ipcpd-eth-llc ipcpd-eth-dix RUNTIME DESTINATION +    ${CMAKE_INSTALL_SBINDIR}) +endif () diff --git a/src/ipcpd/eth/dix.c b/src/ipcpd/eth/dix.c new file mode 100644 index 00000000..dfa92ea3 --- /dev/null +++ b/src/ipcpd/eth/dix.c @@ -0,0 +1,26 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2018 + * + * IPC processes over Ethernet - DIX + * + *    Dimitri Staessens <dimitri.staessens@ugent.be> + *    Sander Vrijders   <sander.vrijders@ugent.be> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#define BUILD_ETH_DIX +#define OUROBOROS_PREFIX "ipcpd/eth-dix" + +#include "eth.c" diff --git a/src/ipcpd/eth/eth.c b/src/ipcpd/eth/eth.c new file mode 100644 index 00000000..ce806dc2 --- /dev/null +++ b/src/ipcpd/eth/eth.c @@ -0,0 +1,1640 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2018 + * + * IPC processes over Ethernet + * + *    Dimitri Staessens <dimitri.staessens@ugent.be> + *    Sander Vrijders   <sander.vrijders@ugent.be> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#if !defined(BUILD_ETH_DIX) && !defined(BUILD_ETH_LLC) +#error Define BUILD_ETH_DIX or BUILD_ETH_LLC to build an Ethernet IPCP +#endif + +#if defined(__APPLE__) +#define _BSD_SOURCE +#define _DARWIN_C_SOURCE +#elif defined(__FreeBSD__) +#define __BSD_VISIBLE 1 +#else +#define _POSIX_C_SOURCE 200112L +#endif + +#include "config.h" + +#include <ouroboros/hash.h> +#include <ouroboros/errno.h> +#include <ouroboros/list.h> +#include <ouroboros/utils.h> +#include <ouroboros/bitmap.h> +#include <ouroboros/dev.h> +#include <ouroboros/ipcp-dev.h> +#include <ouroboros/fqueue.h> +#include <ouroboros/logs.h> +#include <ouroboros/time_utils.h> +#include <ouroboros/fccntl.h> + +#include "ipcp.h" +#include "shim-data.h" + +#include <signal.h> +#include <stdlib.h> +#include <pthread.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/ioctl.h> + +#include <net/if.h> +#include <netinet/in.h> + +#ifdef __linux__ +#include <linux/if_packet.h> +#include <linux/if_ether.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#endif + +#ifdef __FreeBSD__ +#include <net/if_dl.h> +#include <netinet/if_ether.h> +#include <ifaddrs.h> +#endif + +#ifdef __APPLE__ +#include <net/if_dl.h> +#include <ifaddrs.h> +#endif + +#include <poll.h> +#include <sys/mman.h> + +#if defined(HAVE_NETMAP) +#define NETMAP_WITH_LIBS +#include <net/netmap_user.h> +#elif defined(HAVE_BPF) +#define BPF_DEV_MAX               256 +#define BPF_BLEN                  sysconf(_SC_PAGESIZE) +#include <net/bpf.h> +#endif + +#define MAC_SIZE                  6 +#define ETH_TYPE_LENGTH_SIZE      2 +#define ETH_HEADER_SIZE           (2 * MAC_SIZE + ETH_TYPE_LENGTH_SIZE) + +#if defined(BUILD_ETH_DIX) +#define THIS_TYPE                 IPCP_ETH_DIX +#define MGMT_EID                  0 +#define DIX_HEADER_SIZE           2 +#define MAX_EIDS                  (1 << (8 * DIX_HEADER_SIZE)) +#define ETH_MAX_SDU_SIZE          (1500 - DIX_HEADER_SIZE) +#define ETH_FRAME_SIZE            (ETH_HEADER_SIZE + DIX_HEADER_SIZE    \ +                                   + ETH_MAX_SDU_SIZE) +#elif defined(BUILD_ETH_LLC) +#define THIS_TYPE                 IPCP_ETH_LLC +#define MGMT_SAP                  0x01 +#define LLC_HEADER_SIZE           3 +#define MAX_SAPS                  64 +#define ETH_MAX_SDU_SIZE          (1500 - LLC_HEADER_SIZE) +#define ETH_FRAME_SIZE            (ETH_HEADER_SIZE + LLC_HEADER_SIZE    \ +                                   + ETH_MAX_SDU_SIZE) +#endif +#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; +#if defined(BUILD_ETH_DIX) +        uint16_t seid; +        uint16_t deid; +#elif defined(BUILD_ETH_LLC) +        uint8_t ssap; +        uint8_t dsap; +#endif +        uint8_t qoscube; +        int8_t  response; +} __attribute__((packed)); + +struct eth_frame { +        uint8_t dst_hwaddr[MAC_SIZE]; +        uint8_t src_hwaddr[MAC_SIZE]; +#if defined(BUILD_ETH_DIX) +        uint8_t ethertype[ETH_TYPE_LENGTH_SIZE]; +        uint8_t eid[DIX_HEADER_SIZE]; +#elif defined(BUILD_ETH_LLC) +        uint8_t length[ETH_TYPE_LENGTH_SIZE]; +        uint8_t dsap; +        uint8_t ssap; +        uint8_t cf; +#endif +        uint8_t payload; +} __attribute__((packed)); + +struct ef { +#if defined(BUILD_ETH_DIX) +        int32_t r_eid; +#elif defined(BUILD_ETH_LLC) +        int8_t  sap; +        int8_t  r_sap; +#endif +        uint8_t r_addr[MAC_SIZE]; +}; + +struct mgmt_frame { +        struct list_head next; +        uint8_t          r_addr[MAC_SIZE]; +        uint8_t          buf[ETH_FRAME_SIZE]; +}; + +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 */ +#if defined (BUILD_ETH_DIX) +        uint16_t           ethertype; +#elif defined(BUILD_ETH_LLC) +        struct bmp *       saps; +        int *              ef_to_fd; +#endif +        struct ef *        fd_to_ef; +        fset_t *           np1_flows; +        fqueue_t *         fq; +        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_data; + +static int eth_data_init(void) +{ +        int                i; +        int                ret = -ENOMEM; +        pthread_condattr_t cattr; + +        eth_data.fd_to_ef = +                malloc(sizeof(*eth_data.fd_to_ef) * SYS_MAX_FLOWS); +        if (eth_data.fd_to_ef == NULL) +                goto fail_fd_to_ef; + +#ifdef BUILD_ETH_LLC +        eth_data.ef_to_fd = +                malloc(sizeof(*eth_data.ef_to_fd) * MAX_SAPS); +        if (eth_data.ef_to_fd == NULL) +                goto fail_ef_to_fd; + +        for (i = 0; i < MAX_SAPS; ++i) +                eth_data.ef_to_fd[i] = -1; + +        eth_data.saps = bmp_create(MAX_SAPS, 2); +        if (eth_data.saps == NULL) +                goto fail_saps; +#endif +        eth_data.np1_flows = fset_create(); +        if (eth_data.np1_flows == NULL) +                goto fail_np1_flows; + +        eth_data.fq = fqueue_create(); +        if (eth_data.fq == NULL) +                goto fail_fq; + +        for (i = 0; i < SYS_MAX_FLOWS; ++i) { +#if defined(BUILD_ETH_DIX) +                eth_data.fd_to_ef[i].r_eid = -1; +#elif defined(BUILD_ETH_LLC) +                eth_data.fd_to_ef[i].sap   = -1; +                eth_data.fd_to_ef[i].r_sap = -1; +#endif +                memset(ð_data.fd_to_ef[i].r_addr, 0, MAC_SIZE); +        } + +        eth_data.shim_data = shim_data_create(); +        if (eth_data.shim_data == NULL) +                goto fail_shim_data; + +        ret = -1; + +        if (pthread_rwlock_init(ð_data.flows_lock, NULL)) +                goto fail_flows_lock; + +        if (pthread_mutex_init(ð_data.mgmt_lock, NULL)) +                goto fail_mgmt_lock; + +        if (pthread_condattr_init(&cattr)) +                goto fail_condattr; + +#ifndef __APPLE__ +        pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK); +#endif + +        if (pthread_cond_init(ð_data.mgmt_cond, &cattr)) +                goto fail_mgmt_cond; + +        pthread_condattr_destroy(&cattr); + +        list_head_init(ð_data.mgmt_frames); + +        return 0; + + fail_mgmt_cond: +        pthread_condattr_destroy(&cattr); + fail_condattr: +        pthread_mutex_destroy(ð_data.mgmt_lock); + fail_mgmt_lock: +        pthread_rwlock_destroy(ð_data.flows_lock); + fail_flows_lock: +        shim_data_destroy(eth_data.shim_data); + fail_shim_data: +        fqueue_destroy(eth_data.fq); + fail_fq: +        fset_destroy(eth_data.np1_flows); + fail_np1_flows: +#ifdef BUILD_ETH_LLC +        bmp_destroy(eth_data.saps); + fail_saps: +        free(eth_data.ef_to_fd); + fail_ef_to_fd: +#endif +        free(eth_data.fd_to_ef); + fail_fd_to_ef: +        return ret; +} + +static void eth_data_fini(void) +{ +#if defined(HAVE_NETMAP) +        nm_close(eth_data.nmd); +#elif defined(HAVE_BPF) +        close(eth_data.bpf); +#elif defined(HAVE_RAW_SOCKETS) +        close(eth_data.s_fd); +#endif +        pthread_cond_destroy(ð_data.mgmt_cond); +        pthread_mutex_destroy(ð_data.mgmt_lock); +        pthread_rwlock_destroy(ð_data.flows_lock); +        shim_data_destroy(eth_data.shim_data); +        fqueue_destroy(eth_data.fq); +        fset_destroy(eth_data.np1_flows); +#ifdef BUILD_ETH_LLC +        bmp_destroy(eth_data.saps); +        free(eth_data.ef_to_fd); +#endif +        free(eth_data.fd_to_ef); +} + +#ifdef BUILD_ETH_LLC +static uint8_t reverse_bits(uint8_t b) +{ +        b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; +        b = (b & 0xCC) >> 2 | (b & 0x33) << 2; +        b = (b & 0xAA) >> 1 | (b & 0x55) << 1; + +        return b; +} +#endif + +static int eth_ipcp_send_frame(const uint8_t * dst_addr, +#if defined(BUILD_ETH_DIX) +                               uint16_t        deid, +#elif defined(BUILD_ETH_LLC) +                               uint8_t         dsap, +                               uint8_t         ssap, +#endif +                               const uint8_t * payload, +                               size_t          len) +{ +        uint32_t               frame_len = 0; +#ifdef BUILD_ETH_LLC +        uint8_t                cf = 0x03; +        uint16_t               length; +#endif +        uint8_t                frame[ETH_MAX_SDU_SIZE]; +        struct eth_frame *     e_frame; + +        if (payload == NULL) { +                log_err("Payload was NULL."); +                return -1; +        } + +        if (len > ETH_MAX_SDU_SIZE) +                return -1; + +        e_frame = (struct eth_frame *) frame; + +        memcpy(e_frame->dst_hwaddr, dst_addr, MAC_SIZE); +        memcpy(e_frame->src_hwaddr, +#if defined(HAVE_NETMAP) || defined(HAVE_BPF) +               eth_data.hw_addr, +#elif defined(HAVE_RAW_SOCKETS) +               eth_data.device.sll_addr, +#endif /* HAVE_NETMAP */ +               MAC_SIZE); +#if defined(BUILD_ETH_DIX) +        memcpy(&e_frame->ethertype, ð_data.ethertype, ETH_TYPE_LENGTH_SIZE); +        deid = htons(deid); +        memcpy(&e_frame->eid, &deid, DIX_HEADER_SIZE); +        frame_len = ETH_HEADER_SIZE + DIX_HEADER_SIZE + len; +#elif defined(BUILD_ETH_LLC) +        length = htons(LLC_HEADER_SIZE + len); +        memcpy(&e_frame->length, &length, sizeof(length)); +        e_frame->dsap = dsap; +        e_frame->ssap = ssap; +        e_frame->cf   = cf; +        frame_len = ETH_HEADER_SIZE + LLC_HEADER_SIZE + len; +#endif +        memcpy(&e_frame->payload, payload, len); + +#if defined(HAVE_NETMAP) +        if (poll(ð_data.poll_out, 1, -1) < 0) +                return -1; + +        if (nm_inject(eth_data.nmd, frame, frame_len) != (int) frame_len) { +                log_dbg("Failed to send message."); +                return -1; +        } +#elif defined(HAVE_BPF) +        if (write(eth_data.bpf, frame, frame_len) < 0) { +                log_dbg("Failed to send message."); +                return -1; +        } + +#elif defined(HAVE_RAW_SOCKETS) +        if (sendto(eth_data.s_fd, +                   frame, +                   frame_len, +                   0, +                   (struct sockaddr *) ð_data.device, +                   sizeof(eth_data.device)) <= 0) { +                log_dbg("Failed to send message."); +                return -1; +        } +#endif /* HAVE_NETMAP */ +        return 0; +} + +static int eth_ipcp_alloc(const uint8_t * dst_addr, +#if defined(BUILD_ETH_DIX) +                          uint16_t        eid, +#elif defined(BUILD_ETH_LLC) +                          uint8_t         ssap, +#endif +                          const uint8_t * hash, +                          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; +#if defined(BUILD_ETH_DIX) +        msg->seid    = htons(eid); +#elif defined(BUILD_ETH_LLC) +        msg->ssap    = ssap; +#endif +        msg->qoscube = cube; + +        memcpy(msg + 1, hash, ipcp_dir_hash_len()); + +        ret = eth_ipcp_send_frame(dst_addr, +#if defined(BUILD_ETH_DIX) +                                  MGMT_EID, +#elif defined(BUILD_ETH_LLC) +                                  reverse_bits(MGMT_SAP), +                                  reverse_bits(MGMT_SAP), +#endif +                                  buf, len); +        free(buf); + +        return ret; +} + +static int eth_ipcp_alloc_resp(uint8_t * dst_addr, +#if defined(BUILD_ETH_DIX) +                               uint16_t  seid, +                               uint16_t  deid, +#elif defined(BUILD_ETH_LLC) +                               uint8_t   ssap, +                               uint8_t   dsap, +#endif +                               int       response) +{ +        struct mgmt_msg msg; + +        msg.code     = FLOW_REPLY; +#if defined(BUILD_ETH_DIX) +        msg.seid     = htons(seid); +        msg.deid     = htons(deid); +#elif defined(BUILD_ETH_LLC) +        msg.ssap     = ssap; +        msg.dsap     = dsap; +#endif +        msg.response = response; + +        return eth_ipcp_send_frame(dst_addr, +#if defined(BUILD_ETH_DIX) +                                   MGMT_EID, +#elif defined(BUILD_ETH_LLC) +                                   reverse_bits(MGMT_SAP), +                                   reverse_bits(MGMT_SAP), +#endif +                                   (uint8_t *) &msg, sizeof(msg)); +} + +static int eth_ipcp_req(uint8_t *       r_addr, +#if defined(BUILD_ETH_DIX) +                        uint16_t        r_eid, +#elif defined(BUILD_ETH_LLC) +                        uint8_t         r_sap, +#endif +                        const uint8_t * dst, +                        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(ð_data.flows_lock); +#if defined(BUILD_ETH_DIX) +        eth_data.fd_to_ef[fd].r_eid = r_eid; +#elif defined(BUILD_ETH_LLC) +        eth_data.fd_to_ef[fd].r_sap = r_sap; +#endif +        memcpy(eth_data.fd_to_ef[fd].r_addr, r_addr, MAC_SIZE); + +        pthread_rwlock_unlock(ð_data.flows_lock); + +        ipcpi.alloc_id = fd; +        pthread_cond_broadcast(&ipcpi.alloc_cond); + +        pthread_mutex_unlock(&ipcpi.alloc_lock); + +#if defined(BUILD_ETH_DIX) +        log_dbg("New flow request, fd %d, remote endpoint %d.", fd, r_eid); +#elif defined(BUILD_ETH_LLC) +        log_dbg("New flow request, fd %d, remote SAP %d.", fd, r_sap); +#endif + +        return 0; +} + +static int eth_ipcp_alloc_reply(uint8_t * r_addr, +#if defined(BUILD_ETH_DIX) +                                uint16_t  seid, +                                uint16_t  deid, +#elif defined(BUILD_ETH_LLC) +                                uint8_t   ssap, +                                int       dsap, +#endif +                                int       response) +{ +        int ret = 0; +        int fd = -1; + +        pthread_rwlock_wrlock(ð_data.flows_lock); + +#if defined(BUILD_ETH_DIX) +        fd = deid; +#elif defined(BUILD_ETH_LLC) +        fd = eth_data.ef_to_fd[dsap]; +#endif +        if (fd < 0) { +                pthread_rwlock_unlock(& eth_data.flows_lock); +                log_err("No flow found with that SAP."); +                return -1; /* -EFLOWNOTFOUND */ +        } + +        if (response) { +#ifdef BUILD_ETH_LLC +                bmp_release(eth_data.saps, eth_data.fd_to_ef[fd].sap); +#endif +        } else { +#if defined(BUILD_ETH_DIX) +                eth_data.fd_to_ef[fd].r_eid = seid; +#elif defined(BUILD_ETH_LLC) +                eth_data.fd_to_ef[fd].r_sap = ssap; +#endif +                memcpy(eth_data.fd_to_ef[fd].r_addr, r_addr, MAC_SIZE); +        } + +        pthread_rwlock_unlock(ð_data.flows_lock); + +#if defined(BUILD_ETH_DIX) +        log_dbg("Flow reply, fd %d, src eid %d, dst eid %d.", fd, seid, deid); +#elif defined(BUILD_ETH_LLC) +        log_dbg("Flow reply, fd %d, SSAP %d, DSAP %d.", fd, ssap, dsap); +#endif +        if ((ret = ipcp_flow_alloc_reply(fd, response)) < 0) +                return -1; + +        return ret; + +} + +static int eth_ipcp_name_query_req(const uint8_t * hash, +                                   uint8_t *       r_addr) +{ +        uint8_t *         buf; +        struct mgmt_msg * msg; +        size_t            len; + +        if (shim_data_reg_has(eth_data.shim_data, hash)) { +                len = sizeof(*msg) + ipcp_dir_hash_len(); + +                buf = malloc(len); +                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_ipcp_send_frame(r_addr, +#if defined(BUILD_ETH_DIX) +                                        MGMT_EID, +#elif defined(BUILD_ETH_LLC) +                                        reverse_bits(MGMT_SAP), +                                        reverse_bits(MGMT_SAP), +#endif +                                        buf, len)) { +                        log_err("Failed to send management frame."); +                        free(buf); +                        return -1; +                } + +                free(buf); +        } + +        return 0; +} + +static int eth_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_data.shim_data, hash, address); + +        shim_data_dir_query_respond(eth_data.shim_data, hash); + +        return 0; +} + +static int eth_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_data.shim_data, +                                      buf + sizeof(*msg))) { +                        eth_ipcp_req(r_addr, +#if defined(BUILD_ETH_DIX) +                                     ntohs(msg->seid), +#elif defined(BUILD_ETH_LLC) +                                     msg->ssap, +#endif +                                     buf + sizeof(*msg), +                                     msg->qoscube); +                } +                break; +        case FLOW_REPLY: +                eth_ipcp_alloc_reply(r_addr, +#if defined(BUILD_ETH_DIX) +                                     ntohs(msg->seid), +                                     ntohs(msg->deid), +#elif defined(BUILD_ETH_LLC) +                                     msg->ssap, +                                     msg->dsap, +#endif +                                     msg->response); +                break; +        case NAME_QUERY_REQ: +                eth_ipcp_name_query_req(buf + sizeof(*msg), r_addr); +                break; +        case NAME_QUERY_REPLY: +                eth_ipcp_name_query_reply(buf + sizeof(*msg), r_addr); +                break; +        default: +                log_err("Unknown message received %d.", msg->code); +                return -1; +        } + +        return 0; +} + +static void * eth_ipcp_mgmt_handler(void * o) +{ +        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 *) ð_data.mgmt_lock); + +        while (true) { +                ret = 0; + +                clock_gettime(PTHREAD_COND_CLOCK, &abstime); +                ts_add(&abstime, &timeout, &abstime); + +                pthread_mutex_lock(ð_data.mgmt_lock); + +                while (list_is_empty(ð_data.mgmt_frames) && +                       ret != -ETIMEDOUT) +                        ret = -pthread_cond_timedwait(ð_data.mgmt_cond, +                                                      ð_data.mgmt_lock, +                                                      &abstime); + +                if (ret == -ETIMEDOUT) { +                        pthread_mutex_unlock(ð_data.mgmt_lock); +                        continue; +                } + +                frame = list_first_entry((ð_data.mgmt_frames), +                                         struct mgmt_frame, next); +                if (frame == NULL) { +                        pthread_mutex_unlock(ð_data.mgmt_lock); +                        continue; +                } + +                list_del(&frame->next); +                pthread_mutex_unlock(ð_data.mgmt_lock); + +                eth_ipcp_mgmt_frame(frame->buf, frame->r_addr); +                free(frame); +        } + +        pthread_cleanup_pop(false); + +        return (void *) 0; +} + +static void * eth_ipcp_sdu_reader(void * o) +{ +        uint8_t             br_addr[MAC_SIZE]; +#if defined(BUILD_ETH_DIX) +        uint16_t            deid; +#elif defined(BUILD_ETH_LLC) +        uint8_t             dsap; +        uint8_t             ssap; +#endif +        uint16_t            length; +        int                 fd; +#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_frame *  e_frame; +        struct mgmt_frame * frame; + +        (void) o; + +        memset(br_addr, 0xff, MAC_SIZE * sizeof(uint8_t)); + +        while (true) { +#if defined(HAVE_NETMAP) +                if (poll(ð_data.poll_in, 1, -1) < 0) +                        continue; +                if (eth_data.poll_in.revents == 0) /* TIMED OUT */ +                        continue; + +                buf = nm_nextpkt(eth_data.nmd, &hdr); +                if (buf == NULL) { +                        log_err("Bad read from netmap device."); +                        continue; +                } +#elif defined(HAVE_BPF) +                frame_len = read(eth_data.bpf, buf, BPF_BLEN); +#elif defined(HAVE_RAW_SOCKETS) +                frame_len = recv(eth_data.s_fd, buf, +                                 ETH_MAX_SDU_SIZE, 0); +#endif +                if (frame_len <= 0) +                        continue; + +#if defined(HAVE_BPF) && !defined(HAVE_NETMAP) +                e_frame = (struct eth_frame *) +                        (buf + ((struct bpf_hdr *) buf)->bh_hdrlen); +#else +                e_frame = (struct eth_frame *) buf; +#endif +                assert(e_frame->dst_hwaddr); + +#if !defined(HAVE_BPF) +    #if defined(HAVE_NETMAP) +                if (memcmp(eth_data.hw_addr, +    #elif defined(HAVE_RAW_SOCKETS) +                if (memcmp(eth_data.device.sll_addr, +    #endif /* HAVE_NETMAP */ +                           e_frame->dst_hwaddr, +                           MAC_SIZE) && +                    memcmp(br_addr, e_frame->dst_hwaddr, MAC_SIZE)) { +                } +#endif + +#if defined(BUILD_ETH_DIX) +                length = frame_len - ETH_HEADER_SIZE - DIX_HEADER_SIZE; +                memcpy(&deid, &e_frame->eid, sizeof(deid)); +                deid = ntohs(deid); + +                if (deid == MGMT_EID) { +#elif defined (BUILD_ETH_LLC) +                memcpy(&length, &e_frame->length, sizeof(length)); +                length = ntohs(length); + +                if (length > 0x05FF) /* DIX */ +                        continue; + +                length -= LLC_HEADER_SIZE; + +                dsap = reverse_bits(e_frame->dsap); +                ssap = reverse_bits(e_frame->ssap); + +                if (ssap == MGMT_SAP && dsap == MGMT_SAP) { +#endif +                        pthread_mutex_lock(ð_data.mgmt_lock); + +                        frame = malloc(sizeof(*frame)); +                        if (frame == NULL) { +                                pthread_mutex_unlock(ð_data.mgmt_lock); +                                continue; +                        } + +                        memcpy(frame->buf, &e_frame->payload, length); +                        memcpy(frame->r_addr, e_frame->src_hwaddr, MAC_SIZE); +                        list_add(&frame->next, ð_data.mgmt_frames); +                        pthread_cond_signal(ð_data.mgmt_cond); +                        pthread_mutex_unlock(ð_data.mgmt_lock); +                } else { +                        pthread_rwlock_rdlock(ð_data.flows_lock); + +#if defined(BUILD_ETH_DIX) +                        fd = deid; +#elif defined(BUILD_ETH_LLC) +                        fd = eth_data.ef_to_fd[dsap]; +#endif +                        if (fd < 0) { +                                pthread_rwlock_unlock(ð_data.flows_lock); +                                continue; +                        } + +#ifdef BUILD_ETH_LLC +                        if (eth_data.fd_to_ef[fd].r_sap != ssap +                            || memcmp(eth_data.fd_to_ef[fd].r_addr, +                                      e_frame->src_hwaddr, MAC_SIZE)) { +                                pthread_rwlock_unlock(ð_data.flows_lock); +                                continue; +                        } +#endif +                        pthread_rwlock_unlock(ð_data.flows_lock); + +                        flow_write(fd, &e_frame->payload, length); +                } +        } + +        return (void *) 0; +} + +static void * eth_ipcp_sdu_writer(void * o) +{ +        int                  fd; +        struct shm_du_buff * sdb; +#if defined(BUILD_ETH_DIX) +        uint16_t             deid; +#elif defined(BUILD_ETH_LLC) +        uint8_t              dsap; +        uint8_t              ssap; +#endif +        uint8_t              r_addr[MAC_SIZE]; + +        (void) o; + +        while (true) { +                fevent(eth_data.np1_flows, eth_data.fq, NULL); + +                pthread_rwlock_rdlock(ð_data.flows_lock); +                while ((fd = fqueue_next(eth_data.fq)) >= 0) { +                        if (ipcp_flow_read(fd, &sdb)) { +                                log_err("Bad read from fd %d.", fd); +                                continue; +                        } + +#if defined(BUILD_ETH_DIX) +                        deid = eth_data.fd_to_ef[fd].r_eid; +#elif defined(BUILD_ETH_LLC) +                        dsap = reverse_bits(eth_data.fd_to_ef[fd].r_sap); +                        ssap = reverse_bits(eth_data.fd_to_ef[fd].sap); +#endif +                        memcpy(r_addr, +                               eth_data.fd_to_ef[fd].r_addr, +                               MAC_SIZE); + +                        eth_ipcp_send_frame(r_addr, +#if defined(BUILD_ETH_DIX) +                                            deid, +#elif defined(BUILD_ETH_LLC) +                                            dsap, ssap, +#endif +                                            shm_du_buff_head(sdb), +                                            shm_du_buff_tail(sdb) +                                            - shm_du_buff_head(sdb)); +                        ipcp_sdb_release(sdb); +                } +                pthread_rwlock_unlock(ð_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(ð_data.flows_lock); + +#if defined(BUILD_ETH_DIX) +        for (i = 0; i < SYS_MAX_FLOWS; ++i) +                if (eth_data.fd_to_ef[i].r_eid != -1) { +                        fccntl(i, FLOWGFLAGS, &flags); +                        if (up) +                                fccntl(i, FLOWSFLAGS, flags & ~FLOWFDOWN); +                        else +                                fccntl(i, FLOWSFLAGS, flags | FLOWFDOWN); +                } +#elif defined(BUILD_ETH_LLC) +        for (i = 0; i < MAX_SAPS; i++) +                if (eth_data.ef_to_fd[i] != -1) { +                        fccntl(eth_data.ef_to_fd[i], FLOWGFLAGS, &flags); +                        if (up) +                                fccntl(eth_data.ef_to_fd[i], +                                       FLOWSFLAGS, flags & ~FLOWFDOWN); +                        else +                                fccntl(eth_data.ef_to_fd[i], +                                       FLOWSFLAGS, flags | FLOWFDOWN); +                } +#endif + +        pthread_rwlock_unlock(ð_data.flows_lock); +} + +static void close_ptr(void * o) +{ +        close(*((int *) o)); +} + + +static void * eth_ipcp_if_monitor(void * o) +{ +        int                fd; +        int                status; +        char               buf[4096]; +        struct iovec       iov = {buf, sizeof(buf)}; +        struct sockaddr_nl snl; +        struct msghdr      msg = {(void *) &snl, sizeof(snl), +                                  &iov, 1, NULL, 0, 0}; +        struct nlmsghdr *  h; +        struct ifinfomsg * ifi; + +        (void ) o; + +        fd = open_netlink_socket(); +        if (fd < 0) { +                log_err("Failed to open socket."); +                return (void *) -1; +        } + +        pthread_cleanup_push(close_ptr, &fd); + +        while (true) { +                status = recvmsg(fd, &msg, 0); +                if (status < 0) +                        continue; + +                for (h = (struct nlmsghdr *) buf; +                     NLMSG_OK(h, (unsigned int) status); +                     h = NLMSG_NEXT(h, status)) { + +                        /* Finish reading */ +                        if (h->nlmsg_type == NLMSG_DONE) +                                break; + +                        /* Message is some kind of error */ +                        if (h->nlmsg_type == NLMSG_ERROR) +                                continue; + +                        /* Only interested in link up/down */ +                        if (h->nlmsg_type != RTM_NEWLINK) +                                continue; + +                        ifi = NLMSG_DATA(h); + +                        /* Not our interface */ +                        if (ifi->ifi_index != eth_data.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_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->dev == NULL) { +                log_err("Device name is NULL."); +                return -1; +        } + +        memset(&ifr, 0, sizeof(ifr)); +        memcpy(ifr.ifr_name, conf->dev, strlen(conf->dev)); + +#ifdef BUILD_ETH_DIX +        eth_data.ethertype = htons(conf->ethertype); +#endif + +#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->dev)) +                        continue; +                log_dbg("Interface %s found.", conf->dev); + +    #if defined(HAVE_NETMAP) || defined(HAVE_BPF) +                memcpy(eth_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->dev); +        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->dev); + +        eth_data.nmd = nm_open(ifn, NULL, 0, NULL); +        if (eth_data.nmd == NULL) { +                log_err("Failed to open netmap device."); +                return -1; +        } + +        memset(ð_data.poll_in, 0, sizeof(eth_data.poll_in)); +        memset(ð_data.poll_out, 0, sizeof(eth_data.poll_out)); + +        eth_data.poll_in.fd      = NETMAP_FD(eth_data.nmd); +        eth_data.poll_in.events  = POLLIN; +        eth_data.poll_out.fd     = NETMAP_FD(eth_data.nmd); +        eth_data.poll_out.events = POLLOUT; + +        log_info("Using netmap device."); +#elif defined(HAVE_BPF) /* !HAVE_NETMAP */ +        eth_data.bpf = open_bpf_device(); +        if (eth_data.bpf < 0) { +                log_err("Failed to open bpf device."); +                return -1; +        } + +        ioctl(eth_data.bpf, BIOCGBLEN, &blen); +        if (BPF_BLEN < blen) { +                log_err("BPF buffer too small (is: %ld must be: %d).", +                        BPF_BLEN, blen); +                goto fail_device; +        } + +        if (ioctl(eth_data.bpf, BIOCSETIF, &ifr) < 0) { +                log_err("Failed to set interface."); +                goto fail_device; +        } + +        if (ioctl(eth_data.bpf, BIOCSHDRCMPLT, &enable) < 0) { +                log_err("Failed to set BIOCSHDRCMPLT."); +                goto fail_device; +        } + +        if (ioctl(eth_data.bpf, BIOCSSEESENT, &disable) < 0) { +                log_err("Failed to set BIOCSSEESENT."); +                goto fail_device; +        } + +        if (ioctl(eth_data.bpf, BIOCIMMEDIATE, &enable) < 0) { +                log_err("Failed to set BIOCIMMEDIATE."); +                goto fail_device; +        } + +        log_info("Using Berkeley Packet Filter."); +#elif defined(HAVE_RAW_SOCKETS) +        memset(&(eth_data.device), 0, sizeof(eth_data.device)); +        eth_data.device.sll_ifindex  = idx; +        eth_data.device.sll_family   = AF_PACKET; +        memcpy(eth_data.device.sll_addr, ifr.ifr_hwaddr.sa_data, MAC_SIZE); +        eth_data.device.sll_halen    = MAC_SIZE; +        eth_data.device.sll_protocol = htons(ETH_P_ALL); + +    #if defined (BUILD_ETH_DIX) +        eth_data.s_fd = socket(AF_PACKET, SOCK_RAW, eth_data.ethertype); +    #elif defined (BUILD_ETH_LLC) +        eth_data.s_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_802_2)); +    #endif + +        log_info("Using raw socket device."); + +        if (eth_data.s_fd < 0) { +                log_err("Failed to create socket."); +                return -1; +        } + +        if (bind(eth_data.s_fd, (struct sockaddr *) ð_data.device, +                sizeof(eth_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(ð_data.if_monitor, +                           NULL, +                           eth_ipcp_if_monitor, +                           NULL)) { +                ipcp_set_state(IPCP_INIT); +                goto fail_device; +        } +#endif + +        if (pthread_create(ð_data.mgmt_handler, +                           NULL, +                           eth_ipcp_mgmt_handler, +                           NULL)) { +                ipcp_set_state(IPCP_INIT); +                goto fail_mgmt_handler; +        } + +        if (pthread_create(ð_data.sdu_reader, +                           NULL, +                           eth_ipcp_sdu_reader, +                           NULL)) { +                ipcp_set_state(IPCP_INIT); +                goto fail_sdu_reader; +        } + +        if (pthread_create(ð_data.sdu_writer, +                           NULL, +                           eth_ipcp_sdu_writer, +                           NULL)) { +                ipcp_set_state(IPCP_INIT); +                goto fail_sdu_writer; +        } + +#if defined(BUILD_ETH_DIX) +        log_dbg("Bootstrapped IPCP over DIX Ethernet with pid %d " +                "and Ethertype 0x%X.", getpid(), conf->ethertype); +#elif defined(BUILD_ETH_LLC) +        log_dbg("Bootstrapped IPCP over Ethernet with LLC with pid %d.", +                getpid()); +#endif + +        return 0; + + fail_sdu_writer: +        pthread_cancel(eth_data.sdu_reader); +        pthread_join(eth_data.sdu_reader, NULL); + fail_sdu_reader: +        pthread_cancel(eth_data.mgmt_handler); +        pthread_join(eth_data.mgmt_handler, NULL); + fail_mgmt_handler: +#if defined(__linux__) +        pthread_cancel(eth_data.if_monitor); +        pthread_join(eth_data.if_monitor, NULL); +#endif +#if !defined(HAVE_NETMAP) + fail_device: +#endif +#if defined(HAVE_NETMAP) +        nm_close(eth_data.nmd); +#elif defined(HAVE_BPF) +        close(eth_data.bpf); +#elif defined(HAVE_RAW_SOCKETS) +        close(eth_data.s_fd); +#endif +        return -1; +} + +static int eth_ipcp_reg(const uint8_t * hash) +{ +        if (shim_data_reg_add_entry(eth_data.shim_data, hash)) { +                log_err("Failed to add " HASH_FMT " to local registry.", +                        HASH_VAL(hash)); +                return -1; +        } + +        log_dbg("Registered " HASH_FMT ".", HASH_VAL(hash)); + +        return 0; +} + +static int eth_ipcp_unreg(const uint8_t * hash) +{ +        shim_data_reg_del_entry(eth_data.shim_data, hash); + +        return 0; +} + +static int eth_ipcp_query(const uint8_t * hash) +{ +        uint8_t            r_addr[MAC_SIZE]; +        struct timespec    timeout = {(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_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_data.shim_data, hash); +        if (query == NULL) { +                free(buf); +                return -1; +        } + +        if (eth_ipcp_send_frame(r_addr, +#if defined(BUILD_ETH_DIX) +                                MGMT_EID, +#elif defined(BUILD_ETH_LLC) +                                reverse_bits(MGMT_SAP), +                                reverse_bits(MGMT_SAP), +#endif +                                buf, len)) { +                log_err("Failed to send management frame."); +                shim_data_dir_query_destroy(eth_data.shim_data, query); +                free(buf); +                return -1; +        } + +        free(buf); + +        ret = shim_data_dir_query_wait(query, &timeout); + +        shim_data_dir_query_destroy(eth_data.shim_data, query); + +        return ret; +} + +static int eth_ipcp_flow_alloc(int             fd, +                               const uint8_t * hash, +                               qoscube_t       cube) +{ +#ifdef BUILD_ETH_LLC +        uint8_t  ssap = 0; +#endif +        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_data.shim_data, hash)) { +                log_err("Destination unreachable."); +                return -1; +        } +        addr = shim_data_dir_get_addr(eth_data.shim_data, hash); + +        pthread_rwlock_wrlock(ð_data.flows_lock); +#ifdef BUILD_ETH_LLC +        ssap = bmp_allocate(eth_data.saps); +        if (!bmp_is_id_valid(eth_data.saps, ssap)) { +                pthread_rwlock_unlock(ð_data.flows_lock); +                return -1; +        } + +        eth_data.fd_to_ef[fd].sap = ssap; +        eth_data.ef_to_fd[ssap]   = fd; +#endif +        pthread_rwlock_unlock(ð_data.flows_lock); + +        memcpy(r_addr, &addr, MAC_SIZE); + +        if (eth_ipcp_alloc(r_addr, +#if defined(BUILD_ETH_DIX) +                           fd, +#elif defined(BUILD_ETH_LLC) +                           ssap, +#endif +                           hash, cube) < 0) { +#ifdef BUILD_ETH_LLC +                pthread_rwlock_wrlock(ð_data.flows_lock); +                bmp_release(eth_data.saps, eth_data.fd_to_ef[fd].sap); +                eth_data.fd_to_ef[fd].sap = -1; +                eth_data.ef_to_fd[ssap]   = -1; +                pthread_rwlock_unlock(ð_data.flows_lock); +#endif +                return -1; +        } + +        fset_add(eth_data.np1_flows, fd); +#if defined(BUILD_ETH_DIX) +        log_dbg("Pending flow with fd %d.", fd); +#elif defined(BUILD_ETH_LLC) +        log_dbg("Pending flow with fd %d on SAP %d.", fd, ssap); +#endif +        return 0; +} + +static int eth_ipcp_flow_alloc_resp(int fd, +                                    int response) +{ +        struct timespec ts    = {0, ALLOC_TIMEO * MILLION}; +        struct timespec abstime; +#if defined(BUILD_ETH_DIX) +        uint16_t        r_eid; +#elif defined(BUILD_ETH_LLC) +        uint8_t         ssap; +        uint8_t         r_sap; +#endif +        uint8_t         r_addr[MAC_SIZE]; + +        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(ð_data.flows_lock); +#if defined(BUILD_ETH_DIX) +        r_eid = eth_data.fd_to_ef[fd].r_eid; +#elif defined(BUILD_ETH_LLC) +        ssap = bmp_allocate(eth_data.saps); +        if (!bmp_is_id_valid(eth_data.saps, ssap)) { +                pthread_rwlock_unlock(ð_data.flows_lock); +                return -1; +        } + +        eth_data.fd_to_ef[fd].sap = ssap; +        r_sap = eth_data.fd_to_ef[fd].r_sap; +        eth_data.ef_to_fd[ssap] = fd; +#endif +        memcpy(r_addr, eth_data.fd_to_ef[fd].r_addr, MAC_SIZE); + +        pthread_rwlock_unlock(ð_data.flows_lock); + +        if (eth_ipcp_alloc_resp(r_addr, +#if defined(BUILD_ETH_DIX) +                                fd, r_eid, +#elif defined(BUILD_ETH_LLC) +                                ssap, r_sap, +#endif +                                response) < 0) { +#ifdef BUILD_ETH_LLC +                pthread_rwlock_wrlock(ð_data.flows_lock); +                bmp_release(eth_data.saps, eth_data.fd_to_ef[fd].sap); +                pthread_rwlock_unlock(ð_data.flows_lock); +#endif +                return -1; +        } + +        fset_add(eth_data.np1_flows, fd); +#if defined(BUILD_ETH_DIX) +        log_dbg("Accepted flow, fd %d.", fd); +#elif defined(BUILD_ETH_LLC) +        log_dbg("Accepted flow, fd %d, SAP %d.", fd, (uint8_t)ssap); +#endif +        return 0; +} + +static int eth_ipcp_flow_dealloc(int fd) +{ +#ifdef BUILD_ETH_LLC +        uint8_t sap; +#endif +        ipcp_flow_fini(fd); + +        pthread_rwlock_wrlock(ð_data.flows_lock); + +        fset_del(eth_data.np1_flows, fd); + +#if defined(BUILD_ETH_DIX) +        eth_data.fd_to_ef[fd].r_eid = -1; +#elif defined BUILD_ETH_LLC +        sap = eth_data.fd_to_ef[fd].sap; +        bmp_release(eth_data.saps, sap); +        eth_data.fd_to_ef[fd].sap = -1; +        eth_data.fd_to_ef[fd].r_sap = -1; +        eth_data.ef_to_fd[sap] = -1; +#endif +        memset(ð_data.fd_to_ef[fd].r_addr, 0, MAC_SIZE); + +        pthread_rwlock_unlock(ð_data.flows_lock); + +        flow_dealloc(fd); + +        log_dbg("Flow with fd %d deallocated.", fd); + +        return 0; +} + +static struct ipcp_ops eth_ops = { +        .ipcp_bootstrap       = eth_ipcp_bootstrap, +        .ipcp_enroll          = NULL, +        .ipcp_connect         = NULL, +        .ipcp_disconnect      = NULL, +        .ipcp_reg             = eth_ipcp_reg, +        .ipcp_unreg           = eth_ipcp_unreg, +        .ipcp_query           = eth_ipcp_query, +        .ipcp_flow_alloc      = eth_ipcp_flow_alloc, +        .ipcp_flow_alloc_resp = eth_ipcp_flow_alloc_resp, +        .ipcp_flow_dealloc    = eth_ipcp_flow_dealloc +}; + +int main(int    argc, +         char * argv[]) +{ +        if (ipcp_init(argc, argv, ð_ops) < 0) +                goto fail_init; + +        if (eth_data_init() < 0) { +#if defined(BUILD_ETH_DIX) +                log_err("Failed to init eth-llc data."); +#elif defined(BUILD_ETH_LLC) +                log_err("Failed to init eth-dix data."); +#endif +                goto fail_data_init; +        } + +        if (ipcp_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_data.sdu_writer); +                pthread_cancel(eth_data.sdu_reader); +                pthread_cancel(eth_data.mgmt_handler); +#ifdef __linux__ +                pthread_cancel(eth_data.if_monitor); +#endif +                pthread_join(eth_data.sdu_writer, NULL); +                pthread_join(eth_data.sdu_reader, NULL); +                pthread_join(eth_data.mgmt_handler, NULL); +#ifdef __linux__ +                pthread_join(eth_data.if_monitor, NULL); +#endif +        } + +        eth_data_fini(); + +        ipcp_fini(); + +        exit(EXIT_SUCCESS); + + fail_create_r: +        ipcp_shutdown(); + fail_boot: +        eth_data_fini(); + fail_data_init: +        ipcp_fini(); + fail_init: +        ipcp_create_r(getpid(), -1); +        exit(EXIT_FAILURE); +} diff --git a/src/ipcpd/eth/llc.c b/src/ipcpd/eth/llc.c new file mode 100644 index 00000000..fa332189 --- /dev/null +++ b/src/ipcpd/eth/llc.c @@ -0,0 +1,26 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2018 + * + * IPC processes over Ethernet - LLC + * + *    Dimitri Staessens <dimitri.staessens@ugent.be> + *    Sander Vrijders   <sander.vrijders@ugent.be> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#define BUILD_ETH_LLC +#define OUROBOROS_PREFIX "ipcpd/eth-llc" + +#include "eth.c" | 
