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