1 /* 2 * Copyright (C) 2014 BlueKitchen GmbH 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the copyright holders nor the names of 14 * contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 4. Any redistribution, use, or modification is done solely for 17 * personal benefit and not for any commercial purpose or for 18 * monetary gain. 19 * 20 * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN 24 * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * Please inquire about commercial licensing options at 34 * [email protected] 35 * 36 */ 37 38 #define BTSTACK_FILE__ "btstack_network_posix.c" 39 40 /* 41 * btstack_network.c 42 * Implementation of the btstack_network.h interface for POSIX systems 43 */ 44 45 46 #include "btstack_network.h" 47 48 #include "btstack_config.h" 49 50 #include <arpa/inet.h> 51 #include <errno.h> 52 #include <fcntl.h> 53 #include <ifaddrs.h> 54 #include <net/if_arp.h> 55 #include <stdint.h> 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <string.h> 59 #include <unistd.h> 60 61 #if defined(__APPLE__) || defined(__FreeBSD__) 62 #include <net/if.h> 63 #include <net/if_types.h> 64 #include <netinet/if_ether.h> 65 #include <netinet/in.h> 66 #endif 67 68 #include <sys/ioctl.h> 69 #include <sys/param.h> 70 #include <sys/socket.h> 71 #include <sys/stat.h> 72 #include <sys/types.h> 73 74 #ifdef __linux 75 #include <linux/if.h> 76 #include <linux/if_tun.h> 77 #endif 78 79 #include "btstack.h" 80 81 static int tap_fd = -1; 82 static uint8_t network_buffer[BNEP_MTU_MIN]; 83 static size_t network_buffer_len = 0; 84 static char tap_dev_name[16]; 85 86 #if defined(__APPLE__) || defined(__FreeBSD__) 87 // tuntaposx provides fixed set of tapX devices 88 static const char * tap_dev = "/dev/tap0"; 89 static const char * tap_dev_name_template = "tap0"; 90 #endif 91 92 #ifdef __linux 93 // Linux uses single control device to bring up tunX or tapX interface 94 static const char * tap_dev = "/dev/net/tun"; 95 static const char * tap_dev_name_template = "bnep%d"; 96 #endif 97 98 static btstack_data_source_t tap_dev_ds; 99 100 static void (*btstack_network_send_packet_callback)(const uint8_t * packet, uint16_t size); 101 102 /* 103 * @text Listing processTapData shows how a packet is received from the TAP network interface 104 * and forwarded over the BNEP connection. 105 * 106 * After successfully reading a network packet, the call to 107 * the *bnep_can_send_packet_now* function checks, if BTstack can forward 108 * a network packet now. If that's not possible, the received data stays 109 * in the network buffer and the data source elements is removed from the 110 * run loop. The *process_tap_dev_data* function will not be called until 111 * the data source is registered again. This provides a basic flow control. 112 */ 113 114 /* LISTING_START(processTapData): Process incoming network packets */ 115 static void process_tap_dev_data(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type) 116 { 117 UNUSED(ds); 118 UNUSED(callback_type); 119 120 ssize_t len; 121 len = read(ds->source.fd, network_buffer, sizeof(network_buffer)); 122 if (len <= 0){ 123 fprintf(stderr, "TAP: Error while reading: %s\n", strerror(errno)); 124 return; 125 } 126 127 network_buffer_len = len; 128 129 // disable reading from netif 130 btstack_run_loop_disable_data_source_callbacks(&tap_dev_ds, DATA_SOURCE_CALLBACK_READ); 131 132 // let client now 133 (*btstack_network_send_packet_callback)(network_buffer, network_buffer_len); 134 } 135 136 /** 137 * @brief Initialize network interface 138 * @param send_packet_callback 139 */ 140 void btstack_network_init(void (*send_packet_callback)(const uint8_t * packet, uint16_t size)){ 141 btstack_network_send_packet_callback = send_packet_callback; 142 } 143 144 /** 145 * @text This code requries a TUN/TAP interface to connect the Bluetooth network interface 146 * with the native system. It has been tested on Linux and OS X, but should work on any 147 * system that provides TUN/TAP with minor modifications. 148 * 149 * On Linux, TUN/TAP is available by default. On OS X, tuntaposx from 150 * http://tuntaposx.sourceforge.net needs to be installed. 151 * 152 * The *tap_alloc* function sets up a virtual network interface with the given Bluetooth Address. 153 * It is rather low-level as it sets up and configures a network interface. 154 * 155 * @brief Bring up network interfacd 156 * @param network_address 157 * @return 0 if ok 158 */ 159 int btstack_network_up(bd_addr_t network_address){ 160 161 struct ifreq ifr; 162 int fd_dev; 163 int fd_socket; 164 165 if( (fd_dev = open(tap_dev, O_RDWR)) < 0 ) { 166 fprintf(stderr, "TAP: Error opening %s: %s\n", tap_dev, strerror(errno)); 167 return -1; 168 } 169 170 #ifdef __linux 171 memset(&ifr, 0, sizeof(ifr)); 172 ifr.ifr_flags = IFF_TAP | IFF_NO_PI; 173 strncpy(ifr.ifr_name, tap_dev_name_template, IFNAMSIZ); // device name pattern 174 175 int err; 176 if( (err = ioctl(fd_dev, TUNSETIFF, (void *) &ifr)) < 0 ) { 177 fprintf(stderr, "TAP: Error setting device name: %s\n", strerror(errno)); 178 close(fd_dev); 179 return -1; 180 } 181 strcpy(tap_dev_name, ifr.ifr_name); 182 #endif 183 #ifdef __APPLE__ 184 strcpy(tap_dev_name, tap_dev_name_template); 185 #endif 186 187 fd_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); 188 if (fd_socket < 0) { 189 close(fd_dev); 190 fprintf(stderr, "TAP: Error opening netlink socket: %s\n", strerror(errno)); 191 return -1; 192 } 193 194 // Configure the MAC address of the newly created bnep(x) 195 // device to the local bd_address 196 memset (&ifr, 0, sizeof(struct ifreq)); 197 strcpy(ifr.ifr_name, tap_dev_name); 198 #ifdef __linux 199 ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; 200 memcpy(ifr.ifr_hwaddr.sa_data, network_address, sizeof(bd_addr_t)); 201 if (ioctl(fd_socket, SIOCSIFHWADDR, &ifr) == -1) { 202 close(fd_dev); 203 close(fd_socket); 204 fprintf(stderr, "TAP: Error setting hw addr: %s\n", strerror(errno)); 205 exit(1); 206 return -1; 207 } 208 #endif 209 #ifdef __APPLE__ 210 ifr.ifr_addr.sa_len = ETHER_ADDR_LEN; 211 ifr.ifr_addr.sa_family = AF_LINK; 212 (void)memcpy(ifr.ifr_addr.sa_data, network_address, ETHER_ADDR_LEN); 213 if (ioctl(fd_socket, SIOCSIFLLADDR, &ifr) == -1) { 214 close(fd_dev); 215 close(fd_socket); 216 fprintf(stderr, "TAP: Error setting hw addr: %s\n", strerror(errno)); 217 exit(1); 218 return -1; 219 } 220 #endif 221 222 // Bring the interface up 223 if (ioctl(fd_socket, SIOCGIFFLAGS, &ifr) == -1) { 224 close(fd_dev); 225 close(fd_socket); 226 fprintf(stderr, "TAP: Error reading interface flags: %s\n", strerror(errno)); 227 return -1; 228 } 229 230 if ((ifr.ifr_flags & IFF_UP) == 0) { 231 ifr.ifr_flags |= IFF_UP; 232 233 if (ioctl(fd_socket, SIOCSIFFLAGS, &ifr) == -1) { 234 close(fd_dev); 235 close(fd_socket); 236 fprintf(stderr, "TAP: Error set IFF_UP: %s\n", strerror(errno)); 237 return -1; 238 } 239 } 240 241 close(fd_socket); 242 243 tap_fd = fd_dev; 244 log_info("BNEP device \"%s\" allocated", tap_dev_name); 245 246 /* Create and register a new runloop data source */ 247 btstack_run_loop_set_data_source_fd(&tap_dev_ds, tap_fd); 248 btstack_run_loop_set_data_source_handler(&tap_dev_ds, &process_tap_dev_data); 249 btstack_run_loop_add_data_source(&tap_dev_ds); 250 btstack_run_loop_enable_data_source_callbacks(&tap_dev_ds, DATA_SOURCE_CALLBACK_READ); 251 252 return 0; 253 } 254 255 /** 256 * @brief Get network name after network was activated 257 * @note e.g. tapX on Linux, might not be useful on all platforms 258 * @returns network name 259 */ 260 const char * btstack_network_get_name(void){ 261 return tap_dev_name; 262 } 263 264 /** 265 * @brief Bring up network interface 266 * @param network_address 267 * @return 0 if ok 268 */ 269 int btstack_network_down(void){ 270 log_info("BNEP channel closed"); 271 btstack_run_loop_remove_data_source(&tap_dev_ds); 272 if (tap_fd >= 0){ 273 close(tap_fd); 274 } 275 tap_fd = -1; 276 return 0; 277 } 278 279 /** 280 * @brief Receive packet on network interface, e.g., forward packet to TCP/IP stack 281 * @param packet 282 * @param size 283 */ 284 void btstack_network_process_packet(const uint8_t * packet, uint16_t size){ 285 286 if (tap_fd < 0) return; 287 // Write out the ethernet frame to the tap device 288 289 int rc = write(tap_fd, packet, size); 290 if (rc < 0) { 291 log_error("TAP: Could not write to TAP device: %s", strerror(errno)); 292 } else 293 if (rc != size) { 294 log_error("TAP: Package written only partially %d of %d bytes", rc, size); 295 } 296 } 297 298 /** 299 * @brief Notify network interface that packet from send_packet_callback was sent and the next packet can be delivered. 300 */ 301 void btstack_network_packet_sent(void){ 302 303 network_buffer_len = 0; 304 305 // Re-enable the tap device data source 306 btstack_run_loop_enable_data_source_callbacks(&tap_dev_ds, DATA_SOURCE_CALLBACK_READ); 307 } 308