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