blob: 6ca9be76f4152c4c7ca5838233ba8376bc0b1f67 [file] [log] [blame]
jjako52c24142002-12-16 13:33:51 +00001/*
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>
jjako52c24142002-12-16 13:33:51 +00005 *
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.
jjako52c24142002-12-16 13:33:51 +000010 *
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
jjako0141d202004-01-09 15:19:20 +000056#elif defined (__sun__)
jjakoec89e9f2004-01-10 06:38:43 +000057#include <stropts.h>
58#include <sys/sockio.h>
59#include <net/if.h>
jjako0141d202004-01-09 15:19:20 +000060#include <net/if_tun.h>
jjakoec89e9f2004-01-10 06:38:43 +000061/*#include "sun_if_tun.h"*/
jjako409b8552004-02-04 22:57:41 +000062
63#else
64#error "Unknown platform!"
jjako0141d202004-01-09 15:19:20 +000065#endif
66
jjako52c24142002-12-16 13:33:51 +000067#include "tun.h"
jjakoa7cd2492003-04-11 09:40:12 +000068#include "syserr.h"
jjako52c24142002-12-16 13:33:51 +000069
Harald Welte2e48a442017-08-03 00:47:03 +020070static int tun_setaddr4(struct tun_t *this, struct in_addr *addr,
71 struct in_addr *dstaddr, struct in_addr *netmask);
72
jjako409b8552004-02-04 22:57:41 +000073#if defined(__linux__)
74
Harald Welte2e48a442017-08-03 00:47:03 +020075#include <linux/ipv6.h>
76
jjakoa7cd2492003-04-11 09:40:12 +000077int tun_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, int dlen)
jjako52c24142002-12-16 13:33:51 +000078{
Harald Weltebed35df2011-11-02 13:06:18 +010079 int len = RTA_LENGTH(dlen);
80 int alen = NLMSG_ALIGN(n->nlmsg_len);
81 struct rtattr *rta = (struct rtattr *)(((void *)n) + alen);
82 if (alen + len > nsize)
83 return -1;
84 rta->rta_len = len;
85 rta->rta_type = type;
86 memcpy(RTA_DATA(rta), d, dlen);
87 n->nlmsg_len = alen + len;
88 return 0;
jjakoa7cd2492003-04-11 09:40:12 +000089}
jjakoec89e9f2004-01-10 06:38:43 +000090#endif
jjako52c24142002-12-16 13:33:51 +000091
Harald Weltebed35df2011-11-02 13:06:18 +010092int tun_sifflags(struct tun_t *this, int flags)
93{
94 struct ifreq ifr;
95 int fd;
jjakoa7cd2492003-04-11 09:40:12 +000096
Harald Weltebed35df2011-11-02 13:06:18 +010097 memset(&ifr, '\0', sizeof(ifr));
98 ifr.ifr_flags = flags;
99 strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
100 ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
101 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100102 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
Michael McTernanb07d0702015-05-02 07:52:23 +0200103 return -1;
Harald Weltebed35df2011-11-02 13:06:18 +0100104 }
105 if (ioctl(fd, SIOCSIFFLAGS, &ifr)) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100106 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100107 "ioctl(SIOCSIFFLAGS) failed");
108 close(fd);
109 return -1;
110 }
111 close(fd);
112 return 0;
jjakoa7cd2492003-04-11 09:40:12 +0000113}
114
jjako0141d202004-01-09 15:19:20 +0000115/* Currently unused
jjakoa7cd2492003-04-11 09:40:12 +0000116int tun_addroute2(struct tun_t *this,
117 struct in_addr *dst,
118 struct in_addr *gateway,
119 struct in_addr *mask) {
120
121 struct {
122 struct nlmsghdr n;
123 struct rtmsg r;
124 char buf[TUN_NLBUFSIZE];
125 } req;
126
127 struct sockaddr_nl local;
128 int addr_len;
129 int fd;
130 int status;
131 struct sockaddr_nl nladdr;
132 struct iovec iov;
133 struct msghdr msg;
134
135 memset(&req, 0, sizeof(req));
136 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
137 req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
138 req.n.nlmsg_type = RTM_NEWROUTE;
139 req.r.rtm_family = AF_INET;
140 req.r.rtm_table = RT_TABLE_MAIN;
141 req.r.rtm_protocol = RTPROT_BOOT;
142 req.r.rtm_scope = RT_SCOPE_UNIVERSE;
143 req.r.rtm_type = RTN_UNICAST;
144 tun_nlattr(&req.n, sizeof(req), RTA_DST, dst, 4);
145 tun_nlattr(&req.n, sizeof(req), RTA_GATEWAY, gateway, 4);
146
147 if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100148 SYS_ERR(DTUN, LOGL_ERROR, errno,
jjakoa7cd2492003-04-11 09:40:12 +0000149 "socket() failed");
jjako52c24142002-12-16 13:33:51 +0000150 return -1;
151 }
152
jjakoa7cd2492003-04-11 09:40:12 +0000153 memset(&local, 0, sizeof(local));
154 local.nl_family = AF_NETLINK;
155 local.nl_groups = 0;
156
157 if (bind(fd, (struct sockaddr*)&local, sizeof(local)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100158 SYS_ERR(DTUN, LOGL_ERROR, errno,
jjakoa7cd2492003-04-11 09:40:12 +0000159 "bind() failed");
160 close(fd);
161 return -1;
162 }
163
164 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,
jjakoa7cd2492003-04-11 09:40:12 +0000167 "getsockname() failed");
168 close(fd);
169 return -1;
170 }
171
172 if (addr_len != sizeof(local)) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100173 SYS_ERR(DTUN, LOGL_ERROR, 0,
jjakoa7cd2492003-04-11 09:40:12 +0000174 "Wrong address length %d", addr_len);
175 close(fd);
176 return -1;
177 }
178
179 if (local.nl_family != AF_NETLINK) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100180 SYS_ERR(DTUN, LOGL_ERROR, 0,
jjakoa7cd2492003-04-11 09:40:12 +0000181 "Wrong address family %d", local.nl_family);
182 close(fd);
183 return -1;
184 }
185
186 iov.iov_base = (void*)&req.n;
187 iov.iov_len = req.n.nlmsg_len;
188
189 msg.msg_name = (void*)&nladdr;
190 msg.msg_namelen = sizeof(nladdr),
191 msg.msg_iov = &iov;
192 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
jjako0141d202004-01-09 15:19:20 +0000205 status = sendmsg(fd, &msg, 0); * TODO: Error check *
jjakoa7cd2492003-04-11 09:40:12 +0000206 close(fd);
207 return 0;
208}
jjako0141d202004-01-09 15:19:20 +0000209*/
jjakoa7cd2492003-04-11 09:40:12 +0000210
211int tun_addaddr(struct tun_t *this,
212 struct in_addr *addr,
Harald Weltebed35df2011-11-02 13:06:18 +0100213 struct in_addr *dstaddr, struct in_addr *netmask)
214{
jjako0141d202004-01-09 15:19:20 +0000215
jjako409b8552004-02-04 22:57:41 +0000216#if defined(__linux__)
Harald Weltebed35df2011-11-02 13:06:18 +0100217 struct {
218 struct nlmsghdr n;
219 struct ifaddrmsg i;
220 char buf[TUN_NLBUFSIZE];
221 } req;
jjakoa7cd2492003-04-11 09:40:12 +0000222
Harald Weltebed35df2011-11-02 13:06:18 +0100223 struct sockaddr_nl local;
224 socklen_t addr_len;
225 int fd;
226 int status;
jjakoa7cd2492003-04-11 09:40:12 +0000227
Harald Weltebed35df2011-11-02 13:06:18 +0100228 struct sockaddr_nl nladdr;
229 struct iovec iov;
230 struct msghdr msg;
jjakoa7cd2492003-04-11 09:40:12 +0000231
Harald Weltebed35df2011-11-02 13:06:18 +0100232 if (!this->addrs) /* Use ioctl for first addr to make ping work */
Harald Welte2e48a442017-08-03 00:47:03 +0200233 return tun_setaddr4(this, addr, dstaddr, netmask);
jjakoa7cd2492003-04-11 09:40:12 +0000234
Harald Weltebed35df2011-11-02 13:06:18 +0100235 memset(&req, 0, sizeof(req));
236 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
237 req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
238 req.n.nlmsg_type = RTM_NEWADDR;
239 req.i.ifa_family = AF_INET;
240 req.i.ifa_prefixlen = 32; /* 32 FOR IPv4 */
241 req.i.ifa_flags = 0;
242 req.i.ifa_scope = RT_SCOPE_HOST; /* TODO or 0 */
Harald Welteab6d1892017-08-11 12:31:59 +0200243 req.i.ifa_index = if_nametoindex(this->devname);
244 if (!req.i.ifa_index) {
245 SYS_ERR(DTUN, LOGL_ERROR, errno, "Unable to get ifindex for %s", this->devname);
Harald Weltebed35df2011-11-02 13:06:18 +0100246 return -1;
247 }
jjakoa7cd2492003-04-11 09:40:12 +0000248
Harald Weltebed35df2011-11-02 13:06:18 +0100249 tun_nlattr(&req.n, sizeof(req), IFA_ADDRESS, addr, sizeof(addr));
250 tun_nlattr(&req.n, sizeof(req), IFA_LOCAL, dstaddr, sizeof(dstaddr));
jjakoa7cd2492003-04-11 09:40:12 +0000251
Harald Weltebed35df2011-11-02 13:06:18 +0100252 if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100253 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100254 return -1;
255 }
jjakoa7cd2492003-04-11 09:40:12 +0000256
Harald Weltebed35df2011-11-02 13:06:18 +0100257 memset(&local, 0, sizeof(local));
258 local.nl_family = AF_NETLINK;
259 local.nl_groups = 0;
jjakoa7cd2492003-04-11 09:40:12 +0000260
Harald Weltebed35df2011-11-02 13:06:18 +0100261 if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100262 SYS_ERR(DTUN, LOGL_ERROR, errno, "bind() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100263 close(fd);
264 return -1;
265 }
jjakoa7cd2492003-04-11 09:40:12 +0000266
Harald Weltebed35df2011-11-02 13:06:18 +0100267 addr_len = sizeof(local);
268 if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100269 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100270 "getsockname() failed");
271 close(fd);
272 return -1;
273 }
jjakoa7cd2492003-04-11 09:40:12 +0000274
Harald Weltebed35df2011-11-02 13:06:18 +0100275 if (addr_len != sizeof(local)) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100276 SYS_ERR(DTUN, LOGL_ERROR, 0,
Harald Weltebed35df2011-11-02 13:06:18 +0100277 "Wrong address length %d", addr_len);
278 close(fd);
279 return -1;
280 }
jjakoa7cd2492003-04-11 09:40:12 +0000281
Harald Weltebed35df2011-11-02 13:06:18 +0100282 if (local.nl_family != AF_NETLINK) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100283 SYS_ERR(DTUN, LOGL_ERROR, 0,
Harald Weltebed35df2011-11-02 13:06:18 +0100284 "Wrong address family %d", local.nl_family);
285 close(fd);
286 return -1;
287 }
jjakoa7cd2492003-04-11 09:40:12 +0000288
Harald Weltebed35df2011-11-02 13:06:18 +0100289 iov.iov_base = (void *)&req.n;
290 iov.iov_len = req.n.nlmsg_len;
jjakoa7cd2492003-04-11 09:40:12 +0000291
Harald Weltebed35df2011-11-02 13:06:18 +0100292 msg.msg_name = (void *)&nladdr;
Michael McTernanb07d0702015-05-02 07:52:23 +0200293 msg.msg_namelen = sizeof(nladdr);
294 msg.msg_iov = &iov;
Harald Weltebed35df2011-11-02 13:06:18 +0100295 msg.msg_iovlen = 1;
296 msg.msg_control = NULL;
297 msg.msg_controllen = 0;
298 msg.msg_flags = 0;
299
300 memset(&nladdr, 0, sizeof(nladdr));
301 nladdr.nl_family = AF_NETLINK;
302 nladdr.nl_pid = 0;
303 nladdr.nl_groups = 0;
304
305 req.n.nlmsg_seq = 0;
306 req.n.nlmsg_flags |= NLM_F_ACK;
307
Michael McTernanb07d0702015-05-02 07:52:23 +0200308 status = sendmsg(fd, &msg, 0);
309 if (status != req.n.nlmsg_len) {
310 SYS_ERR(DTUN, LOGL_ERROR, errno, "sendmsg() failed, returned %d", status);
311 close(fd);
312 return -1;
313 }
Harald Weltebed35df2011-11-02 13:06:18 +0100314
Michael McTernanb07d0702015-05-02 07:52:23 +0200315 status = tun_sifflags(this, IFF_UP | IFF_RUNNING);
316 if (status == -1) {
317 close(fd);
318 return -1;
319 }
320
321
Harald Weltebed35df2011-11-02 13:06:18 +0100322 close(fd);
323 this->addrs++;
324 return 0;
jjako409b8552004-02-04 22:57:41 +0000325
jjako0fe0df02004-09-17 11:30:40 +0000326#elif defined (__FreeBSD__) || defined (__APPLE__)
jjako409b8552004-02-04 22:57:41 +0000327
Harald Weltebed35df2011-11-02 13:06:18 +0100328 int fd;
329 struct ifaliasreq areq;
jjako409b8552004-02-04 22:57:41 +0000330
Harald Weltebed35df2011-11-02 13:06:18 +0100331 /* TODO: Is this needed on FreeBSD? */
332 if (!this->addrs) /* Use ioctl for first addr to make ping work */
Harald Welte2e48a442017-08-03 00:47:03 +0200333 return tun_setaddr4(this, addr, dstaddr, netmask); /* TODO dstaddr */
jjako409b8552004-02-04 22:57:41 +0000334
Harald Weltebed35df2011-11-02 13:06:18 +0100335 memset(&areq, 0, sizeof(areq));
jjako409b8552004-02-04 22:57:41 +0000336
Harald Weltebed35df2011-11-02 13:06:18 +0100337 /* Set up interface name */
338 strncpy(areq.ifra_name, this->devname, IFNAMSIZ);
339 areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
jjako409b8552004-02-04 22:57:41 +0000340
Harald Weltebed35df2011-11-02 13:06:18 +0100341 ((struct sockaddr_in *)&areq.ifra_addr)->sin_family = AF_INET;
342 ((struct sockaddr_in *)&areq.ifra_addr)->sin_len =
343 sizeof(areq.ifra_addr);
344 ((struct sockaddr_in *)&areq.ifra_addr)->sin_addr.s_addr = addr->s_addr;
jjako409b8552004-02-04 22:57:41 +0000345
Harald Weltebed35df2011-11-02 13:06:18 +0100346 ((struct sockaddr_in *)&areq.ifra_mask)->sin_family = AF_INET;
347 ((struct sockaddr_in *)&areq.ifra_mask)->sin_len =
348 sizeof(areq.ifra_mask);
349 ((struct sockaddr_in *)&areq.ifra_mask)->sin_addr.s_addr =
350 netmask->s_addr;
jjako409b8552004-02-04 22:57:41 +0000351
Harald Weltebed35df2011-11-02 13:06:18 +0100352 /* For some reason FreeBSD uses ifra_broadcast for specifying dstaddr */
353 ((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_family = AF_INET;
354 ((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_len =
355 sizeof(areq.ifra_broadaddr);
356 ((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_addr.s_addr =
357 dstaddr->s_addr;
jjako409b8552004-02-04 22:57:41 +0000358
Harald Weltebed35df2011-11-02 13:06:18 +0100359 /* Create a channel to the NET kernel. */
360 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100361 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100362 return -1;
363 }
jjako409b8552004-02-04 22:57:41 +0000364
Harald Weltebed35df2011-11-02 13:06:18 +0100365 if (ioctl(fd, SIOCAIFADDR, (void *)&areq) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100366 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100367 "ioctl(SIOCAIFADDR) failed");
368 close(fd);
369 return -1;
370 }
371
372 close(fd);
373 this->addrs++;
374 return 0;
jjako409b8552004-02-04 22:57:41 +0000375
376#elif defined (__sun__)
Harald Weltebed35df2011-11-02 13:06:18 +0100377
378 if (!this->addrs) /* Use ioctl for first addr to make ping work */
Harald Welte2e48a442017-08-03 00:47:03 +0200379 return tun_setaddr4(this, addr, dstaddr, netmask);
Harald Weltebed35df2011-11-02 13:06:18 +0100380
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100381 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100382 "Setting multiple addresses not possible on Solaris");
383 return -1;
jjako409b8552004-02-04 22:57:41 +0000384
385#else
386#error "Unknown platform!"
jjako0141d202004-01-09 15:19:20 +0000387#endif
jjakoa7cd2492003-04-11 09:40:12 +0000388
Harald Weltebed35df2011-11-02 13:06:18 +0100389}
jjakoa7cd2492003-04-11 09:40:12 +0000390
Harald Welte2e48a442017-08-03 00:47:03 +0200391static int tun_setaddr4(struct tun_t *this, struct in_addr *addr,
392 struct in_addr *dstaddr, struct in_addr *netmask)
jjakoa7cd2492003-04-11 09:40:12 +0000393{
Harald Weltebed35df2011-11-02 13:06:18 +0100394 struct ifreq ifr;
395 int fd;
jjakoa7cd2492003-04-11 09:40:12 +0000396
Harald Weltebed35df2011-11-02 13:06:18 +0100397 memset(&ifr, '\0', sizeof(ifr));
398 ifr.ifr_addr.sa_family = AF_INET;
399 ifr.ifr_dstaddr.sa_family = AF_INET;
jjako409b8552004-02-04 22:57:41 +0000400
401#if defined(__linux__)
Harald Weltebed35df2011-11-02 13:06:18 +0100402 ifr.ifr_netmask.sa_family = AF_INET;
jjako409b8552004-02-04 22:57:41 +0000403
jjako0fe0df02004-09-17 11:30:40 +0000404#elif defined(__FreeBSD__) || defined (__APPLE__)
Harald Weltebed35df2011-11-02 13:06:18 +0100405 ((struct sockaddr_in *)&ifr.ifr_addr)->sin_len =
406 sizeof(struct sockaddr_in);
407 ((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_len =
408 sizeof(struct sockaddr_in);
jjako243bfe62004-01-26 23:06:05 +0000409#endif
jjako409b8552004-02-04 22:57:41 +0000410
Harald Weltebed35df2011-11-02 13:06:18 +0100411 strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
412 ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
jjakoa7cd2492003-04-11 09:40:12 +0000413
Harald Weltebed35df2011-11-02 13:06:18 +0100414 /* Create a channel to the NET kernel. */
415 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100416 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100417 return -1;
418 }
jjakoa7cd2492003-04-11 09:40:12 +0000419
Harald Weltebed35df2011-11-02 13:06:18 +0100420 if (addr) { /* Set the interface address */
421 this->addr.s_addr = addr->s_addr;
422 memcpy(&((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr, addr,
423 sizeof(*addr));
424 if (ioctl(fd, SIOCSIFADDR, (void *)&ifr) < 0) {
425 if (errno != EEXIST) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100426 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100427 "ioctl(SIOCSIFADDR) failed");
428 } else {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100429 SYS_ERR(DTUN, LOGL_NOTICE, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100430 "ioctl(SIOCSIFADDR): Address already exists");
431 }
432 close(fd);
433 return -1;
434 }
435 }
jjakoa7cd2492003-04-11 09:40:12 +0000436
Harald Weltebed35df2011-11-02 13:06:18 +0100437 if (dstaddr) { /* Set the destination address */
438 this->dstaddr.s_addr = dstaddr->s_addr;
439 memcpy(&((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_addr,
440 dstaddr, sizeof(*dstaddr));
441 if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) & ifr) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100442 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100443 "ioctl(SIOCSIFDSTADDR) failed");
444 close(fd);
445 return -1;
446 }
447 }
jjakoa7cd2492003-04-11 09:40:12 +0000448
Harald Weltebed35df2011-11-02 13:06:18 +0100449 if (netmask) { /* Set the netmask */
450 this->netmask.s_addr = netmask->s_addr;
jjako409b8552004-02-04 22:57:41 +0000451#if defined(__linux__)
Harald Weltebed35df2011-11-02 13:06:18 +0100452 memcpy(&((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr,
453 netmask, sizeof(*netmask));
jjako409b8552004-02-04 22:57:41 +0000454
jjako0fe0df02004-09-17 11:30:40 +0000455#elif defined(__FreeBSD__) || defined (__APPLE__)
Harald Weltebed35df2011-11-02 13:06:18 +0100456 ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr =
457 netmask->s_addr;
jjako409b8552004-02-04 22:57:41 +0000458
459#elif defined(__sun__)
Harald Weltebed35df2011-11-02 13:06:18 +0100460 ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr =
461 netmask->s_addr;
jjako409b8552004-02-04 22:57:41 +0000462#else
463#error "Unknown platform!"
jjakoec89e9f2004-01-10 06:38:43 +0000464#endif
jjako409b8552004-02-04 22:57:41 +0000465
Harald Weltebed35df2011-11-02 13:06:18 +0100466 if (ioctl(fd, SIOCSIFNETMASK, (void *)&ifr) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100467 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100468 "ioctl(SIOCSIFNETMASK) failed");
469 close(fd);
470 return -1;
471 }
472 }
jjako1ea66342004-01-28 09:27:34 +0000473
Harald Weltebed35df2011-11-02 13:06:18 +0100474 close(fd);
475 this->addrs++;
jjako409b8552004-02-04 22:57:41 +0000476
Harald Weltebed35df2011-11-02 13:06:18 +0100477 /* On linux the route to the interface is set automatically
478 on FreeBSD we have to do this manually */
479
480 /* TODO: How does it work on Solaris? */
481
482 tun_sifflags(this, IFF_UP | IFF_RUNNING);
jjako163b4552004-12-30 15:33:58 +0000483
jjako0fe0df02004-09-17 11:30:40 +0000484#if defined(__FreeBSD__) || defined (__APPLE__)
Harald Welte2e48a442017-08-03 00:47:03 +0200485 tun_addroute(this, dstaddr, addr, &this->netmask);
Harald Weltebed35df2011-11-02 13:06:18 +0100486 this->routes = 1;
jjako1ea66342004-01-28 09:27:34 +0000487#endif
488
Harald Weltebed35df2011-11-02 13:06:18 +0100489 return 0;
jjakoa7cd2492003-04-11 09:40:12 +0000490}
491
Harald Welte2e48a442017-08-03 00:47:03 +0200492static int tun_setaddr6(struct tun_t *this, struct in6_addr *addr, struct in6_addr *dstaddr,
493 size_t prefixlen)
494{
495 struct in6_ifreq ifr;
496 int fd;
497
498 memset(&ifr, 0, sizeof(ifr));
499
500#if defined(__linux__)
501 ifr.ifr6_prefixlen = prefixlen;
502 ifr.ifr6_ifindex = if_nametoindex(this->devname);
503 if (ifr.ifr6_ifindex == 0) {
504 SYS_ERR(DTUN, LOGL_ERROR, 0, "Error getting ifindex for %s\n", this->devname);
505 return -1;
506 }
507#elif defined(__FreeBSD__) || defined (__APPLE__)
508 strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
509#endif
510
511 /* Create a channel to the NET kernel */
512 if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
513 SYS_ERR(DTUN, LOGL_ERROR, 0, "socket() failed");
514 return -1;
515 }
516
517#if defined(__linux__)
518 if (addr) {
519 memcpy(&this->addr, addr, sizeof(*addr));
520 memcpy(&ifr.ifr6_addr, addr, sizeof(*addr));
521 if (ioctl(fd, SIOCSIFADDR, (void *) &ifr) < 0) {
522 if (errno != EEXIST) {
523 SYS_ERR(DTUN, LOGL_ERROR, 0, "ioctl(SIOCSIFADDR) failed");
524 } else {
525 SYS_ERR(DTUN, LOGL_NOTICE, 0, "ioctl(SIOCSIFADDR): Address alreadsy exists");
526 }
527 close(fd);
528 return -1;
529 }
530 }
531
532#if 0
533 /* FIXME: looks like this is not possible/necessary for IPv6? */
534 if (dstaddr) {
535 memcpy(&this->dstaddr, dstaddr, sizeof(*dstaddr));
536 memcpy(&ifr.ifr6_addr, dstaddr, sizeof(*dstaddr));
537 if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t *) &ifr) < 0) {
538 SYS_ERR(DTUN, LOGL_ERROR, "ioctl(SIOCSIFDSTADDR) failed");
539 close(fd);
540 return -1;
541 }
542 }
543#endif
544
545#elif defined(__FreeBSD__) || defined (__APPLE__)
546 if (addr)
547 memcpy(&ifr.ifr_ifru.ifru_addr, addr, sizeof(ifr.ifr_ifru.ifru_addr));
548 if (dstaddr)
549 memcpy(&ifr.ifr_ifru.ifru_dstaddr, dstaddr, sizeof(ifr.ifr_ifru.ifru_dstaddr));
550
551 if (ioctl(fd, SIOCSIFADDR_IN6, (struct ifreq *)&ifr) < 0) {
552 SYS_ERR(DTUN, LOGL_ERROR, 0, "ioctl(SIOCSIFADDR_IN6) failed");
553 close(fd);
554 return -1;
555 }
556#endif
557
558 close(fd);
559 this->addrs++;
560
561 /* On linux the route to the interface is set automatically
562 on FreeBSD we have to do this manually */
563
564 /* TODO: How does it work on Solaris? */
565
566 tun_sifflags(this, IFF_UP | IFF_RUNNING);
567
568#if 0 /* FIXME */
569//#if defined(__FreeBSD__) || defined (__APPLE__)
570 tun_addroute6(this, dstaddr, addr, prefixlen);
571 this->routes = 1;
572#endif
573
574 return 0;
575}
576
577int tun_setaddr(struct tun_t *this, struct in46_addr *addr, struct in46_addr *dstaddr, size_t prefixlen)
578{
579 struct in_addr netmask;
580 switch (addr->len) {
581 case 4:
582 netmask.s_addr = htonl(0xffffffff << (32 - prefixlen));
583 return tun_setaddr4(this, &addr->v4, dstaddr ? &dstaddr->v4 : NULL, &netmask);
584 case 16:
585 return tun_setaddr6(this, &addr->v6, dstaddr ? &dstaddr->v6 : NULL, prefixlen);
586 default:
587 return -1;
588 }
589}
590
jjako163b4552004-12-30 15:33:58 +0000591int tun_route(struct tun_t *this,
592 struct in_addr *dst,
Harald Weltebed35df2011-11-02 13:06:18 +0100593 struct in_addr *gateway, struct in_addr *mask, int delete)
jjakoa7cd2492003-04-11 09:40:12 +0000594{
jjakoec89e9f2004-01-10 06:38:43 +0000595
Harald Weltebed35df2011-11-02 13:06:18 +0100596 /* TODO: Learn how to set routing table on sun */
jjako409b8552004-02-04 22:57:41 +0000597
598#if defined(__linux__)
jjakoec89e9f2004-01-10 06:38:43 +0000599
Harald Weltebed35df2011-11-02 13:06:18 +0100600 struct rtentry r;
601 int fd;
jjakoa7cd2492003-04-11 09:40:12 +0000602
Harald Weltebed35df2011-11-02 13:06:18 +0100603 memset(&r, '\0', sizeof(r));
604 r.rt_flags = RTF_UP | RTF_GATEWAY; /* RTF_HOST not set */
jjakoa7cd2492003-04-11 09:40:12 +0000605
Harald Weltebed35df2011-11-02 13:06:18 +0100606 /* Create a channel to the NET kernel. */
607 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100608 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100609 return -1;
610 }
jjakoa7cd2492003-04-11 09:40:12 +0000611
Harald Weltebed35df2011-11-02 13:06:18 +0100612 r.rt_dst.sa_family = AF_INET;
613 r.rt_gateway.sa_family = AF_INET;
614 r.rt_genmask.sa_family = AF_INET;
615 memcpy(&((struct sockaddr_in *)&r.rt_dst)->sin_addr, dst, sizeof(*dst));
616 memcpy(&((struct sockaddr_in *)&r.rt_gateway)->sin_addr, gateway,
617 sizeof(*gateway));
618 memcpy(&((struct sockaddr_in *)&r.rt_genmask)->sin_addr, mask,
619 sizeof(*mask));
620
621 if (delete) {
622 if (ioctl(fd, SIOCDELRT, (void *)&r) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100623 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100624 "ioctl(SIOCDELRT) failed");
625 close(fd);
626 return -1;
627 }
628 } else {
629 if (ioctl(fd, SIOCADDRT, (void *)&r) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100630 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100631 "ioctl(SIOCADDRT) failed");
632 close(fd);
633 return -1;
634 }
635 }
636 close(fd);
637 return 0;
638
jjako0fe0df02004-09-17 11:30:40 +0000639#elif defined(__FreeBSD__) || defined (__APPLE__)
jjako3eaf4532004-01-27 19:47:49 +0000640
Harald Weltebed35df2011-11-02 13:06:18 +0100641 struct {
642 struct rt_msghdr rt;
643 struct sockaddr_in dst;
644 struct sockaddr_in gate;
645 struct sockaddr_in mask;
646 } req;
jjako3eaf4532004-01-27 19:47:49 +0000647
Harald Weltebed35df2011-11-02 13:06:18 +0100648 int fd;
649 struct rt_msghdr *rtm;
jjako1ea66342004-01-28 09:27:34 +0000650
Harald Weltebed35df2011-11-02 13:06:18 +0100651 if ((fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100652 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100653 return -1;
654 }
655
656 memset(&req, 0x00, sizeof(req));
657
658 rtm = &req.rt;
659
660 rtm->rtm_msglen = sizeof(req);
661 rtm->rtm_version = RTM_VERSION;
662 if (delete) {
663 rtm->rtm_type = RTM_DELETE;
664 } else {
665 rtm->rtm_type = RTM_ADD;
666 }
667 rtm->rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; /* TODO */
668 rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
669 rtm->rtm_pid = getpid();
670 rtm->rtm_seq = 0044; /* TODO */
671
672 req.dst.sin_family = AF_INET;
673 req.dst.sin_len = sizeof(req.dst);
674 req.mask.sin_family = AF_INET;
675 req.mask.sin_len = sizeof(req.mask);
676 req.gate.sin_family = AF_INET;
677 req.gate.sin_len = sizeof(req.gate);
678
679 req.dst.sin_addr.s_addr = dst->s_addr;
680 req.mask.sin_addr.s_addr = mask->s_addr;
681 req.gate.sin_addr.s_addr = gateway->s_addr;
682
683 if (write(fd, rtm, rtm->rtm_msglen) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100684 SYS_ERR(DTUN, LOGL_ERROR, errno, "write() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100685 close(fd);
686 return -1;
687 }
688 close(fd);
689 return 0;
jjako1ea66342004-01-28 09:27:34 +0000690
jjako409b8552004-02-04 22:57:41 +0000691#elif defined(__sun__)
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100692 SYS_ERR(DTUN, LOGL_NOTICE, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100693 "Could not set up routing on Solaris. Please add route manually.");
694 return 0;
695
jjako409b8552004-02-04 22:57:41 +0000696#else
697#error "Unknown platform!"
jjakoec89e9f2004-01-10 06:38:43 +0000698#endif
699
jjakoa7cd2492003-04-11 09:40:12 +0000700}
701
jjako163b4552004-12-30 15:33:58 +0000702int tun_addroute(struct tun_t *this,
703 struct in_addr *dst,
Harald Weltebed35df2011-11-02 13:06:18 +0100704 struct in_addr *gateway, struct in_addr *mask)
jjako163b4552004-12-30 15:33:58 +0000705{
Harald Weltebed35df2011-11-02 13:06:18 +0100706 return tun_route(this, dst, gateway, mask, 0);
jjako163b4552004-12-30 15:33:58 +0000707}
708
709int tun_delroute(struct tun_t *this,
710 struct in_addr *dst,
Harald Weltebed35df2011-11-02 13:06:18 +0100711 struct in_addr *gateway, struct in_addr *mask)
jjako163b4552004-12-30 15:33:58 +0000712{
Harald Weltebed35df2011-11-02 13:06:18 +0100713 return tun_route(this, dst, gateway, mask, 1);
jjako163b4552004-12-30 15:33:58 +0000714}
715
jjakoa7cd2492003-04-11 09:40:12 +0000716int tun_new(struct tun_t **tun)
717{
jjakoec89e9f2004-01-10 06:38:43 +0000718
jjako409b8552004-02-04 22:57:41 +0000719#if defined(__linux__)
Harald Weltebed35df2011-11-02 13:06:18 +0100720 struct ifreq ifr;
jjako409b8552004-02-04 22:57:41 +0000721
jjako0fe0df02004-09-17 11:30:40 +0000722#elif defined(__FreeBSD__) || defined (__APPLE__)
Harald Weltebed35df2011-11-02 13:06:18 +0100723 char devname[IFNAMSIZ + 5]; /* "/dev/" + ifname */
724 int devnum;
725 struct ifaliasreq areq;
726 int fd;
jjako409b8552004-02-04 22:57:41 +0000727
728#elif defined(__sun__)
Harald Weltebed35df2011-11-02 13:06:18 +0100729 int if_fd, ppa = -1;
730 static int ip_fd = 0;
731 int muxid;
732 struct ifreq ifr;
jjako409b8552004-02-04 22:57:41 +0000733
jjako06e9f122004-01-19 18:37:58 +0000734#else
jjako409b8552004-02-04 22:57:41 +0000735#error "Unknown platform!"
jjakoec89e9f2004-01-10 06:38:43 +0000736#endif
Harald Weltebed35df2011-11-02 13:06:18 +0100737
738 if (!(*tun = calloc(1, sizeof(struct tun_t)))) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100739 SYS_ERR(DTUN, LOGL_ERROR, errno, "calloc() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100740 return EOF;
741 }
742
743 (*tun)->cb_ind = NULL;
744 (*tun)->addrs = 0;
745 (*tun)->routes = 0;
746
jjako409b8552004-02-04 22:57:41 +0000747#if defined(__linux__)
Harald Weltebed35df2011-11-02 13:06:18 +0100748 /* Open the actual tun device */
749 if (((*tun)->fd = open("/dev/net/tun", O_RDWR)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100750 SYS_ERR(DTUN, LOGL_ERROR, errno, "open() failed");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200751 goto err_free;
Harald Weltebed35df2011-11-02 13:06:18 +0100752 }
753
754 /* Set device flags. For some weird reason this is also the method
755 used to obtain the network interface name */
756 memset(&ifr, 0, sizeof(ifr));
757 ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Tun device, no packet info */
758 if (ioctl((*tun)->fd, TUNSETIFF, (void *)&ifr) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100759 SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl() failed");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200760 goto err_close;
Harald Weltebed35df2011-11-02 13:06:18 +0100761 }
762
763 strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ);
764 (*tun)->devname[IFNAMSIZ - 1] = 0;
765
766 ioctl((*tun)->fd, TUNSETNOCSUM, 1); /* Disable checksums */
767 return 0;
768
jjako0fe0df02004-09-17 11:30:40 +0000769#elif defined(__FreeBSD__) || defined (__APPLE__)
jjakoec89e9f2004-01-10 06:38:43 +0000770
Harald Weltebed35df2011-11-02 13:06:18 +0100771 /* Find suitable device */
772 for (devnum = 0; devnum < 255; devnum++) { /* TODO 255 */
773 snprintf(devname, sizeof(devname), "/dev/tun%d", devnum);
Harald Weltebed35df2011-11-02 13:06:18 +0100774 if (((*tun)->fd = open(devname, O_RDWR)) >= 0)
775 break;
776 if (errno != EBUSY)
777 break;
778 }
779 if ((*tun)->fd < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100780 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100781 "Can't find tunnel device");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200782 goto err_free;
Harald Weltebed35df2011-11-02 13:06:18 +0100783 }
jjakoec89e9f2004-01-10 06:38:43 +0000784
Harald Weltebed35df2011-11-02 13:06:18 +0100785 snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", devnum);
Harald Welte81bc2ae2017-08-11 12:56:30 +0200786 (*tun)->devname[sizeof((*tun)->devname)-1] = 0;
jjako1f158642004-02-05 20:39:57 +0000787
Harald Weltebed35df2011-11-02 13:06:18 +0100788 /* The tun device we found might have "old" IP addresses allocated */
789 /* We need to delete those. This problem is not present on Linux */
jjako1f158642004-02-05 20:39:57 +0000790
Harald Weltebed35df2011-11-02 13:06:18 +0100791 memset(&areq, 0, sizeof(areq));
jjako1f158642004-02-05 20:39:57 +0000792
Harald Weltebed35df2011-11-02 13:06:18 +0100793 /* Set up interface name */
794 strncpy(areq.ifra_name, (*tun)->devname, IFNAMSIZ);
795 areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
jjako1f158642004-02-05 20:39:57 +0000796
Harald Weltebed35df2011-11-02 13:06:18 +0100797 /* Create a channel to the NET kernel. */
798 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100799 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200800 goto err_close;
Harald Weltebed35df2011-11-02 13:06:18 +0100801 }
jjako1f158642004-02-05 20:39:57 +0000802
Harald Weltebed35df2011-11-02 13:06:18 +0100803 /* Delete any IP addresses until SIOCDIFADDR fails */
804 while (ioctl(fd, SIOCDIFADDR, (void *)&areq) != -1) ;
805
806 close(fd);
807 return 0;
jjako409b8552004-02-04 22:57:41 +0000808
809#elif defined(__sun__)
jjakoec89e9f2004-01-10 06:38:43 +0000810
Harald Weltebed35df2011-11-02 13:06:18 +0100811 if ((ip_fd = open("/dev/udp", O_RDWR, 0)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100812 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100813 "Can't open /dev/udp");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200814 goto err_free;
Harald Weltebed35df2011-11-02 13:06:18 +0100815 }
jjakoec89e9f2004-01-10 06:38:43 +0000816
Harald Weltebed35df2011-11-02 13:06:18 +0100817 if (((*tun)->fd = open("/dev/tun", O_RDWR, 0)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100818 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100819 "Can't open /dev/tun");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200820 close(ip_fd);
821 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 /* Assign a new PPA and get its unit number. */
825 if ((ppa = ioctl((*tun)->fd, TUNNEWPPA, -1)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100826 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100827 "Can't assign new interface");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200828 goto sun_close_ip;
Harald Weltebed35df2011-11-02 13:06:18 +0100829 }
jjakoc6762cf2004-04-28 14:52:58 +0000830
Harald Weltebed35df2011-11-02 13:06:18 +0100831 if ((if_fd = open("/dev/tun", O_RDWR, 0)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100832 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100833 "Can't open /dev/tun (2)");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200834 goto sun_close_ip;
Harald Weltebed35df2011-11-02 13:06:18 +0100835 }
836 if (ioctl(if_fd, I_PUSH, "ip") < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100837 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100838 "Can't push IP module");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200839 goto sun_close_if;
Harald Weltebed35df2011-11-02 13:06:18 +0100840 }
jjakoc6762cf2004-04-28 14:52:58 +0000841
Harald Weltebed35df2011-11-02 13:06:18 +0100842 /* Assign ppa according to the unit number returned by tun device */
843 if (ioctl(if_fd, IF_UNITSEL, (char *)&ppa) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100844 SYS_ERR(DTUN, LOGL_ERROR, errno, "Can't set PPA %d",
Harald Weltebed35df2011-11-02 13:06:18 +0100845 ppa);
Harald Welte9e6dfa02017-08-12 15:06:19 +0200846 goto sun_close_if;
Harald Weltebed35df2011-11-02 13:06:18 +0100847 }
848
849 /* Link the two streams */
850 if ((muxid = ioctl(ip_fd, I_LINK, if_fd)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100851 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100852 "Can't link TUN device to IP");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200853 goto sun_close_if;
854 }
855
856 /* Link the two streams */
857 if ((muxid = ioctl(ip_fd, I_LINK, if_fd)) < 0) {
858 SYS_ERR(DTUN, LOGL_ERROR, errno,
859 "Can't link TUN device to IP");
860 goto sun_close_if;
Harald Weltebed35df2011-11-02 13:06:18 +0100861 }
862
863 close(if_fd);
864
865 snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", ppa);
866 (*tun)->devname[sizeof((*tun)->devname)] = 0;
867
868 memset(&ifr, 0, sizeof(ifr));
869 strcpy(ifr.ifr_name, (*tun)->devname);
870 ifr.ifr_ip_muxid = muxid;
871
872 if (ioctl(ip_fd, SIOCSIFMUXID, &ifr) < 0) {
873 ioctl(ip_fd, I_PUNLINK, muxid);
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100874 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100875 "Can't set multiplexor id");
Harald Welte9e6dfa02017-08-12 15:06:19 +0200876 goto sun_close_ip;
Harald Weltebed35df2011-11-02 13:06:18 +0100877 }
878
879 /* if (fcntl (fd, F_SETFL, O_NONBLOCK) < 0)
880 msg (M_ERR, "Set file descriptor to non-blocking failed"); */
881
882 return 0;
jjako409b8552004-02-04 22:57:41 +0000883
Harald Welte9e6dfa02017-08-12 15:06:19 +0200884sun_close_if:
885 close(if_fd);
886sun_close_ip:
887 close(ip_fd);
888 goto err_close;
889
jjako409b8552004-02-04 22:57:41 +0000890#else
891#error "Unknown platform!"
892#endif
893
Harald Welte9e6dfa02017-08-12 15:06:19 +0200894err_close:
895 close((*tun)->fd);
896err_free:
897 free(*tun);
898 *tun = NULL;
899 return -1;
jjako52c24142002-12-16 13:33:51 +0000900}
901
jjakoa7cd2492003-04-11 09:40:12 +0000902int tun_free(struct tun_t *tun)
jjako52c24142002-12-16 13:33:51 +0000903{
jjako163b4552004-12-30 15:33:58 +0000904
Harald Weltebed35df2011-11-02 13:06:18 +0100905 if (tun->routes) {
906 tun_delroute(tun, &tun->dstaddr, &tun->addr, &tun->netmask);
907 }
jjako163b4552004-12-30 15:33:58 +0000908
Harald Weltebed35df2011-11-02 13:06:18 +0100909 if (close(tun->fd)) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100910 SYS_ERR(DTUN, LOGL_ERROR, errno, "close() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100911 }
jjakoa7cd2492003-04-11 09:40:12 +0000912
Harald Weltebed35df2011-11-02 13:06:18 +0100913 /* TODO: For solaris we need to unlink streams */
jjakoec89e9f2004-01-10 06:38:43 +0000914
Harald Weltebed35df2011-11-02 13:06:18 +0100915 free(tun);
916 return 0;
jjako52c24142002-12-16 13:33:51 +0000917}
918
Harald Weltebed35df2011-11-02 13:06:18 +0100919int tun_set_cb_ind(struct tun_t *this,
920 int (*cb_ind) (struct tun_t * tun, void *pack, unsigned len))
921{
922 this->cb_ind = cb_ind;
923 return 0;
jjakoa7cd2492003-04-11 09:40:12 +0000924}
925
jjakoa7cd2492003-04-11 09:40:12 +0000926int tun_decaps(struct tun_t *this)
jjako52c24142002-12-16 13:33:51 +0000927{
jjakoc6762cf2004-04-28 14:52:58 +0000928
jjako0fe0df02004-09-17 11:30:40 +0000929#if defined(__linux__) || defined (__FreeBSD__) || defined (__APPLE__)
jjakoc6762cf2004-04-28 14:52:58 +0000930
Harald Weltebed35df2011-11-02 13:06:18 +0100931 unsigned char buffer[PACKET_MAX];
932 int status;
jjako52c24142002-12-16 13:33:51 +0000933
Harald Weltebed35df2011-11-02 13:06:18 +0100934 if ((status = read(this->fd, buffer, sizeof(buffer))) <= 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100935 SYS_ERR(DTUN, LOGL_ERROR, errno, "read() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100936 return -1;
937 }
938
939 if (this->cb_ind)
940 return this->cb_ind(this, buffer, status);
941
942 return 0;
jjakoc6762cf2004-04-28 14:52:58 +0000943
944#elif defined (__sun__)
945
Harald Weltebed35df2011-11-02 13:06:18 +0100946 unsigned char buffer[PACKET_MAX];
947 struct strbuf sbuf;
948 int f = 0;
jjakoc6762cf2004-04-28 14:52:58 +0000949
Harald Weltebed35df2011-11-02 13:06:18 +0100950 sbuf.maxlen = PACKET_MAX;
951 sbuf.buf = buffer;
952 if (getmsg(this->fd, NULL, &sbuf, &f) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100953 SYS_ERR(DTUN, LOGL_ERROR, errno, "getmsg() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100954 return -1;
955 }
jjakoc6762cf2004-04-28 14:52:58 +0000956
Harald Weltebed35df2011-11-02 13:06:18 +0100957 if (this->cb_ind)
958 return this->cb_ind(this, buffer, sbuf.len);
959
960 return 0;
961
jjakoc6762cf2004-04-28 14:52:58 +0000962#endif
963
jjako52c24142002-12-16 13:33:51 +0000964}
965
966int tun_encaps(struct tun_t *tun, void *pack, unsigned len)
967{
jjakoc6762cf2004-04-28 14:52:58 +0000968
jjako0fe0df02004-09-17 11:30:40 +0000969#if defined(__linux__) || defined (__FreeBSD__) || defined (__APPLE__)
jjakoc6762cf2004-04-28 14:52:58 +0000970
Harald Weltebed35df2011-11-02 13:06:18 +0100971 return write(tun->fd, pack, len);
jjakoc6762cf2004-04-28 14:52:58 +0000972
973#elif defined (__sun__)
974
Harald Weltebed35df2011-11-02 13:06:18 +0100975 struct strbuf sbuf;
976 sbuf.len = len;
977 sbuf.buf = pack;
978 return putmsg(tun->fd, NULL, &sbuf, 0);
jjakoc6762cf2004-04-28 14:52:58 +0000979
980#endif
jjakoa7cd2492003-04-11 09:40:12 +0000981}
982
Harald Weltebed35df2011-11-02 13:06:18 +0100983int tun_runscript(struct tun_t *tun, char *script)
984{
jjakoa7cd2492003-04-11 09:40:12 +0000985
Harald Weltebed35df2011-11-02 13:06:18 +0100986 char buf[TUN_SCRIPTSIZE];
987 char snet[TUN_ADDRSIZE];
988 char smask[TUN_ADDRSIZE];
989 int rc;
990
991 strncpy(snet, inet_ntoa(tun->addr), sizeof(snet));
992 snet[sizeof(snet) - 1] = 0;
993 strncpy(smask, inet_ntoa(tun->netmask), sizeof(smask));
994 smask[sizeof(smask) - 1] = 0;
995
996 /* system("ipup /dev/tun0 192.168.0.10 255.255.255.0"); */
997 snprintf(buf, sizeof(buf), "%s %s %s %s",
998 script, tun->devname, snet, smask);
999 buf[sizeof(buf) - 1] = 0;
1000 rc = system(buf);
1001 if (rc == -1) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +01001002 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +01001003 "Error executing command %s", buf);
1004 return -1;
1005 }
1006 return 0;
jjako52c24142002-12-16 13:33:51 +00001007}