blob: d6ca24e3deb54a8d843a320ea43bb47c7b212539 [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>
22#include <sys/types.h>
23#include <sys/socket.h>
24#include <netinet/in.h>
25#include <arpa/inet.h>
26#include <sys/stat.h>
27#include <sys/time.h>
28#include <unistd.h>
29#include <string.h>
30#include <errno.h>
31#include <fcntl.h>
32
33#include <stdio.h>
34#include <fcntl.h>
35#include <unistd.h>
36#include <sys/time.h>
jjako52c24142002-12-16 13:33:51 +000037#include <sys/ioctl.h>
38#include <sys/socket.h>
jjako52c24142002-12-16 13:33:51 +000039#include <errno.h>
jjakoa7cd2492003-04-11 09:40:12 +000040#include <net/route.h>
Harald Welte2e48a442017-08-03 00:47:03 +020041#include <net/if.h>
jjako52c24142002-12-16 13:33:51 +000042
jjako409b8552004-02-04 22:57:41 +000043#if defined(__linux__)
jjako0141d202004-01-09 15:19:20 +000044#include <linux/if_tun.h>
jjakoa7cd2492003-04-11 09:40:12 +000045#include <linux/netlink.h>
46#include <linux/rtnetlink.h>
jjako409b8552004-02-04 22:57:41 +000047
48#elif defined (__FreeBSD__)
jjako409b8552004-02-04 22:57:41 +000049#include <net/if_tun.h>
Harald Welte2e48a442017-08-03 00:47:03 +020050#include <net/if_var.h>
51#include <netinet/in_var.h>
jjako409b8552004-02-04 22:57:41 +000052
jjako0fe0df02004-09-17 11:30:40 +000053#elif defined (__APPLE__)
54#include <net/if.h>
55
jjako409b8552004-02-04 22:57:41 +000056#else
57#error "Unknown platform!"
jjako0141d202004-01-09 15:19:20 +000058#endif
59
jjako52c24142002-12-16 13:33:51 +000060#include "tun.h"
jjakoa7cd2492003-04-11 09:40:12 +000061#include "syserr.h"
jjako52c24142002-12-16 13:33:51 +000062
jjako409b8552004-02-04 22:57:41 +000063#if defined(__linux__)
64
Harald Welte2e48a442017-08-03 00:47:03 +020065#include <linux/ipv6.h>
66
Harald Welte2778ae22017-08-12 16:38:44 +020067static int tun_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, int dlen)
jjako52c24142002-12-16 13:33:51 +000068{
Harald Weltebed35df2011-11-02 13:06:18 +010069 int len = RTA_LENGTH(dlen);
70 int alen = NLMSG_ALIGN(n->nlmsg_len);
71 struct rtattr *rta = (struct rtattr *)(((void *)n) + alen);
72 if (alen + len > nsize)
73 return -1;
74 rta->rta_len = len;
75 rta->rta_type = type;
76 memcpy(RTA_DATA(rta), d, dlen);
77 n->nlmsg_len = alen + len;
78 return 0;
jjakoa7cd2492003-04-11 09:40:12 +000079}
jjakoec89e9f2004-01-10 06:38:43 +000080#endif
jjako52c24142002-12-16 13:33:51 +000081
Harald Weltedf3dcac2018-04-25 16:20:32 +020082static int netdev_sifflags(const char *devname, int flags)
Harald Weltebed35df2011-11-02 13:06:18 +010083{
84 struct ifreq ifr;
85 int fd;
jjakoa7cd2492003-04-11 09:40:12 +000086
Harald Weltebed35df2011-11-02 13:06:18 +010087 memset(&ifr, '\0', sizeof(ifr));
88 ifr.ifr_flags = flags;
Harald Weltedf3dcac2018-04-25 16:20:32 +020089 strncpy(ifr.ifr_name, devname, IFNAMSIZ);
Harald Weltebed35df2011-11-02 13:06:18 +010090 ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
91 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +010092 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
Michael McTernanb07d0702015-05-02 07:52:23 +020093 return -1;
Harald Weltebed35df2011-11-02 13:06:18 +010094 }
95 if (ioctl(fd, SIOCSIFFLAGS, &ifr)) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +010096 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +010097 "ioctl(SIOCSIFFLAGS) failed");
98 close(fd);
99 return -1;
100 }
101 close(fd);
102 return 0;
jjakoa7cd2492003-04-11 09:40:12 +0000103}
104
Harald Welte2e48a442017-08-03 00:47:03 +0200105static int tun_setaddr4(struct tun_t *this, struct in_addr *addr,
106 struct in_addr *dstaddr, struct in_addr *netmask)
jjakoa7cd2492003-04-11 09:40:12 +0000107{
Harald Weltebed35df2011-11-02 13:06:18 +0100108 struct ifreq ifr;
109 int fd;
jjakoa7cd2492003-04-11 09:40:12 +0000110
Harald Weltebed35df2011-11-02 13:06:18 +0100111 memset(&ifr, '\0', sizeof(ifr));
112 ifr.ifr_addr.sa_family = AF_INET;
113 ifr.ifr_dstaddr.sa_family = AF_INET;
jjako409b8552004-02-04 22:57:41 +0000114
115#if defined(__linux__)
Harald Weltebed35df2011-11-02 13:06:18 +0100116 ifr.ifr_netmask.sa_family = AF_INET;
jjako409b8552004-02-04 22:57:41 +0000117
jjako0fe0df02004-09-17 11:30:40 +0000118#elif defined(__FreeBSD__) || defined (__APPLE__)
Harald Weltebed35df2011-11-02 13:06:18 +0100119 ((struct sockaddr_in *)&ifr.ifr_addr)->sin_len =
120 sizeof(struct sockaddr_in);
121 ((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_len =
122 sizeof(struct sockaddr_in);
jjako243bfe62004-01-26 23:06:05 +0000123#endif
jjako409b8552004-02-04 22:57:41 +0000124
Harald Weltebed35df2011-11-02 13:06:18 +0100125 strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
126 ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
jjakoa7cd2492003-04-11 09:40:12 +0000127
Harald Weltebed35df2011-11-02 13:06:18 +0100128 /* Create a channel to the NET kernel. */
129 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100130 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100131 return -1;
132 }
jjakoa7cd2492003-04-11 09:40:12 +0000133
Harald Weltebed35df2011-11-02 13:06:18 +0100134 if (addr) { /* Set the interface address */
135 this->addr.s_addr = addr->s_addr;
136 memcpy(&((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr, addr,
137 sizeof(*addr));
138 if (ioctl(fd, SIOCSIFADDR, (void *)&ifr) < 0) {
139 if (errno != EEXIST) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100140 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100141 "ioctl(SIOCSIFADDR) failed");
142 } else {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100143 SYS_ERR(DTUN, LOGL_NOTICE, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100144 "ioctl(SIOCSIFADDR): Address already exists");
145 }
146 close(fd);
147 return -1;
148 }
149 }
jjakoa7cd2492003-04-11 09:40:12 +0000150
Harald Weltebed35df2011-11-02 13:06:18 +0100151 if (dstaddr) { /* Set the destination address */
152 this->dstaddr.s_addr = dstaddr->s_addr;
153 memcpy(&((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_addr,
154 dstaddr, sizeof(*dstaddr));
155 if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) & ifr) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100156 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100157 "ioctl(SIOCSIFDSTADDR) failed");
158 close(fd);
159 return -1;
160 }
161 }
jjakoa7cd2492003-04-11 09:40:12 +0000162
Harald Weltebed35df2011-11-02 13:06:18 +0100163 if (netmask) { /* Set the netmask */
164 this->netmask.s_addr = netmask->s_addr;
jjako409b8552004-02-04 22:57:41 +0000165#if defined(__linux__)
Harald Weltebed35df2011-11-02 13:06:18 +0100166 memcpy(&((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr,
167 netmask, sizeof(*netmask));
jjako409b8552004-02-04 22:57:41 +0000168
jjako0fe0df02004-09-17 11:30:40 +0000169#elif defined(__FreeBSD__) || defined (__APPLE__)
Harald Weltebed35df2011-11-02 13:06:18 +0100170 ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr =
171 netmask->s_addr;
jjakoec89e9f2004-01-10 06:38:43 +0000172#endif
jjako409b8552004-02-04 22:57:41 +0000173
Harald Weltebed35df2011-11-02 13:06:18 +0100174 if (ioctl(fd, SIOCSIFNETMASK, (void *)&ifr) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100175 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100176 "ioctl(SIOCSIFNETMASK) failed");
177 close(fd);
178 return -1;
179 }
180 }
jjako1ea66342004-01-28 09:27:34 +0000181
Harald Weltebed35df2011-11-02 13:06:18 +0100182 close(fd);
183 this->addrs++;
jjako409b8552004-02-04 22:57:41 +0000184
Harald Weltebed35df2011-11-02 13:06:18 +0100185 /* On linux the route to the interface is set automatically
186 on FreeBSD we have to do this manually */
187
188 /* TODO: How does it work on Solaris? */
189
Harald Weltedf3dcac2018-04-25 16:20:32 +0200190 netdev_sifflags(this->devname, IFF_UP | IFF_RUNNING);
jjako163b4552004-12-30 15:33:58 +0000191
jjako0fe0df02004-09-17 11:30:40 +0000192#if defined(__FreeBSD__) || defined (__APPLE__)
Harald Welte2e48a442017-08-03 00:47:03 +0200193 tun_addroute(this, dstaddr, addr, &this->netmask);
Harald Weltebed35df2011-11-02 13:06:18 +0100194 this->routes = 1;
jjako1ea66342004-01-28 09:27:34 +0000195#endif
196
Harald Weltebed35df2011-11-02 13:06:18 +0100197 return 0;
jjakoa7cd2492003-04-11 09:40:12 +0000198}
199
Harald Welte2e48a442017-08-03 00:47:03 +0200200static int tun_setaddr6(struct tun_t *this, struct in6_addr *addr, struct in6_addr *dstaddr,
201 size_t prefixlen)
202{
203 struct in6_ifreq ifr;
204 int fd;
205
206 memset(&ifr, 0, sizeof(ifr));
207
208#if defined(__linux__)
209 ifr.ifr6_prefixlen = prefixlen;
210 ifr.ifr6_ifindex = if_nametoindex(this->devname);
211 if (ifr.ifr6_ifindex == 0) {
212 SYS_ERR(DTUN, LOGL_ERROR, 0, "Error getting ifindex for %s\n", this->devname);
213 return -1;
214 }
215#elif defined(__FreeBSD__) || defined (__APPLE__)
216 strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
217#endif
218
219 /* Create a channel to the NET kernel */
220 if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
221 SYS_ERR(DTUN, LOGL_ERROR, 0, "socket() failed");
222 return -1;
223 }
224
225#if defined(__linux__)
226 if (addr) {
Harald Welte2e48a442017-08-03 00:47:03 +0200227 memcpy(&ifr.ifr6_addr, addr, sizeof(*addr));
228 if (ioctl(fd, SIOCSIFADDR, (void *) &ifr) < 0) {
229 if (errno != EEXIST) {
230 SYS_ERR(DTUN, LOGL_ERROR, 0, "ioctl(SIOCSIFADDR) failed");
231 } else {
Pau Espin Pedrold9fff0c2017-12-01 15:39:28 +0100232 SYS_ERR(DTUN, LOGL_NOTICE, 0, "ioctl(SIOCSIFADDR): Address already exists");
Harald Welte2e48a442017-08-03 00:47:03 +0200233 }
234 close(fd);
235 return -1;
236 }
237 }
238
239#if 0
240 /* FIXME: looks like this is not possible/necessary for IPv6? */
241 if (dstaddr) {
242 memcpy(&this->dstaddr, dstaddr, sizeof(*dstaddr));
243 memcpy(&ifr.ifr6_addr, dstaddr, sizeof(*dstaddr));
244 if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t *) &ifr) < 0) {
245 SYS_ERR(DTUN, LOGL_ERROR, "ioctl(SIOCSIFDSTADDR) failed");
246 close(fd);
247 return -1;
248 }
249 }
250#endif
251
252#elif defined(__FreeBSD__) || defined (__APPLE__)
253 if (addr)
254 memcpy(&ifr.ifr_ifru.ifru_addr, addr, sizeof(ifr.ifr_ifru.ifru_addr));
255 if (dstaddr)
256 memcpy(&ifr.ifr_ifru.ifru_dstaddr, dstaddr, sizeof(ifr.ifr_ifru.ifru_dstaddr));
257
258 if (ioctl(fd, SIOCSIFADDR_IN6, (struct ifreq *)&ifr) < 0) {
259 SYS_ERR(DTUN, LOGL_ERROR, 0, "ioctl(SIOCSIFADDR_IN6) failed");
260 close(fd);
261 return -1;
262 }
263#endif
264
265 close(fd);
266 this->addrs++;
267
268 /* On linux the route to the interface is set automatically
269 on FreeBSD we have to do this manually */
270
271 /* TODO: How does it work on Solaris? */
272
Harald Weltedf3dcac2018-04-25 16:20:32 +0200273 netdev_sifflags(this->devname, IFF_UP | IFF_RUNNING);
Harald Welte2e48a442017-08-03 00:47:03 +0200274
275#if 0 /* FIXME */
276//#if defined(__FreeBSD__) || defined (__APPLE__)
277 tun_addroute6(this, dstaddr, addr, prefixlen);
278 this->routes = 1;
279#endif
280
281 return 0;
282}
283
284int tun_setaddr(struct tun_t *this, struct in46_addr *addr, struct in46_addr *dstaddr, size_t prefixlen)
285{
286 struct in_addr netmask;
287 switch (addr->len) {
288 case 4:
289 netmask.s_addr = htonl(0xffffffff << (32 - prefixlen));
290 return tun_setaddr4(this, &addr->v4, dstaddr ? &dstaddr->v4 : NULL, &netmask);
291 case 16:
292 return tun_setaddr6(this, &addr->v6, dstaddr ? &dstaddr->v6 : NULL, prefixlen);
293 default:
294 return -1;
295 }
296}
297
Pau Espin Pedrolf5e40b72017-12-14 14:01:23 +0100298static int tun_addaddr4(struct tun_t *this,
Pau Espin Pedrolb9ace142017-12-01 14:45:31 +0100299 struct in_addr *addr,
300 struct in_addr *dstaddr, struct in_addr *netmask)
301{
302
303#if defined(__linux__)
304 struct {
305 struct nlmsghdr n;
306 struct ifaddrmsg i;
307 char buf[TUN_NLBUFSIZE];
308 } req;
309
310 struct sockaddr_nl local;
311 socklen_t addr_len;
312 int fd;
313 int status;
314
315 struct sockaddr_nl nladdr;
316 struct iovec iov;
317 struct msghdr msg;
318
319 if (!this->addrs) /* Use ioctl for first addr to make ping work */
320 return tun_setaddr4(this, addr, dstaddr, netmask);
321
322 memset(&req, 0, sizeof(req));
323 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
324 req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
325 req.n.nlmsg_type = RTM_NEWADDR;
326 req.i.ifa_family = AF_INET;
327 req.i.ifa_prefixlen = 32; /* 32 FOR IPv4 */
328 req.i.ifa_flags = 0;
329 req.i.ifa_scope = RT_SCOPE_HOST; /* TODO or 0 */
330 req.i.ifa_index = if_nametoindex(this->devname);
331 if (!req.i.ifa_index) {
332 SYS_ERR(DTUN, LOGL_ERROR, errno, "Unable to get ifindex for %s", this->devname);
333 return -1;
334 }
335
Pau Espin Pedrol02e21af2017-12-14 13:48:12 +0100336 tun_nlattr(&req.n, sizeof(req), IFA_ADDRESS, addr, sizeof(*addr));
337 if (dstaddr)
338 tun_nlattr(&req.n, sizeof(req), IFA_LOCAL, dstaddr, sizeof(*dstaddr));
Pau Espin Pedrolb9ace142017-12-01 14:45:31 +0100339
340 if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
341 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
342 return -1;
343 }
344
345 memset(&local, 0, sizeof(local));
346 local.nl_family = AF_NETLINK;
347 local.nl_groups = 0;
348
349 if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
350 SYS_ERR(DTUN, LOGL_ERROR, errno, "bind() failed");
351 close(fd);
352 return -1;
353 }
354
355 addr_len = sizeof(local);
356 if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0) {
357 SYS_ERR(DTUN, LOGL_ERROR, errno,
358 "getsockname() failed");
359 close(fd);
360 return -1;
361 }
362
363 if (addr_len != sizeof(local)) {
364 SYS_ERR(DTUN, LOGL_ERROR, 0,
365 "Wrong address length %d", addr_len);
366 close(fd);
367 return -1;
368 }
369
370 if (local.nl_family != AF_NETLINK) {
371 SYS_ERR(DTUN, LOGL_ERROR, 0,
372 "Wrong address family %d", local.nl_family);
373 close(fd);
374 return -1;
375 }
376
377 iov.iov_base = (void *)&req.n;
378 iov.iov_len = req.n.nlmsg_len;
379
380 msg.msg_name = (void *)&nladdr;
381 msg.msg_namelen = sizeof(nladdr);
382 msg.msg_iov = &iov;
383 msg.msg_iovlen = 1;
384 msg.msg_control = NULL;
385 msg.msg_controllen = 0;
386 msg.msg_flags = 0;
387
388 memset(&nladdr, 0, sizeof(nladdr));
389 nladdr.nl_family = AF_NETLINK;
390 nladdr.nl_pid = 0;
391 nladdr.nl_groups = 0;
392
393 req.n.nlmsg_seq = 0;
394 req.n.nlmsg_flags |= NLM_F_ACK;
395
396 status = sendmsg(fd, &msg, 0);
397 if (status != req.n.nlmsg_len) {
398 SYS_ERR(DTUN, LOGL_ERROR, errno, "sendmsg() failed, returned %d", status);
399 close(fd);
400 return -1;
401 }
402
Harald Weltedf3dcac2018-04-25 16:20:32 +0200403 status = netdev_sifflags(this->devname, IFF_UP | IFF_RUNNING);
Pau Espin Pedrolb9ace142017-12-01 14:45:31 +0100404 if (status == -1) {
405 close(fd);
406 return -1;
407 }
408
409
410 close(fd);
411 this->addrs++;
412 return 0;
413
414#elif defined (__FreeBSD__) || defined (__APPLE__)
415
416 int fd;
417 struct ifaliasreq areq;
418
419 /* TODO: Is this needed on FreeBSD? */
420 if (!this->addrs) /* Use ioctl for first addr to make ping work */
421 return tun_setaddr4(this, addr, dstaddr, netmask); /* TODO dstaddr */
422
423 memset(&areq, 0, sizeof(areq));
424
425 /* Set up interface name */
426 strncpy(areq.ifra_name, this->devname, IFNAMSIZ);
427 areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
428
429 ((struct sockaddr_in *)&areq.ifra_addr)->sin_family = AF_INET;
430 ((struct sockaddr_in *)&areq.ifra_addr)->sin_len =
431 sizeof(areq.ifra_addr);
432 ((struct sockaddr_in *)&areq.ifra_addr)->sin_addr.s_addr = addr->s_addr;
433
434 ((struct sockaddr_in *)&areq.ifra_mask)->sin_family = AF_INET;
435 ((struct sockaddr_in *)&areq.ifra_mask)->sin_len =
436 sizeof(areq.ifra_mask);
437 ((struct sockaddr_in *)&areq.ifra_mask)->sin_addr.s_addr =
438 netmask->s_addr;
439
440 /* For some reason FreeBSD uses ifra_broadcast for specifying dstaddr */
441 ((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_family = AF_INET;
442 ((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_len =
443 sizeof(areq.ifra_broadaddr);
444 ((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_addr.s_addr =
445 dstaddr->s_addr;
446
447 /* Create a channel to the NET kernel. */
448 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
449 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
450 return -1;
451 }
452
453 if (ioctl(fd, SIOCAIFADDR, (void *)&areq) < 0) {
454 SYS_ERR(DTUN, LOGL_ERROR, errno,
455 "ioctl(SIOCAIFADDR) failed");
456 close(fd);
457 return -1;
458 }
459
460 close(fd);
461 this->addrs++;
462 return 0;
463
464#endif
465
466}
467
Pau Espin Pedrolf5e40b72017-12-14 14:01:23 +0100468static int tun_addaddr6(struct tun_t *this,
469 struct in6_addr *addr,
470 struct in6_addr *dstaddr, int prefixlen)
471{
472
473#if defined(__linux__)
474 struct {
475 struct nlmsghdr n;
476 struct ifaddrmsg i;
477 char buf[TUN_NLBUFSIZE];
478 } req;
479
480 struct sockaddr_nl local;
481 socklen_t addr_len;
482 int fd;
483 int status;
484
485 struct sockaddr_nl nladdr;
486 struct iovec iov;
487 struct msghdr msg;
488
489 if (!this->addrs) /* Use ioctl for first addr to make ping work */
490 return tun_setaddr6(this, addr, dstaddr, prefixlen);
491
492 memset(&req, 0, sizeof(req));
493 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
494 req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
495 req.n.nlmsg_type = RTM_NEWADDR;
496 req.i.ifa_family = AF_INET6;
497 req.i.ifa_prefixlen = 64; /* 64 FOR IPv6 */
498 req.i.ifa_flags = 0;
499 req.i.ifa_scope = RT_SCOPE_HOST; /* TODO or 0 */
500 req.i.ifa_index = if_nametoindex(this->devname);
501 if (!req.i.ifa_index) {
502 SYS_ERR(DTUN, LOGL_ERROR, errno, "Unable to get ifindex for %s", this->devname);
503 return -1;
504 }
505
506 tun_nlattr(&req.n, sizeof(req), IFA_ADDRESS, addr, sizeof(*addr));
507 if (dstaddr)
508 tun_nlattr(&req.n, sizeof(req), IFA_LOCAL, dstaddr, sizeof(*dstaddr));
509
510 if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
511 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
512 return -1;
513 }
514
515 memset(&local, 0, sizeof(local));
516 local.nl_family = AF_NETLINK;
517 local.nl_groups = 0;
518
519 if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
520 SYS_ERR(DTUN, LOGL_ERROR, errno, "bind() failed");
521 close(fd);
522 return -1;
523 }
524
525 addr_len = sizeof(local);
526 if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0) {
527 SYS_ERR(DTUN, LOGL_ERROR, errno,
528 "getsockname() failed");
529 close(fd);
530 return -1;
531 }
532
533 if (addr_len != sizeof(local)) {
534 SYS_ERR(DTUN, LOGL_ERROR, 0,
535 "Wrong address length %d", addr_len);
536 close(fd);
537 return -1;
538 }
539
540 if (local.nl_family != AF_NETLINK) {
541 SYS_ERR(DTUN, LOGL_ERROR, 0,
542 "Wrong address family %d", local.nl_family);
543 close(fd);
544 return -1;
545 }
546
547 iov.iov_base = (void *)&req.n;
548 iov.iov_len = req.n.nlmsg_len;
549
550 msg.msg_name = (void *)&nladdr;
551 msg.msg_namelen = sizeof(nladdr);
552 msg.msg_iov = &iov;
553 msg.msg_iovlen = 1;
554 msg.msg_control = NULL;
555 msg.msg_controllen = 0;
556 msg.msg_flags = 0;
557
558 memset(&nladdr, 0, sizeof(nladdr));
559 nladdr.nl_family = AF_NETLINK;
560 nladdr.nl_pid = 0;
561 nladdr.nl_groups = 0;
562
563 req.n.nlmsg_seq = 0;
564 req.n.nlmsg_flags |= NLM_F_ACK;
565
566 status = sendmsg(fd, &msg, 0);
567 if (status != req.n.nlmsg_len) {
568 SYS_ERR(DTUN, LOGL_ERROR, errno, "sendmsg() failed, returned %d", status);
569 close(fd);
570 return -1;
571 }
572
Harald Weltedf3dcac2018-04-25 16:20:32 +0200573 status = netdev_sifflags(this->devname, IFF_UP | IFF_RUNNING);
Pau Espin Pedrolf5e40b72017-12-14 14:01:23 +0100574 if (status == -1) {
575 close(fd);
576 return -1;
577 }
578
579
580 close(fd);
581 this->addrs++;
582 return 0;
583
584#elif defined (__FreeBSD__) || defined (__APPLE__)
585
586 int fd;
587 struct ifaliasreq areq;
588
589 /* TODO: Is this needed on FreeBSD? */
590 if (!this->addrs) /* Use ioctl for first addr to make ping work */
591 return tun_setaddr6(this, addr, dstaddr, netmask); /* TODO dstaddr */
592
593 memset(&areq, 0, sizeof(areq));
594
595 /* Set up interface name */
596 strncpy(areq.ifra_name, this->devname, IFNAMSIZ);
597 areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
598
599 ((struct sockaddr_in6 *)&areq.ifra_addr)->sin6_family = AF_INET6;
600 ((struct sockaddr_in6 *)&areq.ifra_addr)->sin6_len = sizeof(areq.ifra_addr);
601 ((struct sockaddr_in6 *)&areq.ifra_addr)->sin6_addr.s6_addr = addr->s6_addr;
602
603 ((struct sockaddr_in6 *)&areq.ifra_mask)->sin6_family = AF_INET6;
604 ((struct sockaddr_in6 *)&areq.ifra_mask)->sin6_len = sizeof(areq.ifra_mask);
605 ((struct sockaddr_in6 *)&areq.ifra_mask)->sin6_addr.s6_addr = netmask->s6_addr;
606
607 /* For some reason FreeBSD uses ifra_broadcast for specifying dstaddr */
608 ((struct sockaddr_in6 *)&areq.ifra_broadaddr)->sin6_family = AF_INET6;
609 ((struct sockaddr_in6 *)&areq.ifra_broadaddr)->sin6_len = sizeof(areq.ifra_broadaddr);
610 ((struct sockaddr_in6 *)&areq.ifra_broadaddr)->sin6_addr.s6_addr = dstaddr->s6_addr;
611
612 /* Create a channel to the NET kernel. */
613 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
614 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
615 return -1;
616 }
617
618 if (ioctl(fd, SIOCAIFADDR, (void *)&areq) < 0) {
619 SYS_ERR(DTUN, LOGL_ERROR, errno,
620 "ioctl(SIOCAIFADDR) failed");
621 close(fd);
622 return -1;
623 }
624
625 close(fd);
626 this->addrs++;
627 return 0;
628
629#endif
630
631}
632
633int tun_addaddr(struct tun_t *this, struct in46_addr *addr, struct in46_addr *dstaddr, size_t prefixlen)
634{
635 struct in_addr netmask;
636 switch (addr->len) {
637 case 4:
638 netmask.s_addr = htonl(0xffffffff << (32 - prefixlen));
639 return tun_addaddr4(this, &addr->v4, dstaddr ? &dstaddr->v4 : NULL, &netmask);
640 case 16:
641 return tun_addaddr6(this, &addr->v6, dstaddr ? &dstaddr->v6 : NULL, prefixlen);
642 default:
643 return -1;
644 }
645}
646
Harald Welte2778ae22017-08-12 16:38:44 +0200647static int tun_route(struct tun_t *this,
jjako163b4552004-12-30 15:33:58 +0000648 struct in_addr *dst,
Harald Weltebed35df2011-11-02 13:06:18 +0100649 struct in_addr *gateway, struct in_addr *mask, int delete)
jjakoa7cd2492003-04-11 09:40:12 +0000650{
jjakoec89e9f2004-01-10 06:38:43 +0000651
jjako409b8552004-02-04 22:57:41 +0000652#if defined(__linux__)
jjakoec89e9f2004-01-10 06:38:43 +0000653
Harald Weltebed35df2011-11-02 13:06:18 +0100654 struct rtentry r;
655 int fd;
jjakoa7cd2492003-04-11 09:40:12 +0000656
Harald Weltebed35df2011-11-02 13:06:18 +0100657 memset(&r, '\0', sizeof(r));
658 r.rt_flags = RTF_UP | RTF_GATEWAY; /* RTF_HOST not set */
jjakoa7cd2492003-04-11 09:40:12 +0000659
Harald Weltebed35df2011-11-02 13:06:18 +0100660 /* Create a channel to the NET kernel. */
661 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100662 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100663 return -1;
664 }
jjakoa7cd2492003-04-11 09:40:12 +0000665
Harald Weltebed35df2011-11-02 13:06:18 +0100666 r.rt_dst.sa_family = AF_INET;
667 r.rt_gateway.sa_family = AF_INET;
668 r.rt_genmask.sa_family = AF_INET;
669 memcpy(&((struct sockaddr_in *)&r.rt_dst)->sin_addr, dst, sizeof(*dst));
670 memcpy(&((struct sockaddr_in *)&r.rt_gateway)->sin_addr, gateway,
671 sizeof(*gateway));
672 memcpy(&((struct sockaddr_in *)&r.rt_genmask)->sin_addr, mask,
673 sizeof(*mask));
674
675 if (delete) {
676 if (ioctl(fd, SIOCDELRT, (void *)&r) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100677 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100678 "ioctl(SIOCDELRT) failed");
679 close(fd);
680 return -1;
681 }
682 } else {
683 if (ioctl(fd, SIOCADDRT, (void *)&r) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100684 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100685 "ioctl(SIOCADDRT) failed");
686 close(fd);
687 return -1;
688 }
689 }
690 close(fd);
691 return 0;
692
jjako0fe0df02004-09-17 11:30:40 +0000693#elif defined(__FreeBSD__) || defined (__APPLE__)
jjako3eaf4532004-01-27 19:47:49 +0000694
Harald Weltebed35df2011-11-02 13:06:18 +0100695 struct {
696 struct rt_msghdr rt;
697 struct sockaddr_in dst;
698 struct sockaddr_in gate;
699 struct sockaddr_in mask;
700 } req;
jjako3eaf4532004-01-27 19:47:49 +0000701
Harald Weltebed35df2011-11-02 13:06:18 +0100702 int fd;
703 struct rt_msghdr *rtm;
jjako1ea66342004-01-28 09:27:34 +0000704
Harald Weltebed35df2011-11-02 13:06:18 +0100705 if ((fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100706 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100707 return -1;
708 }
709
710 memset(&req, 0x00, sizeof(req));
711
712 rtm = &req.rt;
713
714 rtm->rtm_msglen = sizeof(req);
715 rtm->rtm_version = RTM_VERSION;
716 if (delete) {
717 rtm->rtm_type = RTM_DELETE;
718 } else {
719 rtm->rtm_type = RTM_ADD;
720 }
721 rtm->rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; /* TODO */
722 rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
723 rtm->rtm_pid = getpid();
724 rtm->rtm_seq = 0044; /* TODO */
725
726 req.dst.sin_family = AF_INET;
727 req.dst.sin_len = sizeof(req.dst);
728 req.mask.sin_family = AF_INET;
729 req.mask.sin_len = sizeof(req.mask);
730 req.gate.sin_family = AF_INET;
731 req.gate.sin_len = sizeof(req.gate);
732
733 req.dst.sin_addr.s_addr = dst->s_addr;
734 req.mask.sin_addr.s_addr = mask->s_addr;
735 req.gate.sin_addr.s_addr = gateway->s_addr;
736
737 if (write(fd, rtm, rtm->rtm_msglen) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100738 SYS_ERR(DTUN, LOGL_ERROR, errno, "write() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100739 close(fd);
740 return -1;
741 }
742 close(fd);
743 return 0;
jjakoec89e9f2004-01-10 06:38:43 +0000744#endif
745
jjakoa7cd2492003-04-11 09:40:12 +0000746}
747
jjako163b4552004-12-30 15:33:58 +0000748int tun_addroute(struct tun_t *this,
749 struct in_addr *dst,
Harald Weltebed35df2011-11-02 13:06:18 +0100750 struct in_addr *gateway, struct in_addr *mask)
jjako163b4552004-12-30 15:33:58 +0000751{
Harald Weltebed35df2011-11-02 13:06:18 +0100752 return tun_route(this, dst, gateway, mask, 0);
jjako163b4552004-12-30 15:33:58 +0000753}
754
755int tun_delroute(struct tun_t *this,
756 struct in_addr *dst,
Harald Weltebed35df2011-11-02 13:06:18 +0100757 struct in_addr *gateway, struct in_addr *mask)
jjako163b4552004-12-30 15:33:58 +0000758{
Harald Weltebed35df2011-11-02 13:06:18 +0100759 return tun_route(this, dst, gateway, mask, 1);
jjako163b4552004-12-30 15:33:58 +0000760}
761
Harald Weltedda21ed2017-08-12 15:07:02 +0200762int tun_new(struct tun_t **tun, const char *dev_name)
jjakoa7cd2492003-04-11 09:40:12 +0000763{
jjakoec89e9f2004-01-10 06:38:43 +0000764
jjako409b8552004-02-04 22:57:41 +0000765#if defined(__linux__)
Harald Weltebed35df2011-11-02 13:06:18 +0100766 struct ifreq ifr;
jjako409b8552004-02-04 22:57:41 +0000767
jjako0fe0df02004-09-17 11:30:40 +0000768#elif defined(__FreeBSD__) || defined (__APPLE__)
Harald Weltebed35df2011-11-02 13:06:18 +0100769 char devname[IFNAMSIZ + 5]; /* "/dev/" + ifname */
770 int devnum;
771 struct ifaliasreq areq;
772 int fd;
jjakoec89e9f2004-01-10 06:38:43 +0000773#endif
Harald Weltebed35df2011-11-02 13:06:18 +0100774
775 if (!(*tun = calloc(1, sizeof(struct tun_t)))) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100776 SYS_ERR(DTUN, LOGL_ERROR, errno, "calloc() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100777 return EOF;
778 }
779
780 (*tun)->cb_ind = NULL;
781 (*tun)->addrs = 0;
782 (*tun)->routes = 0;
783
jjako409b8552004-02-04 22:57:41 +0000784#if defined(__linux__)
Harald Weltebed35df2011-11-02 13:06:18 +0100785 /* Open the actual tun device */
786 if (((*tun)->fd = open("/dev/net/tun", O_RDWR)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100787 SYS_ERR(DTUN, LOGL_ERROR, errno, "open() failed");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200788 goto err_free;
Harald Weltebed35df2011-11-02 13:06:18 +0100789 }
790
791 /* Set device flags. For some weird reason this is also the method
792 used to obtain the network interface name */
793 memset(&ifr, 0, sizeof(ifr));
Harald Weltedda21ed2017-08-12 15:07:02 +0200794 if (dev_name)
795 strcpy(ifr.ifr_name, dev_name);
Harald Weltebed35df2011-11-02 13:06:18 +0100796 ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Tun device, no packet info */
797 if (ioctl((*tun)->fd, TUNSETIFF, (void *)&ifr) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100798 SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl() failed");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200799 goto err_close;
Harald Weltebed35df2011-11-02 13:06:18 +0100800 }
801
802 strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ);
803 (*tun)->devname[IFNAMSIZ - 1] = 0;
804
805 ioctl((*tun)->fd, TUNSETNOCSUM, 1); /* Disable checksums */
806 return 0;
807
jjako0fe0df02004-09-17 11:30:40 +0000808#elif defined(__FreeBSD__) || defined (__APPLE__)
jjakoec89e9f2004-01-10 06:38:43 +0000809
Harald Weltebed35df2011-11-02 13:06:18 +0100810 /* Find suitable device */
811 for (devnum = 0; devnum < 255; devnum++) { /* TODO 255 */
812 snprintf(devname, sizeof(devname), "/dev/tun%d", devnum);
Harald Weltebed35df2011-11-02 13:06:18 +0100813 if (((*tun)->fd = open(devname, O_RDWR)) >= 0)
814 break;
815 if (errno != EBUSY)
816 break;
817 }
818 if ((*tun)->fd < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100819 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100820 "Can't find tunnel device");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200821 goto err_free;
Harald Weltebed35df2011-11-02 13:06:18 +0100822 }
jjakoec89e9f2004-01-10 06:38:43 +0000823
Harald Weltebed35df2011-11-02 13:06:18 +0100824 snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", devnum);
Harald Welte81bc2ae2017-08-11 12:56:30 +0200825 (*tun)->devname[sizeof((*tun)->devname)-1] = 0;
jjako1f158642004-02-05 20:39:57 +0000826
Harald Weltebed35df2011-11-02 13:06:18 +0100827 /* The tun device we found might have "old" IP addresses allocated */
828 /* We need to delete those. This problem is not present on Linux */
jjako1f158642004-02-05 20:39:57 +0000829
Harald Weltebed35df2011-11-02 13:06:18 +0100830 memset(&areq, 0, sizeof(areq));
jjako1f158642004-02-05 20:39:57 +0000831
Harald Weltebed35df2011-11-02 13:06:18 +0100832 /* Set up interface name */
833 strncpy(areq.ifra_name, (*tun)->devname, IFNAMSIZ);
834 areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
jjako1f158642004-02-05 20:39:57 +0000835
Harald Weltebed35df2011-11-02 13:06:18 +0100836 /* Create a channel to the NET kernel. */
837 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100838 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200839 goto err_close;
Harald Weltebed35df2011-11-02 13:06:18 +0100840 }
jjako1f158642004-02-05 20:39:57 +0000841
Harald Weltebed35df2011-11-02 13:06:18 +0100842 /* Delete any IP addresses until SIOCDIFADDR fails */
843 while (ioctl(fd, SIOCDIFADDR, (void *)&areq) != -1) ;
844
845 close(fd);
846 return 0;
jjako409b8552004-02-04 22:57:41 +0000847#endif
848
Harald Welte9e6dfa02017-08-12 15:06:19 +0200849err_close:
850 close((*tun)->fd);
851err_free:
852 free(*tun);
853 *tun = NULL;
854 return -1;
jjako52c24142002-12-16 13:33:51 +0000855}
856
jjakoa7cd2492003-04-11 09:40:12 +0000857int tun_free(struct tun_t *tun)
jjako52c24142002-12-16 13:33:51 +0000858{
jjako163b4552004-12-30 15:33:58 +0000859
Harald Weltebed35df2011-11-02 13:06:18 +0100860 if (tun->routes) {
861 tun_delroute(tun, &tun->dstaddr, &tun->addr, &tun->netmask);
862 }
jjako163b4552004-12-30 15:33:58 +0000863
Harald Weltebed35df2011-11-02 13:06:18 +0100864 if (close(tun->fd)) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100865 SYS_ERR(DTUN, LOGL_ERROR, errno, "close() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100866 }
jjakoa7cd2492003-04-11 09:40:12 +0000867
Harald Weltebed35df2011-11-02 13:06:18 +0100868 /* TODO: For solaris we need to unlink streams */
jjakoec89e9f2004-01-10 06:38:43 +0000869
Harald Weltebed35df2011-11-02 13:06:18 +0100870 free(tun);
871 return 0;
jjako52c24142002-12-16 13:33:51 +0000872}
873
Harald Weltebed35df2011-11-02 13:06:18 +0100874int tun_set_cb_ind(struct tun_t *this,
875 int (*cb_ind) (struct tun_t * tun, void *pack, unsigned len))
876{
877 this->cb_ind = cb_ind;
878 return 0;
jjakoa7cd2492003-04-11 09:40:12 +0000879}
880
jjakoa7cd2492003-04-11 09:40:12 +0000881int tun_decaps(struct tun_t *this)
jjako52c24142002-12-16 13:33:51 +0000882{
Harald Weltebed35df2011-11-02 13:06:18 +0100883 unsigned char buffer[PACKET_MAX];
884 int status;
jjako52c24142002-12-16 13:33:51 +0000885
Harald Weltebed35df2011-11-02 13:06:18 +0100886 if ((status = read(this->fd, buffer, sizeof(buffer))) <= 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100887 SYS_ERR(DTUN, LOGL_ERROR, errno, "read() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100888 return -1;
889 }
890
891 if (this->cb_ind)
892 return this->cb_ind(this, buffer, status);
893
894 return 0;
jjako52c24142002-12-16 13:33:51 +0000895}
896
897int tun_encaps(struct tun_t *tun, void *pack, unsigned len)
898{
Harald Weltebed35df2011-11-02 13:06:18 +0100899 return write(tun->fd, pack, len);
jjakoa7cd2492003-04-11 09:40:12 +0000900}
901
Harald Weltebed35df2011-11-02 13:06:18 +0100902int tun_runscript(struct tun_t *tun, char *script)
903{
jjakoa7cd2492003-04-11 09:40:12 +0000904
Harald Weltebed35df2011-11-02 13:06:18 +0100905 char buf[TUN_SCRIPTSIZE];
906 char snet[TUN_ADDRSIZE];
907 char smask[TUN_ADDRSIZE];
908 int rc;
909
910 strncpy(snet, inet_ntoa(tun->addr), sizeof(snet));
911 snet[sizeof(snet) - 1] = 0;
912 strncpy(smask, inet_ntoa(tun->netmask), sizeof(smask));
913 smask[sizeof(smask) - 1] = 0;
914
915 /* system("ipup /dev/tun0 192.168.0.10 255.255.255.0"); */
916 snprintf(buf, sizeof(buf), "%s %s %s %s",
917 script, tun->devname, snet, smask);
918 buf[sizeof(buf) - 1] = 0;
919 rc = system(buf);
920 if (rc == -1) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100921 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100922 "Error executing command %s", buf);
923 return -1;
924 }
925 return 0;
jjako52c24142002-12-16 13:33:51 +0000926}
Harald Weltef85fe972017-09-24 20:00:34 +0800927
928#include <ifaddrs.h>
929
Harald Welte4c7d2912017-11-08 15:19:17 +0900930/*! Obtain the local address of a network device
931 * \param[in] devname Target device owning the IP
Pau Espin Pedrola037e592017-10-16 14:41:37 +0200932 * \param[out] prefix_list List of prefix structures to fill with each IPv4/6 and prefix length found.
933 * \param[in] prefix_size Amount of elements allowed to be fill in the prefix_list array.
934 * \param[in] flags Specify which kind of IP to look for: IP_TYPE_IPv4, IP_TYPE_IPv6_LINK, IP_TYPE_IPv6_NONLINK
935 * \returns The number of ips found following the criteria specified by flags, -1 on error.
936 *
937 * This function will fill prefix_list with up to prefix_size IPs following the
938 * criteria specified by flags parameter. It returns the number of IPs matching
939 * the criteria. As a result, the number returned can be bigger than
940 * prefix_size. It can be used with prefix_size=0 to get an estimate of the size
941 * needed for prefix_list.
942 */
Harald Welte4c7d2912017-11-08 15:19:17 +0900943int netdev_ip_local_get(const char *devname, struct in46_prefix *prefix_list, size_t prefix_size, int flags)
Harald Weltef85fe972017-09-24 20:00:34 +0800944{
Harald Weltef85fe972017-09-24 20:00:34 +0800945 static const uint8_t ll_prefix[] = { 0xfe,0x80, 0,0, 0,0, 0,0 };
Pau Espin Pedrola037e592017-10-16 14:41:37 +0200946 struct ifaddrs *ifaddr, *ifa;
947 struct in46_addr netmask;
948 size_t count = 0;
949 bool is_ipv6_ll;
Harald Weltef85fe972017-09-24 20:00:34 +0800950
951 if (getifaddrs(&ifaddr) == -1) {
952 return -1;
953 }
954
955 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
Harald Weltef85fe972017-09-24 20:00:34 +0800956 if (ifa->ifa_addr == NULL)
957 continue;
958
Harald Welte4c7d2912017-11-08 15:19:17 +0900959 if (strcmp(ifa->ifa_name, devname))
Harald Weltef85fe972017-09-24 20:00:34 +0800960 continue;
961
Pau Espin Pedrola037e592017-10-16 14:41:37 +0200962 if (ifa->ifa_addr->sa_family == AF_INET && (flags & IP_TYPE_IPv4)) {
963 struct sockaddr_in *sin4 = (struct sockaddr_in *) ifa->ifa_addr;
964 struct sockaddr_in *netmask4 = (struct sockaddr_in *) ifa->ifa_netmask;
Harald Weltef85fe972017-09-24 20:00:34 +0800965
Pau Espin Pedrola037e592017-10-16 14:41:37 +0200966 if (count < prefix_size) {
967 netmask.len = sizeof(netmask4->sin_addr);
968 netmask.v4 = netmask4->sin_addr;
969 prefix_list[count].addr.len = sizeof(sin4->sin_addr);
970 prefix_list[count].addr.v4 = sin4->sin_addr;
971 prefix_list[count].prefixlen = in46a_netmasklen(&netmask);
972 }
973 count++;
974 }
975
976 if (ifa->ifa_addr->sa_family == AF_INET6 && (flags & IP_TYPE_IPv6)) {
977 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) ifa->ifa_addr;
978 struct sockaddr_in6 *netmask6 = (struct sockaddr_in6 *) ifa->ifa_netmask;
979
980 is_ipv6_ll = !memcmp(sin6->sin6_addr.s6_addr, ll_prefix, sizeof(ll_prefix));
981 if ((flags & IP_TYPE_IPv6_NONLINK) && is_ipv6_ll)
982 continue;
983 if ((flags & IP_TYPE_IPv6_LINK) && !is_ipv6_ll)
984 continue;
985
986 if (count < prefix_size) {
987 netmask.len = sizeof(netmask6->sin6_addr);
988 netmask.v6 = netmask6->sin6_addr;
989 prefix_list[count].addr.len = sizeof(sin6->sin6_addr);
990 prefix_list[count].addr.v6 = sin6->sin6_addr;
991 prefix_list[count].prefixlen = in46a_netmasklen(&netmask);
992 }
993 count++;
994 }
Harald Weltef85fe972017-09-24 20:00:34 +0800995 }
Pau Espin Pedrola037e592017-10-16 14:41:37 +0200996
Pau Espin Pedrol58c0da72017-10-12 16:42:46 +0200997 freeifaddrs(ifaddr);
Pau Espin Pedrola037e592017-10-16 14:41:37 +0200998 return count;
Harald Weltef85fe972017-09-24 20:00:34 +0800999}
Harald Welte4c7d2912017-11-08 15:19:17 +09001000
1001/*! Obtain the local address of the tun device.
1002 * \param[in] tun Target device owning the IP
1003 * \param[out] prefix_list List of prefix structures to fill with each IPv4/6 and prefix length found.
1004 * \param[in] prefix_size Amount of elements allowed to be fill in the prefix_list array.
1005 * \param[in] flags Specify which kind of IP to look for: IP_TYPE_IPv4, IP_TYPE_IPv6_LINK, IP_TYPE_IPv6_NONLINK
1006 * \returns The number of ips found following the criteria specified by flags, -1 on error.
1007 *
1008 * This function will fill prefix_list with up to prefix_size IPs following the
1009 * criteria specified by flags parameter. It returns the number of IPs matching
1010 * the criteria. As a result, the number returned can be bigger than
1011 * prefix_size. It can be used with prefix_size=0 to get an estimate of the size
1012 * needed for prefix_list.
1013 */
1014int tun_ip_local_get(const struct tun_t *tun, struct in46_prefix *prefix_list, size_t prefix_size, int flags)
1015{
1016 return netdev_ip_local_get(tun->devname, prefix_list, prefix_size, flags);
1017}