blob: f6869efa042340b0f071360972e6e78d3b9182a2 [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 Weltebed35df2011-11-02 13:06:18 +0100751 return -1;
752 }
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 Weltebed35df2011-11-02 13:06:18 +0100760 close((*tun)->fd);
761 return -1;
762 }
763
764 strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ);
765 (*tun)->devname[IFNAMSIZ - 1] = 0;
766
767 ioctl((*tun)->fd, TUNSETNOCSUM, 1); /* Disable checksums */
768 return 0;
769
jjako0fe0df02004-09-17 11:30:40 +0000770#elif defined(__FreeBSD__) || defined (__APPLE__)
jjakoec89e9f2004-01-10 06:38:43 +0000771
Harald Weltebed35df2011-11-02 13:06:18 +0100772 /* Find suitable device */
773 for (devnum = 0; devnum < 255; devnum++) { /* TODO 255 */
774 snprintf(devname, sizeof(devname), "/dev/tun%d", devnum);
Harald Weltebed35df2011-11-02 13:06:18 +0100775 if (((*tun)->fd = open(devname, O_RDWR)) >= 0)
776 break;
777 if (errno != EBUSY)
778 break;
779 }
780 if ((*tun)->fd < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100781 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100782 "Can't find tunnel device");
783 return -1;
784 }
jjakoec89e9f2004-01-10 06:38:43 +0000785
Harald Weltebed35df2011-11-02 13:06:18 +0100786 snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", devnum);
787 (*tun)->devname[sizeof((*tun)->devname)] = 0;
jjako1f158642004-02-05 20:39:57 +0000788
Harald Weltebed35df2011-11-02 13:06:18 +0100789 /* The tun device we found might have "old" IP addresses allocated */
790 /* We need to delete those. This problem is not present on Linux */
jjako1f158642004-02-05 20:39:57 +0000791
Harald Weltebed35df2011-11-02 13:06:18 +0100792 memset(&areq, 0, sizeof(areq));
jjako1f158642004-02-05 20:39:57 +0000793
Harald Weltebed35df2011-11-02 13:06:18 +0100794 /* Set up interface name */
795 strncpy(areq.ifra_name, (*tun)->devname, IFNAMSIZ);
796 areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
jjako1f158642004-02-05 20:39:57 +0000797
Harald Weltebed35df2011-11-02 13:06:18 +0100798 /* Create a channel to the NET kernel. */
799 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100800 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100801 return -1;
802 }
jjako1f158642004-02-05 20:39:57 +0000803
Harald Weltebed35df2011-11-02 13:06:18 +0100804 /* Delete any IP addresses until SIOCDIFADDR fails */
805 while (ioctl(fd, SIOCDIFADDR, (void *)&areq) != -1) ;
806
807 close(fd);
808 return 0;
jjako409b8552004-02-04 22:57:41 +0000809
810#elif defined(__sun__)
jjakoec89e9f2004-01-10 06:38:43 +0000811
Harald Weltebed35df2011-11-02 13:06:18 +0100812 if ((ip_fd = open("/dev/udp", O_RDWR, 0)) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100813 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100814 "Can't open /dev/udp");
815 return -1;
816 }
jjakoec89e9f2004-01-10 06:38:43 +0000817
Harald Weltebed35df2011-11-02 13:06:18 +0100818 if (((*tun)->fd = open("/dev/tun", O_RDWR, 0)) < 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 open /dev/tun");
821 return -1;
822 }
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");
828 return -1;
829 }
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)");
834 return -1;
835 }
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");
839 return -1;
840 }
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);
846 return -1;
847 }
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");
853 return -1;
854 }
855
856 close(if_fd);
857
858 snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", ppa);
859 (*tun)->devname[sizeof((*tun)->devname)] = 0;
860
861 memset(&ifr, 0, sizeof(ifr));
862 strcpy(ifr.ifr_name, (*tun)->devname);
863 ifr.ifr_ip_muxid = muxid;
864
865 if (ioctl(ip_fd, SIOCSIFMUXID, &ifr) < 0) {
866 ioctl(ip_fd, I_PUNLINK, muxid);
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100867 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100868 "Can't set multiplexor id");
869 return -1;
870 }
871
872 /* if (fcntl (fd, F_SETFL, O_NONBLOCK) < 0)
873 msg (M_ERR, "Set file descriptor to non-blocking failed"); */
874
875 return 0;
jjako409b8552004-02-04 22:57:41 +0000876
877#else
878#error "Unknown platform!"
879#endif
880
jjako52c24142002-12-16 13:33:51 +0000881}
882
jjakoa7cd2492003-04-11 09:40:12 +0000883int tun_free(struct tun_t *tun)
jjako52c24142002-12-16 13:33:51 +0000884{
jjako163b4552004-12-30 15:33:58 +0000885
Harald Weltebed35df2011-11-02 13:06:18 +0100886 if (tun->routes) {
887 tun_delroute(tun, &tun->dstaddr, &tun->addr, &tun->netmask);
888 }
jjako163b4552004-12-30 15:33:58 +0000889
Harald Weltebed35df2011-11-02 13:06:18 +0100890 if (close(tun->fd)) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100891 SYS_ERR(DTUN, LOGL_ERROR, errno, "close() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100892 }
jjakoa7cd2492003-04-11 09:40:12 +0000893
Harald Weltebed35df2011-11-02 13:06:18 +0100894 /* TODO: For solaris we need to unlink streams */
jjakoec89e9f2004-01-10 06:38:43 +0000895
Harald Weltebed35df2011-11-02 13:06:18 +0100896 free(tun);
897 return 0;
jjako52c24142002-12-16 13:33:51 +0000898}
899
Harald Weltebed35df2011-11-02 13:06:18 +0100900int tun_set_cb_ind(struct tun_t *this,
901 int (*cb_ind) (struct tun_t * tun, void *pack, unsigned len))
902{
903 this->cb_ind = cb_ind;
904 return 0;
jjakoa7cd2492003-04-11 09:40:12 +0000905}
906
jjakoa7cd2492003-04-11 09:40:12 +0000907int tun_decaps(struct tun_t *this)
jjako52c24142002-12-16 13:33:51 +0000908{
jjakoc6762cf2004-04-28 14:52:58 +0000909
jjako0fe0df02004-09-17 11:30:40 +0000910#if defined(__linux__) || defined (__FreeBSD__) || defined (__APPLE__)
jjakoc6762cf2004-04-28 14:52:58 +0000911
Harald Weltebed35df2011-11-02 13:06:18 +0100912 unsigned char buffer[PACKET_MAX];
913 int status;
jjako52c24142002-12-16 13:33:51 +0000914
Harald Weltebed35df2011-11-02 13:06:18 +0100915 if ((status = read(this->fd, buffer, sizeof(buffer))) <= 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100916 SYS_ERR(DTUN, LOGL_ERROR, errno, "read() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100917 return -1;
918 }
919
920 if (this->cb_ind)
921 return this->cb_ind(this, buffer, status);
922
923 return 0;
jjakoc6762cf2004-04-28 14:52:58 +0000924
925#elif defined (__sun__)
926
Harald Weltebed35df2011-11-02 13:06:18 +0100927 unsigned char buffer[PACKET_MAX];
928 struct strbuf sbuf;
929 int f = 0;
jjakoc6762cf2004-04-28 14:52:58 +0000930
Harald Weltebed35df2011-11-02 13:06:18 +0100931 sbuf.maxlen = PACKET_MAX;
932 sbuf.buf = buffer;
933 if (getmsg(this->fd, NULL, &sbuf, &f) < 0) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100934 SYS_ERR(DTUN, LOGL_ERROR, errno, "getmsg() failed");
Harald Weltebed35df2011-11-02 13:06:18 +0100935 return -1;
936 }
jjakoc6762cf2004-04-28 14:52:58 +0000937
Harald Weltebed35df2011-11-02 13:06:18 +0100938 if (this->cb_ind)
939 return this->cb_ind(this, buffer, sbuf.len);
940
941 return 0;
942
jjakoc6762cf2004-04-28 14:52:58 +0000943#endif
944
jjako52c24142002-12-16 13:33:51 +0000945}
946
947int tun_encaps(struct tun_t *tun, void *pack, unsigned len)
948{
jjakoc6762cf2004-04-28 14:52:58 +0000949
jjako0fe0df02004-09-17 11:30:40 +0000950#if defined(__linux__) || defined (__FreeBSD__) || defined (__APPLE__)
jjakoc6762cf2004-04-28 14:52:58 +0000951
Harald Weltebed35df2011-11-02 13:06:18 +0100952 return write(tun->fd, pack, len);
jjakoc6762cf2004-04-28 14:52:58 +0000953
954#elif defined (__sun__)
955
Harald Weltebed35df2011-11-02 13:06:18 +0100956 struct strbuf sbuf;
957 sbuf.len = len;
958 sbuf.buf = pack;
959 return putmsg(tun->fd, NULL, &sbuf, 0);
jjakoc6762cf2004-04-28 14:52:58 +0000960
961#endif
jjakoa7cd2492003-04-11 09:40:12 +0000962}
963
Harald Weltebed35df2011-11-02 13:06:18 +0100964int tun_runscript(struct tun_t *tun, char *script)
965{
jjakoa7cd2492003-04-11 09:40:12 +0000966
Harald Weltebed35df2011-11-02 13:06:18 +0100967 char buf[TUN_SCRIPTSIZE];
968 char snet[TUN_ADDRSIZE];
969 char smask[TUN_ADDRSIZE];
970 int rc;
971
972 strncpy(snet, inet_ntoa(tun->addr), sizeof(snet));
973 snet[sizeof(snet) - 1] = 0;
974 strncpy(smask, inet_ntoa(tun->netmask), sizeof(smask));
975 smask[sizeof(smask) - 1] = 0;
976
977 /* system("ipup /dev/tun0 192.168.0.10 255.255.255.0"); */
978 snprintf(buf, sizeof(buf), "%s %s %s %s",
979 script, tun->devname, snet, smask);
980 buf[sizeof(buf) - 1] = 0;
981 rc = system(buf);
982 if (rc == -1) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100983 SYS_ERR(DTUN, LOGL_ERROR, errno,
Harald Weltebed35df2011-11-02 13:06:18 +0100984 "Error executing command %s", buf);
985 return -1;
986 }
987 return 0;
jjako52c24142002-12-16 13:33:51 +0000988}