blob: 6ae006bea0e4caffa38a48d4738e522ed13b3891 [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
Stefan Sperlingaee905b2018-11-21 14:12:22 +0100196 /* Disable checksums */
197 if (ioctl((*tun)->fd, TUNSETNOCSUM, 1) < 0) {
198 SYS_ERR(DTUN, LOGL_NOTICE, errno, "could not disable checksum on %s", (*tun)->devname);
199 }
Harald Weltef2286392018-04-25 19:02:31 +0200200 return 0;
201 } else {
202 strncpy((*tun)->devname, dev_name, IFNAMSIZ);
203 (*tun)->devname[IFNAMSIZ - 1] = 0;
204 (*tun)->fd = -1;
205
206 if (gtp_kernel_create(-1, dev_name, fd0, fd1u) < 0) {
207 LOGP(DTUN, LOGL_ERROR, "cannot create GTP tunnel device: %s\n",
208 strerror(errno));
209 return -1;
210 }
211 LOGP(DTUN, LOGL_NOTICE, "GTP kernel configured\n");
212 return 0;
Harald Weltebed35df2011-11-02 13:06:18 +0100213 }
214
jjako0fe0df02004-09-17 11:30:40 +0000215#elif defined(__FreeBSD__) || defined (__APPLE__)
jjakoec89e9f2004-01-10 06:38:43 +0000216
Harald Weltef2286392018-04-25 19:02:31 +0200217 if (use_kernel) {
218 LOGP(DTUN, LOGL_ERROR, "No kernel GTP-U support in FreeBSD!\n");
219 return -1;
220 }
221
Harald Weltebed35df2011-11-02 13:06:18 +0100222 /* Find suitable device */
223 for (devnum = 0; devnum < 255; devnum++) { /* TODO 255 */
224 snprintf(devname, sizeof(devname), "/dev/tun%d", devnum);
Harald Weltebed35df2011-11-02 13:06:18 +0100225 if (((*tun)->fd = open(devname, O_RDWR)) >= 0)
226 break;
227 if (errno != EBUSY)
228 break;
229 }
230 if ((*tun)->fd < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100231 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100232 "Can't find tunnel device");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200233 goto err_free;
Harald Weltebed35df2011-11-02 13:06:18 +0100234 }
jjakoec89e9f2004-01-10 06:38:43 +0000235
Harald Weltebed35df2011-11-02 13:06:18 +0100236 snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", devnum);
Harald Welte81bc2ae2017-08-11 12:56:30 +0200237 (*tun)->devname[sizeof((*tun)->devname)-1] = 0;
jjako1f158642004-02-05 20:39:57 +0000238
Harald Weltebed35df2011-11-02 13:06:18 +0100239 /* The tun device we found might have "old" IP addresses allocated */
240 /* We need to delete those. This problem is not present on Linux */
jjako1f158642004-02-05 20:39:57 +0000241
Harald Weltebed35df2011-11-02 13:06:18 +0100242 memset(&areq, 0, sizeof(areq));
jjako1f158642004-02-05 20:39:57 +0000243
Harald Weltebed35df2011-11-02 13:06:18 +0100244 /* Set up interface name */
245 strncpy(areq.ifra_name, (*tun)->devname, IFNAMSIZ);
246 areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
jjako1f158642004-02-05 20:39:57 +0000247
Harald Weltebed35df2011-11-02 13:06:18 +0100248 /* Create a channel to the NET kernel. */
249 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100250 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200251 goto err_close;
Harald Weltebed35df2011-11-02 13:06:18 +0100252 }
jjako1f158642004-02-05 20:39:57 +0000253
Harald Weltebed35df2011-11-02 13:06:18 +0100254 /* Delete any IP addresses until SIOCDIFADDR fails */
255 while (ioctl(fd, SIOCDIFADDR, (void *)&areq) != -1) ;
256
257 close(fd);
258 return 0;
jjako409b8552004-02-04 22:57:41 +0000259#endif
260
Harald Welte9e6dfa02017-08-12 15:06:19 +0200261err_close:
262 close((*tun)->fd);
263err_free:
264 free(*tun);
265 *tun = NULL;
266 return -1;
jjako52c24142002-12-16 13:33:51 +0000267}
268
jjakoa7cd2492003-04-11 09:40:12 +0000269int tun_free(struct tun_t *tun)
jjako52c24142002-12-16 13:33:51 +0000270{
jjako163b4552004-12-30 15:33:58 +0000271
Harald Weltebed35df2011-11-02 13:06:18 +0100272 if (tun->routes) {
Harald Welteb4c08282018-04-25 16:55:39 +0200273 netdev_delroute(&tun->dstaddr, &tun->addr, &tun->netmask);
Harald Weltebed35df2011-11-02 13:06:18 +0100274 }
jjako163b4552004-12-30 15:33:58 +0000275
Harald Weltef2286392018-04-25 19:02:31 +0200276 if (tun->fd >= 0) {
277 if (close(tun->fd)) {
278 SYS_ERR(DTUN, LOGL_ERROR, errno, "close() failed");
279 }
Harald Weltebed35df2011-11-02 13:06:18 +0100280 }
jjakoa7cd2492003-04-11 09:40:12 +0000281
Harald Weltef2286392018-04-25 19:02:31 +0200282 gtp_kernel_stop(tun->devname);
283
Harald Weltebed35df2011-11-02 13:06:18 +0100284 /* TODO: For solaris we need to unlink streams */
jjakoec89e9f2004-01-10 06:38:43 +0000285
Harald Weltebed35df2011-11-02 13:06:18 +0100286 free(tun);
287 return 0;
jjako52c24142002-12-16 13:33:51 +0000288}
289
Harald Weltebed35df2011-11-02 13:06:18 +0100290int tun_set_cb_ind(struct tun_t *this,
291 int (*cb_ind) (struct tun_t * tun, void *pack, unsigned len))
292{
293 this->cb_ind = cb_ind;
294 return 0;
jjakoa7cd2492003-04-11 09:40:12 +0000295}
296
jjakoa7cd2492003-04-11 09:40:12 +0000297int tun_decaps(struct tun_t *this)
jjako52c24142002-12-16 13:33:51 +0000298{
Harald Weltebed35df2011-11-02 13:06:18 +0100299 unsigned char buffer[PACKET_MAX];
300 int status;
jjako52c24142002-12-16 13:33:51 +0000301
Harald Weltebed35df2011-11-02 13:06:18 +0100302 if ((status = read(this->fd, buffer, sizeof(buffer))) <= 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100303 SYS_ERR(DTUN, LOGL_ERROR, errno, "read() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100304 return -1;
305 }
306
307 if (this->cb_ind)
308 return this->cb_ind(this, buffer, status);
309
310 return 0;
jjako52c24142002-12-16 13:33:51 +0000311}
312
313int tun_encaps(struct tun_t *tun, void *pack, unsigned len)
314{
Harald Weltebed35df2011-11-02 13:06:18 +0100315 return write(tun->fd, pack, len);
jjakoa7cd2492003-04-11 09:40:12 +0000316}
317
Harald Weltebed35df2011-11-02 13:06:18 +0100318int tun_runscript(struct tun_t *tun, char *script)
319{
jjakoa7cd2492003-04-11 09:40:12 +0000320
Harald Weltebed35df2011-11-02 13:06:18 +0100321 char buf[TUN_SCRIPTSIZE];
322 char snet[TUN_ADDRSIZE];
323 char smask[TUN_ADDRSIZE];
324 int rc;
325
326 strncpy(snet, inet_ntoa(tun->addr), sizeof(snet));
327 snet[sizeof(snet) - 1] = 0;
328 strncpy(smask, inet_ntoa(tun->netmask), sizeof(smask));
329 smask[sizeof(smask) - 1] = 0;
330
331 /* system("ipup /dev/tun0 192.168.0.10 255.255.255.0"); */
332 snprintf(buf, sizeof(buf), "%s %s %s %s",
333 script, tun->devname, snet, smask);
334 buf[sizeof(buf) - 1] = 0;
335 rc = system(buf);
336 if (rc == -1) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100337 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100338 "Error executing command %s", buf);
339 return -1;
340 }
341 return 0;
jjako52c24142002-12-16 13:33:51 +0000342}
Harald Weltef85fe972017-09-24 20:00:34 +0800343
Harald Welte4c7d2912017-11-08 15:19:17 +0900344/*! Obtain the local address of the tun device.
345 * \param[in] tun Target device owning the IP
346 * \param[out] prefix_list List of prefix structures to fill with each IPv4/6 and prefix length found.
347 * \param[in] prefix_size Amount of elements allowed to be fill in the prefix_list array.
348 * \param[in] flags Specify which kind of IP to look for: IP_TYPE_IPv4, IP_TYPE_IPv6_LINK, IP_TYPE_IPv6_NONLINK
349 * \returns The number of ips found following the criteria specified by flags, -1 on error.
350 *
351 * This function will fill prefix_list with up to prefix_size IPs following the
352 * criteria specified by flags parameter. It returns the number of IPs matching
353 * the criteria. As a result, the number returned can be bigger than
354 * prefix_size. It can be used with prefix_size=0 to get an estimate of the size
355 * needed for prefix_list.
356 */
357int tun_ip_local_get(const struct tun_t *tun, struct in46_prefix *prefix_list, size_t prefix_size, int flags)
358{
359 return netdev_ip_local_get(tun->devname, prefix_list, prefix_size, flags);
360}