blob: 5960197993249a0cce5ca632a909a18a771a356a [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 Welte2e48a442017-08-03 00:47:03 +02004 * Copyright (C) 2017 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
Harald Welte2e48a442017-08-03 00:47:03 +020063static int tun_setaddr4(struct tun_t *this, struct in_addr *addr,
64 struct in_addr *dstaddr, struct in_addr *netmask);
65
jjako409b8552004-02-04 22:57:41 +000066#if defined(__linux__)
67
Harald Welte2e48a442017-08-03 00:47:03 +020068#include <linux/ipv6.h>
69
Harald Welte2778ae22017-08-12 16:38:44 +020070static int tun_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, int dlen)
jjako52c24142002-12-16 13:33:51 +000071{
Harald Weltebed35df2011-11-02 13:06:18 +010072 int len = RTA_LENGTH(dlen);
73 int alen = NLMSG_ALIGN(n->nlmsg_len);
74 struct rtattr *rta = (struct rtattr *)(((void *)n) + alen);
75 if (alen + len > nsize)
76 return -1;
77 rta->rta_len = len;
78 rta->rta_type = type;
79 memcpy(RTA_DATA(rta), d, dlen);
80 n->nlmsg_len = alen + len;
81 return 0;
jjakoa7cd2492003-04-11 09:40:12 +000082}
jjakoec89e9f2004-01-10 06:38:43 +000083#endif
jjako52c24142002-12-16 13:33:51 +000084
Harald Welte2778ae22017-08-12 16:38:44 +020085static int tun_sifflags(struct tun_t *this, int flags)
Harald Weltebed35df2011-11-02 13:06:18 +010086{
87 struct ifreq ifr;
88 int fd;
jjakoa7cd2492003-04-11 09:40:12 +000089
Harald Weltebed35df2011-11-02 13:06:18 +010090 memset(&ifr, '\0', sizeof(ifr));
91 ifr.ifr_flags = flags;
92 strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
93 ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
94 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +010095 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
Michael McTernanb07d0702015-05-02 07:52:23 +020096 return -1;
Harald Weltebed35df2011-11-02 13:06:18 +010097 }
98 if (ioctl(fd, SIOCSIFFLAGS, &ifr)) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +010099 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100100 "ioctl(SIOCSIFFLAGS) failed");
101 close(fd);
102 return -1;
103 }
104 close(fd);
105 return 0;
jjakoa7cd2492003-04-11 09:40:12 +0000106}
107
jjakoa7cd2492003-04-11 09:40:12 +0000108int tun_addaddr(struct tun_t *this,
109 struct in_addr *addr,
Harald Weltebed35df2011-11-02 13:06:18 +0100110 struct in_addr *dstaddr, struct in_addr *netmask)
111{
jjako0141d202004-01-09 15:19:20 +0000112
jjako409b8552004-02-04 22:57:41 +0000113#if defined(__linux__)
Harald Weltebed35df2011-11-02 13:06:18 +0100114 struct {
115 struct nlmsghdr n;
116 struct ifaddrmsg i;
117 char buf[TUN_NLBUFSIZE];
118 } req;
jjakoa7cd2492003-04-11 09:40:12 +0000119
Harald Weltebed35df2011-11-02 13:06:18 +0100120 struct sockaddr_nl local;
121 socklen_t addr_len;
122 int fd;
123 int status;
jjakoa7cd2492003-04-11 09:40:12 +0000124
Harald Weltebed35df2011-11-02 13:06:18 +0100125 struct sockaddr_nl nladdr;
126 struct iovec iov;
127 struct msghdr msg;
jjakoa7cd2492003-04-11 09:40:12 +0000128
Harald Weltebed35df2011-11-02 13:06:18 +0100129 if (!this->addrs) /* Use ioctl for first addr to make ping work */
Harald Welte2e48a442017-08-03 00:47:03 +0200130 return tun_setaddr4(this, addr, dstaddr, netmask);
jjakoa7cd2492003-04-11 09:40:12 +0000131
Harald Weltebed35df2011-11-02 13:06:18 +0100132 memset(&req, 0, sizeof(req));
133 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
134 req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
135 req.n.nlmsg_type = RTM_NEWADDR;
136 req.i.ifa_family = AF_INET;
137 req.i.ifa_prefixlen = 32; /* 32 FOR IPv4 */
138 req.i.ifa_flags = 0;
139 req.i.ifa_scope = RT_SCOPE_HOST; /* TODO or 0 */
Harald Welteab6d1892017-08-11 12:31:59 +0200140 req.i.ifa_index = if_nametoindex(this->devname);
141 if (!req.i.ifa_index) {
142 SYS_ERR(DTUN, LOGL_ERROR, errno, "Unable to get ifindex for %s", this->devname);
Harald Weltebed35df2011-11-02 13:06:18 +0100143 return -1;
144 }
jjakoa7cd2492003-04-11 09:40:12 +0000145
Harald Weltebed35df2011-11-02 13:06:18 +0100146 tun_nlattr(&req.n, sizeof(req), IFA_ADDRESS, addr, sizeof(addr));
147 tun_nlattr(&req.n, sizeof(req), IFA_LOCAL, dstaddr, sizeof(dstaddr));
jjakoa7cd2492003-04-11 09:40:12 +0000148
Harald Weltebed35df2011-11-02 13:06:18 +0100149 if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100150 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100151 return -1;
152 }
jjakoa7cd2492003-04-11 09:40:12 +0000153
Harald Weltebed35df2011-11-02 13:06:18 +0100154 memset(&local, 0, sizeof(local));
155 local.nl_family = AF_NETLINK;
156 local.nl_groups = 0;
jjakoa7cd2492003-04-11 09:40:12 +0000157
Harald Weltebed35df2011-11-02 13:06:18 +0100158 if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100159 SYS_ERR(DTUN, LOGL_ERROR, errno, "bind() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100160 close(fd);
161 return -1;
162 }
jjakoa7cd2492003-04-11 09:40:12 +0000163
Harald Weltebed35df2011-11-02 13:06:18 +0100164 addr_len = sizeof(local);
165 if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100166 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100167 "getsockname() failed");
168 close(fd);
169 return -1;
170 }
jjakoa7cd2492003-04-11 09:40:12 +0000171
Harald Weltebed35df2011-11-02 13:06:18 +0100172 if (addr_len != sizeof(local)) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100173 SYS_ERR(DTUN, LOGL_ERROR, 0,
Harald Weltebed35df2011-11-02 13:06:18 +0100174 "Wrong address length %d", addr_len);
175 close(fd);
176 return -1;
177 }
jjakoa7cd2492003-04-11 09:40:12 +0000178
Harald Weltebed35df2011-11-02 13:06:18 +0100179 if (local.nl_family != AF_NETLINK) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100180 SYS_ERR(DTUN, LOGL_ERROR, 0,
Harald Weltebed35df2011-11-02 13:06:18 +0100181 "Wrong address family %d", local.nl_family);
182 close(fd);
183 return -1;
184 }
jjakoa7cd2492003-04-11 09:40:12 +0000185
Harald Weltebed35df2011-11-02 13:06:18 +0100186 iov.iov_base = (void *)&req.n;
187 iov.iov_len = req.n.nlmsg_len;
jjakoa7cd2492003-04-11 09:40:12 +0000188
Harald Weltebed35df2011-11-02 13:06:18 +0100189 msg.msg_name = (void *)&nladdr;
Michael McTernanb07d0702015-05-02 07:52:23 +0200190 msg.msg_namelen = sizeof(nladdr);
191 msg.msg_iov = &iov;
Harald Weltebed35df2011-11-02 13:06:18 +0100192 msg.msg_iovlen = 1;
193 msg.msg_control = NULL;
194 msg.msg_controllen = 0;
195 msg.msg_flags = 0;
196
197 memset(&nladdr, 0, sizeof(nladdr));
198 nladdr.nl_family = AF_NETLINK;
199 nladdr.nl_pid = 0;
200 nladdr.nl_groups = 0;
201
202 req.n.nlmsg_seq = 0;
203 req.n.nlmsg_flags |= NLM_F_ACK;
204
Michael McTernanb07d0702015-05-02 07:52:23 +0200205 status = sendmsg(fd, &msg, 0);
206 if (status != req.n.nlmsg_len) {
207 SYS_ERR(DTUN, LOGL_ERROR, errno, "sendmsg() failed, returned %d", status);
208 close(fd);
209 return -1;
210 }
Harald Weltebed35df2011-11-02 13:06:18 +0100211
Michael McTernanb07d0702015-05-02 07:52:23 +0200212 status = tun_sifflags(this, IFF_UP | IFF_RUNNING);
213 if (status == -1) {
214 close(fd);
215 return -1;
216 }
217
218
Harald Weltebed35df2011-11-02 13:06:18 +0100219 close(fd);
220 this->addrs++;
221 return 0;
jjako409b8552004-02-04 22:57:41 +0000222
jjako0fe0df02004-09-17 11:30:40 +0000223#elif defined (__FreeBSD__) || defined (__APPLE__)
jjako409b8552004-02-04 22:57:41 +0000224
Harald Weltebed35df2011-11-02 13:06:18 +0100225 int fd;
226 struct ifaliasreq areq;
jjako409b8552004-02-04 22:57:41 +0000227
Harald Weltebed35df2011-11-02 13:06:18 +0100228 /* TODO: Is this needed on FreeBSD? */
229 if (!this->addrs) /* Use ioctl for first addr to make ping work */
Harald Welte2e48a442017-08-03 00:47:03 +0200230 return tun_setaddr4(this, addr, dstaddr, netmask); /* TODO dstaddr */
jjako409b8552004-02-04 22:57:41 +0000231
Harald Weltebed35df2011-11-02 13:06:18 +0100232 memset(&areq, 0, sizeof(areq));
jjako409b8552004-02-04 22:57:41 +0000233
Harald Weltebed35df2011-11-02 13:06:18 +0100234 /* Set up interface name */
235 strncpy(areq.ifra_name, this->devname, IFNAMSIZ);
236 areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
jjako409b8552004-02-04 22:57:41 +0000237
Harald Weltebed35df2011-11-02 13:06:18 +0100238 ((struct sockaddr_in *)&areq.ifra_addr)->sin_family = AF_INET;
239 ((struct sockaddr_in *)&areq.ifra_addr)->sin_len =
240 sizeof(areq.ifra_addr);
241 ((struct sockaddr_in *)&areq.ifra_addr)->sin_addr.s_addr = addr->s_addr;
jjako409b8552004-02-04 22:57:41 +0000242
Harald Weltebed35df2011-11-02 13:06:18 +0100243 ((struct sockaddr_in *)&areq.ifra_mask)->sin_family = AF_INET;
244 ((struct sockaddr_in *)&areq.ifra_mask)->sin_len =
245 sizeof(areq.ifra_mask);
246 ((struct sockaddr_in *)&areq.ifra_mask)->sin_addr.s_addr =
247 netmask->s_addr;
jjako409b8552004-02-04 22:57:41 +0000248
Harald Weltebed35df2011-11-02 13:06:18 +0100249 /* For some reason FreeBSD uses ifra_broadcast for specifying dstaddr */
250 ((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_family = AF_INET;
251 ((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_len =
252 sizeof(areq.ifra_broadaddr);
253 ((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_addr.s_addr =
254 dstaddr->s_addr;
jjako409b8552004-02-04 22:57:41 +0000255
Harald Weltebed35df2011-11-02 13:06:18 +0100256 /* Create a channel to the NET kernel. */
257 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100258 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100259 return -1;
260 }
jjako409b8552004-02-04 22:57:41 +0000261
Harald Weltebed35df2011-11-02 13:06:18 +0100262 if (ioctl(fd, SIOCAIFADDR, (void *)&areq) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100263 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100264 "ioctl(SIOCAIFADDR) failed");
265 close(fd);
266 return -1;
267 }
268
269 close(fd);
270 this->addrs++;
271 return 0;
jjako409b8552004-02-04 22:57:41 +0000272
jjako0141d202004-01-09 15:19:20 +0000273#endif
jjakoa7cd2492003-04-11 09:40:12 +0000274
Harald Weltebed35df2011-11-02 13:06:18 +0100275}
jjakoa7cd2492003-04-11 09:40:12 +0000276
Harald Welte2e48a442017-08-03 00:47:03 +0200277static int tun_setaddr4(struct tun_t *this, struct in_addr *addr,
278 struct in_addr *dstaddr, struct in_addr *netmask)
jjakoa7cd2492003-04-11 09:40:12 +0000279{
Harald Weltebed35df2011-11-02 13:06:18 +0100280 struct ifreq ifr;
281 int fd;
jjakoa7cd2492003-04-11 09:40:12 +0000282
Harald Weltebed35df2011-11-02 13:06:18 +0100283 memset(&ifr, '\0', sizeof(ifr));
284 ifr.ifr_addr.sa_family = AF_INET;
285 ifr.ifr_dstaddr.sa_family = AF_INET;
jjako409b8552004-02-04 22:57:41 +0000286
287#if defined(__linux__)
Harald Weltebed35df2011-11-02 13:06:18 +0100288 ifr.ifr_netmask.sa_family = AF_INET;
jjako409b8552004-02-04 22:57:41 +0000289
jjako0fe0df02004-09-17 11:30:40 +0000290#elif defined(__FreeBSD__) || defined (__APPLE__)
Harald Weltebed35df2011-11-02 13:06:18 +0100291 ((struct sockaddr_in *)&ifr.ifr_addr)->sin_len =
292 sizeof(struct sockaddr_in);
293 ((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_len =
294 sizeof(struct sockaddr_in);
jjako243bfe62004-01-26 23:06:05 +0000295#endif
jjako409b8552004-02-04 22:57:41 +0000296
Harald Weltebed35df2011-11-02 13:06:18 +0100297 strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
298 ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
jjakoa7cd2492003-04-11 09:40:12 +0000299
Harald Weltebed35df2011-11-02 13:06:18 +0100300 /* Create a channel to the NET kernel. */
301 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100302 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100303 return -1;
304 }
jjakoa7cd2492003-04-11 09:40:12 +0000305
Harald Weltebed35df2011-11-02 13:06:18 +0100306 if (addr) { /* Set the interface address */
307 this->addr.s_addr = addr->s_addr;
308 memcpy(&((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr, addr,
309 sizeof(*addr));
310 if (ioctl(fd, SIOCSIFADDR, (void *)&ifr) < 0) {
311 if (errno != EEXIST) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100312 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100313 "ioctl(SIOCSIFADDR) failed");
314 } else {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100315 SYS_ERR(DTUN, LOGL_NOTICE, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100316 "ioctl(SIOCSIFADDR): Address already exists");
317 }
318 close(fd);
319 return -1;
320 }
321 }
jjakoa7cd2492003-04-11 09:40:12 +0000322
Harald Weltebed35df2011-11-02 13:06:18 +0100323 if (dstaddr) { /* Set the destination address */
324 this->dstaddr.s_addr = dstaddr->s_addr;
325 memcpy(&((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_addr,
326 dstaddr, sizeof(*dstaddr));
327 if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) & ifr) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100328 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100329 "ioctl(SIOCSIFDSTADDR) failed");
330 close(fd);
331 return -1;
332 }
333 }
jjakoa7cd2492003-04-11 09:40:12 +0000334
Harald Weltebed35df2011-11-02 13:06:18 +0100335 if (netmask) { /* Set the netmask */
336 this->netmask.s_addr = netmask->s_addr;
jjako409b8552004-02-04 22:57:41 +0000337#if defined(__linux__)
Harald Weltebed35df2011-11-02 13:06:18 +0100338 memcpy(&((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr,
339 netmask, sizeof(*netmask));
jjako409b8552004-02-04 22:57:41 +0000340
jjako0fe0df02004-09-17 11:30:40 +0000341#elif defined(__FreeBSD__) || defined (__APPLE__)
Harald Weltebed35df2011-11-02 13:06:18 +0100342 ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr =
343 netmask->s_addr;
jjakoec89e9f2004-01-10 06:38:43 +0000344#endif
jjako409b8552004-02-04 22:57:41 +0000345
Harald Weltebed35df2011-11-02 13:06:18 +0100346 if (ioctl(fd, SIOCSIFNETMASK, (void *)&ifr) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100347 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100348 "ioctl(SIOCSIFNETMASK) failed");
349 close(fd);
350 return -1;
351 }
352 }
jjako1ea66342004-01-28 09:27:34 +0000353
Harald Weltebed35df2011-11-02 13:06:18 +0100354 close(fd);
355 this->addrs++;
jjako409b8552004-02-04 22:57:41 +0000356
Harald Weltebed35df2011-11-02 13:06:18 +0100357 /* On linux the route to the interface is set automatically
358 on FreeBSD we have to do this manually */
359
360 /* TODO: How does it work on Solaris? */
361
362 tun_sifflags(this, IFF_UP | IFF_RUNNING);
jjako163b4552004-12-30 15:33:58 +0000363
jjako0fe0df02004-09-17 11:30:40 +0000364#if defined(__FreeBSD__) || defined (__APPLE__)
Harald Welte2e48a442017-08-03 00:47:03 +0200365 tun_addroute(this, dstaddr, addr, &this->netmask);
Harald Weltebed35df2011-11-02 13:06:18 +0100366 this->routes = 1;
jjako1ea66342004-01-28 09:27:34 +0000367#endif
368
Harald Weltebed35df2011-11-02 13:06:18 +0100369 return 0;
jjakoa7cd2492003-04-11 09:40:12 +0000370}
371
Harald Welte2e48a442017-08-03 00:47:03 +0200372static int tun_setaddr6(struct tun_t *this, struct in6_addr *addr, struct in6_addr *dstaddr,
373 size_t prefixlen)
374{
375 struct in6_ifreq ifr;
376 int fd;
377
378 memset(&ifr, 0, sizeof(ifr));
379
380#if defined(__linux__)
381 ifr.ifr6_prefixlen = prefixlen;
382 ifr.ifr6_ifindex = if_nametoindex(this->devname);
383 if (ifr.ifr6_ifindex == 0) {
384 SYS_ERR(DTUN, LOGL_ERROR, 0, "Error getting ifindex for %s\n", this->devname);
385 return -1;
386 }
387#elif defined(__FreeBSD__) || defined (__APPLE__)
388 strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
389#endif
390
391 /* Create a channel to the NET kernel */
392 if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
393 SYS_ERR(DTUN, LOGL_ERROR, 0, "socket() failed");
394 return -1;
395 }
396
397#if defined(__linux__)
398 if (addr) {
Harald Welte2e48a442017-08-03 00:47:03 +0200399 memcpy(&ifr.ifr6_addr, addr, sizeof(*addr));
400 if (ioctl(fd, SIOCSIFADDR, (void *) &ifr) < 0) {
401 if (errno != EEXIST) {
402 SYS_ERR(DTUN, LOGL_ERROR, 0, "ioctl(SIOCSIFADDR) failed");
403 } else {
Pau Espin Pedrold9fff0c2017-12-01 15:39:28 +0100404 SYS_ERR(DTUN, LOGL_NOTICE, 0, "ioctl(SIOCSIFADDR): Address already exists");
Harald Welte2e48a442017-08-03 00:47:03 +0200405 }
406 close(fd);
407 return -1;
408 }
409 }
410
411#if 0
412 /* FIXME: looks like this is not possible/necessary for IPv6? */
413 if (dstaddr) {
414 memcpy(&this->dstaddr, dstaddr, sizeof(*dstaddr));
415 memcpy(&ifr.ifr6_addr, dstaddr, sizeof(*dstaddr));
416 if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t *) &ifr) < 0) {
417 SYS_ERR(DTUN, LOGL_ERROR, "ioctl(SIOCSIFDSTADDR) failed");
418 close(fd);
419 return -1;
420 }
421 }
422#endif
423
424#elif defined(__FreeBSD__) || defined (__APPLE__)
425 if (addr)
426 memcpy(&ifr.ifr_ifru.ifru_addr, addr, sizeof(ifr.ifr_ifru.ifru_addr));
427 if (dstaddr)
428 memcpy(&ifr.ifr_ifru.ifru_dstaddr, dstaddr, sizeof(ifr.ifr_ifru.ifru_dstaddr));
429
430 if (ioctl(fd, SIOCSIFADDR_IN6, (struct ifreq *)&ifr) < 0) {
431 SYS_ERR(DTUN, LOGL_ERROR, 0, "ioctl(SIOCSIFADDR_IN6) failed");
432 close(fd);
433 return -1;
434 }
435#endif
436
437 close(fd);
438 this->addrs++;
439
440 /* On linux the route to the interface is set automatically
441 on FreeBSD we have to do this manually */
442
443 /* TODO: How does it work on Solaris? */
444
445 tun_sifflags(this, IFF_UP | IFF_RUNNING);
446
447#if 0 /* FIXME */
448//#if defined(__FreeBSD__) || defined (__APPLE__)
449 tun_addroute6(this, dstaddr, addr, prefixlen);
450 this->routes = 1;
451#endif
452
453 return 0;
454}
455
456int tun_setaddr(struct tun_t *this, struct in46_addr *addr, struct in46_addr *dstaddr, size_t prefixlen)
457{
458 struct in_addr netmask;
459 switch (addr->len) {
460 case 4:
461 netmask.s_addr = htonl(0xffffffff << (32 - prefixlen));
462 return tun_setaddr4(this, &addr->v4, dstaddr ? &dstaddr->v4 : NULL, &netmask);
463 case 16:
464 return tun_setaddr6(this, &addr->v6, dstaddr ? &dstaddr->v6 : NULL, prefixlen);
465 default:
466 return -1;
467 }
468}
469
Harald Welte2778ae22017-08-12 16:38:44 +0200470static int tun_route(struct tun_t *this,
jjako163b4552004-12-30 15:33:58 +0000471 struct in_addr *dst,
Harald Weltebed35df2011-11-02 13:06:18 +0100472 struct in_addr *gateway, struct in_addr *mask, int delete)
jjakoa7cd2492003-04-11 09:40:12 +0000473{
jjakoec89e9f2004-01-10 06:38:43 +0000474
jjako409b8552004-02-04 22:57:41 +0000475#if defined(__linux__)
jjakoec89e9f2004-01-10 06:38:43 +0000476
Harald Weltebed35df2011-11-02 13:06:18 +0100477 struct rtentry r;
478 int fd;
jjakoa7cd2492003-04-11 09:40:12 +0000479
Harald Weltebed35df2011-11-02 13:06:18 +0100480 memset(&r, '\0', sizeof(r));
481 r.rt_flags = RTF_UP | RTF_GATEWAY; /* RTF_HOST not set */
jjakoa7cd2492003-04-11 09:40:12 +0000482
Harald Weltebed35df2011-11-02 13:06:18 +0100483 /* Create a channel to the NET kernel. */
484 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100485 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100486 return -1;
487 }
jjakoa7cd2492003-04-11 09:40:12 +0000488
Harald Weltebed35df2011-11-02 13:06:18 +0100489 r.rt_dst.sa_family = AF_INET;
490 r.rt_gateway.sa_family = AF_INET;
491 r.rt_genmask.sa_family = AF_INET;
492 memcpy(&((struct sockaddr_in *)&r.rt_dst)->sin_addr, dst, sizeof(*dst));
493 memcpy(&((struct sockaddr_in *)&r.rt_gateway)->sin_addr, gateway,
494 sizeof(*gateway));
495 memcpy(&((struct sockaddr_in *)&r.rt_genmask)->sin_addr, mask,
496 sizeof(*mask));
497
498 if (delete) {
499 if (ioctl(fd, SIOCDELRT, (void *)&r) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100500 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100501 "ioctl(SIOCDELRT) failed");
502 close(fd);
503 return -1;
504 }
505 } else {
506 if (ioctl(fd, SIOCADDRT, (void *)&r) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100507 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100508 "ioctl(SIOCADDRT) failed");
509 close(fd);
510 return -1;
511 }
512 }
513 close(fd);
514 return 0;
515
jjako0fe0df02004-09-17 11:30:40 +0000516#elif defined(__FreeBSD__) || defined (__APPLE__)
jjako3eaf4532004-01-27 19:47:49 +0000517
Harald Weltebed35df2011-11-02 13:06:18 +0100518 struct {
519 struct rt_msghdr rt;
520 struct sockaddr_in dst;
521 struct sockaddr_in gate;
522 struct sockaddr_in mask;
523 } req;
jjako3eaf4532004-01-27 19:47:49 +0000524
Harald Weltebed35df2011-11-02 13:06:18 +0100525 int fd;
526 struct rt_msghdr *rtm;
jjako1ea66342004-01-28 09:27:34 +0000527
Harald Weltebed35df2011-11-02 13:06:18 +0100528 if ((fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100529 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100530 return -1;
531 }
532
533 memset(&req, 0x00, sizeof(req));
534
535 rtm = &req.rt;
536
537 rtm->rtm_msglen = sizeof(req);
538 rtm->rtm_version = RTM_VERSION;
539 if (delete) {
540 rtm->rtm_type = RTM_DELETE;
541 } else {
542 rtm->rtm_type = RTM_ADD;
543 }
544 rtm->rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; /* TODO */
545 rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
546 rtm->rtm_pid = getpid();
547 rtm->rtm_seq = 0044; /* TODO */
548
549 req.dst.sin_family = AF_INET;
550 req.dst.sin_len = sizeof(req.dst);
551 req.mask.sin_family = AF_INET;
552 req.mask.sin_len = sizeof(req.mask);
553 req.gate.sin_family = AF_INET;
554 req.gate.sin_len = sizeof(req.gate);
555
556 req.dst.sin_addr.s_addr = dst->s_addr;
557 req.mask.sin_addr.s_addr = mask->s_addr;
558 req.gate.sin_addr.s_addr = gateway->s_addr;
559
560 if (write(fd, rtm, rtm->rtm_msglen) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100561 SYS_ERR(DTUN, LOGL_ERROR, errno, "write() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100562 close(fd);
563 return -1;
564 }
565 close(fd);
566 return 0;
jjakoec89e9f2004-01-10 06:38:43 +0000567#endif
568
jjakoa7cd2492003-04-11 09:40:12 +0000569}
570
jjako163b4552004-12-30 15:33:58 +0000571int tun_addroute(struct tun_t *this,
572 struct in_addr *dst,
Harald Weltebed35df2011-11-02 13:06:18 +0100573 struct in_addr *gateway, struct in_addr *mask)
jjako163b4552004-12-30 15:33:58 +0000574{
Harald Weltebed35df2011-11-02 13:06:18 +0100575 return tun_route(this, dst, gateway, mask, 0);
jjako163b4552004-12-30 15:33:58 +0000576}
577
578int tun_delroute(struct tun_t *this,
579 struct in_addr *dst,
Harald Weltebed35df2011-11-02 13:06:18 +0100580 struct in_addr *gateway, struct in_addr *mask)
jjako163b4552004-12-30 15:33:58 +0000581{
Harald Weltebed35df2011-11-02 13:06:18 +0100582 return tun_route(this, dst, gateway, mask, 1);
jjako163b4552004-12-30 15:33:58 +0000583}
584
Harald Weltedda21ed2017-08-12 15:07:02 +0200585int tun_new(struct tun_t **tun, const char *dev_name)
jjakoa7cd2492003-04-11 09:40:12 +0000586{
jjakoec89e9f2004-01-10 06:38:43 +0000587
jjako409b8552004-02-04 22:57:41 +0000588#if defined(__linux__)
Harald Weltebed35df2011-11-02 13:06:18 +0100589 struct ifreq ifr;
jjako409b8552004-02-04 22:57:41 +0000590
jjako0fe0df02004-09-17 11:30:40 +0000591#elif defined(__FreeBSD__) || defined (__APPLE__)
Harald Weltebed35df2011-11-02 13:06:18 +0100592 char devname[IFNAMSIZ + 5]; /* "/dev/" + ifname */
593 int devnum;
594 struct ifaliasreq areq;
595 int fd;
jjakoec89e9f2004-01-10 06:38:43 +0000596#endif
Harald Weltebed35df2011-11-02 13:06:18 +0100597
598 if (!(*tun = calloc(1, sizeof(struct tun_t)))) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100599 SYS_ERR(DTUN, LOGL_ERROR, errno, "calloc() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100600 return EOF;
601 }
602
603 (*tun)->cb_ind = NULL;
604 (*tun)->addrs = 0;
605 (*tun)->routes = 0;
606
jjako409b8552004-02-04 22:57:41 +0000607#if defined(__linux__)
Harald Weltebed35df2011-11-02 13:06:18 +0100608 /* Open the actual tun device */
609 if (((*tun)->fd = open("/dev/net/tun", O_RDWR)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100610 SYS_ERR(DTUN, LOGL_ERROR, errno, "open() failed");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200611 goto err_free;
Harald Weltebed35df2011-11-02 13:06:18 +0100612 }
613
614 /* Set device flags. For some weird reason this is also the method
615 used to obtain the network interface name */
616 memset(&ifr, 0, sizeof(ifr));
Harald Weltedda21ed2017-08-12 15:07:02 +0200617 if (dev_name)
618 strcpy(ifr.ifr_name, dev_name);
Harald Weltebed35df2011-11-02 13:06:18 +0100619 ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Tun device, no packet info */
620 if (ioctl((*tun)->fd, TUNSETIFF, (void *)&ifr) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100621 SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl() failed");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200622 goto err_close;
Harald Weltebed35df2011-11-02 13:06:18 +0100623 }
624
625 strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ);
626 (*tun)->devname[IFNAMSIZ - 1] = 0;
627
628 ioctl((*tun)->fd, TUNSETNOCSUM, 1); /* Disable checksums */
629 return 0;
630
jjako0fe0df02004-09-17 11:30:40 +0000631#elif defined(__FreeBSD__) || defined (__APPLE__)
jjakoec89e9f2004-01-10 06:38:43 +0000632
Harald Weltebed35df2011-11-02 13:06:18 +0100633 /* Find suitable device */
634 for (devnum = 0; devnum < 255; devnum++) { /* TODO 255 */
635 snprintf(devname, sizeof(devname), "/dev/tun%d", devnum);
Harald Weltebed35df2011-11-02 13:06:18 +0100636 if (((*tun)->fd = open(devname, O_RDWR)) >= 0)
637 break;
638 if (errno != EBUSY)
639 break;
640 }
641 if ((*tun)->fd < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100642 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100643 "Can't find tunnel device");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200644 goto err_free;
Harald Weltebed35df2011-11-02 13:06:18 +0100645 }
jjakoec89e9f2004-01-10 06:38:43 +0000646
Harald Weltebed35df2011-11-02 13:06:18 +0100647 snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", devnum);
Harald Welte81bc2ae2017-08-11 12:56:30 +0200648 (*tun)->devname[sizeof((*tun)->devname)-1] = 0;
jjako1f158642004-02-05 20:39:57 +0000649
Harald Weltebed35df2011-11-02 13:06:18 +0100650 /* The tun device we found might have "old" IP addresses allocated */
651 /* We need to delete those. This problem is not present on Linux */
jjako1f158642004-02-05 20:39:57 +0000652
Harald Weltebed35df2011-11-02 13:06:18 +0100653 memset(&areq, 0, sizeof(areq));
jjako1f158642004-02-05 20:39:57 +0000654
Harald Weltebed35df2011-11-02 13:06:18 +0100655 /* Set up interface name */
656 strncpy(areq.ifra_name, (*tun)->devname, IFNAMSIZ);
657 areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
jjako1f158642004-02-05 20:39:57 +0000658
Harald Weltebed35df2011-11-02 13:06:18 +0100659 /* Create a channel to the NET kernel. */
660 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100661 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200662 goto err_close;
Harald Weltebed35df2011-11-02 13:06:18 +0100663 }
jjako1f158642004-02-05 20:39:57 +0000664
Harald Weltebed35df2011-11-02 13:06:18 +0100665 /* Delete any IP addresses until SIOCDIFADDR fails */
666 while (ioctl(fd, SIOCDIFADDR, (void *)&areq) != -1) ;
667
668 close(fd);
669 return 0;
jjako409b8552004-02-04 22:57:41 +0000670#endif
671
Harald Welte9e6dfa02017-08-12 15:06:19 +0200672err_close:
673 close((*tun)->fd);
674err_free:
675 free(*tun);
676 *tun = NULL;
677 return -1;
jjako52c24142002-12-16 13:33:51 +0000678}
679
jjakoa7cd2492003-04-11 09:40:12 +0000680int tun_free(struct tun_t *tun)
jjako52c24142002-12-16 13:33:51 +0000681{
jjako163b4552004-12-30 15:33:58 +0000682
Harald Weltebed35df2011-11-02 13:06:18 +0100683 if (tun->routes) {
684 tun_delroute(tun, &tun->dstaddr, &tun->addr, &tun->netmask);
685 }
jjako163b4552004-12-30 15:33:58 +0000686
Harald Weltebed35df2011-11-02 13:06:18 +0100687 if (close(tun->fd)) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100688 SYS_ERR(DTUN, LOGL_ERROR, errno, "close() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100689 }
jjakoa7cd2492003-04-11 09:40:12 +0000690
Harald Weltebed35df2011-11-02 13:06:18 +0100691 /* TODO: For solaris we need to unlink streams */
jjakoec89e9f2004-01-10 06:38:43 +0000692
Harald Weltebed35df2011-11-02 13:06:18 +0100693 free(tun);
694 return 0;
jjako52c24142002-12-16 13:33:51 +0000695}
696
Harald Weltebed35df2011-11-02 13:06:18 +0100697int tun_set_cb_ind(struct tun_t *this,
698 int (*cb_ind) (struct tun_t * tun, void *pack, unsigned len))
699{
700 this->cb_ind = cb_ind;
701 return 0;
jjakoa7cd2492003-04-11 09:40:12 +0000702}
703
jjakoa7cd2492003-04-11 09:40:12 +0000704int tun_decaps(struct tun_t *this)
jjako52c24142002-12-16 13:33:51 +0000705{
Harald Weltebed35df2011-11-02 13:06:18 +0100706 unsigned char buffer[PACKET_MAX];
707 int status;
jjako52c24142002-12-16 13:33:51 +0000708
Harald Weltebed35df2011-11-02 13:06:18 +0100709 if ((status = read(this->fd, buffer, sizeof(buffer))) <= 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100710 SYS_ERR(DTUN, LOGL_ERROR, errno, "read() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100711 return -1;
712 }
713
714 if (this->cb_ind)
715 return this->cb_ind(this, buffer, status);
716
717 return 0;
jjako52c24142002-12-16 13:33:51 +0000718}
719
720int tun_encaps(struct tun_t *tun, void *pack, unsigned len)
721{
Harald Weltebed35df2011-11-02 13:06:18 +0100722 return write(tun->fd, pack, len);
jjakoa7cd2492003-04-11 09:40:12 +0000723}
724
Harald Weltebed35df2011-11-02 13:06:18 +0100725int tun_runscript(struct tun_t *tun, char *script)
726{
jjakoa7cd2492003-04-11 09:40:12 +0000727
Harald Weltebed35df2011-11-02 13:06:18 +0100728 char buf[TUN_SCRIPTSIZE];
729 char snet[TUN_ADDRSIZE];
730 char smask[TUN_ADDRSIZE];
731 int rc;
732
733 strncpy(snet, inet_ntoa(tun->addr), sizeof(snet));
734 snet[sizeof(snet) - 1] = 0;
735 strncpy(smask, inet_ntoa(tun->netmask), sizeof(smask));
736 smask[sizeof(smask) - 1] = 0;
737
738 /* system("ipup /dev/tun0 192.168.0.10 255.255.255.0"); */
739 snprintf(buf, sizeof(buf), "%s %s %s %s",
740 script, tun->devname, snet, smask);
741 buf[sizeof(buf) - 1] = 0;
742 rc = system(buf);
743 if (rc == -1) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100744 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100745 "Error executing command %s", buf);
746 return -1;
747 }
748 return 0;
jjako52c24142002-12-16 13:33:51 +0000749}
Harald Weltef85fe972017-09-24 20:00:34 +0800750
751#include <ifaddrs.h>
752
Harald Welte4c7d2912017-11-08 15:19:17 +0900753/*! Obtain the local address of a network device
754 * \param[in] devname Target device owning the IP
Pau Espin Pedrola037e592017-10-16 14:41:37 +0200755 * \param[out] prefix_list List of prefix structures to fill with each IPv4/6 and prefix length found.
756 * \param[in] prefix_size Amount of elements allowed to be fill in the prefix_list array.
757 * \param[in] flags Specify which kind of IP to look for: IP_TYPE_IPv4, IP_TYPE_IPv6_LINK, IP_TYPE_IPv6_NONLINK
758 * \returns The number of ips found following the criteria specified by flags, -1 on error.
759 *
760 * This function will fill prefix_list with up to prefix_size IPs following the
761 * criteria specified by flags parameter. It returns the number of IPs matching
762 * the criteria. As a result, the number returned can be bigger than
763 * prefix_size. It can be used with prefix_size=0 to get an estimate of the size
764 * needed for prefix_list.
765 */
Harald Welte4c7d2912017-11-08 15:19:17 +0900766int 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 +0800767{
Harald Weltef85fe972017-09-24 20:00:34 +0800768 static const uint8_t ll_prefix[] = { 0xfe,0x80, 0,0, 0,0, 0,0 };
Pau Espin Pedrola037e592017-10-16 14:41:37 +0200769 struct ifaddrs *ifaddr, *ifa;
770 struct in46_addr netmask;
771 size_t count = 0;
772 bool is_ipv6_ll;
Harald Weltef85fe972017-09-24 20:00:34 +0800773
774 if (getifaddrs(&ifaddr) == -1) {
775 return -1;
776 }
777
778 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
Harald Weltef85fe972017-09-24 20:00:34 +0800779 if (ifa->ifa_addr == NULL)
780 continue;
781
Harald Welte4c7d2912017-11-08 15:19:17 +0900782 if (strcmp(ifa->ifa_name, devname))
Harald Weltef85fe972017-09-24 20:00:34 +0800783 continue;
784
Pau Espin Pedrola037e592017-10-16 14:41:37 +0200785 if (ifa->ifa_addr->sa_family == AF_INET && (flags & IP_TYPE_IPv4)) {
786 struct sockaddr_in *sin4 = (struct sockaddr_in *) ifa->ifa_addr;
787 struct sockaddr_in *netmask4 = (struct sockaddr_in *) ifa->ifa_netmask;
Harald Weltef85fe972017-09-24 20:00:34 +0800788
Pau Espin Pedrola037e592017-10-16 14:41:37 +0200789 if (count < prefix_size) {
790 netmask.len = sizeof(netmask4->sin_addr);
791 netmask.v4 = netmask4->sin_addr;
792 prefix_list[count].addr.len = sizeof(sin4->sin_addr);
793 prefix_list[count].addr.v4 = sin4->sin_addr;
794 prefix_list[count].prefixlen = in46a_netmasklen(&netmask);
795 }
796 count++;
797 }
798
799 if (ifa->ifa_addr->sa_family == AF_INET6 && (flags & IP_TYPE_IPv6)) {
800 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) ifa->ifa_addr;
801 struct sockaddr_in6 *netmask6 = (struct sockaddr_in6 *) ifa->ifa_netmask;
802
803 is_ipv6_ll = !memcmp(sin6->sin6_addr.s6_addr, ll_prefix, sizeof(ll_prefix));
804 if ((flags & IP_TYPE_IPv6_NONLINK) && is_ipv6_ll)
805 continue;
806 if ((flags & IP_TYPE_IPv6_LINK) && !is_ipv6_ll)
807 continue;
808
809 if (count < prefix_size) {
810 netmask.len = sizeof(netmask6->sin6_addr);
811 netmask.v6 = netmask6->sin6_addr;
812 prefix_list[count].addr.len = sizeof(sin6->sin6_addr);
813 prefix_list[count].addr.v6 = sin6->sin6_addr;
814 prefix_list[count].prefixlen = in46a_netmasklen(&netmask);
815 }
816 count++;
817 }
Harald Weltef85fe972017-09-24 20:00:34 +0800818 }
Pau Espin Pedrola037e592017-10-16 14:41:37 +0200819
Pau Espin Pedrol58c0da72017-10-12 16:42:46 +0200820 freeifaddrs(ifaddr);
Pau Espin Pedrola037e592017-10-16 14:41:37 +0200821 return count;
Harald Weltef85fe972017-09-24 20:00:34 +0800822}
Harald Welte4c7d2912017-11-08 15:19:17 +0900823
824/*! Obtain the local address of the tun device.
825 * \param[in] tun Target device owning the IP
826 * \param[out] prefix_list List of prefix structures to fill with each IPv4/6 and prefix length found.
827 * \param[in] prefix_size Amount of elements allowed to be fill in the prefix_list array.
828 * \param[in] flags Specify which kind of IP to look for: IP_TYPE_IPv4, IP_TYPE_IPv6_LINK, IP_TYPE_IPv6_NONLINK
829 * \returns The number of ips found following the criteria specified by flags, -1 on error.
830 *
831 * This function will fill prefix_list with up to prefix_size IPs following the
832 * criteria specified by flags parameter. It returns the number of IPs matching
833 * the criteria. As a result, the number returned can be bigger than
834 * prefix_size. It can be used with prefix_size=0 to get an estimate of the size
835 * needed for prefix_list.
836 */
837int tun_ip_local_get(const struct tun_t *tun, struct in46_prefix *prefix_list, size_t prefix_size, int flags)
838{
839 return netdev_ip_local_get(tun->devname, prefix_list, prefix_size, flags);
840}