diff options
author | dimitri staessens <[email protected]> | 2017-08-17 14:02:34 +0200 |
---|---|---|
committer | dimitri staessens <[email protected]> | 2017-08-17 16:10:03 +0200 |
commit | 7ae3d85c7249b9e8846bc87fdf44e8621a370565 (patch) | |
tree | 89a861ef5dd33a2055650347beddfb4ca39a9a35 /src/ipcpd/shim-eth-llc/main.c | |
parent | aa3416215e980a2ec87ac259be0ea39f1caf96df (diff) | |
download | ouroboros-7ae3d85c7249b9e8846bc87fdf44e8621a370565.tar.gz ouroboros-7ae3d85c7249b9e8846bc87fdf44e8621a370565.zip |
ipcpd: Support Berkeley Packet Filter in LLC shim
This adds support reading Ethernet frames using the Berkeley Packet
Filter (BPF). Ouroboros now fully runs on OS X Sierra.
Diffstat (limited to 'src/ipcpd/shim-eth-llc/main.c')
-rw-r--r-- | src/ipcpd/shim-eth-llc/main.c | 330 |
1 files changed, 205 insertions, 125 deletions
diff --git a/src/ipcpd/shim-eth-llc/main.c b/src/ipcpd/shim-eth-llc/main.c index 6d8d0895..55406a00 100644 --- a/src/ipcpd/shim-eth-llc/main.c +++ b/src/ipcpd/shim-eth-llc/main.c @@ -20,8 +20,6 @@ * Foundation, Inc., http://www.fsf.org/about/contact/. */ -#define _DEFAULT_SOURCE - #define OUROBOROS_PREFIX "ipcpd/shim-eth-llc" #include <ouroboros/config.h> @@ -39,7 +37,6 @@ #include "ipcp.h" #include "shim_eth_llc_messages.pb-c.h" -#include <net/if.h> #include <signal.h> #include <stdlib.h> #include <pthread.h> @@ -49,6 +46,8 @@ #include <sys/socket.h> #include <sys/types.h> #include <sys/ioctl.h> + +#include <net/if.h> #include <netinet/in.h> #ifdef __linux__ @@ -62,29 +61,37 @@ #include <ifaddrs.h> #endif +#ifdef __APPLE__ +#include <net/if_dl.h> +#include <ifaddrs.h> +#endif + #include <poll.h> #include <sys/mman.h> -#ifdef HAVE_NETMAP +#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 -typedef ShimEthLlcMsg shim_eth_llc_msg_t; - -#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 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 EVENT_WAIT_TIMEOUT 10000 /* us */ +#define NAME_QUERY_TIMEOUT 2000 /* ms */ +#define MGMT_TIMEOUT 100 /* ms */ -#define EVENT_WAIT_TIMEOUT 100 /* us */ -#define NAME_QUERY_TIMEOUT 2000 /* ms */ -#define MGMT_TIMEOUT 100 /* ms */ +typedef ShimEthLlcMsg shim_eth_llc_msg_t; struct eth_llc_frame { uint8_t dst_hwaddr[MAC_SIZE]; @@ -110,18 +117,17 @@ struct mgmt_frame { }; struct { -#ifdef HAVE_NETMAP +#if defined(HAVE_NETMAP) struct nm_desc * nmd; uint8_t hw_addr[MAC_SIZE]; struct pollfd poll_in; struct pollfd poll_out; -#else +#elif defined(HAVE_BPF) + int bpf; + uint8_t hw_addr[MAC_SIZE]; +#elif defined __linux__ int s_fd; - #ifdef __FreeBSD__ - struct sockaddr_dl device; - #else struct sockaddr_ll device; - #endif #endif /* HAVE_NETMAP */ struct bmp * saps; @@ -145,36 +151,28 @@ struct { static int eth_llc_data_init(void) { int i; - int ret = -1; + int ret = -ENOMEM; pthread_condattr_t cattr; eth_llc_data.fd_to_ef = malloc(sizeof(struct ef) * IRMD_MAX_FLOWS); if (eth_llc_data.fd_to_ef == NULL) - return -ENOMEM; + goto fail_fd_to_ef; eth_llc_data.ef_to_fd = malloc(sizeof(struct ef) * MAX_SAPS); - if (eth_llc_data.ef_to_fd == NULL) { - ret = -ENOMEM; - goto free_fd_to_ef; - } + 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) { - ret = -ENOMEM; - goto free_ef_to_fd; - } + if (eth_llc_data.saps == NULL) + goto fail_saps; eth_llc_data.np1_flows = flow_set_create(); - if (eth_llc_data.np1_flows == NULL) { - ret = -ENOMEM; - goto bmp_destroy; - } + if (eth_llc_data.np1_flows == NULL) + goto fail_np1_flows; eth_llc_data.fq = fqueue_create(); - if (eth_llc_data.fq == NULL) { - ret = -ENOMEM; - goto flow_set_destroy; - } + if (eth_llc_data.fq == NULL) + goto fail_fq; for (i = 0; i < MAX_SAPS; ++i) eth_llc_data.ef_to_fd[i] = -1; @@ -185,49 +183,56 @@ static int eth_llc_data_init(void) memset(ð_llc_data.fd_to_ef[i].r_addr, 0, MAC_SIZE); } + ret = -1; + if (pthread_rwlock_init(ð_llc_data.flows_lock, NULL)) - goto fqueue_destroy; + goto fail_flows_lock; if (pthread_mutex_init(ð_llc_data.mgmt_lock, NULL)) - goto flow_lock_destroy; + goto fail_mgmt_lock; if (pthread_condattr_init(&cattr)) - goto mgmt_lock_destroy;; + goto fail_condattr; #ifndef __APPLE__ pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK); #endif if (pthread_cond_init(ð_llc_data.mgmt_cond, &cattr)) - goto mgmt_lock_destroy; + goto fail_mgmt_cond; + + pthread_condattr_destroy(&cattr); list_head_init(ð_llc_data.mgmt_frames); return 0; - - mgmt_lock_destroy: + fail_mgmt_cond: + pthread_condattr_destroy(&cattr); + fail_condattr: pthread_mutex_destroy(ð_llc_data.mgmt_lock); - flow_lock_destroy: + fail_mgmt_lock: pthread_rwlock_destroy(ð_llc_data.flows_lock); - fqueue_destroy: + fail_flows_lock: fqueue_destroy(eth_llc_data.fq); - flow_set_destroy: + fail_fq: flow_set_destroy(eth_llc_data.np1_flows); - bmp_destroy: + fail_np1_flows: bmp_destroy(eth_llc_data.saps); - free_ef_to_fd: + fail_saps: free(eth_llc_data.ef_to_fd); - free_fd_to_ef: + fail_ef_to_fd: free(eth_llc_data.fd_to_ef); - + fail_fd_to_ef: return ret; } void eth_llc_data_fini(void) { -#ifdef HAVE_NETMAP +#if defined(HAVE_NETMAP) nm_close(eth_llc_data.nmd); -#else +#elif defined(HAVE_BPF) + close(eth_llc_data.bpf); +#elif defined(__linux__) close(eth_llc_data.s_fd); #endif pthread_cond_destroy(ð_llc_data.mgmt_cond); @@ -273,11 +278,9 @@ static int eth_llc_ipcp_send_frame(const uint8_t * dst_addr, memcpy(llc_frame->dst_hwaddr, dst_addr, MAC_SIZE); memcpy(llc_frame->src_hwaddr, -#ifdef HAVE_NETMAP +#if defined(HAVE_NETMAP) || defined(HAVE_BPF) eth_llc_data.hw_addr, -#elif defined ( __FreeBSD__ ) - LLADDR(ð_llc_data.device), -#else +#elif defined(__linux__) eth_llc_data.device.sll_addr, #endif /* HAVE_NETMAP */ MAC_SIZE); @@ -289,22 +292,28 @@ static int eth_llc_ipcp_send_frame(const uint8_t * dst_addr, memcpy(&llc_frame->payload, payload, len); frame_len = ETH_HEADER_SIZE + LLC_HEADER_SIZE + len; -#ifdef HAVE_NETMAP +#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_err("Failed to send message."); + log_dbg("Failed to send message."); return -1; } -#else +#elif defined(HAVE_BPF) + if (write(eth_llc_data.bpf, frame, frame_len) < 0) { + log_dbg("Failed to send message."); + return -1; + } + +#elif defined(__linux__) if (sendto(eth_llc_data.s_fd, frame, frame_len, 0, (struct sockaddr *) ð_llc_data.device, sizeof(eth_llc_data.device)) <= 0) { - log_err("Failed to send message."); + log_dbg("Failed to send message."); return -1; } #endif /* HAVE_NETMAP */ @@ -381,7 +390,7 @@ static int eth_llc_ipcp_sap_req(uint8_t r_sap, const uint8_t * dst, qoscube_t cube) { - struct timespec ts = {0, EVENT_WAIT_TIMEOUT * 1000}; + struct timespec ts = {0, EVENT_WAIT_TIMEOUT * 100}; struct timespec abstime; int fd; @@ -489,6 +498,7 @@ static int eth_llc_ipcp_name_query_reply(const uint8_t * hash, shim_data_dir_add_entry(ipcpi.shim_data, hash, address); pthread_mutex_lock(&ipcpi.shim_data->dir_queries_lock); + list_for_each(pos, &ipcpi.shim_data->dir_queries) { struct dir_query * e = list_entry(pos, struct dir_query, next); @@ -496,6 +506,7 @@ static int eth_llc_ipcp_name_query_reply(const uint8_t * hash, shim_data_dir_query_respond(e); } } + pthread_mutex_unlock(&ipcpi.shim_data->dir_queries_lock); return 0; @@ -598,17 +609,17 @@ static void * eth_llc_ipcp_sdu_reader(void * o) uint8_t dsap; uint8_t ssap; int fd; -#ifdef HAVE_NETMAP +#if defined(HAVE_NETMAP) uint8_t * buf; -#else + struct nm_pkthdr hdr; +#elif defined(HAVE_BPF) + uint8_t buf[BPF_BLEN]; +#elif defined(__linux__) uint8_t buf[ETH_FRAME_SIZE]; #endif int frame_len = 0; struct eth_llc_frame * llc_frame; struct mgmt_frame * frame; -#ifdef HAVE_NETMAP - struct nm_pkthdr hdr; -#endif (void) o; @@ -617,8 +628,8 @@ static void * eth_llc_ipcp_sdu_reader(void * o) while (true) { if (ipcp_get_state() != IPCP_OPERATIONAL) return (void *) 0; -#ifdef HAVE_NETMAP - if (poll(ð_llc_data.poll_in, 1, EVENT_WAIT_TIMEOUT) < 0) +#if defined(HAVE_NETMAP) + if (poll(ð_llc_data.poll_in, 1, EVENT_WAIT_TIMEOUT / 1000) < 0) continue; if (eth_llc_data.poll_in.revents == 0) /* TIMED OUT */ continue; @@ -628,28 +639,34 @@ static void * eth_llc_ipcp_sdu_reader(void * o) log_err("Bad read from netmap device."); continue; } -#else +#elif defined(HAVE_BPF) + frame_len = read(eth_llc_data.bpf, buf, BPF_BLEN); +#elif defined(__linux__) frame_len = recv(eth_llc_data.s_fd, buf, SHIM_ETH_LLC_MAX_SDU_SIZE, 0); #endif - if (frame_len < 0) + 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); -#ifdef HAVE_NETMAP + +#if !defined(HAVE_BPF) + #if defined(HAVE_NETMAP) if (memcmp(eth_llc_data.hw_addr, -#elif defined ( __FreeBSD__ ) - if (memcmp(LLADDR(ð_llc_data.device), -#else + #elif defined(__linux__) if (memcmp(eth_llc_data.device.sll_addr, -#endif /* HAVE_NETMAP */ + #endif /* HAVE_NETMAP */ llc_frame->dst_hwaddr, MAC_SIZE) && - memcmp(br_addr, llc_frame->dst_hwaddr, MAC_SIZE)) - continue; - + memcmp(br_addr, llc_frame->dst_hwaddr, MAC_SIZE)) { + } +#endif memcpy(&length, &llc_frame->length, sizeof(length)); length = ntohs(length); @@ -744,20 +761,45 @@ static void * eth_llc_ipcp_sdu_writer(void * o) return (void *) 1; } +#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; -#ifdef HAVE_NETMAP +#if defined(HAVE_NETMAP) char ifn[IFNAMSIZ]; -#else - struct timeval tv = {0, EVENT_WAIT_TIMEOUT * 1000}; +#elif defined(HAVE_BPF) + int enable = 1; + int disable = 0; + int blen; + struct timeval tv = {0, EVENT_WAIT_TIMEOUT}; +#elif defined(__linux__) + struct timeval tv = {0, EVENT_WAIT_TIMEOUT}; #endif /* HAVE_NETMAP */ -#ifdef __FreeBSD__ +#if defined(__FreeBSD__) || defined(__APPLE__) struct ifaddrs * ifaddr; struct ifaddrs * ifa; -#else +#elif defined(__linux__) int skfd; #endif assert(conf); @@ -771,7 +813,7 @@ static int eth_llc_ipcp_bootstrap(const struct ipcp_config * conf) memset(&ifr, 0, sizeof(ifr)); memcpy(ifr.ifr_name, conf->if_name, strlen(conf->if_name)); -#ifdef __FreeBSD__ +#if defined(__FreeBSD__) || defined(__APPLE__) if (getifaddrs(&ifaddr) < 0) { log_err("Could not get interfaces."); return -1; @@ -781,7 +823,12 @@ static int eth_llc_ipcp_bootstrap(const struct ipcp_config * conf) 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); + #else memcpy(&ifr.ifr_addr, ifa->ifa_addr, sizeof(*ifa->ifa_addr)); + #endif break; } @@ -792,7 +839,7 @@ static int eth_llc_ipcp_bootstrap(const struct ipcp_config * conf) } freeifaddrs(ifaddr); -#else +#elif defined(__linux__) skfd = socket(AF_UNIX, SOCK_STREAM, 0); if (skfd < 0) { log_err("Failed to open socket."); @@ -815,14 +862,10 @@ static int eth_llc_ipcp_bootstrap(const struct ipcp_config * conf) } #endif /* __FreeBSD__ */ -#ifdef HAVE_NETMAP +#if defined(HAVE_NETMAP) strcpy(ifn, "netmap:"); strcat(ifn, conf->if_name); - #ifdef __FreeBSD__ - memcpy(eth_llc_data.hw_addr, ifr.ifr_addr.sa_data, MAC_SIZE); - #else - memcpy(eth_llc_data.hw_addr, ifr.ifr_hwaddr.sa_data, MAC_SIZE); - #endif /* __FreeBSD__ */ + eth_llc_data.nmd = nm_open(ifn, NULL, 0, NULL); if (eth_llc_data.nmd == NULL) { log_err("Failed to open netmap device."); @@ -838,17 +881,54 @@ static int eth_llc_ipcp_bootstrap(const struct ipcp_config * conf) eth_llc_data.poll_out.events = POLLOUT; log_info("Using netmap device."); -#else /* !HAVE_NETMAP */ +#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); + close(eth_llc_data.bpf); + return -1; + } + + if (ioctl(eth_llc_data.bpf, BIOCSETIF, &ifr) < 0) { + log_err("Failed to set interface."); + close(eth_llc_data.bpf); + return -1; + } + + if (ioctl(eth_llc_data.bpf, BIOCSHDRCMPLT, &enable) < 0) { + log_err("Failed to set BIOCSHDRCMPLT."); + close(eth_llc_data.bpf); + return -1; + } + + if (ioctl(eth_llc_data.bpf, BIOCSSEESENT, &disable) < 0) { + log_err("Failed to set BIOCSSEESENT."); + close(eth_llc_data.bpf); + return -1; + } + + if (ioctl(eth_llc_data.bpf, BIOCSRTIMEOUT, &tv) < 0) { + log_err("Failed to set BIOCSRTIMEOUT."); + close(eth_llc_data.bpf); + return -1; + } + + if (ioctl(eth_llc_data.bpf, BIOCIMMEDIATE, &enable) < 0) { + log_err("Failed to set BIOCIMMEDIATE."); + close(eth_llc_data.bpf); + return -1; + } + + log_info("Using Berkeley Packet Filter."); +#elif defined(__linux__) memset(&(eth_llc_data.device), 0, sizeof(eth_llc_data.device)); - #ifdef __FreeBSD__ - eth_llc_data.device.sdl_index = idx; - eth_llc_data.device.sdl_family = AF_LINK; - memcpy(LLADDR(ð_llc_data.device), ifr.ifr_addr.sa_data, MAC_SIZE); - eth_llc_data.device.sdl_alen = MAC_SIZE; - eth_llc_data.s_fd = socket(AF_LINK, SOCK_RAW, 0); - - log_info("Using berkeley packet filter."); /* TODO */ - #else 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); @@ -857,7 +937,6 @@ static int eth_llc_ipcp_bootstrap(const struct ipcp_config * conf) eth_llc_data.s_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_802_2)); log_info("Using raw socket device."); - #endif /* __FreeBSD__ */ if (eth_llc_data.s_fd < 0) { log_err("Failed to create socket."); @@ -872,8 +951,8 @@ static int eth_llc_ipcp_bootstrap(const struct ipcp_config * conf) } if (setsockopt(eth_llc_data.s_fd, SOL_SOCKET, SO_RCVTIMEO, - (void *) &tv, sizeof(tv))) { - log_err("Failed to set socket timeout."); + &tv, sizeof(tv))) { + log_err("Failed to set socket timeout: %s.", strerror(errno)); close(eth_llc_data.s_fd); return -1; } @@ -1027,7 +1106,7 @@ static int eth_llc_ipcp_flow_alloc(int fd, static int eth_llc_ipcp_flow_alloc_resp(int fd, int response) { - struct timespec ts = {0, EVENT_WAIT_TIMEOUT * 1000}; + struct timespec ts = {0, EVENT_WAIT_TIMEOUT * 100}; struct timespec abstime; uint8_t ssap = 0; uint8_t r_sap = 0; @@ -1126,33 +1205,23 @@ static struct ipcp_ops eth_llc_ops = { int main(int argc, char * argv[]) { - if (ipcp_init(argc, argv, THIS_TYPE, ð_llc_ops) < 0) { - ipcp_create_r(getpid(), -1); - exit(EXIT_FAILURE); - } + if (ipcp_init(argc, argv, THIS_TYPE, ð_llc_ops) < 0) + goto fail_init; if (eth_llc_data_init() < 0) { log_err("Failed to init shim-eth-llc data."); - ipcp_create_r(getpid(), -1); - ipcp_fini(); - exit(EXIT_FAILURE); + goto fail_data_init; } if (ipcp_boot() < 0) { log_err("Failed to boot IPCP."); - ipcp_create_r(getpid(), -1); - eth_llc_data_fini(); - ipcp_fini(); - exit(EXIT_FAILURE); + goto fail_boot; } if (ipcp_create_r(getpid(), 0)) { log_err("Failed to notify IRMd we are initialized."); ipcp_set_state(IPCP_NULL); - ipcp_shutdown(); - eth_llc_data_fini(); - ipcp_fini(); - exit(EXIT_FAILURE); + goto fail_create_r; } ipcp_shutdown(); @@ -1168,4 +1237,15 @@ int main(int argc, 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); + } |