/* * Copyright (C) 2014 BlueKitchen GmbH * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * 4. Any redistribution, use, or modification is done solely for * personal benefit and not for any commercial purpose or for * monetary gain. * * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Please inquire about commercial licensing options at * contact@bluekitchen-gmbh.com * */ #define BTSTACK_FILE__ "btstack_network_posix.c" /* * btstack_network.c * Implementation of the btstack_network.h interface for POSIX systems */ #include "btstack_network.h" #include "btstack_config.h" #include #include #include #include #include #include #include #include #include #include #if defined(__APPLE__) || defined(__FreeBSD__) #include #include #include #include #endif #include #include #include #include #include #ifdef __linux #include #include #endif #include "btstack.h" static int tap_fd = -1; static uint8_t network_buffer[BNEP_MTU_MIN]; static size_t network_buffer_len = 0; static char tap_dev_name[16]; #if defined(__APPLE__) || defined(__FreeBSD__) // tuntaposx provides fixed set of tapX devices static const char * tap_dev = "/dev/tap0"; static const char * tap_dev_name_template = "tap0"; #endif #ifdef __linux // Linux uses single control device to bring up tunX or tapX interface static const char * tap_dev = "/dev/net/tun"; static const char * tap_dev_name_template = "bnep%d"; #endif static btstack_data_source_t tap_dev_ds; static void (*btstack_network_send_packet_callback)(const uint8_t * packet, uint16_t size); /* * @text Listing processTapData shows how a packet is received from the TAP network interface * and forwarded over the BNEP connection. * * After successfully reading a network packet, the call to * the *bnep_can_send_packet_now* function checks, if BTstack can forward * a network packet now. If that's not possible, the received data stays * in the network buffer and the data source elements is removed from the * run loop. The *process_tap_dev_data* function will not be called until * the data source is registered again. This provides a basic flow control. */ /* LISTING_START(processTapData): Process incoming network packets */ static void process_tap_dev_data(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type) { UNUSED(ds); UNUSED(callback_type); ssize_t len; len = read(ds->source.fd, network_buffer, sizeof(network_buffer)); if (len <= 0){ fprintf(stderr, "TAP: Error while reading: %s\n", strerror(errno)); return; } network_buffer_len = len; // disable reading from netif btstack_run_loop_disable_data_source_callbacks(&tap_dev_ds, DATA_SOURCE_CALLBACK_READ); // let client now (*btstack_network_send_packet_callback)(network_buffer, network_buffer_len); } /** * @brief Initialize network interface * @param send_packet_callback */ void btstack_network_init(void (*send_packet_callback)(const uint8_t * packet, uint16_t size)){ btstack_network_send_packet_callback = send_packet_callback; } /** * @text This code requries a TUN/TAP interface to connect the Bluetooth network interface * with the native system. It has been tested on Linux and OS X, but should work on any * system that provides TUN/TAP with minor modifications. * * On Linux, TUN/TAP is available by default. On OS X, tuntaposx from * http://tuntaposx.sourceforge.net needs to be installed. * * The *tap_alloc* function sets up a virtual network interface with the given Bluetooth Address. * It is rather low-level as it sets up and configures a network interface. * * @brief Bring up network interfacd * @param network_address * @return 0 if ok */ int btstack_network_up(bd_addr_t network_address){ struct ifreq ifr; int fd_dev; int fd_socket; if( (fd_dev = open(tap_dev, O_RDWR)) < 0 ) { fprintf(stderr, "TAP: Error opening %s: %s\n", tap_dev, strerror(errno)); return -1; } #ifdef __linux memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TAP | IFF_NO_PI; strncpy(ifr.ifr_name, tap_dev_name_template, IFNAMSIZ); // device name pattern int err; if( (err = ioctl(fd_dev, TUNSETIFF, (void *) &ifr)) < 0 ) { fprintf(stderr, "TAP: Error setting device name: %s\n", strerror(errno)); close(fd_dev); return -1; } strcpy(tap_dev_name, ifr.ifr_name); #endif #ifdef __APPLE__ strcpy(tap_dev_name, tap_dev_name_template); #endif fd_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); if (fd_socket < 0) { close(fd_dev); fprintf(stderr, "TAP: Error opening netlink socket: %s\n", strerror(errno)); return -1; } // Configure the MAC address of the newly created bnep(x) // device to the local bd_address memset (&ifr, 0, sizeof(struct ifreq)); strcpy(ifr.ifr_name, tap_dev_name); #ifdef __linux ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; memcpy(ifr.ifr_hwaddr.sa_data, network_address, sizeof(bd_addr_t)); if (ioctl(fd_socket, SIOCSIFHWADDR, &ifr) == -1) { close(fd_dev); close(fd_socket); fprintf(stderr, "TAP: Error setting hw addr: %s\n", strerror(errno)); exit(1); return -1; } #endif #ifdef __APPLE__ ifr.ifr_addr.sa_len = ETHER_ADDR_LEN; ifr.ifr_addr.sa_family = AF_LINK; (void)memcpy(ifr.ifr_addr.sa_data, network_address, ETHER_ADDR_LEN); if (ioctl(fd_socket, SIOCSIFLLADDR, &ifr) == -1) { close(fd_dev); close(fd_socket); fprintf(stderr, "TAP: Error setting hw addr: %s\n", strerror(errno)); exit(1); return -1; } #endif // Bring the interface up if (ioctl(fd_socket, SIOCGIFFLAGS, &ifr) == -1) { close(fd_dev); close(fd_socket); fprintf(stderr, "TAP: Error reading interface flags: %s\n", strerror(errno)); return -1; } if ((ifr.ifr_flags & IFF_UP) == 0) { ifr.ifr_flags |= IFF_UP; if (ioctl(fd_socket, SIOCSIFFLAGS, &ifr) == -1) { close(fd_dev); close(fd_socket); fprintf(stderr, "TAP: Error set IFF_UP: %s\n", strerror(errno)); return -1; } } close(fd_socket); tap_fd = fd_dev; log_info("BNEP device \"%s\" allocated", tap_dev_name); /* Create and register a new runloop data source */ btstack_run_loop_set_data_source_fd(&tap_dev_ds, tap_fd); btstack_run_loop_set_data_source_handler(&tap_dev_ds, &process_tap_dev_data); btstack_run_loop_add_data_source(&tap_dev_ds); btstack_run_loop_enable_data_source_callbacks(&tap_dev_ds, DATA_SOURCE_CALLBACK_READ); return 0; } /** * @brief Get network name after network was activated * @note e.g. tapX on Linux, might not be useful on all platforms * @returns network name */ const char * btstack_network_get_name(void){ return tap_dev_name; } /** * @brief Bring up network interface * @param network_address * @return 0 if ok */ int btstack_network_down(void){ log_info("BNEP channel closed"); btstack_run_loop_remove_data_source(&tap_dev_ds); if (tap_fd >= 0){ close(tap_fd); } tap_fd = -1; return 0; } /** * @brief Receive packet on network interface, e.g., forward packet to TCP/IP stack * @param packet * @param size */ void btstack_network_process_packet(const uint8_t * packet, uint16_t size){ if (tap_fd < 0) return; // Write out the ethernet frame to the tap device int rc = write(tap_fd, packet, size); if (rc < 0) { log_error("TAP: Could not write to TAP device: %s", strerror(errno)); } else if (rc != size) { log_error("TAP: Package written only partially %d of %d bytes", rc, size); } } /** * @brief Notify network interface that packet from send_packet_callback was sent and the next packet can be delivered. */ void btstack_network_packet_sent(void){ network_buffer_len = 0; // Re-enable the tap device data source btstack_run_loop_enable_data_source_callbacks(&tap_dev_ds, DATA_SOURCE_CALLBACK_READ); }