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 #include "btstack_debug.h"
50 #include "btstack_run_loop.h"
51
52 #include <sys/types.h>
53 #include <sys/socket.h>
54 #include <sys/time.h>
55 #include <sys/ioctl.h>
56 #include <sys/param.h>
57 #include <sys/stat.h>
58
59 #include <arpa/inet.h>
60 #include <errno.h>
61 #include <fcntl.h>
62 #include <ifaddrs.h>
63 #include <net/if_arp.h>
64 #include <stdint.h>
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <string.h>
68 #include <unistd.h>
69
70 #if defined(__APPLE__) || defined(__FreeBSD__)
71 #include <net/if.h>
72 #include <net/if_types.h>
73 #include <netinet/if_ether.h>
74 #include <netinet/in.h>
75 #endif
76
77 #ifdef __linux
78 #include <linux/if.h>
79 #include <linux/if_tun.h>
80 #endif
81
82 static int tap_fd = -1;
83 static uint8_t network_buffer[BNEP_MTU_MIN];
84 static size_t network_buffer_len = 0;
85 static char tap_dev_name[16];
86
87 #if defined(__APPLE__) || defined(__FreeBSD__)
88 // tuntaposx provides fixed set of tapX devices
89 static const char * tap_dev = "/dev/tap0";
90 static const char * tap_dev_name_template = "tap0";
91 #endif
92
93 #ifdef __linux
94 // Linux uses single control device to bring up tunX or tapX interface
95 static const char * tap_dev = "/dev/net/tun";
96 static const char * tap_dev_name_template = "bnep%d";
97 #endif
98
99 static btstack_data_source_t tap_dev_ds;
100
101 static void (*btstack_network_send_packet_callback)(const uint8_t * packet, uint16_t size);
102
103 /*
104 * @text Listing processTapData shows how a packet is received from the TAP network interface
105 * and forwarded over the BNEP connection.
106 *
107 * After successfully reading a network packet, the call to
108 * the *bnep_can_send_packet_now* function checks, if BTstack can forward
109 * a network packet now. If that's not possible, the received data stays
110 * in the network buffer and the data source elements is removed from the
111 * run loop. The *process_tap_dev_data* function will not be called until
112 * the data source is registered again. This provides a basic flow control.
113 */
114
115 /* LISTING_START(processTapData): Process incoming network packets */
process_tap_dev_data(btstack_data_source_t * ds,btstack_data_source_callback_type_t callback_type)116 static void process_tap_dev_data(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type)
117 {
118 UNUSED(ds);
119 UNUSED(callback_type);
120
121 ssize_t len;
122 len = read(ds->source.fd, network_buffer, sizeof(network_buffer));
123 if (len <= 0){
124 fprintf(stderr, "TAP: Error while reading: %s\n", strerror(errno));
125 return;
126 }
127
128 network_buffer_len = len;
129
130 // disable reading from netif
131 btstack_run_loop_disable_data_source_callbacks(&tap_dev_ds, DATA_SOURCE_CALLBACK_READ);
132
133 // let client now
134 (*btstack_network_send_packet_callback)(network_buffer, network_buffer_len);
135 }
136
137 /**
138 * @brief Initialize network interface
139 * @param send_packet_callback
140 */
btstack_network_init(void (* send_packet_callback)(const uint8_t * packet,uint16_t size))141 void btstack_network_init(void (*send_packet_callback)(const uint8_t * packet, uint16_t size)){
142 btstack_network_send_packet_callback = send_packet_callback;
143 }
144
145 /**
146 * @text This code requries a TUN/TAP interface to connect the Bluetooth network interface
147 * with the native system. It has been tested on Linux and OS X, but should work on any
148 * system that provides TUN/TAP with minor modifications.
149 *
150 * On Linux, TUN/TAP is available by default. On OS X, tuntaposx from
151 * http://tuntaposx.sourceforge.net needs to be installed.
152 *
153 * The *tap_alloc* function sets up a virtual network interface with the given Bluetooth Address.
154 * It is rather low-level as it sets up and configures a network interface.
155 *
156 * @brief Bring up network interfacd
157 * @param network_address
158 * @return 0 if ok
159 */
btstack_network_up(bd_addr_t network_address)160 int btstack_network_up(bd_addr_t network_address){
161
162 struct ifreq ifr;
163 int fd_dev;
164 int fd_socket;
165
166 if( (fd_dev = open(tap_dev, O_RDWR)) < 0 ) {
167 fprintf(stderr, "TAP: Error opening %s: %s\n", tap_dev, strerror(errno));
168 return -1;
169 }
170
171 #ifdef __linux
172 memset(&ifr, 0, sizeof(ifr));
173 ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
174 strncpy(ifr.ifr_name, tap_dev_name_template, IFNAMSIZ); // device name pattern
175
176 int err;
177 if( (err = ioctl(fd_dev, TUNSETIFF, (void *) &ifr)) < 0 ) {
178 fprintf(stderr, "TAP: Error setting device name: %s\n", strerror(errno));
179 close(fd_dev);
180 return -1;
181 }
182 strcpy(tap_dev_name, ifr.ifr_name);
183 #endif
184 #if defined(__APPLE__) || defined(__FreeBSD__)
185 strcpy(tap_dev_name, tap_dev_name_template);
186 #endif
187
188 fd_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
189 if (fd_socket < 0) {
190 close(fd_dev);
191 fprintf(stderr, "TAP: Error opening netlink socket: %s\n", strerror(errno));
192 return -1;
193 }
194
195 // Configure the MAC address of the newly created bnep(x)
196 // device to the local bd_address
197 memset (&ifr, 0, sizeof(struct ifreq));
198 strcpy(ifr.ifr_name, tap_dev_name);
199 #ifdef __linux
200 ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
201 memcpy(ifr.ifr_hwaddr.sa_data, network_address, sizeof(bd_addr_t));
202 if (ioctl(fd_socket, SIOCSIFHWADDR, &ifr) == -1) {
203 close(fd_dev);
204 close(fd_socket);
205 fprintf(stderr, "TAP: Error setting hw addr: %s\n", strerror(errno));
206 exit(1);
207 return -1;
208 }
209 #endif
210 #ifdef __APPLE__
211 ifr.ifr_addr.sa_len = ETHER_ADDR_LEN;
212 ifr.ifr_addr.sa_family = AF_LINK;
213 (void)memcpy(ifr.ifr_addr.sa_data, network_address, ETHER_ADDR_LEN);
214 if (ioctl(fd_socket, SIOCSIFLLADDR, &ifr) == -1) {
215 close(fd_dev);
216 close(fd_socket);
217 fprintf(stderr, "TAP: Error setting hw addr: %s\n", strerror(errno));
218 exit(1);
219 return -1;
220 }
221 #endif
222
223 // Bring the interface up
224 if (ioctl(fd_socket, SIOCGIFFLAGS, &ifr) == -1) {
225 close(fd_dev);
226 close(fd_socket);
227 fprintf(stderr, "TAP: Error reading interface flags: %s\n", strerror(errno));
228 return -1;
229 }
230
231 if ((ifr.ifr_flags & IFF_UP) == 0) {
232 ifr.ifr_flags |= IFF_UP;
233
234 if (ioctl(fd_socket, SIOCSIFFLAGS, &ifr) == -1) {
235 close(fd_dev);
236 close(fd_socket);
237 fprintf(stderr, "TAP: Error set IFF_UP: %s\n", strerror(errno));
238 return -1;
239 }
240 }
241
242 close(fd_socket);
243
244 tap_fd = fd_dev;
245 log_info("BNEP device \"%s\" allocated", tap_dev_name);
246
247 /* Create and register a new runloop data source */
248 btstack_run_loop_set_data_source_fd(&tap_dev_ds, tap_fd);
249 btstack_run_loop_set_data_source_handler(&tap_dev_ds, &process_tap_dev_data);
250 btstack_run_loop_add_data_source(&tap_dev_ds);
251 btstack_run_loop_enable_data_source_callbacks(&tap_dev_ds, DATA_SOURCE_CALLBACK_READ);
252
253 return 0;
254 }
255
256 /**
257 * @brief Get network name after network was activated
258 * @note e.g. tapX on Linux, might not be useful on all platforms
259 * @returns network name
260 */
btstack_network_get_name(void)261 const char * btstack_network_get_name(void){
262 return tap_dev_name;
263 }
264
265 /**
266 * @brief Bring up network interface
267 * @param network_address
268 * @return 0 if ok
269 */
btstack_network_down(void)270 int btstack_network_down(void){
271 log_info("BNEP channel closed");
272 btstack_run_loop_remove_data_source(&tap_dev_ds);
273 if (tap_fd >= 0){
274 close(tap_fd);
275 }
276 tap_fd = -1;
277 return 0;
278 }
279
280 /**
281 * @brief Receive packet on network interface, e.g., forward packet to TCP/IP stack
282 * @param packet
283 * @param size
284 */
btstack_network_process_packet(const uint8_t * packet,uint16_t size)285 void btstack_network_process_packet(const uint8_t * packet, uint16_t size){
286
287 if (tap_fd < 0) return;
288 // Write out the ethernet frame to the tap device
289
290 int rc = write(tap_fd, packet, size);
291 if (rc < 0) {
292 log_error("TAP: Could not write to TAP device: %s", strerror(errno));
293 } else
294 if (rc != size) {
295 log_error("TAP: Package written only partially %d of %d bytes", rc, size);
296 }
297 }
298
299 /**
300 * @brief Notify network interface that packet from send_packet_callback was sent and the next packet can be delivered.
301 */
btstack_network_packet_sent(void)302 void btstack_network_packet_sent(void){
303
304 network_buffer_len = 0;
305
306 // Re-enable the tap device data source
307 btstack_run_loop_enable_data_source_callbacks(&tap_dev_ds, DATA_SOURCE_CALLBACK_READ);
308 }
309