fixed compilation warning against gcc-4.4
[openggsn.git] / lib / tun.c
blob0f39994420017337951cf4b36bc40712f7a390d7
1 /*
2 * TUN interface functions.
3 * Copyright (C) 2002, 2003, 2004 Mondru AB.
4 *
5 * The contents of this file may be used under the terms of the GNU
6 * General Public License Version 2, provided that the above copyright
7 * notice and this permission notice is included in all copies or
8 * substantial portions of the software.
9 *
13 * tun.c: Contains all TUN functionality. Is able to handle multiple
14 * tunnels in the same program. Each tunnel is identified by the struct,
15 * which is passed to functions.
20 #include <syslog.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <sys/stat.h>
28 #include <sys/time.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <fcntl.h>
34 #include <stdio.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <sys/time.h>
38 #include <sys/ioctl.h>
39 #include <sys/socket.h>
40 #include <errno.h>
41 #include <net/route.h>
43 #if defined(__linux__)
44 #include <linux/if.h>
45 #include <linux/if_tun.h>
46 #include <linux/netlink.h>
47 #include <linux/rtnetlink.h>
49 #elif defined (__FreeBSD__)
50 #include <net/if.h>
51 #include <net/if_tun.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
68 #include "tun.h"
69 #include "syserr.h"
72 #if defined(__linux__)
74 int tun_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, int dlen)
76 int len = RTA_LENGTH(dlen);
77 int alen = NLMSG_ALIGN(n->nlmsg_len);
78 struct rtattr *rta = (struct rtattr*) (((void*)n) + alen);
79 if (alen + len > nsize)
80 return -1;
81 rta->rta_len = len;
82 rta->rta_type = type;
83 memcpy(RTA_DATA(rta), d, dlen);
84 n->nlmsg_len = alen + len;
85 return 0;
88 int tun_gifindex(struct tun_t *this, __u32 *index) {
89 struct ifreq ifr;
90 int fd;
92 memset (&ifr, '\0', sizeof (ifr));
93 ifr.ifr_addr.sa_family = AF_INET;
94 ifr.ifr_dstaddr.sa_family = AF_INET;
95 ifr.ifr_netmask.sa_family = AF_INET;
96 strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
97 ifr.ifr_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
98 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
99 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
100 "socket() failed");
102 if (ioctl(fd, SIOCGIFINDEX, &ifr)) {
103 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
104 "ioctl() failed");
105 close(fd);
106 return -1;
108 close(fd);
109 *index = ifr.ifr_ifindex;
110 return 0;
112 #endif
114 int tun_sifflags(struct tun_t *this, int flags) {
115 struct ifreq ifr;
116 int fd;
118 memset (&ifr, '\0', sizeof (ifr));
119 ifr.ifr_flags = flags;
120 strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
121 ifr.ifr_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
122 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
123 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
124 "socket() failed");
126 if (ioctl(fd, SIOCSIFFLAGS, &ifr)) {
127 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
128 "ioctl(SIOCSIFFLAGS) failed");
129 close(fd);
130 return -1;
132 close(fd);
133 return 0;
137 /* Currently unused
138 int tun_addroute2(struct tun_t *this,
139 struct in_addr *dst,
140 struct in_addr *gateway,
141 struct in_addr *mask) {
143 struct {
144 struct nlmsghdr n;
145 struct rtmsg r;
146 char buf[TUN_NLBUFSIZE];
147 } req;
149 struct sockaddr_nl local;
150 int addr_len;
151 int fd;
152 int status;
153 struct sockaddr_nl nladdr;
154 struct iovec iov;
155 struct msghdr msg;
157 memset(&req, 0, sizeof(req));
158 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
159 req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
160 req.n.nlmsg_type = RTM_NEWROUTE;
161 req.r.rtm_family = AF_INET;
162 req.r.rtm_table = RT_TABLE_MAIN;
163 req.r.rtm_protocol = RTPROT_BOOT;
164 req.r.rtm_scope = RT_SCOPE_UNIVERSE;
165 req.r.rtm_type = RTN_UNICAST;
166 tun_nlattr(&req.n, sizeof(req), RTA_DST, dst, 4);
167 tun_nlattr(&req.n, sizeof(req), RTA_GATEWAY, gateway, 4);
169 if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
170 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
171 "socket() failed");
172 return -1;
175 memset(&local, 0, sizeof(local));
176 local.nl_family = AF_NETLINK;
177 local.nl_groups = 0;
179 if (bind(fd, (struct sockaddr*)&local, sizeof(local)) < 0) {
180 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
181 "bind() failed");
182 close(fd);
183 return -1;
186 addr_len = sizeof(local);
187 if (getsockname(fd, (struct sockaddr*)&local, &addr_len) < 0) {
188 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
189 "getsockname() failed");
190 close(fd);
191 return -1;
194 if (addr_len != sizeof(local)) {
195 sys_err(LOG_ERR, __FILE__, __LINE__, 0,
196 "Wrong address length %d", addr_len);
197 close(fd);
198 return -1;
201 if (local.nl_family != AF_NETLINK) {
202 sys_err(LOG_ERR, __FILE__, __LINE__, 0,
203 "Wrong address family %d", local.nl_family);
204 close(fd);
205 return -1;
208 iov.iov_base = (void*)&req.n;
209 iov.iov_len = req.n.nlmsg_len;
211 msg.msg_name = (void*)&nladdr;
212 msg.msg_namelen = sizeof(nladdr),
213 msg.msg_iov = &iov;
214 msg.msg_iovlen = 1;
215 msg.msg_control = NULL;
216 msg.msg_controllen = 0;
217 msg.msg_flags = 0;
219 memset(&nladdr, 0, sizeof(nladdr));
220 nladdr.nl_family = AF_NETLINK;
221 nladdr.nl_pid = 0;
222 nladdr.nl_groups = 0;
224 req.n.nlmsg_seq = 0;
225 req.n.nlmsg_flags |= NLM_F_ACK;
227 status = sendmsg(fd, &msg, 0); * TODO: Error check *
228 close(fd);
229 return 0;
233 int tun_addaddr(struct tun_t *this,
234 struct in_addr *addr,
235 struct in_addr *dstaddr,
236 struct in_addr *netmask) {
238 #if defined(__linux__)
239 struct {
240 struct nlmsghdr n;
241 struct ifaddrmsg i;
242 char buf[TUN_NLBUFSIZE];
243 } req;
245 struct sockaddr_nl local;
246 socklen_t addr_len;
247 int fd;
248 int status;
250 struct sockaddr_nl nladdr;
251 struct iovec iov;
252 struct msghdr msg;
254 if (!this->addrs) /* Use ioctl for first addr to make ping work */
255 return tun_setaddr(this, addr, dstaddr, netmask);
257 memset(&req, 0, sizeof(req));
258 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
259 req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
260 req.n.nlmsg_type = RTM_NEWADDR;
261 req.i.ifa_family = AF_INET;
262 req.i.ifa_prefixlen = 32; /* 32 FOR IPv4 */
263 req.i.ifa_flags = 0;
264 req.i.ifa_scope = RT_SCOPE_HOST; /* TODO or 0 */
265 if (tun_gifindex(this, &req.i.ifa_index)) {
266 return -1;
269 tun_nlattr(&req.n, sizeof(req), IFA_ADDRESS, addr, sizeof(addr));
270 tun_nlattr(&req.n, sizeof(req), IFA_LOCAL, dstaddr, sizeof(dstaddr));
272 if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
273 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
274 "socket() failed");
275 return -1;
278 memset(&local, 0, sizeof(local));
279 local.nl_family = AF_NETLINK;
280 local.nl_groups = 0;
282 if (bind(fd, (struct sockaddr*)&local, sizeof(local)) < 0) {
283 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
284 "bind() failed");
285 close(fd);
286 return -1;
289 addr_len = sizeof(local);
290 if (getsockname(fd, (struct sockaddr*)&local, &addr_len) < 0) {
291 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
292 "getsockname() failed");
293 close(fd);
294 return -1;
297 if (addr_len != sizeof(local)) {
298 sys_err(LOG_ERR, __FILE__, __LINE__, 0,
299 "Wrong address length %d", addr_len);
300 close(fd);
301 return -1;
304 if (local.nl_family != AF_NETLINK) {
305 sys_err(LOG_ERR, __FILE__, __LINE__, 0,
306 "Wrong address family %d", local.nl_family);
307 close(fd);
308 return -1;
311 iov.iov_base = (void*)&req.n;
312 iov.iov_len = req.n.nlmsg_len;
314 msg.msg_name = (void*)&nladdr;
315 msg.msg_namelen = sizeof(nladdr),
316 msg.msg_iov = &iov;
317 msg.msg_iovlen = 1;
318 msg.msg_control = NULL;
319 msg.msg_controllen = 0;
320 msg.msg_flags = 0;
322 memset(&nladdr, 0, sizeof(nladdr));
323 nladdr.nl_family = AF_NETLINK;
324 nladdr.nl_pid = 0;
325 nladdr.nl_groups = 0;
327 req.n.nlmsg_seq = 0;
328 req.n.nlmsg_flags |= NLM_F_ACK;
330 status = sendmsg(fd, &msg, 0); /* TODO Error check */
332 tun_sifflags(this, IFF_UP | IFF_RUNNING); /* TODO */
333 close(fd);
334 this->addrs++;
335 return 0;
337 #elif defined (__FreeBSD__) || defined (__APPLE__)
339 int fd;
340 struct ifaliasreq areq;
342 /* TODO: Is this needed on FreeBSD? */
343 if (!this->addrs) /* Use ioctl for first addr to make ping work */
344 return tun_setaddr(this, addr, dstaddr, netmask); /* TODO dstaddr */
346 memset(&areq, 0, sizeof(areq));
348 /* Set up interface name */
349 strncpy(areq.ifra_name, this->devname, IFNAMSIZ);
350 areq.ifra_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
352 ((struct sockaddr_in*) &areq.ifra_addr)->sin_family = AF_INET;
353 ((struct sockaddr_in*) &areq.ifra_addr)->sin_len = sizeof(areq.ifra_addr);
354 ((struct sockaddr_in*) &areq.ifra_addr)->sin_addr.s_addr = addr->s_addr;
356 ((struct sockaddr_in*) &areq.ifra_mask)->sin_family = AF_INET;
357 ((struct sockaddr_in*) &areq.ifra_mask)->sin_len = sizeof(areq.ifra_mask);
358 ((struct sockaddr_in*) &areq.ifra_mask)->sin_addr.s_addr = netmask->s_addr;
360 /* For some reason FreeBSD uses ifra_broadcast for specifying dstaddr */
361 ((struct sockaddr_in*) &areq.ifra_broadaddr)->sin_family = AF_INET;
362 ((struct sockaddr_in*) &areq.ifra_broadaddr)->sin_len =
363 sizeof(areq.ifra_broadaddr);
364 ((struct sockaddr_in*) &areq.ifra_broadaddr)->sin_addr.s_addr =
365 dstaddr->s_addr;
367 /* Create a channel to the NET kernel. */
368 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
369 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
370 "socket() failed");
371 return -1;
374 if (ioctl(fd, SIOCAIFADDR, (void *) &areq) < 0) {
375 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
376 "ioctl(SIOCAIFADDR) failed");
377 close(fd);
378 return -1;
381 close(fd);
382 this->addrs++;
383 return 0;
385 #elif defined (__sun__)
387 if (!this->addrs) /* Use ioctl for first addr to make ping work */
388 return tun_setaddr(this, addr, dstaddr, netmask);
390 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
391 "Setting multiple addresses not possible on Solaris");
392 return -1;
394 #else
395 #error "Unknown platform!"
396 #endif
401 int tun_setaddr(struct tun_t *this,
402 struct in_addr *addr,
403 struct in_addr *dstaddr,
404 struct in_addr *netmask)
406 struct ifreq ifr;
407 int fd;
409 memset (&ifr, '\0', sizeof (ifr));
410 ifr.ifr_addr.sa_family = AF_INET;
411 ifr.ifr_dstaddr.sa_family = AF_INET;
413 #if defined(__linux__)
414 ifr.ifr_netmask.sa_family = AF_INET;
416 #elif defined(__FreeBSD__) || defined (__APPLE__)
417 ((struct sockaddr_in *) &ifr.ifr_addr)->sin_len =
418 sizeof (struct sockaddr_in);
419 ((struct sockaddr_in *) &ifr.ifr_dstaddr)->sin_len =
420 sizeof (struct sockaddr_in);
421 #endif
423 strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
424 ifr.ifr_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
426 /* Create a channel to the NET kernel. */
427 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
428 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
429 "socket() failed");
430 return -1;
433 if (addr) { /* Set the interface address */
434 this->addr.s_addr = addr->s_addr;
435 memcpy(&((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr, addr,
436 sizeof(*addr));
437 if (ioctl(fd, SIOCSIFADDR, (void *) &ifr) < 0) {
438 if (errno != EEXIST) {
439 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
440 "ioctl(SIOCSIFADDR) failed");
442 else {
443 sys_err(LOG_WARNING, __FILE__, __LINE__, errno,
444 "ioctl(SIOCSIFADDR): Address already exists");
446 close(fd);
447 return -1;
451 if (dstaddr) { /* Set the destination address */
452 this->dstaddr.s_addr = dstaddr->s_addr;
453 memcpy(&((struct sockaddr_in *) &ifr.ifr_dstaddr)->sin_addr,
454 dstaddr, sizeof(*dstaddr));
455 if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) {
456 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
457 "ioctl(SIOCSIFDSTADDR) failed");
458 close(fd);
459 return -1;
463 if (netmask) { /* Set the netmask */
464 this->netmask.s_addr = netmask->s_addr;
465 #if defined(__linux__)
466 memcpy(&((struct sockaddr_in *) &ifr.ifr_netmask)->sin_addr,
467 netmask, sizeof(*netmask));
469 #elif defined(__FreeBSD__) || defined (__APPLE__)
470 ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr =
471 netmask->s_addr;
473 #elif defined(__sun__)
474 ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr =
475 netmask->s_addr;
476 #else
477 #error "Unknown platform!"
478 #endif
480 if (ioctl(fd, SIOCSIFNETMASK, (void *) &ifr) < 0) {
481 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
482 "ioctl(SIOCSIFNETMASK) failed");
483 close(fd);
484 return -1;
488 close(fd);
489 this->addrs++;
491 /* On linux the route to the interface is set automatically
492 on FreeBSD we have to do this manually */
494 /* TODO: How does it work on Solaris? */
496 tun_sifflags(this, IFF_UP | IFF_RUNNING);
498 #if defined(__FreeBSD__) || defined (__APPLE__)
499 tun_addroute(this, dstaddr, addr, netmask);
500 this->routes = 1;
501 #endif
503 return 0;
506 int tun_route(struct tun_t *this,
507 struct in_addr *dst,
508 struct in_addr *gateway,
509 struct in_addr *mask,
510 int delete)
514 /* TODO: Learn how to set routing table on sun */
516 #if defined(__linux__)
518 struct rtentry r;
519 int fd;
521 memset (&r, '\0', sizeof (r));
522 r.rt_flags = RTF_UP | RTF_GATEWAY; /* RTF_HOST not set */
524 /* Create a channel to the NET kernel. */
525 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
526 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
527 "socket() failed");
528 return -1;
531 r.rt_dst.sa_family = AF_INET;
532 r.rt_gateway.sa_family = AF_INET;
533 r.rt_genmask.sa_family = AF_INET;
534 memcpy(&((struct sockaddr_in *) &r.rt_dst)->sin_addr, dst, sizeof(*dst));
535 memcpy(&((struct sockaddr_in *) &r.rt_gateway)->sin_addr, gateway,
536 sizeof(*gateway));
537 memcpy(&((struct sockaddr_in *) &r.rt_genmask)->sin_addr, mask,
538 sizeof(*mask));
540 if (delete) {
541 if (ioctl(fd, SIOCDELRT, (void *) &r) < 0) {
542 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
543 "ioctl(SIOCDELRT) failed");
544 close(fd);
545 return -1;
548 else {
549 if (ioctl(fd, SIOCADDRT, (void *) &r) < 0) {
550 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
551 "ioctl(SIOCADDRT) failed");
552 close(fd);
553 return -1;
556 close(fd);
557 return 0;
559 #elif defined(__FreeBSD__) || defined (__APPLE__)
561 struct {
562 struct rt_msghdr rt;
563 struct sockaddr_in dst;
564 struct sockaddr_in gate;
565 struct sockaddr_in mask;
566 } req;
568 int fd;
569 struct rt_msghdr *rtm;
571 if((fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) {
572 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
573 "socket() failed");
574 return -1;
577 memset(&req, 0x00, sizeof(req));
579 rtm = &req.rt;
581 rtm->rtm_msglen = sizeof(req);
582 rtm->rtm_version = RTM_VERSION;
583 if (delete) {
584 rtm->rtm_type = RTM_DELETE;
586 else {
587 rtm->rtm_type = RTM_ADD;
589 rtm->rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; /* TODO */
590 rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
591 rtm->rtm_pid = getpid();
592 rtm->rtm_seq = 0044; /* TODO */
594 req.dst.sin_family = AF_INET;
595 req.dst.sin_len = sizeof(req.dst);
596 req.mask.sin_family = AF_INET;
597 req.mask.sin_len = sizeof(req.mask);
598 req.gate.sin_family = AF_INET;
599 req.gate.sin_len = sizeof(req.gate);
601 req.dst.sin_addr.s_addr = dst->s_addr;
602 req.mask.sin_addr.s_addr = mask->s_addr;
603 req.gate.sin_addr.s_addr = gateway->s_addr;
605 if(write(fd, rtm, rtm->rtm_msglen) < 0) {
606 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
607 "write() failed");
608 close(fd);
609 return -1;
611 close(fd);
612 return 0;
614 #elif defined(__sun__)
615 sys_err(LOG_WARNING, __FILE__, __LINE__, errno,
616 "Could not set up routing on Solaris. Please add route manually.");
617 return 0;
619 #else
620 #error "Unknown platform!"
621 #endif
625 int tun_addroute(struct tun_t *this,
626 struct in_addr *dst,
627 struct in_addr *gateway,
628 struct in_addr *mask)
630 return tun_route(this, dst, gateway, mask, 0);
633 int tun_delroute(struct tun_t *this,
634 struct in_addr *dst,
635 struct in_addr *gateway,
636 struct in_addr *mask)
638 return tun_route(this, dst, gateway, mask, 1);
642 int tun_new(struct tun_t **tun)
645 #if defined(__linux__)
646 struct ifreq ifr;
648 #elif defined(__FreeBSD__) || defined (__APPLE__)
649 char devname[IFNAMSIZ+5]; /* "/dev/" + ifname */
650 int devnum;
651 struct ifaliasreq areq;
652 int fd;
654 #elif defined(__sun__)
655 int if_fd, ppa = -1;
656 static int ip_fd = 0;
657 int muxid;
658 struct ifreq ifr;
660 #else
661 #error "Unknown platform!"
662 #endif
664 if (!(*tun = calloc(1, sizeof(struct tun_t)))) {
665 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "calloc() failed");
666 return EOF;
669 (*tun)->cb_ind = NULL;
670 (*tun)->addrs = 0;
671 (*tun)->routes = 0;
673 #if defined(__linux__)
674 /* Open the actual tun device */
675 if (((*tun)->fd = open("/dev/net/tun", O_RDWR)) < 0) {
676 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "open() failed");
677 return -1;
680 /* Set device flags. For some weird reason this is also the method
681 used to obtain the network interface name */
682 memset(&ifr, 0, sizeof(ifr));
683 ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Tun device, no packet info */
684 if (ioctl((*tun)->fd, TUNSETIFF, (void *) &ifr) < 0) {
685 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "ioctl() failed");
686 close((*tun)->fd);
687 return -1;
690 strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ);
691 (*tun)->devname[IFNAMSIZ] = 0;
693 ioctl((*tun)->fd, TUNSETNOCSUM, 1); /* Disable checksums */
694 return 0;
696 #elif defined(__FreeBSD__) || defined (__APPLE__)
698 /* Find suitable device */
699 for (devnum = 0; devnum < 255; devnum++) { /* TODO 255 */
700 snprintf(devname, sizeof(devname), "/dev/tun%d", devnum);
701 devname[sizeof(devname)] = 0;
702 if (((*tun)->fd = open(devname, O_RDWR)) >= 0) break;
703 if (errno != EBUSY) break;
705 if ((*tun)->fd < 0) {
706 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Can't find tunnel device");
707 return -1;
710 snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", devnum);
711 (*tun)->devname[sizeof((*tun)->devname)] = 0;
713 /* The tun device we found might have "old" IP addresses allocated */
714 /* We need to delete those. This problem is not present on Linux */
716 memset(&areq, 0, sizeof(areq));
718 /* Set up interface name */
719 strncpy(areq.ifra_name, (*tun)->devname, IFNAMSIZ);
720 areq.ifra_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
722 /* Create a channel to the NET kernel. */
723 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
724 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
725 "socket() failed");
726 return -1;
729 /* Delete any IP addresses until SIOCDIFADDR fails */
730 while (ioctl(fd, SIOCDIFADDR, (void *) &areq) != -1);
732 close(fd);
733 return 0;
735 #elif defined(__sun__)
737 if( (ip_fd = open("/dev/udp", O_RDWR, 0)) < 0){
738 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Can't open /dev/udp");
739 return -1;
742 if( ((*tun)->fd = open("/dev/tun", O_RDWR, 0)) < 0){
743 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Can't open /dev/tun");
744 return -1;
747 /* Assign a new PPA and get its unit number. */
748 if( (ppa = ioctl((*tun)->fd, TUNNEWPPA, -1)) < 0){
749 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Can't assign new interface");
750 return -1;
753 if( (if_fd = open("/dev/tun", O_RDWR, 0)) < 0){
754 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Can't open /dev/tun (2)");
755 return -1;
757 if(ioctl(if_fd, I_PUSH, "ip") < 0){
758 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Can't push IP module");
759 return -1;
762 /* Assign ppa according to the unit number returned by tun device */
763 if(ioctl(if_fd, IF_UNITSEL, (char *)&ppa) < 0){
764 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Can't set PPA %d", ppa);
765 return -1;
768 /* Link the two streams */
769 if ((muxid = ioctl(ip_fd, I_LINK, if_fd)) < 0) {
770 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Can't link TUN device to IP");
771 return -1;
774 close (if_fd);
776 snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", ppa);
777 (*tun)->devname[sizeof((*tun)->devname)] = 0;
779 memset(&ifr, 0, sizeof(ifr));
780 strcpy(ifr.ifr_name, (*tun)->devname);
781 ifr.ifr_ip_muxid = muxid;
783 if (ioctl(ip_fd, SIOCSIFMUXID, &ifr) < 0) {
784 ioctl(ip_fd, I_PUNLINK, muxid);
785 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Can't set multiplexor id");
786 return -1;
789 /* if (fcntl (fd, F_SETFL, O_NONBLOCK) < 0)
790 msg (M_ERR, "Set file descriptor to non-blocking failed"); */
792 return 0;
794 #else
795 #error "Unknown platform!"
796 #endif
800 int tun_free(struct tun_t *tun)
803 if (tun->routes) {
804 tun_delroute(tun, &tun->dstaddr, &tun->addr, &tun->netmask);
807 if (close(tun->fd)) {
808 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "close() failed");
811 /* TODO: For solaris we need to unlink streams */
813 free(tun);
814 return 0;
818 int tun_set_cb_ind(struct tun_t *this,
819 int (*cb_ind) (struct tun_t *tun, void *pack, unsigned len)) {
820 this->cb_ind = cb_ind;
821 return 0;
825 int tun_decaps(struct tun_t *this)
828 #if defined(__linux__) || defined (__FreeBSD__) || defined (__APPLE__)
830 unsigned char buffer[PACKET_MAX];
831 int status;
833 if ((status = read(this->fd, buffer, sizeof(buffer))) <= 0) {
834 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "read() failed");
835 return -1;
838 if (this->cb_ind)
839 return this->cb_ind(this, buffer, status);
841 return 0;
843 #elif defined (__sun__)
845 unsigned char buffer[PACKET_MAX];
846 struct strbuf sbuf;
847 int f = 0;
849 sbuf.maxlen = PACKET_MAX;
850 sbuf.buf = buffer;
851 if (getmsg(this->fd, NULL, &sbuf, &f) < 0) {
852 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "getmsg() failed");
853 return -1;
856 if (this->cb_ind)
857 return this->cb_ind(this, buffer, sbuf.len);
859 return 0;
861 #endif
866 int tun_encaps(struct tun_t *tun, void *pack, unsigned len)
869 #if defined(__linux__) || defined (__FreeBSD__) || defined (__APPLE__)
871 return write(tun->fd, pack, len);
873 #elif defined (__sun__)
875 struct strbuf sbuf;
876 sbuf.len = len;
877 sbuf.buf = pack;
878 return putmsg(tun->fd, NULL, &sbuf, 0);
880 #endif
883 int tun_runscript(struct tun_t *tun, char* script) {
885 char buf[TUN_SCRIPTSIZE];
886 char snet[TUN_ADDRSIZE];
887 char smask[TUN_ADDRSIZE];
888 int rc;
890 strncpy(snet, inet_ntoa(tun->addr), sizeof(snet));
891 snet[sizeof(snet)-1] = 0;
892 strncpy(smask, inet_ntoa(tun->netmask), sizeof(smask));
893 smask[sizeof(smask)-1] = 0;
895 /* system("ipup /dev/tun0 192.168.0.10 255.255.255.0"); */
896 snprintf(buf, sizeof(buf), "%s %s %s %s",
897 script, tun->devname, snet, smask);
898 buf[sizeof(buf)-1] = 0;
899 rc = system(buf);
900 if (rc == -1) {
901 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Error executing command %s",
902 buf);
903 return -1;
905 return 0;