blob: fa4c37d6e8cecd1da2ab206be910cef45c78e43a [file] [log] [blame]
Pau Espin Pedrolfdd732b2017-10-13 14:32:24 +02001/*
jjakoa7cd2492003-04-11 09:40:12 +00002 * TUN interface functions.
jjako0fe0df02004-09-17 11:30:40 +00003 * Copyright (C) 2002, 2003, 2004 Mondru AB.
Harald Weltedf3dcac2018-04-25 16:20:32 +02004 * Copyright (C) 2017-2018 by Harald Welte <laforge@gnumonks.org>
Pau Espin Pedrolfdd732b2017-10-13 14:32:24 +02005 *
jjakoa7cd2492003-04-11 09:40:12 +00006 * The contents of this file may be used under the terms of the GNU
7 * General Public License Version 2, provided that the above copyright
8 * notice and this permission notice is included in all copies or
9 * substantial portions of the software.
Pau Espin Pedrolfdd732b2017-10-13 14:32:24 +020010 *
jjako52c24142002-12-16 13:33:51 +000011 */
12
13/*
jjakoa7cd2492003-04-11 09:40:12 +000014 * tun.c: Contains all TUN functionality. Is able to handle multiple
15 * tunnels in the same program. Each tunnel is identified by the struct,
16 * which is passed to functions.
jjako52c24142002-12-16 13:33:51 +000017 *
jjako52c24142002-12-16 13:33:51 +000018 */
19
jjako52c24142002-12-16 13:33:51 +000020#include <stdio.h>
21#include <stdlib.h>
Harald Weltef2286392018-04-25 19:02:31 +020022#include <stdbool.h>
jjako52c24142002-12-16 13:33:51 +000023#include <sys/types.h>
24#include <sys/socket.h>
25#include <netinet/in.h>
26#include <arpa/inet.h>
27#include <sys/stat.h>
28#include <sys/time.h>
29#include <unistd.h>
30#include <string.h>
31#include <errno.h>
32#include <fcntl.h>
33
34#include <stdio.h>
35#include <fcntl.h>
36#include <unistd.h>
37#include <sys/time.h>
jjako52c24142002-12-16 13:33:51 +000038#include <sys/ioctl.h>
39#include <sys/socket.h>
jjako52c24142002-12-16 13:33:51 +000040#include <errno.h>
jjakoa7cd2492003-04-11 09:40:12 +000041#include <net/route.h>
Harald Welte2e48a442017-08-03 00:47:03 +020042#include <net/if.h>
jjako52c24142002-12-16 13:33:51 +000043
jjako409b8552004-02-04 22:57:41 +000044#if defined(__linux__)
jjako0141d202004-01-09 15:19:20 +000045#include <linux/if_tun.h>
jjako409b8552004-02-04 22:57:41 +000046
47#elif defined (__FreeBSD__)
jjako409b8552004-02-04 22:57:41 +000048#include <net/if_tun.h>
Harald Welte2e48a442017-08-03 00:47:03 +020049#include <net/if_var.h>
50#include <netinet/in_var.h>
jjako409b8552004-02-04 22:57:41 +000051
jjako0fe0df02004-09-17 11:30:40 +000052#elif defined (__APPLE__)
53#include <net/if.h>
54
jjako409b8552004-02-04 22:57:41 +000055#else
56#error "Unknown platform!"
jjako0141d202004-01-09 15:19:20 +000057#endif
58
jjako52c24142002-12-16 13:33:51 +000059#include "tun.h"
jjakoa7cd2492003-04-11 09:40:12 +000060#include "syserr.h"
Harald Weltef2286392018-04-25 19:02:31 +020061#include "gtp-kernel.h"
jjako52c24142002-12-16 13:33:51 +000062
Harald Welte9a6da452018-04-25 17:19:18 +020063static int tun_setaddr4(struct tun_t *this, struct in_addr *addr,
64 struct in_addr *dstaddr, struct in_addr *netmask)
65{
66 int rc;
67 rc = netdev_setaddr4(this->devname, addr, dstaddr, netmask);
68 if (rc < 0)
69 return rc;
70
71 if (addr)
72 this->addr.s_addr = addr->s_addr;
73 if (dstaddr)
74 this->dstaddr.s_addr = dstaddr->s_addr;
75 if (netmask)
76 this->netmask.s_addr = netmask->s_addr;
77 this->addrs++;
78#if defined(__FreeBSD__) || defined (__APPLE__)
79 this->routes = 1;
80#endif
81
82 return rc;
83}
84
Harald Welte9a6da452018-04-25 17:19:18 +020085static int tun_setaddr6(struct tun_t *this, struct in6_addr *addr, struct in6_addr *dstaddr,
86 size_t prefixlen)
87{
88 int rc;
89 rc = netdev_setaddr6(this->devname, addr, dstaddr, prefixlen);
90 if (rc < 0)
91 return rc;
92 if (dstaddr)
93 memcpy(&this->dstaddr, dstaddr, sizeof(*dstaddr));
94 this->addrs++;
95#if defined(__FreeBSD__) || defined (__APPLE__)
96 this->routes = 1;
97#endif
98
99 return rc;
100}
101
Harald Welte9a6da452018-04-25 17:19:18 +0200102static int tun_addaddr4(struct tun_t *this, struct in_addr *addr,
103 struct in_addr *dstaddr, struct in_addr *netmask)
104{
105 int rc;
106
107 /* TODO: Is this needed on FreeBSD? */
108 if (!this->addrs) /* Use ioctl for first addr to make ping work */
109 return tun_setaddr4(this, addr, dstaddr, netmask); /* TODO dstaddr */
110
111 rc = netdev_addaddr4(this->devname, addr, dstaddr, netmask);
112 if (rc < 0)
113 return rc;
114
115 this->addrs++;
116
117 return rc;
118}
119
Harald Welte9a6da452018-04-25 17:19:18 +0200120static int tun_addaddr6(struct tun_t *this,
121 struct in6_addr *addr,
122 struct in6_addr *dstaddr, int prefixlen)
123{
124 int rc;
125
126 if (!this->addrs) /* Use ioctl for first addr to make ping work */
127 return tun_setaddr6(this, addr, dstaddr, prefixlen);
128
129 rc = netdev_addaddr6(this->devname, addr, dstaddr, prefixlen);
130 if (rc < 0)
131 return rc;
132
133 this->addrs++;
134
135 return rc;
136}
137
Pau Espin Pedrolf5e40b72017-12-14 14:01:23 +0100138int tun_addaddr(struct tun_t *this, struct in46_addr *addr, struct in46_addr *dstaddr, size_t prefixlen)
139{
140 struct in_addr netmask;
141 switch (addr->len) {
142 case 4:
143 netmask.s_addr = htonl(0xffffffff << (32 - prefixlen));
144 return tun_addaddr4(this, &addr->v4, dstaddr ? &dstaddr->v4 : NULL, &netmask);
145 case 16:
146 return tun_addaddr6(this, &addr->v6, dstaddr ? &dstaddr->v6 : NULL, prefixlen);
147 default:
148 return -1;
149 }
150}
151
Harald Weltef2286392018-04-25 19:02:31 +0200152int tun_new(struct tun_t **tun, const char *dev_name, bool use_kernel, int fd0, int fd1u)
jjakoa7cd2492003-04-11 09:40:12 +0000153{
jjakoec89e9f2004-01-10 06:38:43 +0000154
jjako409b8552004-02-04 22:57:41 +0000155#if defined(__linux__)
Harald Weltebed35df2011-11-02 13:06:18 +0100156 struct ifreq ifr;
jjako409b8552004-02-04 22:57:41 +0000157
jjako0fe0df02004-09-17 11:30:40 +0000158#elif defined(__FreeBSD__) || defined (__APPLE__)
Harald Weltebed35df2011-11-02 13:06:18 +0100159 char devname[IFNAMSIZ + 5]; /* "/dev/" + ifname */
160 int devnum;
161 struct ifaliasreq areq;
162 int fd;
jjakoec89e9f2004-01-10 06:38:43 +0000163#endif
Harald Weltebed35df2011-11-02 13:06:18 +0100164
165 if (!(*tun = calloc(1, sizeof(struct tun_t)))) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100166 SYS_ERR(DTUN, LOGL_ERROR, errno, "calloc() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100167 return EOF;
168 }
169
170 (*tun)->cb_ind = NULL;
171 (*tun)->addrs = 0;
172 (*tun)->routes = 0;
173
jjako409b8552004-02-04 22:57:41 +0000174#if defined(__linux__)
Harald Weltef2286392018-04-25 19:02:31 +0200175 if (!use_kernel) {
176 /* Open the actual tun device */
177 if (((*tun)->fd = open("/dev/net/tun", O_RDWR)) < 0) {
178 SYS_ERR(DTUN, LOGL_ERROR, errno, "open() failed");
179 goto err_free;
180 }
181
182 /* Set device flags. For some weird reason this is also the method
183 used to obtain the network interface name */
184 memset(&ifr, 0, sizeof(ifr));
185 if (dev_name)
186 strcpy(ifr.ifr_name, dev_name);
187 ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Tun device, no packet info */
188 if (ioctl((*tun)->fd, TUNSETIFF, (void *)&ifr) < 0) {
189 SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl() failed");
190 goto err_close;
191 }
192
193 strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ);
194 (*tun)->devname[IFNAMSIZ - 1] = 0;
195
196 ioctl((*tun)->fd, TUNSETNOCSUM, 1); /* Disable checksums */
197 return 0;
198 } else {
199 strncpy((*tun)->devname, dev_name, IFNAMSIZ);
200 (*tun)->devname[IFNAMSIZ - 1] = 0;
201 (*tun)->fd = -1;
202
203 if (gtp_kernel_create(-1, dev_name, fd0, fd1u) < 0) {
204 LOGP(DTUN, LOGL_ERROR, "cannot create GTP tunnel device: %s\n",
205 strerror(errno));
206 return -1;
207 }
208 LOGP(DTUN, LOGL_NOTICE, "GTP kernel configured\n");
209 return 0;
Harald Weltebed35df2011-11-02 13:06:18 +0100210 }
211
jjako0fe0df02004-09-17 11:30:40 +0000212#elif defined(__FreeBSD__) || defined (__APPLE__)
jjakoec89e9f2004-01-10 06:38:43 +0000213
Harald Weltef2286392018-04-25 19:02:31 +0200214 if (use_kernel) {
215 LOGP(DTUN, LOGL_ERROR, "No kernel GTP-U support in FreeBSD!\n");
216 return -1;
217 }
218
Harald Weltebed35df2011-11-02 13:06:18 +0100219 /* Find suitable device */
220 for (devnum = 0; devnum < 255; devnum++) { /* TODO 255 */
221 snprintf(devname, sizeof(devname), "/dev/tun%d", devnum);
Harald Weltebed35df2011-11-02 13:06:18 +0100222 if (((*tun)->fd = open(devname, O_RDWR)) >= 0)
223 break;
224 if (errno != EBUSY)
225 break;
226 }
227 if ((*tun)->fd < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100228 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100229 "Can't find tunnel device");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200230 goto err_free;
Harald Weltebed35df2011-11-02 13:06:18 +0100231 }
jjakoec89e9f2004-01-10 06:38:43 +0000232
Harald Weltebed35df2011-11-02 13:06:18 +0100233 snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", devnum);
Harald Welte81bc2ae2017-08-11 12:56:30 +0200234 (*tun)->devname[sizeof((*tun)->devname)-1] = 0;
jjako1f158642004-02-05 20:39:57 +0000235
Harald Weltebed35df2011-11-02 13:06:18 +0100236 /* The tun device we found might have "old" IP addresses allocated */
237 /* We need to delete those. This problem is not present on Linux */
jjako1f158642004-02-05 20:39:57 +0000238
Harald Weltebed35df2011-11-02 13:06:18 +0100239 memset(&areq, 0, sizeof(areq));
jjako1f158642004-02-05 20:39:57 +0000240
Harald Weltebed35df2011-11-02 13:06:18 +0100241 /* Set up interface name */
242 strncpy(areq.ifra_name, (*tun)->devname, IFNAMSIZ);
243 areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
jjako1f158642004-02-05 20:39:57 +0000244
Harald Weltebed35df2011-11-02 13:06:18 +0100245 /* Create a channel to the NET kernel. */
246 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100247 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200248 goto err_close;
Harald Weltebed35df2011-11-02 13:06:18 +0100249 }
jjako1f158642004-02-05 20:39:57 +0000250
Harald Weltebed35df2011-11-02 13:06:18 +0100251 /* Delete any IP addresses until SIOCDIFADDR fails */
252 while (ioctl(fd, SIOCDIFADDR, (void *)&areq) != -1) ;
253
254 close(fd);
255 return 0;
jjako409b8552004-02-04 22:57:41 +0000256#endif
257
Harald Welte9e6dfa02017-08-12 15:06:19 +0200258err_close:
259 close((*tun)->fd);
260err_free:
261 free(*tun);
262 *tun = NULL;
263 return -1;
jjako52c24142002-12-16 13:33:51 +0000264}
265
jjakoa7cd2492003-04-11 09:40:12 +0000266int tun_free(struct tun_t *tun)
jjako52c24142002-12-16 13:33:51 +0000267{
jjako163b4552004-12-30 15:33:58 +0000268
Harald Weltebed35df2011-11-02 13:06:18 +0100269 if (tun->routes) {
Harald Welteb4c08282018-04-25 16:55:39 +0200270 netdev_delroute(&tun->dstaddr, &tun->addr, &tun->netmask);
Harald Weltebed35df2011-11-02 13:06:18 +0100271 }
jjako163b4552004-12-30 15:33:58 +0000272
Harald Weltef2286392018-04-25 19:02:31 +0200273 if (tun->fd >= 0) {
274 if (close(tun->fd)) {
275 SYS_ERR(DTUN, LOGL_ERROR, errno, "close() failed");
276 }
Harald Weltebed35df2011-11-02 13:06:18 +0100277 }
jjakoa7cd2492003-04-11 09:40:12 +0000278
Harald Weltef2286392018-04-25 19:02:31 +0200279 gtp_kernel_stop(tun->devname);
280
Harald Weltebed35df2011-11-02 13:06:18 +0100281 /* TODO: For solaris we need to unlink streams */
jjakoec89e9f2004-01-10 06:38:43 +0000282
Harald Weltebed35df2011-11-02 13:06:18 +0100283 free(tun);
284 return 0;
jjako52c24142002-12-16 13:33:51 +0000285}
286
Harald Weltebed35df2011-11-02 13:06:18 +0100287int tun_set_cb_ind(struct tun_t *this,
288 int (*cb_ind) (struct tun_t * tun, void *pack, unsigned len))
289{
290 this->cb_ind = cb_ind;
291 return 0;
jjakoa7cd2492003-04-11 09:40:12 +0000292}
293
jjakoa7cd2492003-04-11 09:40:12 +0000294int tun_decaps(struct tun_t *this)
jjako52c24142002-12-16 13:33:51 +0000295{
Harald Weltebed35df2011-11-02 13:06:18 +0100296 unsigned char buffer[PACKET_MAX];
297 int status;
jjako52c24142002-12-16 13:33:51 +0000298
Harald Weltebed35df2011-11-02 13:06:18 +0100299 if ((status = read(this->fd, buffer, sizeof(buffer))) <= 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100300 SYS_ERR(DTUN, LOGL_ERROR, errno, "read() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100301 return -1;
302 }
303
304 if (this->cb_ind)
305 return this->cb_ind(this, buffer, status);
306
307 return 0;
jjako52c24142002-12-16 13:33:51 +0000308}
309
310int tun_encaps(struct tun_t *tun, void *pack, unsigned len)
311{
Harald Weltebed35df2011-11-02 13:06:18 +0100312 return write(tun->fd, pack, len);
jjakoa7cd2492003-04-11 09:40:12 +0000313}
314
Harald Weltebed35df2011-11-02 13:06:18 +0100315int tun_runscript(struct tun_t *tun, char *script)
316{
jjakoa7cd2492003-04-11 09:40:12 +0000317
Harald Weltebed35df2011-11-02 13:06:18 +0100318 char buf[TUN_SCRIPTSIZE];
319 char snet[TUN_ADDRSIZE];
320 char smask[TUN_ADDRSIZE];
321 int rc;
322
323 strncpy(snet, inet_ntoa(tun->addr), sizeof(snet));
324 snet[sizeof(snet) - 1] = 0;
325 strncpy(smask, inet_ntoa(tun->netmask), sizeof(smask));
326 smask[sizeof(smask) - 1] = 0;
327
328 /* system("ipup /dev/tun0 192.168.0.10 255.255.255.0"); */
329 snprintf(buf, sizeof(buf), "%s %s %s %s",
330 script, tun->devname, snet, smask);
331 buf[sizeof(buf) - 1] = 0;
332 rc = system(buf);
333 if (rc == -1) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100334 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100335 "Error executing command %s", buf);
336 return -1;
337 }
338 return 0;
jjako52c24142002-12-16 13:33:51 +0000339}
Harald Weltef85fe972017-09-24 20:00:34 +0800340
Harald Welte4c7d2912017-11-08 15:19:17 +0900341/*! Obtain the local address of the tun device.
342 * \param[in] tun Target device owning the IP
343 * \param[out] prefix_list List of prefix structures to fill with each IPv4/6 and prefix length found.
344 * \param[in] prefix_size Amount of elements allowed to be fill in the prefix_list array.
345 * \param[in] flags Specify which kind of IP to look for: IP_TYPE_IPv4, IP_TYPE_IPv6_LINK, IP_TYPE_IPv6_NONLINK
346 * \returns The number of ips found following the criteria specified by flags, -1 on error.
347 *
348 * This function will fill prefix_list with up to prefix_size IPs following the
349 * criteria specified by flags parameter. It returns the number of IPs matching
350 * the criteria. As a result, the number returned can be bigger than
351 * prefix_size. It can be used with prefix_size=0 to get an estimate of the size
352 * needed for prefix_list.
353 */
354int tun_ip_local_get(const struct tun_t *tun, struct in46_prefix *prefix_list, size_t prefix_size, int flags)
355{
356 return netdev_ip_local_get(tun->devname, prefix_list, prefix_size, flags);
357}