/* * Ouroboros - Copyright (C) 2016 * * Shim IPC process over Ethernet with LLC * * Sander Vrijders <sander.vrijders@intec.ugent.be> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <ouroboros/config.h> #define _DEFAULT_SOURCE #include "ipcp.h" #include "flow.h" #include <ouroboros/errno.h> #include <ouroboros/shm_du_map.h> #include <ouroboros/shm_ap_rbuff.h> #include <ouroboros/list.h> #include <ouroboros/utils.h> #include <ouroboros/ipcp.h> #include <ouroboros/irm_config.h> #include <ouroboros/sockets.h> #include <ouroboros/bitmap.h> #include <ouroboros/flow.h> #include <ouroboros/dev.h> #define OUROBOROS_PREFIX "ipcpd/shim-eth-llc" #include <ouroboros/logs.h> #include <net/if.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 <netinet/in.h> #ifdef __linux__ #include <linux/if_packet.h> #include <linux/if_ether.h> #endif #ifdef __FreeBSD__ #include <net/if_dl.h> #include <netinet/if_ether.h> #endif #include <poll.h> #include <sys/mman.h> #include "shim_eth_llc_messages.pb-c.h" typedef ShimEthLlcMsg shim_eth_llc_msg_t; #define THIS_TYPE IPCP_SHIM_ETH_LLC #define MGMT_SAP 0x01 #define SHIM_ETH_LLC_MAX_SDU_SIZE 1500 #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 (SHIM_ETH_LLC_MAX_SDU_SIZE + ETH_HEADER_SIZE + \ LLC_HEADER_SIZE + 2) /* global for trapping signal */ int irmd_api; struct ipcp * _ipcp; #define shim_data(type) ((struct eth_llc_ipcp_data *) type->data) #define ipcp_flow(index) ((struct flow *) &(shim_data(_ipcp)->flows[index])) 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 size[2]; uint8_t payload; }; struct eth_llc_flow { struct flow flow; uint8_t sap; uint8_t r_sap; uint8_t r_addr[MAC_SIZE]; }; struct eth_llc_ipcp_data { /* Keep ipcp_data first for polymorphism. */ struct ipcp_data ipcp_data; #ifdef __FreeBSD__ struct sockaddr_dl device; #else struct sockaddr_ll device; #endif int s_fd; struct bmp * indices; struct bmp * saps; struct shm_du_map * dum; struct shm_ap_rbuff * rb; uint8_t * rx_ring; uint8_t * tx_ring; int tx_offset; struct eth_llc_flow flows[AP_MAX_FLOWS]; pthread_rwlock_t flows_lock; pthread_t mainloop; pthread_t sdu_writer; pthread_t sdu_reader; }; struct eth_llc_ipcp_data * eth_llc_ipcp_data_create() { struct eth_llc_ipcp_data * eth_llc_data; enum ipcp_type ipcp_type; eth_llc_data = malloc(sizeof(*eth_llc_data)); if (eth_llc_data == NULL) { LOG_ERR("Failed to allocate."); return NULL; } ipcp_type = THIS_TYPE; if (ipcp_data_init((struct ipcp_data *) eth_llc_data, ipcp_type) == NULL) { free(eth_llc_data); return NULL; } eth_llc_data->dum = shm_du_map_open(); if (eth_llc_data->dum == NULL) { free(eth_llc_data); return NULL; } eth_llc_data->rb = shm_ap_rbuff_create(); if (eth_llc_data->rb == NULL) { shm_du_map_close(eth_llc_data->dum); free(eth_llc_data); return NULL; } eth_llc_data->indices = bmp_create(AP_MAX_FLOWS, 0); if (eth_llc_data->indices == NULL) { shm_ap_rbuff_destroy(eth_llc_data->rb); shm_du_map_close(eth_llc_data->dum); free(eth_llc_data); return NULL; } eth_llc_data->saps = bmp_create(MAX_SAPS, 2); if (eth_llc_data->indices == NULL) { bmp_destroy(eth_llc_data->indices); shm_ap_rbuff_destroy(eth_llc_data->rb); shm_du_map_close(eth_llc_data->dum); free(eth_llc_data); return NULL; } pthread_rwlock_init(ð_llc_data->flows_lock, NULL); return eth_llc_data; } void eth_llc_ipcp_data_destroy() { int i = 0; if (_ipcp == NULL) return; pthread_rwlock_wrlock(&_ipcp->state_lock); if (_ipcp->state != IPCP_SHUTDOWN) LOG_WARN("Cleaning up while not in shutdown."); if (shim_data(_ipcp)->dum != NULL) shm_du_map_close_on_exit(shim_data(_ipcp)->dum); if (shim_data(_ipcp)->rb != NULL) shm_ap_rbuff_destroy(shim_data(_ipcp)->rb); if (shim_data(_ipcp)->indices != NULL) bmp_destroy(shim_data(_ipcp)->indices); if (shim_data(_ipcp)->saps != NULL) bmp_destroy(shim_data(_ipcp)->saps); pthread_rwlock_wrlock(&shim_data(_ipcp)->flows_lock); for (i = 0; i < AP_MAX_FLOWS; i ++) if (ipcp_flow(i)->rb != NULL) shm_ap_rbuff_close(ipcp_flow(i)->rb); pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); pthread_rwlock_unlock(&_ipcp->state_lock); ipcp_data_destroy(_ipcp->data); } /* only call this under flows_lock */ static int port_id_to_index(int port_id) { int i; for (i = 0; i < AP_MAX_FLOWS; ++i) { if (ipcp_flow(i)->port_id == port_id && ipcp_flow(i)->state != FLOW_NULL) return i; } return -1; } /* only call this under flows_lock */ static int addr_and_saps_to_index(const uint8_t * r_addr, uint8_t r_sap, uint8_t sap) { int i = 0; for (i = 0; i < AP_MAX_FLOWS; i++) { if (ipcp_flow(i)->state == FLOW_ALLOCATED && shim_data(_ipcp)->flows[i].r_sap == r_sap && shim_data(_ipcp)->flows[i].sap == sap && !memcmp(shim_data(_ipcp)->flows[i].r_addr, r_addr, MAC_SIZE)) { return i; } } return -1; } /* only call this under flows_lock */ static int sap_to_index(uint8_t sap) { int i = 0; for (i = 0; i < AP_MAX_FLOWS; i++) { if (shim_data(_ipcp)->flows[i].sap == sap) { return i; } } return -1; } /* only call this under flows_lock */ static void destroy_ipcp_flow(int index) { ipcp_flow(index)->port_id = -1; if (ipcp_flow(index)->rb != NULL) shm_ap_rbuff_close(ipcp_flow(index)->rb); ipcp_flow(index)->rb = NULL; ipcp_flow(index)->state = FLOW_NULL; bmp_release(shim_data(_ipcp)->indices, index); bmp_release(shim_data(_ipcp)->saps, shim_data(_ipcp)->flows[index].sap); } 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(uint8_t dst_addr[MAC_SIZE], uint8_t dsap, uint8_t ssap, uint8_t * payload, size_t len) { int frame_len = 0; uint8_t cf = 0x03; int fd; uint16_t size; uint16_t length; #if defined(PACKET_RX_RING) && defined(PACKET_TX_RING) struct pollfd pfd; struct tpacket_hdr * header; uint8_t * frame; #else uint8_t frame[SHIM_ETH_LLC_MAX_SDU_SIZE]; #ifdef __FreeBSD__ struct sockaddr_dl device; #else struct sockaddr_ll device; #endif #endif 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; fd = (shim_data(_ipcp))->s_fd; #if defined(PACKET_RX_RING) && defined(PACKET_TX_RING) header = (void *) shim_data(_ipcp)->tx_ring + (shim_data(_ipcp)->tx_offset * SHM_DU_BUFF_BLOCK_SIZE); while (header->tp_status != TP_STATUS_AVAILABLE) { pfd.fd = fd; pfd.revents = 0; pfd.events = POLLIN | POLLRDNORM | POLLERR; if (poll(&pfd, 1, -1) <= 0) { LOG_ERR("Failed to poll: %s.", strerror(errno)); continue; } header = (void *) shim_data(_ipcp)->tx_ring + (shim_data(_ipcp)->tx_offset * SHM_DU_BUFF_BLOCK_SIZE); } frame = (void *) header + TPACKET_HDRLEN - sizeof(struct sockaddr_ll); #endif llc_frame = (struct eth_llc_frame *) frame; memcpy(&llc_frame->dst_hwaddr, dst_addr, MAC_SIZE); memcpy(&llc_frame->src_hwaddr, #ifdef __FreeBSD__ LLADDR(&shim_data(_ipcp)->device), #else shim_data(_ipcp)->device.sll_addr, #endif MAC_SIZE); length = htons(LLC_HEADER_SIZE + sizeof(size) + len); memcpy(&llc_frame->length, &length, sizeof(length)); llc_frame->dsap = dsap; llc_frame->ssap = ssap; llc_frame->cf = cf; /* write the payload length, can't trust the driver */ size = htons(len); memcpy(&llc_frame->size, &size, sizeof(size)); memcpy(&llc_frame->payload, payload, len); frame_len = ETH_HEADER_SIZE + LLC_HEADER_SIZE + sizeof(uint16_t) + len; #if defined(PACKET_RX_RING) && defined(PACKET_TX_RING) header->tp_len = frame_len; header->tp_status = TP_STATUS_SEND_REQUEST; if (send(fd, NULL, 0, MSG_DONTWAIT) < 0) { LOG_ERR("Failed to write frame into TX_RING."); return -1; } shim_data(_ipcp)->tx_offset = (shim_data(_ipcp)->tx_offset + 1) & (SHM_BUFFER_SIZE -1); #else device = (shim_data(_ipcp))->device; if (sendto(fd, frame, frame_len, 0, (struct sockaddr *) &device, sizeof(device)) <= 0) { LOG_ERR("Failed to send message."); return -1; } #endif return 0; } static int eth_llc_ipcp_send_mgmt_frame(shim_eth_llc_msg_t * msg, uint8_t dst_addr[MAC_SIZE]) { size_t len; uint8_t * buf; len = shim_eth_llc_msg__get_packed_size(msg); if (len == 0) return -1; buf = malloc(len); if (buf == NULL) return -1; shim_eth_llc_msg__pack(msg, buf); if (eth_llc_ipcp_send_frame(dst_addr, reverse_bits(MGMT_SAP), reverse_bits(MGMT_SAP), buf, len)) { LOG_ERR("Failed to send management frame."); return -1; } free(buf); return 0; } static int eth_llc_ipcp_port_alloc(uint8_t dst_addr[MAC_SIZE], uint8_t ssap, char * dst_name, char * src_ae_name) { shim_eth_llc_msg_t msg = SHIM_ETH_LLC_MSG__INIT; msg.code = SHIM_ETH_LLC_MSG_CODE__FLOW_REQ; msg.ssap = ssap; msg.dst_name = dst_name; msg.src_ae_name = src_ae_name; return eth_llc_ipcp_send_mgmt_frame(&msg, dst_addr); } static int eth_llc_ipcp_port_alloc_resp(uint8_t dst_addr[MAC_SIZE], uint8_t ssap, uint8_t dsap, int response) { shim_eth_llc_msg_t msg = SHIM_ETH_LLC_MSG__INIT; msg.code = SHIM_ETH_LLC_MSG_CODE__FLOW_REPLY; msg.ssap = ssap; msg.has_dsap = true; msg.dsap = dsap; msg.has_response = true; msg.response = response; return eth_llc_ipcp_send_mgmt_frame(&msg, dst_addr); } static int eth_llc_ipcp_port_dealloc(uint8_t dst_addr[MAC_SIZE], uint8_t ssap) { shim_eth_llc_msg_t msg = SHIM_ETH_LLC_MSG__INIT; msg.code = SHIM_ETH_LLC_MSG_CODE__FLOW_DEALLOC; msg.ssap = ssap; return eth_llc_ipcp_send_mgmt_frame(&msg, dst_addr); } static int eth_llc_ipcp_port_req(uint8_t r_sap, uint8_t r_addr[MAC_SIZE], char * dst_name, char * src_ae_name) { int port_id; ssize_t index = 0; int i; pthread_rwlock_wrlock(&_ipcp->state_lock); pthread_rwlock_wrlock(&shim_data(_ipcp)->flows_lock); index = bmp_allocate(shim_data(_ipcp)->indices); if (index < 0) { pthread_rwlock_unlock(&_ipcp->state_lock); pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); LOG_ERR("Out of free indices."); return -1; } /* reply to IRM */ port_id = ipcp_flow_req_arr(getpid(), dst_name, src_ae_name); if (port_id < 0) { bmp_release(shim_data(_ipcp)->indices, index); pthread_rwlock_unlock(&_ipcp->state_lock); pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); LOG_ERR("Could not get port id from IRMd."); return -1; } ipcp_flow(index)->port_id = port_id; ipcp_flow(index)->rb = NULL; ipcp_flow(index)->state = FLOW_PENDING; shim_data(_ipcp)->flows[index].r_sap = r_sap; for (i = 0; i < MAC_SIZE; i++) { shim_data(_ipcp)->flows[index].r_addr[i] = r_addr[i]; } pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); pthread_rwlock_unlock(&_ipcp->state_lock); LOG_DBG("New flow request, port_id %d, remote SAP %d.", port_id, r_sap); return 0; } static int eth_llc_ipcp_port_alloc_reply(uint8_t ssap, uint8_t r_addr[MAC_SIZE], int dsap, int response) { int index = -1; int ret = 0; int port_id = -1; int i; pthread_rwlock_rdlock(&_ipcp->state_lock); pthread_rwlock_rdlock(&shim_data(_ipcp)->flows_lock); index = sap_to_index(ssap); if (index < 0) { pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); pthread_rwlock_unlock(&_ipcp->state_lock); LOG_ERR("No flow found with that SAP."); return -1; /* -EFLOWNOTFOUND */ } if (ipcp_flow(index)->state != FLOW_PENDING) { pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); pthread_rwlock_unlock(&_ipcp->state_lock); return -1; /* -EFLOWNOTPENDING */ } port_id = ipcp_flow(index)->port_id; if (response) { destroy_ipcp_flow(index); } else { ipcp_flow(index)->state = FLOW_ALLOCATED; shim_data(_ipcp)->flows[index].r_sap = dsap; for (i = 0; i < MAC_SIZE; i++) { shim_data(_ipcp)->flows[index].r_addr[i] = r_addr[i]; } } pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); pthread_rwlock_unlock(&_ipcp->state_lock); LOG_DBG("Flow reply, port_id %d, remote SAP %d.", port_id, dsap); if ((ret = ipcp_flow_alloc_reply(getpid(), port_id, response)) < 0) { return -1; /* -EPIPE */ } return ret; } static int eth_llc_ipcp_flow_dealloc_req(uint8_t ssap, uint8_t r_addr[MAC_SIZE]) { int port_id = -1; int i = 0; pthread_rwlock_rdlock(&_ipcp->state_lock); pthread_rwlock_wrlock(&shim_data(_ipcp)->flows_lock); i = sap_to_index(ssap); if (i < 0) { pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); pthread_rwlock_unlock(&_ipcp->state_lock); LOG_ERR("No flow found for remote deallocation request."); return 0; } port_id = ipcp_flow(i)->port_id; destroy_ipcp_flow(i); pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); pthread_rwlock_unlock(&_ipcp->state_lock); ipcp_flow_dealloc(0, port_id); LOG_DBG("Flow with port_id %d deallocated.", port_id); return 0; } static int eth_llc_ipcp_mgmt_frame(uint8_t * buf, size_t len, uint8_t r_addr[MAC_SIZE]) { shim_eth_llc_msg_t * msg = NULL; msg = shim_eth_llc_msg__unpack(NULL, len, buf); if (msg == NULL) { LOG_ERR("Failed to unpack."); return -1; } switch (msg->code) { case SHIM_ETH_LLC_MSG_CODE__FLOW_REQ: if (ipcp_data_is_in_registry(_ipcp->data, msg->dst_name)) { eth_llc_ipcp_port_req(msg->ssap, r_addr, msg->dst_name, msg->src_ae_name); } break; case SHIM_ETH_LLC_MSG_CODE__FLOW_REPLY: eth_llc_ipcp_port_alloc_reply(msg->ssap, r_addr, msg->dsap, msg->response); break; case SHIM_ETH_LLC_MSG_CODE__FLOW_DEALLOC: eth_llc_ipcp_flow_dealloc_req(msg->ssap, r_addr); break; default: LOG_ERR("Unknown message received %d.", msg->code); shim_eth_llc_msg__free_unpacked(msg, NULL); return -1; } shim_eth_llc_msg__free_unpacked(msg, NULL); return 0; } static void * eth_llc_ipcp_sdu_reader(void * o) { ssize_t index; struct rb_entry e; uint8_t br_addr[MAC_SIZE]; uint8_t dsap; uint8_t ssap; int i = 0; struct eth_llc_frame * llc_frame; uint16_t size; #if defined(PACKET_RX_RING) && defined(PACKET_TX_RING) struct pollfd pfd; int offset = 0; struct tpacket_hdr * header; uint8_t * buf = NULL; #else uint8_t buf[ETH_FRAME_SIZE]; int frame_len = 0; #endif memset(br_addr, 0xff, MAC_SIZE * sizeof(uint8_t)); while (true) { pthread_rwlock_rdlock(&_ipcp->state_lock); if (_ipcp->state != IPCP_ENROLLED) { pthread_rwlock_unlock(&_ipcp->state_lock); return (void *) 1; /* -ENOTENROLLED */ } pthread_rwlock_unlock(&_ipcp->state_lock); #if defined(PACKET_RX_RING) && defined(PACKET_TX_RING) header = (void *) shim_data(_ipcp)->rx_ring + (offset * SHM_DU_BUFF_BLOCK_SIZE); while (!(header->tp_status & TP_STATUS_USER)) { pfd.fd = shim_data(_ipcp)->s_fd; pfd.revents = 0; pfd.events = POLLIN | POLLRDNORM | POLLERR; if (poll(&pfd, 1, -1) <= 0) { LOG_ERR("Failed to poll: %s.", strerror(errno)); continue; } header = (void *) shim_data(_ipcp)->rx_ring + (offset * SHM_DU_BUFF_BLOCK_SIZE); } buf = (void * ) header + header->tp_mac; #else frame_len = recv(shim_data(_ipcp)->s_fd, buf, SHIM_ETH_LLC_MAX_SDU_SIZE, 0); if (frame_len < 0) { LOG_ERR("Failed to recv frame."); continue; } #endif llc_frame = (struct eth_llc_frame *) buf; #ifdef __FreeBSD__ if (memcmp(LLADDR(&shim_data(_ipcp)->device), #else if (memcmp(shim_data(_ipcp)->device.sll_addr, #endif &llc_frame->dst_hwaddr, MAC_SIZE) && memcmp(br_addr, &llc_frame->dst_hwaddr, MAC_SIZE)) { #if defined(PACKET_RX_RING) && defined(PACKET_TX_RING) offset = (offset + 1) & (SHM_BUFFER_SIZE - 1); header->tp_status = TP_STATUS_KERNEL; #endif continue; } dsap = reverse_bits(llc_frame->dsap); ssap = reverse_bits(llc_frame->ssap); memcpy(&size, &llc_frame->size, sizeof(size)); if (ssap == MGMT_SAP && dsap == MGMT_SAP) { eth_llc_ipcp_mgmt_frame(&llc_frame->payload, ntohs(size), llc_frame->src_hwaddr); } else { pthread_rwlock_rdlock(&shim_data(_ipcp)->flows_lock); i = addr_and_saps_to_index(llc_frame->src_hwaddr, ssap, dsap); if (i < 0) { pthread_rwlock_unlock(&shim_data(_ipcp)-> flows_lock); #if defined(PACKET_RX_RING) && defined(PACKET_TX_RING) offset = (offset + 1) & (SHM_BUFFER_SIZE - 1); header->tp_status = TP_STATUS_KERNEL; #endif continue; } while ((index = shm_du_map_write(shim_data(_ipcp)->dum, ipcp_flow(i)->api, 0, 0, &llc_frame->payload, ntohs(size))) < 0) ; e.index = index; e.port_id = ipcp_flow(i)->port_id; while (shm_ap_rbuff_write(ipcp_flow(i)->rb, &e) < 0) ; pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); } #if defined(PACKET_RX_RING) && defined(PACKET_TX_RING) offset = (offset + 1) & (SHM_BUFFER_SIZE -1); header->tp_status = TP_STATUS_KERNEL; #endif } return (void *) 0; } static void * eth_llc_ipcp_sdu_writer(void * o) { while (true) { struct rb_entry * e; int i; int len = 0; uint8_t * buf; uint8_t ssap; uint8_t dsap; e = shm_ap_rbuff_read(shim_data(_ipcp)->rb); if (e == NULL) continue; pthread_rwlock_rdlock(&_ipcp->state_lock); if (_ipcp->state != IPCP_ENROLLED) { pthread_rwlock_unlock(&_ipcp->state_lock); return (void *) 1; /* -ENOTENROLLED */ } pthread_rwlock_unlock(&_ipcp->state_lock); len = shm_du_map_read((uint8_t **) &buf, shim_data(_ipcp)->dum, e->index); if (len <= 0) { free(e); LOG_ERR("Length of du map read was %d.", len); continue; } pthread_rwlock_rdlock(&shim_data(_ipcp)->flows_lock); i = port_id_to_index(e->port_id); if (i < 0) { free(e); pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); continue; } ssap = reverse_bits(shim_data(_ipcp)->flows[i].sap); dsap = reverse_bits(shim_data(_ipcp)->flows[i].r_sap); eth_llc_ipcp_send_frame(shim_data(_ipcp)->flows[i].r_addr, dsap, ssap, buf, len); pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); if (shim_data(_ipcp)->dum != NULL) shm_du_map_remove(shim_data(_ipcp)->dum, e->index); free(e); } return (void *) 1; } void ipcp_sig_handler(int sig, siginfo_t * info, void * c) { sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGINT); switch(sig) { case SIGINT: case SIGTERM: case SIGHUP: if (info->si_pid == irmd_api) { bool clean_threads = false; LOG_DBG("Terminating by order of %d. Bye.", info->si_pid); pthread_rwlock_wrlock(&_ipcp->state_lock); if (_ipcp->state == IPCP_ENROLLED) clean_threads = true; _ipcp->state = IPCP_SHUTDOWN; pthread_rwlock_unlock(&_ipcp->state_lock); if (clean_threads) { pthread_cancel(shim_data(_ipcp)->sdu_reader); pthread_cancel(shim_data(_ipcp)->sdu_writer); pthread_join(shim_data(_ipcp)->sdu_writer, NULL); pthread_join(shim_data(_ipcp)->sdu_reader, NULL); } pthread_cancel(shim_data(_ipcp)->mainloop); } default: return; } } static int eth_llc_ipcp_bootstrap(struct dif_config * conf) { int fd = -1; struct ifreq ifr; int index; #ifdef __FreeBSD__ struct ifaddrs * ifaddr; struct ifaddrs * ifa; struct sockaddr_dl device; #else struct sockaddr_ll device; #endif #if defined(PACKET_RX_RING) && defined(PACKET_TX_RING) struct tpacket_req req; #endif if (conf == NULL) return -1; /* -EINVAL */ if (conf->type != THIS_TYPE) { LOG_ERR("Config doesn't match IPCP type."); return -1; } if (conf->if_name == NULL) { LOG_ERR("Interface name is NULL."); return -1; } memset(&ifr, 0, sizeof(ifr)); fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) { LOG_ERR("Failed to open socket."); return -1; } memcpy(ifr.ifr_name, conf->if_name, strlen(conf->if_name)); #ifdef __FreeBSD__ if (getifaddrs(&ifaddr) < 0) { close(fd); LOG_ERR("Could not get interfaces."); return -1; } for (ifa = ifaddr, index = 0; ifa != NULL; ifa = ifa->ifa_next, ++index) { if (strcmp(ifa->ifa_name, conf->if_name)) continue; LOG_DBGF("Interface %s found.", conf->if_name); memcpy(&ifr.ifr_addr, ifa->ifa_addr, sizeof(*ifa->ifa_addr)); break; } if (ifa == NULL) { LOG_ERR("Interface not found."); freeifaddrs(ifaddr); return -1; } freeifaddrs(ifaddr); #else if (ioctl(fd, SIOCGIFHWADDR, &ifr)) { close(fd); LOG_ERR("Failed to ioctl: %s.", strerror(errno)); return -1; } index = if_nametoindex(conf->if_name); if (index == 0) { LOG_ERR("Failed to retrieve interface index."); return -1; } #endif close(fd); memset(&(device), 0, sizeof(device)); #ifdef __FreeBSD__ device.sdl_index = index; device.sdl_family = AF_LINK; memcpy(LLADDR(&device), ifr.ifr_addr.sa_data, MAC_SIZE * sizeof (uint8_t)); device.sdl_alen = MAC_SIZE; /* TODO: replace socket calls with bpf for BSD */ LOG_MISSING; fd = socket(AF_LINK, SOCK_RAW, 0); #else device.sll_ifindex = index; device.sll_family = AF_PACKET; memcpy(device.sll_addr, ifr.ifr_hwaddr.sa_data, MAC_SIZE * sizeof (uint8_t)); device.sll_halen = MAC_SIZE; device.sll_protocol = htons(ETH_P_ALL); fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_802_2)); #endif if (fd < 0) { LOG_ERR("Failed to create socket: %s.", strerror(errno)); return -1; } #if defined(PACKET_RX_RING) && defined(PACKET_TX_RING) if (SHIM_ETH_LLC_MAX_SDU_SIZE > SHM_DU_BUFF_BLOCK_SIZE) { LOG_ERR("Max SDU size is bigger than DU map block size."); close(fd); return -1; } req.tp_block_size = SHM_DU_BUFF_BLOCK_SIZE; req.tp_frame_size = SHM_DU_BUFF_BLOCK_SIZE; req.tp_block_nr = SHM_BUFFER_SIZE; req.tp_frame_nr = SHM_BUFFER_SIZE; if (setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, sizeof(req))) { LOG_ERR("Failed to set sockopt PACKET_RX_RING"); close(fd); return -1; } if (setsockopt(fd, SOL_PACKET, PACKET_TX_RING, (void *) &req, sizeof(req))) { LOG_ERR("Failed to set sockopt PACKET_TX_RING"); close(fd); return -1; } #endif if (bind(fd,(struct sockaddr *) &device, sizeof(device))) { LOG_ERR("Failed to bind socket to interface"); close(fd); return -1; } #if defined(PACKET_RX_RING) && defined(PACKET_TX_RING) shim_data(_ipcp)->rx_ring = mmap(NULL, 2 * SHM_DU_BUFF_BLOCK_SIZE * SHM_BUFFER_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (shim_data(_ipcp)->rx_ring == NULL) { LOG_ERR("Failed to mmap"); close(fd); return -1; } shim_data(_ipcp)->tx_ring = shim_data(_ipcp)->rx_ring + (SHM_DU_BUFF_BLOCK_SIZE * SHM_BUFFER_SIZE); #endif pthread_rwlock_wrlock(&_ipcp->state_lock); if (_ipcp->state != IPCP_INIT) { pthread_rwlock_unlock(&_ipcp->state_lock); LOG_ERR("IPCP in wrong state."); close(fd); return -1; } shim_data(_ipcp)->s_fd = fd; shim_data(_ipcp)->device = device; shim_data(_ipcp)->tx_offset = 0; _ipcp->state = IPCP_ENROLLED; pthread_create(&shim_data(_ipcp)->sdu_reader, NULL, eth_llc_ipcp_sdu_reader, NULL); pthread_create(&shim_data(_ipcp)->sdu_writer, NULL, eth_llc_ipcp_sdu_writer, NULL); pthread_rwlock_unlock(&_ipcp->state_lock); LOG_DBG("Bootstrapped shim IPCP over Ethernet with LLC with api %d.", getpid()); return 0; } static int eth_llc_ipcp_name_reg(char * name) { pthread_rwlock_rdlock(&_ipcp->state_lock); if (_ipcp->state != IPCP_ENROLLED) { pthread_rwlock_unlock(&_ipcp->state_lock); LOG_DBGF("Won't register with non-enrolled IPCP."); return -1; /* -ENOTENROLLED */ } if (ipcp_data_add_reg_entry(_ipcp->data, name)) { pthread_rwlock_unlock(&_ipcp->state_lock); LOG_ERR("Failed to add %s to local registry.", name); return -1; } pthread_rwlock_unlock(&_ipcp->state_lock); LOG_DBG("Registered %s.", name); return 0; } static int eth_llc_ipcp_name_unreg(char * name) { pthread_rwlock_rdlock(&_ipcp->state_lock); ipcp_data_del_reg_entry(_ipcp->data, name); pthread_rwlock_unlock(&_ipcp->state_lock); return 0; } static int eth_llc_ipcp_flow_alloc(pid_t n_api, int port_id, char * dst_name, char * src_ae_name, enum qos_cube qos) { struct shm_ap_rbuff * rb; uint8_t ssap = 0; uint8_t r_addr[MAC_SIZE]; int index = 0; LOG_INFO("Allocating flow to %s.", dst_name); if (dst_name == NULL || src_ae_name == NULL) return -1; if (qos != QOS_CUBE_BE) LOG_DBGF("QoS requested. Ethernet LLC can't do that. For now."); rb = shm_ap_rbuff_open(n_api); if (rb == NULL) return -1; /* -ENORBUFF */ pthread_rwlock_wrlock(&_ipcp->state_lock); if (_ipcp->state != IPCP_ENROLLED) { shm_ap_rbuff_close(rb); pthread_rwlock_unlock(&_ipcp->state_lock); LOG_DBGF("Won't allocate flow with non-enrolled IPCP."); return -1; /* -ENOTENROLLED */ } index = bmp_allocate(shim_data(_ipcp)->indices); if (index < 0) { shm_ap_rbuff_close(rb); pthread_rwlock_unlock(&_ipcp->state_lock); return -1; } pthread_rwlock_wrlock(&shim_data(_ipcp)->flows_lock); ssap = bmp_allocate(shim_data(_ipcp)->saps); if (ssap < 0) { shm_ap_rbuff_close(rb); bmp_release(shim_data(_ipcp)->indices, index); pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); pthread_rwlock_unlock(&_ipcp->state_lock); return -1; } ipcp_flow(index)->port_id = port_id; ipcp_flow(index)->state = FLOW_PENDING; ipcp_flow(index)->rb = rb; shim_data(_ipcp)->flows[index].sap = ssap; pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); pthread_rwlock_unlock(&_ipcp->state_lock); memset(r_addr, 0xff, MAC_SIZE * sizeof(uint8_t)); if (eth_llc_ipcp_port_alloc(r_addr, ssap, dst_name, src_ae_name) < 0) { LOG_DBGF("Port alloc returned -1."); pthread_rwlock_wrlock(&_ipcp->state_lock); pthread_rwlock_wrlock(&shim_data(_ipcp)->flows_lock); destroy_ipcp_flow(index); pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); pthread_rwlock_unlock(&_ipcp->state_lock); return -1; } LOG_DBG("Pending flow with port_id %d on SAP %d.", port_id, ssap); return index; } static int eth_llc_ipcp_flow_alloc_resp(pid_t n_api, int port_id, int response) { struct shm_ap_rbuff * rb; int index = -1; uint8_t ssap = 0; pthread_rwlock_wrlock(&_ipcp->state_lock); pthread_rwlock_wrlock(&shim_data(_ipcp)->flows_lock); index = port_id_to_index(port_id); if (index < 0) { pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); pthread_rwlock_unlock(&_ipcp->state_lock); LOG_DBGF("Could not find flow with port_id %d.", port_id); return -1; } if (ipcp_flow(index)->state != FLOW_PENDING) { pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); pthread_rwlock_unlock(&_ipcp->state_lock); LOG_DBGF("Flow was not pending."); return -1; } rb = shm_ap_rbuff_open(n_api); if (rb == NULL) { LOG_ERR("Could not open N + 1 ringbuffer."); ipcp_flow(index)->state = FLOW_NULL; ipcp_flow(index)->port_id = -1; bmp_release(shim_data(_ipcp)->indices, index); pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); pthread_rwlock_unlock(&_ipcp->state_lock); return -1; } ssap = bmp_allocate(shim_data(_ipcp)->saps); if (ssap < 0) { ipcp_flow(index)->state = FLOW_NULL; ipcp_flow(index)->port_id = -1; shm_ap_rbuff_close(ipcp_flow(index)->rb); bmp_release(shim_data(_ipcp)->indices, index); pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); pthread_rwlock_unlock(&_ipcp->state_lock); return -1; } ipcp_flow(index)->state = FLOW_ALLOCATED; ipcp_flow(index)->rb = rb; shim_data(_ipcp)->flows[index].sap = ssap; pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); pthread_rwlock_unlock(&_ipcp->state_lock); if (eth_llc_ipcp_port_alloc_resp(shim_data(_ipcp)->flows[index].r_addr, shim_data(_ipcp)->flows[index].r_sap, ssap, response) < 0) { pthread_rwlock_rdlock(&_ipcp->state_lock); pthread_rwlock_wrlock(&shim_data(_ipcp)->flows_lock); destroy_ipcp_flow(index); pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); pthread_rwlock_unlock(&_ipcp->state_lock); LOG_DBGF("Could not send response."); return -1; } LOG_DBG("Accepted flow, port_id %d, SAP %d.", port_id, ssap); return 0; } static int eth_llc_ipcp_flow_dealloc(int port_id) { int index = -1; uint8_t sap; uint8_t addr[MAC_SIZE]; int i; int ret; pthread_rwlock_rdlock(&_ipcp->state_lock); pthread_rwlock_wrlock(&shim_data(_ipcp)->flows_lock); index = port_id_to_index(port_id); if (index < 0) { pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); pthread_rwlock_unlock(&_ipcp->state_lock); return 0; } sap = shim_data(_ipcp)->flows[index].r_sap; for (i = 0; i < MAC_SIZE; i++) { addr[i] = shim_data(_ipcp)->flows[index].r_addr[i]; } destroy_ipcp_flow(index); pthread_rwlock_unlock(&shim_data(_ipcp)->flows_lock); ret = eth_llc_ipcp_port_dealloc(addr, sap); pthread_rwlock_unlock(&_ipcp->state_lock); if (ret < 0) LOG_DBGF("Could not notify remote."); LOG_DBG("Flow with port_id %d deallocated.", port_id); return 0; } static struct ipcp_ops eth_llc_ops = { .ipcp_bootstrap = eth_llc_ipcp_bootstrap, .ipcp_enroll = NULL, /* shim */ .ipcp_name_reg = eth_llc_ipcp_name_reg, .ipcp_name_unreg = eth_llc_ipcp_name_unreg, .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[]) { struct sigaction sig_act; sigset_t sigset; int i = 0; sigemptyset(&sigset); sigaddset(&sigset, SIGINT); sigaddset(&sigset, SIGQUIT); sigaddset(&sigset, SIGHUP); sigaddset(&sigset, SIGPIPE); if (ipcp_parse_arg(argc, argv)) { LOG_ERR("Failed to parse arguments."); exit(EXIT_FAILURE); } /* store the process id of the irmd */ irmd_api = atoi(argv[1]); /* init sig_act */ memset(&sig_act, 0, sizeof(sig_act)); /* install signal traps */ sig_act.sa_sigaction = &ipcp_sig_handler; sig_act.sa_flags = SA_SIGINFO; sigaction(SIGINT, &sig_act, NULL); sigaction(SIGTERM, &sig_act, NULL); sigaction(SIGHUP, &sig_act, NULL); sigaction(SIGPIPE, &sig_act, NULL); _ipcp = ipcp_instance_create(); if (_ipcp == NULL) { LOG_ERR("Failed to create instance."); close_logfile(); exit(EXIT_FAILURE); } _ipcp->data = (struct ipcp_data *) eth_llc_ipcp_data_create(); if (_ipcp->data == NULL) { LOG_ERR("Failed to create instance data."); free(_ipcp); close_logfile(); exit(EXIT_FAILURE); } for (i = 0; i < AP_MAX_FLOWS; i++) { ipcp_flow(i)->rb = NULL; ipcp_flow(i)->port_id = -1; ipcp_flow(i)->state = FLOW_NULL; } _ipcp->ops = ð_llc_ops; _ipcp->state = IPCP_INIT; pthread_rwlock_wrlock(&_ipcp->state_lock); pthread_sigmask(SIG_BLOCK, &sigset, NULL); pthread_create(&shim_data(_ipcp)->mainloop, NULL, ipcp_main_loop, _ipcp); pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); pthread_rwlock_unlock(&_ipcp->state_lock); if (ipcp_create_r(getpid())) { LOG_ERR("Failed to notify IRMd we are initialized."); close_logfile(); exit(EXIT_FAILURE); } pthread_join(shim_data(_ipcp)->mainloop, NULL); eth_llc_ipcp_data_destroy(); free(_ipcp); close_logfile(); exit(EXIT_SUCCESS); }