xref: /btstack/platform/posix/btstack_network_posix.c (revision db88441f671cf9b797d1a7638cc0e38d13db6ac0)
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 */
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  */
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  */
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  */
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  */
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  */
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  */
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