lib/tun.c: Proper cleanup during tun_new() error paths
[openggsn.git] / lib / tun.c
blob6ca9be76f4152c4c7ca5838233ba8376bc0b1f67
1 /*
2 * TUN interface functions.
3 * Copyright (C) 2002, 2003, 2004 Mondru AB.
4 * Copyright (C) 2017 by Harald Welte <laforge@gnumonks.org>
5 *
6 * 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.
14 * 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.
20 #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>
33 #include <stdio.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <sys/time.h>
37 #include <sys/ioctl.h>
38 #include <sys/socket.h>
39 #include <errno.h>
40 #include <net/route.h>
41 #include <net/if.h>
43 #if defined(__linux__)
44 #include <linux/if_tun.h>
45 #include <linux/netlink.h>
46 #include <linux/rtnetlink.h>
48 #elif defined (__FreeBSD__)
49 #include <net/if_tun.h>
50 #include <net/if_var.h>
51 #include <netinet/in_var.h>
53 #elif defined (__APPLE__)
54 #include <net/if.h>
56 #elif defined (__sun__)
57 #include <stropts.h>
58 #include <sys/sockio.h>
59 #include <net/if.h>
60 #include <net/if_tun.h>
61 /*#include "sun_if_tun.h"*/
63 #else
64 #error "Unknown platform!"
65 #endif
67 #include "tun.h"
68 #include "syserr.h"
70 static int tun_setaddr4(struct tun_t *this, struct in_addr *addr,
71 struct in_addr *dstaddr, struct in_addr *netmask);
73 #if defined(__linux__)
75 #include <linux/ipv6.h>
77 int tun_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, int dlen)
79 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;
90 #endif
92 int tun_sifflags(struct tun_t *this, int flags)
94 struct ifreq ifr;
95 int fd;
97 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) {
102 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
103 return -1;
105 if (ioctl(fd, SIOCSIFFLAGS, &ifr)) {
106 SYS_ERR(DTUN, LOGL_ERROR, errno,
107 "ioctl(SIOCSIFFLAGS) failed");
108 close(fd);
109 return -1;
111 close(fd);
112 return 0;
115 /* Currently unused
116 int tun_addroute2(struct tun_t *this,
117 struct in_addr *dst,
118 struct in_addr *gateway,
119 struct in_addr *mask) {
121 struct {
122 struct nlmsghdr n;
123 struct rtmsg r;
124 char buf[TUN_NLBUFSIZE];
125 } req;
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;
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);
147 if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
148 SYS_ERR(DTUN, LOGL_ERROR, errno,
149 "socket() failed");
150 return -1;
153 memset(&local, 0, sizeof(local));
154 local.nl_family = AF_NETLINK;
155 local.nl_groups = 0;
157 if (bind(fd, (struct sockaddr*)&local, sizeof(local)) < 0) {
158 SYS_ERR(DTUN, LOGL_ERROR, errno,
159 "bind() failed");
160 close(fd);
161 return -1;
164 addr_len = sizeof(local);
165 if (getsockname(fd, (struct sockaddr*)&local, &addr_len) < 0) {
166 SYS_ERR(DTUN, LOGL_ERROR, errno,
167 "getsockname() failed");
168 close(fd);
169 return -1;
172 if (addr_len != sizeof(local)) {
173 SYS_ERR(DTUN, LOGL_ERROR, 0,
174 "Wrong address length %d", addr_len);
175 close(fd);
176 return -1;
179 if (local.nl_family != AF_NETLINK) {
180 SYS_ERR(DTUN, LOGL_ERROR, 0,
181 "Wrong address family %d", local.nl_family);
182 close(fd);
183 return -1;
186 iov.iov_base = (void*)&req.n;
187 iov.iov_len = req.n.nlmsg_len;
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;
197 memset(&nladdr, 0, sizeof(nladdr));
198 nladdr.nl_family = AF_NETLINK;
199 nladdr.nl_pid = 0;
200 nladdr.nl_groups = 0;
202 req.n.nlmsg_seq = 0;
203 req.n.nlmsg_flags |= NLM_F_ACK;
205 status = sendmsg(fd, &msg, 0); * TODO: Error check *
206 close(fd);
207 return 0;
211 int tun_addaddr(struct tun_t *this,
212 struct in_addr *addr,
213 struct in_addr *dstaddr, struct in_addr *netmask)
216 #if defined(__linux__)
217 struct {
218 struct nlmsghdr n;
219 struct ifaddrmsg i;
220 char buf[TUN_NLBUFSIZE];
221 } req;
223 struct sockaddr_nl local;
224 socklen_t addr_len;
225 int fd;
226 int status;
228 struct sockaddr_nl nladdr;
229 struct iovec iov;
230 struct msghdr msg;
232 if (!this->addrs) /* Use ioctl for first addr to make ping work */
233 return tun_setaddr4(this, addr, dstaddr, netmask);
235 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 */
243 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);
246 return -1;
249 tun_nlattr(&req.n, sizeof(req), IFA_ADDRESS, addr, sizeof(addr));
250 tun_nlattr(&req.n, sizeof(req), IFA_LOCAL, dstaddr, sizeof(dstaddr));
252 if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
253 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
254 return -1;
257 memset(&local, 0, sizeof(local));
258 local.nl_family = AF_NETLINK;
259 local.nl_groups = 0;
261 if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
262 SYS_ERR(DTUN, LOGL_ERROR, errno, "bind() failed");
263 close(fd);
264 return -1;
267 addr_len = sizeof(local);
268 if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0) {
269 SYS_ERR(DTUN, LOGL_ERROR, errno,
270 "getsockname() failed");
271 close(fd);
272 return -1;
275 if (addr_len != sizeof(local)) {
276 SYS_ERR(DTUN, LOGL_ERROR, 0,
277 "Wrong address length %d", addr_len);
278 close(fd);
279 return -1;
282 if (local.nl_family != AF_NETLINK) {
283 SYS_ERR(DTUN, LOGL_ERROR, 0,
284 "Wrong address family %d", local.nl_family);
285 close(fd);
286 return -1;
289 iov.iov_base = (void *)&req.n;
290 iov.iov_len = req.n.nlmsg_len;
292 msg.msg_name = (void *)&nladdr;
293 msg.msg_namelen = sizeof(nladdr);
294 msg.msg_iov = &iov;
295 msg.msg_iovlen = 1;
296 msg.msg_control = NULL;
297 msg.msg_controllen = 0;
298 msg.msg_flags = 0;
300 memset(&nladdr, 0, sizeof(nladdr));
301 nladdr.nl_family = AF_NETLINK;
302 nladdr.nl_pid = 0;
303 nladdr.nl_groups = 0;
305 req.n.nlmsg_seq = 0;
306 req.n.nlmsg_flags |= NLM_F_ACK;
308 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;
315 status = tun_sifflags(this, IFF_UP | IFF_RUNNING);
316 if (status == -1) {
317 close(fd);
318 return -1;
322 close(fd);
323 this->addrs++;
324 return 0;
326 #elif defined (__FreeBSD__) || defined (__APPLE__)
328 int fd;
329 struct ifaliasreq areq;
331 /* TODO: Is this needed on FreeBSD? */
332 if (!this->addrs) /* Use ioctl for first addr to make ping work */
333 return tun_setaddr4(this, addr, dstaddr, netmask); /* TODO dstaddr */
335 memset(&areq, 0, sizeof(areq));
337 /* Set up interface name */
338 strncpy(areq.ifra_name, this->devname, IFNAMSIZ);
339 areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
341 ((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;
346 ((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;
352 /* 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;
359 /* Create a channel to the NET kernel. */
360 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
361 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
362 return -1;
365 if (ioctl(fd, SIOCAIFADDR, (void *)&areq) < 0) {
366 SYS_ERR(DTUN, LOGL_ERROR, errno,
367 "ioctl(SIOCAIFADDR) failed");
368 close(fd);
369 return -1;
372 close(fd);
373 this->addrs++;
374 return 0;
376 #elif defined (__sun__)
378 if (!this->addrs) /* Use ioctl for first addr to make ping work */
379 return tun_setaddr4(this, addr, dstaddr, netmask);
381 SYS_ERR(DTUN, LOGL_ERROR, errno,
382 "Setting multiple addresses not possible on Solaris");
383 return -1;
385 #else
386 #error "Unknown platform!"
387 #endif
391 static int tun_setaddr4(struct tun_t *this, struct in_addr *addr,
392 struct in_addr *dstaddr, struct in_addr *netmask)
394 struct ifreq ifr;
395 int fd;
397 memset(&ifr, '\0', sizeof(ifr));
398 ifr.ifr_addr.sa_family = AF_INET;
399 ifr.ifr_dstaddr.sa_family = AF_INET;
401 #if defined(__linux__)
402 ifr.ifr_netmask.sa_family = AF_INET;
404 #elif defined(__FreeBSD__) || defined (__APPLE__)
405 ((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);
409 #endif
411 strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
412 ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
414 /* Create a channel to the NET kernel. */
415 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
416 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
417 return -1;
420 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) {
426 SYS_ERR(DTUN, LOGL_ERROR, errno,
427 "ioctl(SIOCSIFADDR) failed");
428 } else {
429 SYS_ERR(DTUN, LOGL_NOTICE, errno,
430 "ioctl(SIOCSIFADDR): Address already exists");
432 close(fd);
433 return -1;
437 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) {
442 SYS_ERR(DTUN, LOGL_ERROR, errno,
443 "ioctl(SIOCSIFDSTADDR) failed");
444 close(fd);
445 return -1;
449 if (netmask) { /* Set the netmask */
450 this->netmask.s_addr = netmask->s_addr;
451 #if defined(__linux__)
452 memcpy(&((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr,
453 netmask, sizeof(*netmask));
455 #elif defined(__FreeBSD__) || defined (__APPLE__)
456 ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr =
457 netmask->s_addr;
459 #elif defined(__sun__)
460 ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr =
461 netmask->s_addr;
462 #else
463 #error "Unknown platform!"
464 #endif
466 if (ioctl(fd, SIOCSIFNETMASK, (void *)&ifr) < 0) {
467 SYS_ERR(DTUN, LOGL_ERROR, errno,
468 "ioctl(SIOCSIFNETMASK) failed");
469 close(fd);
470 return -1;
474 close(fd);
475 this->addrs++;
477 /* On linux the route to the interface is set automatically
478 on FreeBSD we have to do this manually */
480 /* TODO: How does it work on Solaris? */
482 tun_sifflags(this, IFF_UP | IFF_RUNNING);
484 #if defined(__FreeBSD__) || defined (__APPLE__)
485 tun_addroute(this, dstaddr, addr, &this->netmask);
486 this->routes = 1;
487 #endif
489 return 0;
492 static int tun_setaddr6(struct tun_t *this, struct in6_addr *addr, struct in6_addr *dstaddr,
493 size_t prefixlen)
495 struct in6_ifreq ifr;
496 int fd;
498 memset(&ifr, 0, sizeof(ifr));
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;
507 #elif defined(__FreeBSD__) || defined (__APPLE__)
508 strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
509 #endif
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;
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");
527 close(fd);
528 return -1;
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;
543 #endif
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));
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;
556 #endif
558 close(fd);
559 this->addrs++;
561 /* On linux the route to the interface is set automatically
562 on FreeBSD we have to do this manually */
564 /* TODO: How does it work on Solaris? */
566 tun_sifflags(this, IFF_UP | IFF_RUNNING);
568 #if 0 /* FIXME */
569 //#if defined(__FreeBSD__) || defined (__APPLE__)
570 tun_addroute6(this, dstaddr, addr, prefixlen);
571 this->routes = 1;
572 #endif
574 return 0;
577 int tun_setaddr(struct tun_t *this, struct in46_addr *addr, struct in46_addr *dstaddr, size_t prefixlen)
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;
591 int tun_route(struct tun_t *this,
592 struct in_addr *dst,
593 struct in_addr *gateway, struct in_addr *mask, int delete)
596 /* TODO: Learn how to set routing table on sun */
598 #if defined(__linux__)
600 struct rtentry r;
601 int fd;
603 memset(&r, '\0', sizeof(r));
604 r.rt_flags = RTF_UP | RTF_GATEWAY; /* RTF_HOST not set */
606 /* Create a channel to the NET kernel. */
607 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
608 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
609 return -1;
612 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));
621 if (delete) {
622 if (ioctl(fd, SIOCDELRT, (void *)&r) < 0) {
623 SYS_ERR(DTUN, LOGL_ERROR, errno,
624 "ioctl(SIOCDELRT) failed");
625 close(fd);
626 return -1;
628 } else {
629 if (ioctl(fd, SIOCADDRT, (void *)&r) < 0) {
630 SYS_ERR(DTUN, LOGL_ERROR, errno,
631 "ioctl(SIOCADDRT) failed");
632 close(fd);
633 return -1;
636 close(fd);
637 return 0;
639 #elif defined(__FreeBSD__) || defined (__APPLE__)
641 struct {
642 struct rt_msghdr rt;
643 struct sockaddr_in dst;
644 struct sockaddr_in gate;
645 struct sockaddr_in mask;
646 } req;
648 int fd;
649 struct rt_msghdr *rtm;
651 if ((fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) {
652 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
653 return -1;
656 memset(&req, 0x00, sizeof(req));
658 rtm = &req.rt;
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;
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 */
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);
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;
683 if (write(fd, rtm, rtm->rtm_msglen) < 0) {
684 SYS_ERR(DTUN, LOGL_ERROR, errno, "write() failed");
685 close(fd);
686 return -1;
688 close(fd);
689 return 0;
691 #elif defined(__sun__)
692 SYS_ERR(DTUN, LOGL_NOTICE, errno,
693 "Could not set up routing on Solaris. Please add route manually.");
694 return 0;
696 #else
697 #error "Unknown platform!"
698 #endif
702 int tun_addroute(struct tun_t *this,
703 struct in_addr *dst,
704 struct in_addr *gateway, struct in_addr *mask)
706 return tun_route(this, dst, gateway, mask, 0);
709 int tun_delroute(struct tun_t *this,
710 struct in_addr *dst,
711 struct in_addr *gateway, struct in_addr *mask)
713 return tun_route(this, dst, gateway, mask, 1);
716 int tun_new(struct tun_t **tun)
719 #if defined(__linux__)
720 struct ifreq ifr;
722 #elif defined(__FreeBSD__) || defined (__APPLE__)
723 char devname[IFNAMSIZ + 5]; /* "/dev/" + ifname */
724 int devnum;
725 struct ifaliasreq areq;
726 int fd;
728 #elif defined(__sun__)
729 int if_fd, ppa = -1;
730 static int ip_fd = 0;
731 int muxid;
732 struct ifreq ifr;
734 #else
735 #error "Unknown platform!"
736 #endif
738 if (!(*tun = calloc(1, sizeof(struct tun_t)))) {
739 SYS_ERR(DTUN, LOGL_ERROR, errno, "calloc() failed");
740 return EOF;
743 (*tun)->cb_ind = NULL;
744 (*tun)->addrs = 0;
745 (*tun)->routes = 0;
747 #if defined(__linux__)
748 /* Open the actual tun device */
749 if (((*tun)->fd = open("/dev/net/tun", O_RDWR)) < 0) {
750 SYS_ERR(DTUN, LOGL_ERROR, errno, "open() failed");
751 goto err_free;
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) {
759 SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl() failed");
760 goto err_close;
763 strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ);
764 (*tun)->devname[IFNAMSIZ - 1] = 0;
766 ioctl((*tun)->fd, TUNSETNOCSUM, 1); /* Disable checksums */
767 return 0;
769 #elif defined(__FreeBSD__) || defined (__APPLE__)
771 /* Find suitable device */
772 for (devnum = 0; devnum < 255; devnum++) { /* TODO 255 */
773 snprintf(devname, sizeof(devname), "/dev/tun%d", devnum);
774 if (((*tun)->fd = open(devname, O_RDWR)) >= 0)
775 break;
776 if (errno != EBUSY)
777 break;
779 if ((*tun)->fd < 0) {
780 SYS_ERR(DTUN, LOGL_ERROR, errno,
781 "Can't find tunnel device");
782 goto err_free;
785 snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", devnum);
786 (*tun)->devname[sizeof((*tun)->devname)-1] = 0;
788 /* The tun device we found might have "old" IP addresses allocated */
789 /* We need to delete those. This problem is not present on Linux */
791 memset(&areq, 0, sizeof(areq));
793 /* Set up interface name */
794 strncpy(areq.ifra_name, (*tun)->devname, IFNAMSIZ);
795 areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
797 /* Create a channel to the NET kernel. */
798 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
799 SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
800 goto err_close;
803 /* Delete any IP addresses until SIOCDIFADDR fails */
804 while (ioctl(fd, SIOCDIFADDR, (void *)&areq) != -1) ;
806 close(fd);
807 return 0;
809 #elif defined(__sun__)
811 if ((ip_fd = open("/dev/udp", O_RDWR, 0)) < 0) {
812 SYS_ERR(DTUN, LOGL_ERROR, errno,
813 "Can't open /dev/udp");
814 goto err_free;
817 if (((*tun)->fd = open("/dev/tun", O_RDWR, 0)) < 0) {
818 SYS_ERR(DTUN, LOGL_ERROR, errno,
819 "Can't open /dev/tun");
820 close(ip_fd);
821 goto err_free;
824 /* Assign a new PPA and get its unit number. */
825 if ((ppa = ioctl((*tun)->fd, TUNNEWPPA, -1)) < 0) {
826 SYS_ERR(DTUN, LOGL_ERROR, errno,
827 "Can't assign new interface");
828 goto sun_close_ip;
831 if ((if_fd = open("/dev/tun", O_RDWR, 0)) < 0) {
832 SYS_ERR(DTUN, LOGL_ERROR, errno,
833 "Can't open /dev/tun (2)");
834 goto sun_close_ip;
836 if (ioctl(if_fd, I_PUSH, "ip") < 0) {
837 SYS_ERR(DTUN, LOGL_ERROR, errno,
838 "Can't push IP module");
839 goto sun_close_if;
842 /* Assign ppa according to the unit number returned by tun device */
843 if (ioctl(if_fd, IF_UNITSEL, (char *)&ppa) < 0) {
844 SYS_ERR(DTUN, LOGL_ERROR, errno, "Can't set PPA %d",
845 ppa);
846 goto sun_close_if;
849 /* Link the two streams */
850 if ((muxid = ioctl(ip_fd, I_LINK, if_fd)) < 0) {
851 SYS_ERR(DTUN, LOGL_ERROR, errno,
852 "Can't link TUN device to IP");
853 goto sun_close_if;
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;
863 close(if_fd);
865 snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", ppa);
866 (*tun)->devname[sizeof((*tun)->devname)] = 0;
868 memset(&ifr, 0, sizeof(ifr));
869 strcpy(ifr.ifr_name, (*tun)->devname);
870 ifr.ifr_ip_muxid = muxid;
872 if (ioctl(ip_fd, SIOCSIFMUXID, &ifr) < 0) {
873 ioctl(ip_fd, I_PUNLINK, muxid);
874 SYS_ERR(DTUN, LOGL_ERROR, errno,
875 "Can't set multiplexor id");
876 goto sun_close_ip;
879 /* if (fcntl (fd, F_SETFL, O_NONBLOCK) < 0)
880 msg (M_ERR, "Set file descriptor to non-blocking failed"); */
882 return 0;
884 sun_close_if:
885 close(if_fd);
886 sun_close_ip:
887 close(ip_fd);
888 goto err_close;
890 #else
891 #error "Unknown platform!"
892 #endif
894 err_close:
895 close((*tun)->fd);
896 err_free:
897 free(*tun);
898 *tun = NULL;
899 return -1;
902 int tun_free(struct tun_t *tun)
905 if (tun->routes) {
906 tun_delroute(tun, &tun->dstaddr, &tun->addr, &tun->netmask);
909 if (close(tun->fd)) {
910 SYS_ERR(DTUN, LOGL_ERROR, errno, "close() failed");
913 /* TODO: For solaris we need to unlink streams */
915 free(tun);
916 return 0;
919 int tun_set_cb_ind(struct tun_t *this,
920 int (*cb_ind) (struct tun_t * tun, void *pack, unsigned len))
922 this->cb_ind = cb_ind;
923 return 0;
926 int tun_decaps(struct tun_t *this)
929 #if defined(__linux__) || defined (__FreeBSD__) || defined (__APPLE__)
931 unsigned char buffer[PACKET_MAX];
932 int status;
934 if ((status = read(this->fd, buffer, sizeof(buffer))) <= 0) {
935 SYS_ERR(DTUN, LOGL_ERROR, errno, "read() failed");
936 return -1;
939 if (this->cb_ind)
940 return this->cb_ind(this, buffer, status);
942 return 0;
944 #elif defined (__sun__)
946 unsigned char buffer[PACKET_MAX];
947 struct strbuf sbuf;
948 int f = 0;
950 sbuf.maxlen = PACKET_MAX;
951 sbuf.buf = buffer;
952 if (getmsg(this->fd, NULL, &sbuf, &f) < 0) {
953 SYS_ERR(DTUN, LOGL_ERROR, errno, "getmsg() failed");
954 return -1;
957 if (this->cb_ind)
958 return this->cb_ind(this, buffer, sbuf.len);
960 return 0;
962 #endif
966 int tun_encaps(struct tun_t *tun, void *pack, unsigned len)
969 #if defined(__linux__) || defined (__FreeBSD__) || defined (__APPLE__)
971 return write(tun->fd, pack, len);
973 #elif defined (__sun__)
975 struct strbuf sbuf;
976 sbuf.len = len;
977 sbuf.buf = pack;
978 return putmsg(tun->fd, NULL, &sbuf, 0);
980 #endif
983 int tun_runscript(struct tun_t *tun, char *script)
986 char buf[TUN_SCRIPTSIZE];
987 char snet[TUN_ADDRSIZE];
988 char smask[TUN_ADDRSIZE];
989 int rc;
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;
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) {
1002 SYS_ERR(DTUN, LOGL_ERROR, errno,
1003 "Error executing command %s", buf);
1004 return -1;
1006 return 0;