Contiki-NG
Loading...
Searching...
No Matches
tun6-net.c
1/*
2 * Copyright (c) 2011, Swedish Institute of Computer Science.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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 Institute nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30/**
31 * \author
32 * Niclas Finne <nfi@sics.se>
33 * Joakim Eriksson <joakime@sics.se>
34 */
35
36#include "contiki.h"
37#include "sys/platform.h"
38#include "net/ipv6/uip.h"
39#include "net/ipv6/uip-ds6.h"
40#include "net/netstack.h"
41#include "net/ipv6/uiplib.h"
42#include <stdio.h>
43#include <stdlib.h>
44#include <stdarg.h>
45#include <string.h>
46#include <sys/time.h>
47#include <sys/types.h>
48
49#include <unistd.h>
50#include <errno.h>
51#include <fcntl.h>
52#include <signal.h>
53#include <termios.h>
54#include <sys/ioctl.h>
55#include <sys/socket.h>
56#include <net/if.h>
57#include <err.h>
58
59#define TUN_PRIO CONTIKI_VERBOSE_PRIO + 30
60/*---------------------------------------------------------------------------*/
61/* Log configuration */
62#include "sys/log.h"
63#define LOG_MODULE "Tun6"
64#define LOG_LEVEL LOG_LEVEL_WARN
65
66#ifdef __APPLE__
67/* utun0-3 are in use on Big Sur, so use utun10 as default */
68#define DEFAULT_TUN "utun10"
69#else /* __APPLE__ */
70#define DEFAULT_TUN "tun0"
71#endif /* __APPLE__ */
72
73#define DEFAULT_PREFIX "fd00::1/64"
74static const char *config_ipaddr = DEFAULT_PREFIX;
75static char config_tundev[IFNAMSIZ + 1] = DEFAULT_TUN;
76static void (* tun_input_callback)(void);
77
78/* IPv6 required minimum MTU */
79#define MIN_MTU_SIZE 1500
80static int config_mtu = MIN_MTU_SIZE;
81
82static int tunfd = -1;
83
84static int set_fd(fd_set *rset, fd_set *wset);
85static void handle_fd(fd_set *rset, fd_set *wset);
86static const struct select_callback tun_select_callback = {
87 set_fd,
88 handle_fd
89};
90
91static int ssystem(const char *fmt, ...)
92 __attribute__((__format__ (__printf__, 1, 2)));
93
94static int
95ssystem(const char *fmt, ...)
96{
97 char cmd[128];
98 va_list ap;
99 va_start(ap, fmt);
100 vsnprintf(cmd, sizeof(cmd), fmt, ap);
101 va_end(ap);
102 LOG_INFO("%s\n", cmd);
103 fflush(stdout);
104 return system(cmd);
105}
106
107/*---------------------------------------------------------------------------*/
108static bool
109set_default_prefix(const char *prefix)
110{
111 char *ipaddr = strdup(prefix);
112 if(!ipaddr) {
113 return false;
114 }
115
116 bool success = false;
117 char *s = strchr(ipaddr, '/');
118 if(s) {
119 uip_ip6addr_t prefix_addr;
120 *s = '\0';
121 if(uiplib_ipaddrconv(ipaddr, &prefix_addr)) {
122 uip_ds6_set_default_prefix(&prefix_addr);
123 success = true;
124 }
125 }
126 free(ipaddr);
127 return success;
128}
129/*---------------------------------------------------------------------------*/
130const char *
131tun6_net_get_prefix(void)
132{
133 return config_ipaddr;
134}
135/*---------------------------------------------------------------------------*/
136void
137tun6_net_set_prefix(const char *prefix)
138{
139 if(!set_default_prefix(prefix)) {
140 LOG_WARN("Failed to set default prefix %s\n", prefix);
141 }
142 config_ipaddr = prefix;
143}
144/*---------------------------------------------------------------------------*/
145const char *
146tun6_net_get_tun_name(void)
147{
148 return config_tundev;
149}
150/*---------------------------------------------------------------------------*/
151void
152tun6_net_set_tun_name(const char *tun_name)
153{
154 /* Ignore "/dev/" if present in tun device name */
155 if(strncmp("/dev/", tun_name, 5) == 0) {
156 tun_name += 5;
157 }
158 strncpy(config_tundev, tun_name, sizeof(config_tundev) - 1);
159 config_tundev[sizeof(config_tundev) - 1] = '\0';
160}
161/*---------------------------------------------------------------------------*/
162int
163tun6_net_get_mtu(void)
164{
165 return config_mtu;
166}
167/*---------------------------------------------------------------------------*/
168void
169tun6_net_set_mtu(int mtu_size)
170{
171 if(mtu_size < MIN_MTU_SIZE) {
172 LOG_WARN("ignoring too small MTU size %d, using %d\n",
173 mtu_size, config_mtu);
174 } else {
175 config_mtu = mtu_size;
176 }
177}
178/*---------------------------------------------------------------------------*/
179static int
180tun_dev_callback(const char *optarg)
181{
182 tun6_net_set_tun_name(optarg);
183 return 0;
184}
185CONTIKI_OPTION(TUN_PRIO, { "t", required_argument, NULL, 0 },
186 tun_dev_callback,
187 "name of tun interface (default: " DEFAULT_TUN ")\n");
188
189/*---------------------------------------------------------------------------*/
190static int
191prefix_callback(const char *optarg)
192{
193 tun6_net_set_prefix(optarg);
194 return 0;
195}
196CONTIKI_OPTION(TUN_PRIO + 1, { "prefix", required_argument, NULL, 0 },
197 prefix_callback,
198 "Subnet prefix (default: " DEFAULT_PREFIX ")\n");
199/*---------------------------------------------------------------------------*/
200static int
201mtu_callback(const char *optarg)
202{
203 tun6_net_set_mtu(atoi(optarg));
204 return 0;
205}
206CONTIKI_OPTION(TUN_PRIO + 2, { "mtu", required_argument, NULL, 0 },
207 mtu_callback, "interface MTU size\n");
208/*---------------------------------------------------------------------------*/
209static void
210cleanup(void)
211{
212#define TMPBUFSIZE 128
213 /* Called from signal handler, avoid unsafe functions. */
214 char buf[TMPBUFSIZE];
215#ifdef __APPLE__
216 strcpy(buf, "ifconfig ");
217 /* Will not overflow, but null-terminate to avoid spurious warnings. */
218 buf[TMPBUFSIZE - 1] = '\0';
219 strncat(buf, config_tundev, TMPBUFSIZE - strlen(buf) - 1);
220 strncat(buf, " inet6 ", TMPBUFSIZE - strlen(buf) - 1);
221 strncat(buf, config_ipaddr, TMPBUFSIZE - strlen(buf) - 1);
222 strncat(buf, " remove", TMPBUFSIZE - strlen(buf) - 1);
223 system(buf);
224#endif /* __APPLE__ */
225
226 strcpy(buf, "ifconfig ");
227 /* Will not overflow, but null-terminate to avoid spurious warnings. */
228 buf[TMPBUFSIZE - 1] = '\0';
229 strncat(buf, config_tundev, TMPBUFSIZE - strlen(buf) - 1);
230 strncat(buf, " down", TMPBUFSIZE - strlen(buf) - 1);
231 system(buf);
232
233#ifndef __APPLE__
234#ifndef linux
235 system("sysctl -w net.ipv6.conf.all.forwarding=1");
236#endif
237
238 strcpy(buf, "netstat -nr"
239 " | awk '{ if ($2 == \"");
240 buf[TMPBUFSIZE - 1] = '\0';
241 strncat(buf, config_tundev, TMPBUFSIZE - strlen(buf) - 1);
242 strncat(buf, "\") print \"route delete -net \"$1; }'"
243 " | sh", TMPBUFSIZE - strlen(buf) - 1);
244 system(buf);
245#endif /* !__APPLE__ */
246}
247/*---------------------------------------------------------------------------*/
248static void CC_NORETURN
249sigcleanup(int signo)
250{
251 const char *prefix = "signal ";
252 const char *sig =
253 signo == SIGHUP ? "HUP\n" : signo == SIGTERM ? "TERM\n" : "INT\n";
254 write(fileno(stderr), prefix, strlen(prefix));
255 write(fileno(stderr), sig, strlen(sig));
256 cleanup();
257 _exit(EXIT_SUCCESS);
258}
259/*---------------------------------------------------------------------------*/
260static void
261ifconf_setup(void)
262{
263#ifdef linux
264 ssystem("ifconfig %s inet `hostname` mtu %d up", config_tundev, config_mtu);
265 ssystem("ifconfig %s add %s", config_tundev, config_ipaddr);
266#elif defined(__APPLE__)
267 ssystem("ifconfig %s inet6 mtu %d up", config_tundev, config_mtu);
268 ssystem("ifconfig %s inet6 %s add", config_tundev, config_ipaddr );
269 ssystem("sysctl -w net.inet6.ip6.forwarding=1");
270#else
271 ssystem("ifconfig %s inet `hostname` %s mtu %d up", config_tundev, config_ipaddr, config_mtu);
272 ssystem("sysctl -w net.inet.ip.forwarding=1");
273#endif /* !linux */
274
275 /* Print the configuration to the console. */
276 ssystem("ifconfig %s\n", config_tundev);
277}
278/*---------------------------------------------------------------------------*/
279#ifdef linux
280#include <linux/if.h>
281#include <linux/if_tun.h>
282
283static int
284tun_alloc(void)
285{
286 struct ifreq ifr;
287 int fd, err;
288
289 LOG_INFO("Opening tun interface %s\n", config_tundev);
290
291 if((fd = open("/dev/net/tun", O_RDWR)) < 0) {
292 /* Error message handled by caller */
293 return -1;
294 }
295
296 memset(&ifr, 0, sizeof(ifr));
297
298 /* Flags: IFF_TUN - TUN device (no Ethernet headers)
299 * IFF_NO_PI - Do not provide packet information
300 */
301 ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
302 if(*config_tundev != '\0') {
303 strncpy(ifr.ifr_name, config_tundev, sizeof(ifr.ifr_name) - 1);
304 ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
305 }
306
307 if((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0) {
308 /* Error message handled by caller */
309 close(fd);
310 return err;
311 }
312
313 LOG_INFO("Using '%s' as '%s'\n", config_tundev, ifr.ifr_name);
314 strncpy(config_tundev, ifr.ifr_name, sizeof(config_tundev) - 1);
315 config_tundev[sizeof(config_tundev) - 1] = '\0';
316 return fd;
317}
318#elif defined __APPLE__
319#include <sys/sys_domain.h>
320#include <sys/kern_control.h>
321#include <net/if_utun.h>
322#include <sys/uio.h>
323
324/*
325 * Reference for utun on macOS:
326 * http://newosxbook.com/src.jl?tree=listings&file=17-15-utun.c
327 */
328static int
329tun_alloc(void)
330{
331 unsigned int tunif;
332
333 if(sscanf(config_tundev, "utun%u", &tunif) != 1 || tunif >= UINT8_MAX) {
334 fprintf(stderr, "tun_alloc: invalid utun interface specified: %s\n", config_tundev);
335 return -1;
336 }
337
338 LOG_INFO("Opening tun interface %s\n", config_tundev);
339
340 struct ctl_info ctl_info = { 0 };
341 if(strlcpy(ctl_info.ctl_name, UTUN_CONTROL_NAME, sizeof(ctl_info.ctl_name)) >=
342 sizeof(ctl_info.ctl_name)) {
343 fprintf(stderr, "UTUN_CONTROL_NAME too long");
344 return -1;
345 }
346
347 int fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
348 if(fd == -1) {
349 perror("socket(SYSPROTO_CONTROL)");
350 return -1;
351 }
352
353 if(ioctl(fd, CTLIOCGINFO, &ctl_info) == -1) {
354 perror("ioctl(CTLIOCGINFO)");
355 close(fd);
356 return -1;
357 }
358
359 struct sockaddr_ctl sc;
360 sc.sc_id = ctl_info.ctl_id;
361 sc.sc_len = sizeof(sc);
362 sc.sc_family = AF_SYSTEM;
363 sc.ss_sysaddr = AF_SYS_CONTROL;
364 sc.sc_unit = tunif + 1;
365
366 /*
367 * If the connect is successful, a utun%d device will be created, where "%d"
368 * is our unit number -1
369 */
370
371 if(connect(fd, (struct sockaddr *)&sc, sizeof(sc)) == -1) {
372 perror("connect(AF_SYS_CONTROL)");
373 close(fd);
374 return -1;
375 }
376
377 return fd;
378}
379#else
380static int
381tun_alloc(void)
382{
383 char t[8 + sizeof(config_tundev)] = "/dev/";
384 strncat(t, config_tundev, sizeof(t) - 6);
385 t[sizeof(t) - 1] = '\0';
386 LOG_INFO("Opening tun interface %s\n", t);
387 return open(t, O_RDWR);
388}
389#endif
390/*---------------------------------------------------------------------------*/
391bool
392tun6_net_init(void (* tun_input)(void))
393{
394 if(!tun_input) {
395 return false;
396 }
397 tun_input_callback = tun_input;
398
399 setvbuf(stdout, NULL, _IOLBF, 0); /* Line buffered output. */
400
401 tunfd = tun_alloc();
402 if(tunfd == -1) {
403 return false;
404 }
405
406 LOG_INFO("Tun open:%d\n", tunfd);
407
408 select_set_callback(tunfd, &tun_select_callback);
409
410 fprintf(stderr, "opened %s device ``/dev/%s''\n",
411 "tun", config_tundev);
412
413 atexit(cleanup);
414 signal(SIGHUP, sigcleanup);
415 signal(SIGTERM, sigcleanup);
416 signal(SIGINT, sigcleanup);
417 ifconf_setup();
418 return true;
419}
420/*---------------------------------------------------------------------------*/
421int
422tun6_net_output(uint8_t *data, int len)
423{
424 if(tunfd == -1) {
425 return 0;
426 }
427
428#ifdef __APPLE__
429 /* Fake IFF_NO_PI on macOS by sending a 4 byte header containing AF_INET6 */
430 u_int32_t type = htonl(AF_INET6);
431 struct iovec iv[2];
432
433 iv[0].iov_base = &type;
434 iv[0].iov_len = sizeof(type);
435 iv[1].iov_base = data;
436 iv[1].iov_len = len;
437
438 if(writev(tunfd, iv, 2) != (sizeof(type) + len)) {
439 err(EXIT_FAILURE, "tun6_net_output: writev");
440 }
441#else
442 if(write(tunfd, data, len) != len) {
443 err(EXIT_FAILURE, "tun6_net_output: write");
444 }
445#endif
446
447 return 0;
448}
449/*---------------------------------------------------------------------------*/
450int
451tun6_net_input(uint8_t *data, int maxlen)
452{
453 int size;
454
455 if(tunfd == -1) {
456 /* tun is not open */
457 return 0;
458 }
459
460 if((size = read(tunfd, data, maxlen)) == -1) {
461 err(EXIT_FAILURE, "tun6_net_input: read");
462 }
463
464#ifdef __APPLE__
465#define UTUN_HEADER_LEN 4
466 /* Fake IFF_NO_PI on macOS by ignoring the first 4 bytes containing AF_INET6 */
467 if(size <= UTUN_HEADER_LEN) {
468 err(EXIT_FAILURE, "tun6_net_input: read too small");
469 }
470
471 size -= UTUN_HEADER_LEN;
472 memmove(data, data + UTUN_HEADER_LEN, size);
473#undef UTUN_HEADER_LEN
474#endif /* __APPLE__ */
475
476 return size;
477}
478
479/*---------------------------------------------------------------------------*/
480/* tun select callback */
481/*---------------------------------------------------------------------------*/
482static int
483set_fd(fd_set *rset, fd_set *wset)
484{
485 if(tunfd == -1) {
486 return 0;
487 }
488
489 FD_SET(tunfd, rset);
490 return 1;
491}
492/*---------------------------------------------------------------------------*/
493static void
494handle_fd(fd_set *rset, fd_set *wset)
495{
496 if(tunfd == -1) {
497 /* tun is not open */
498 return;
499 }
500
501 if(FD_ISSET(tunfd, rset)) {
502 tun_input_callback();
503 }
504}
505
506/*---------------------------------------------------------------------------*/
507/* network callbacks */
508/*---------------------------------------------------------------------------*/
509static void
510tun_input(void)
511{
512 int size = tun6_net_input(uip_buf, sizeof(uip_buf));
513 LOG_DBG("TUN data incoming read:%d\n", size);
514 uip_len = size;
515 tcpip_input();
516}
517/*---------------------------------------------------------------------------*/
518static void
519network_init(void)
520{
521 if(!tun6_net_init(tun_input)) {
522 LOG_WARN("Failed to open tun device (you may be lacking permission). Running without network.\n");
523 }
524}
525/*---------------------------------------------------------------------------*/
526static uint8_t
527network_output(const linkaddr_t *localdest)
528{
529 if(uip_len > 0) {
530 LOG_DBG("output: %u bytes to ", uip_len);
531 LOG_DBG_LLADDR(localdest);
532 LOG_DBG_("\n");
533 return tun6_net_output(uip_buf, uip_len);
534 }
535 return 0;
536}
537/*---------------------------------------------------------------------------*/
538static void
539network_input(void)
540{
541 /* should not happen */
542 LOG_DBG("unexpected network input\n");
543}
544/*---------------------------------------------------------------------------*/
545const struct network_driver tun6_net_driver = {
546 "tun6",
547 network_init,
548 network_input,
549 network_output
550};
551/*---------------------------------------------------------------------------*/
#define CC_NORETURN
Configure if the C compiler supports functions that are not meant to return e.g.
Definition cc.h:99
#define CONTIKI_OPTION(prio,...)
Add a command line option when the compilation unit is present.
Definition platform.h:153
void tcpip_input(void)
Deliver an incoming packet to the TCP/IP stack.
Definition tcpip.c:433
#define uiplib_ipaddrconv
Convert a textual representation of an IP address to a numerical representation.
Definition uiplib.h:71
void uip_ds6_set_default_prefix(const uip_ip6addr_t *prefix)
Set the Default IPv6 prefix.
Definition uip-ds6.c:110
#define uip_buf
Macro to access uip_aligned_buf as an array of bytes.
Definition uip.h:465
uint16_t uip_len
The length of the packet in the uip_buf buffer.
Definition uip6.c:159
Header file for the logging system.
Include file for the Contiki low-layer network stack (NETSTACK)
Header file for the Contiki-NG main routine.
The structure of a network driver in Contiki.
Definition netstack.h:115
Header file for IPv6-related data structures.
static uip_ipaddr_t ipaddr
Pointer to prefix information option in uip_buf.
Definition uip-nd6.c:116
Header file for the uIP TCP/IP stack.
Header file for the IP address manipulation library.