blob: 208cca6e422e0842536b8f25034ee0416c8013fd [file] [log] [blame]
jjako52c24142002-12-16 13:33:51 +00001/*
jjakoa7cd2492003-04-11 09:40:12 +00002 * TUN interface functions.
3 * Copyright (C) 2002, 2003 Mondru AB.
jjako52c24142002-12-16 13:33:51 +00004 *
jjakoa7cd2492003-04-11 09:40:12 +00005 * 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.
jjako52c24142002-12-16 13:33:51 +00009 *
jjakoa7cd2492003-04-11 09:40:12 +000010 * The initial developer of the original code is
11 * Jens Jakobsen <jj@openggsn.org>
jjako52c24142002-12-16 13:33:51 +000012 *
jjakoa7cd2492003-04-11 09:40:12 +000013 * Contributor(s):
jjako52c24142002-12-16 13:33:51 +000014 *
15 */
16
17/*
jjakoa7cd2492003-04-11 09:40:12 +000018 * tun.c: Contains all TUN functionality. Is able to handle multiple
19 * tunnels in the same program. Each tunnel is identified by the struct,
20 * which is passed to functions.
jjako52c24142002-12-16 13:33:51 +000021 *
jjako52c24142002-12-16 13:33:51 +000022 */
23
24
25#include <syslog.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <sys/types.h>
29#include <sys/socket.h>
30#include <netinet/in.h>
31#include <arpa/inet.h>
32#include <sys/stat.h>
33#include <sys/time.h>
34#include <unistd.h>
35#include <string.h>
36#include <errno.h>
37#include <fcntl.h>
38
39#include <stdio.h>
40#include <fcntl.h>
41#include <unistd.h>
42#include <sys/time.h>
jjako52c24142002-12-16 13:33:51 +000043#include <sys/ioctl.h>
44#include <sys/socket.h>
jjako52c24142002-12-16 13:33:51 +000045#include <errno.h>
jjakoa7cd2492003-04-11 09:40:12 +000046#include <net/route.h>
jjako52c24142002-12-16 13:33:51 +000047
jjako409b8552004-02-04 22:57:41 +000048#if defined(__linux__)
jjako0141d202004-01-09 15:19:20 +000049#include <linux/if.h>
50#include <linux/if_tun.h>
jjakoa7cd2492003-04-11 09:40:12 +000051#include <linux/netlink.h>
52#include <linux/rtnetlink.h>
jjako409b8552004-02-04 22:57:41 +000053
54#elif defined (__FreeBSD__)
55#include <net/if.h>
56#include <net/if_tun.h>
57
jjako0141d202004-01-09 15:19:20 +000058#elif defined (__sun__)
jjakoec89e9f2004-01-10 06:38:43 +000059#include <stropts.h>
60#include <sys/sockio.h>
61#include <net/if.h>
jjako0141d202004-01-09 15:19:20 +000062#include <net/if_tun.h>
jjakoec89e9f2004-01-10 06:38:43 +000063/*#include "sun_if_tun.h"*/
jjako409b8552004-02-04 22:57:41 +000064
65#else
66#error "Unknown platform!"
jjako0141d202004-01-09 15:19:20 +000067#endif
68
jjako52c24142002-12-16 13:33:51 +000069
70#include "tun.h"
jjakoa7cd2492003-04-11 09:40:12 +000071#include "syserr.h"
jjako52c24142002-12-16 13:33:51 +000072
73
jjako409b8552004-02-04 22:57:41 +000074#if defined(__linux__)
75
jjakoa7cd2492003-04-11 09:40:12 +000076int tun_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, int dlen)
jjako52c24142002-12-16 13:33:51 +000077{
jjakoa7cd2492003-04-11 09:40:12 +000078 int len = RTA_LENGTH(dlen);
79 int alen = NLMSG_ALIGN(n->nlmsg_len);
80 struct rtattr *rta = (struct rtattr*) (((void*)n) + alen);
81 if (alen + len > nsize)
82 return -1;
83 rta->rta_len = len;
84 rta->rta_type = type;
85 memcpy(RTA_DATA(rta), d, dlen);
86 n->nlmsg_len = alen + len;
87 return 0;
88}
89
90int tun_gifindex(struct tun_t *this, int *index) {
jjako52c24142002-12-16 13:33:51 +000091 struct ifreq ifr;
jjakoa7cd2492003-04-11 09:40:12 +000092 int fd;
jjako52c24142002-12-16 13:33:51 +000093
jjakoa7cd2492003-04-11 09:40:12 +000094 memset (&ifr, '\0', sizeof (ifr));
95 ifr.ifr_addr.sa_family = AF_INET;
96 ifr.ifr_dstaddr.sa_family = AF_INET;
97 ifr.ifr_netmask.sa_family = AF_INET;
98 strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
99 ifr.ifr_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
100 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
101 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
102 "socket() failed");
jjako52c24142002-12-16 13:33:51 +0000103 }
jjakoa7cd2492003-04-11 09:40:12 +0000104 if (ioctl(fd, SIOCGIFINDEX, &ifr)) {
105 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
106 "ioctl() failed");
107 close(fd);
108 return -1;
109 }
110 close(fd);
111 *index = ifr.ifr_ifindex;
112 return 0;
113}
jjakoec89e9f2004-01-10 06:38:43 +0000114#endif
jjako52c24142002-12-16 13:33:51 +0000115
jjakoa7cd2492003-04-11 09:40:12 +0000116int tun_sifflags(struct tun_t *this, int flags) {
117 struct ifreq ifr;
118 int fd;
119
120 memset (&ifr, '\0', sizeof (ifr));
121 ifr.ifr_flags = flags;
122 strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
123 ifr.ifr_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
124 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
125 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
126 "socket() failed");
127 }
128 if (ioctl(fd, SIOCSIFFLAGS, &ifr)) {
129 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
130 "ioctl(SIOCSIFFLAGS) failed");
131 close(fd);
132 return -1;
133 }
134 close(fd);
135 return 0;
136}
137
138
jjako0141d202004-01-09 15:19:20 +0000139/* Currently unused
jjakoa7cd2492003-04-11 09:40:12 +0000140int tun_addroute2(struct tun_t *this,
141 struct in_addr *dst,
142 struct in_addr *gateway,
143 struct in_addr *mask) {
144
145 struct {
146 struct nlmsghdr n;
147 struct rtmsg r;
148 char buf[TUN_NLBUFSIZE];
149 } req;
150
151 struct sockaddr_nl local;
152 int addr_len;
153 int fd;
154 int status;
155 struct sockaddr_nl nladdr;
156 struct iovec iov;
157 struct msghdr msg;
158
159 memset(&req, 0, sizeof(req));
160 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
161 req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
162 req.n.nlmsg_type = RTM_NEWROUTE;
163 req.r.rtm_family = AF_INET;
164 req.r.rtm_table = RT_TABLE_MAIN;
165 req.r.rtm_protocol = RTPROT_BOOT;
166 req.r.rtm_scope = RT_SCOPE_UNIVERSE;
167 req.r.rtm_type = RTN_UNICAST;
168 tun_nlattr(&req.n, sizeof(req), RTA_DST, dst, 4);
169 tun_nlattr(&req.n, sizeof(req), RTA_GATEWAY, gateway, 4);
170
171 if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
172 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
173 "socket() failed");
jjako52c24142002-12-16 13:33:51 +0000174 return -1;
175 }
176
jjakoa7cd2492003-04-11 09:40:12 +0000177 memset(&local, 0, sizeof(local));
178 local.nl_family = AF_NETLINK;
179 local.nl_groups = 0;
180
181 if (bind(fd, (struct sockaddr*)&local, sizeof(local)) < 0) {
182 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
183 "bind() failed");
184 close(fd);
185 return -1;
186 }
187
188 addr_len = sizeof(local);
189 if (getsockname(fd, (struct sockaddr*)&local, &addr_len) < 0) {
190 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
191 "getsockname() failed");
192 close(fd);
193 return -1;
194 }
195
196 if (addr_len != sizeof(local)) {
197 sys_err(LOG_ERR, __FILE__, __LINE__, 0,
198 "Wrong address length %d", addr_len);
199 close(fd);
200 return -1;
201 }
202
203 if (local.nl_family != AF_NETLINK) {
204 sys_err(LOG_ERR, __FILE__, __LINE__, 0,
205 "Wrong address family %d", local.nl_family);
206 close(fd);
207 return -1;
208 }
209
210 iov.iov_base = (void*)&req.n;
211 iov.iov_len = req.n.nlmsg_len;
212
213 msg.msg_name = (void*)&nladdr;
214 msg.msg_namelen = sizeof(nladdr),
215 msg.msg_iov = &iov;
216 msg.msg_iovlen = 1;
217 msg.msg_control = NULL;
218 msg.msg_controllen = 0;
219 msg.msg_flags = 0;
220
221 memset(&nladdr, 0, sizeof(nladdr));
222 nladdr.nl_family = AF_NETLINK;
223 nladdr.nl_pid = 0;
224 nladdr.nl_groups = 0;
225
226 req.n.nlmsg_seq = 0;
227 req.n.nlmsg_flags |= NLM_F_ACK;
228
jjako0141d202004-01-09 15:19:20 +0000229 status = sendmsg(fd, &msg, 0); * TODO: Error check *
jjakoa7cd2492003-04-11 09:40:12 +0000230 close(fd);
231 return 0;
232}
jjako0141d202004-01-09 15:19:20 +0000233*/
jjakoa7cd2492003-04-11 09:40:12 +0000234
235int tun_addaddr(struct tun_t *this,
236 struct in_addr *addr,
237 struct in_addr *dstaddr,
238 struct in_addr *netmask) {
jjako0141d202004-01-09 15:19:20 +0000239
jjako409b8552004-02-04 22:57:41 +0000240#if defined(__linux__)
jjakoa7cd2492003-04-11 09:40:12 +0000241 struct {
242 struct nlmsghdr n;
243 struct ifaddrmsg i;
244 char buf[TUN_NLBUFSIZE];
245 } req;
246
247 struct sockaddr_nl local;
248 int addr_len;
249 int fd;
250 int status;
jjako409b8552004-02-04 22:57:41 +0000251
jjakoa7cd2492003-04-11 09:40:12 +0000252 struct sockaddr_nl nladdr;
253 struct iovec iov;
254 struct msghdr msg;
255
256 if (!this->addrs) /* Use ioctl for first addr to make ping work */
257 return tun_setaddr(this, addr, dstaddr, netmask);
258
259 memset(&req, 0, sizeof(req));
260 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
261 req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
262 req.n.nlmsg_type = RTM_NEWADDR;
263 req.i.ifa_family = AF_INET;
264 req.i.ifa_prefixlen = 32; /* 32 FOR IPv4 */
265 req.i.ifa_flags = 0;
266 req.i.ifa_scope = RT_SCOPE_HOST; /* TODO or 0 */
267 if (tun_gifindex(this, &req.i.ifa_index)) {
268 return -1;
269 }
270
271 tun_nlattr(&req.n, sizeof(req), IFA_ADDRESS, addr, sizeof(addr));
272 tun_nlattr(&req.n, sizeof(req), IFA_LOCAL, dstaddr, sizeof(dstaddr));
273
274 if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
275 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
276 "socket() failed");
277 return -1;
278 }
279
280 memset(&local, 0, sizeof(local));
281 local.nl_family = AF_NETLINK;
282 local.nl_groups = 0;
283
284 if (bind(fd, (struct sockaddr*)&local, sizeof(local)) < 0) {
285 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
286 "bind() failed");
287 close(fd);
288 return -1;
289 }
290
291 addr_len = sizeof(local);
292 if (getsockname(fd, (struct sockaddr*)&local, &addr_len) < 0) {
293 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
294 "getsockname() failed");
295 close(fd);
296 return -1;
297 }
298
299 if (addr_len != sizeof(local)) {
300 sys_err(LOG_ERR, __FILE__, __LINE__, 0,
301 "Wrong address length %d", addr_len);
302 close(fd);
303 return -1;
304 }
305
306 if (local.nl_family != AF_NETLINK) {
307 sys_err(LOG_ERR, __FILE__, __LINE__, 0,
308 "Wrong address family %d", local.nl_family);
309 close(fd);
310 return -1;
311 }
312
313 iov.iov_base = (void*)&req.n;
314 iov.iov_len = req.n.nlmsg_len;
315
316 msg.msg_name = (void*)&nladdr;
317 msg.msg_namelen = sizeof(nladdr),
318 msg.msg_iov = &iov;
319 msg.msg_iovlen = 1;
320 msg.msg_control = NULL;
321 msg.msg_controllen = 0;
322 msg.msg_flags = 0;
323
324 memset(&nladdr, 0, sizeof(nladdr));
325 nladdr.nl_family = AF_NETLINK;
326 nladdr.nl_pid = 0;
327 nladdr.nl_groups = 0;
328
329 req.n.nlmsg_seq = 0;
330 req.n.nlmsg_flags |= NLM_F_ACK;
331
332 status = sendmsg(fd, &msg, 0); /* TODO Error check */
333
jjako409b8552004-02-04 22:57:41 +0000334 tun_sifflags(this, IFF_UP | IFF_RUNNING); /* TODO */
jjakoa7cd2492003-04-11 09:40:12 +0000335 close(fd);
336 this->addrs++;
337 return 0;
jjako409b8552004-02-04 22:57:41 +0000338
339#elif defined (__FreeBSD__)
340
341 int fd;
342 struct ifaliasreq areq;
343
344 /* TODO: Is this needed on FreeBSD? */
345 if (!this->addrs) /* Use ioctl for first addr to make ping work */
346 return tun_setaddr(this, addr, dstaddr, netmask);
347
348 memset(&areq, 0, sizeof(areq));
349
350 /* Set up interface name */
351 strncpy(areq.ifra_name, this->devname, IFNAMSIZ);
352 ifr.ifr_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
353
354 ((struct sockaddr_in) areq.ifra_addr).sin_family = AF_INET;
355 ((struct sockaddr_in) areq.ifra_addr).sin_len = sizeof(areq.ifra_addr);
356 ((struct sockaddr_in) areq.ifra_addr).sin_addr.s_addr = addr->s_addr;
357
358 ((struct sockaddr_in) areq.ifra_mask).sin_family = AF_INET;
359 ((struct sockaddr_in) areq.ifra_mask).sin_len = sizeof(areq.ifra_mask);
360 ((struct sockaddr_in) areq.ifra_mask).sin_addr.s_addr = netmask->s_addr;
361
362
363 /* Create a channel to the NET kernel. */
364 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
365 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
366 "socket() failed");
367 return -1;
368 }
369
370 if (ioctl(fd, SIOCAIFADDR, (void *) &areq) < 0) {
371 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
372 "ioctl(SIOCAIFADDR) failed");
373 close(fd);
374 return -1;
375 }
376
377 close(fd);
378 this->addrs++;
379 return 0;
380
381#elif defined (__sun__)
382
383 if (!this->addrs) /* Use ioctl for first addr to make ping work */
384 return tun_setaddr(this, addr, dstaddr, netmask);
385
386 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
387 "Setting multiple addresses not possible on Solaris");
388 return -1;
389
390#else
391#error "Unknown platform!"
jjako0141d202004-01-09 15:19:20 +0000392#endif
jjako409b8552004-02-04 22:57:41 +0000393
jjakoa7cd2492003-04-11 09:40:12 +0000394}
395
396
397int tun_setaddr(struct tun_t *this,
398 struct in_addr *addr,
399 struct in_addr *dstaddr,
400 struct in_addr *netmask)
401{
402 struct ifreq ifr;
403 int fd;
404
405 memset (&ifr, '\0', sizeof (ifr));
406 ifr.ifr_addr.sa_family = AF_INET;
407 ifr.ifr_dstaddr.sa_family = AF_INET;
jjako409b8552004-02-04 22:57:41 +0000408
409#if defined(__linux__)
jjakoa7cd2492003-04-11 09:40:12 +0000410 ifr.ifr_netmask.sa_family = AF_INET;
jjako409b8552004-02-04 22:57:41 +0000411
412#elif defined(__FreeBSD__)
jjako243bfe62004-01-26 23:06:05 +0000413 ((struct sockaddr_in *) &ifr.ifr_addr)->sin_len =
414 sizeof (struct sockaddr_in);
415 ((struct sockaddr_in *) &ifr.ifr_dstaddr)->sin_len =
416 sizeof (struct sockaddr_in);
417#endif
jjako409b8552004-02-04 22:57:41 +0000418
jjakoa7cd2492003-04-11 09:40:12 +0000419 strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
420 ifr.ifr_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
421
422 /* Create a channel to the NET kernel. */
423 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
424 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
425 "socket() failed");
426 return -1;
427 }
428
429 if (addr) { /* Set the interface address */
430 this->addr.s_addr = addr->s_addr;
431 ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = addr->s_addr;
432 if (ioctl(fd, SIOCSIFADDR, (void *) &ifr) < 0) {
433 if (errno != EEXIST) {
434 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
435 "ioctl(SIOCSIFADDR) failed");
436 }
437 else {
438 sys_err(LOG_WARNING, __FILE__, __LINE__, errno,
439 "ioctl(SIOCSIFADDR): Address already exists");
440 }
441 close(fd);
442 return -1;
443 }
444 }
445
446 if (dstaddr) { /* Set the destination address */
447 this->dstaddr.s_addr = dstaddr->s_addr;
448 ((struct sockaddr_in *) &ifr.ifr_dstaddr)->sin_addr.s_addr =
449 dstaddr->s_addr;
450 if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) {
451 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
452 "ioctl(SIOCSIFDSTADDR) failed");
453 close(fd);
454 return -1;
455 }
456 }
457
458 if (netmask) { /* Set the netmask */
459 this->netmask.s_addr = netmask->s_addr;
jjako409b8552004-02-04 22:57:41 +0000460#if defined(__linux__)
461 ((struct sockaddr_in *) &ifr.ifr_netmask)->sin_addr.s_addr =
jjako243bfe62004-01-26 23:06:05 +0000462 netmask->s_addr;
jjako409b8552004-02-04 22:57:41 +0000463
jjako243bfe62004-01-26 23:06:05 +0000464#elif defined(__FreeBSD__)
465 ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr =
466 netmask->s_addr;
jjako409b8552004-02-04 22:57:41 +0000467
468#elif defined(__sun__)
469 ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr =
jjakoa7cd2492003-04-11 09:40:12 +0000470 netmask->s_addr;
jjako409b8552004-02-04 22:57:41 +0000471#else
472#error "Unknown platform!"
jjakoec89e9f2004-01-10 06:38:43 +0000473#endif
jjako409b8552004-02-04 22:57:41 +0000474
jjakoa7cd2492003-04-11 09:40:12 +0000475 if (ioctl(fd, SIOCSIFNETMASK, (void *) &ifr) < 0) {
476 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
477 "ioctl(SIOCSIFNETMASK) failed");
478 close(fd);
479 return -1;
480 }
481 }
jjako409b8552004-02-04 22:57:41 +0000482
jjakoa7cd2492003-04-11 09:40:12 +0000483 close(fd);
484 this->addrs++;
jjako409b8552004-02-04 22:57:41 +0000485
jjako1ea66342004-01-28 09:27:34 +0000486 /* On linux the route to the interface is set automatically
487 on FreeBSD we have to do this manually */
488
jjako409b8552004-02-04 22:57:41 +0000489 /* TODO: How does it work on Solaris? */
490
jjako1ea66342004-01-28 09:27:34 +0000491#if defined(__FreeBSD__)
492 tun_sifflags(this, IFF_UP | IFF_RUNNING); /* TODO */
493 return tun_addroute(this, addr, addr, netmask);
494#else
495 return tun_sifflags(this, IFF_UP | IFF_RUNNING);
496#endif
497
jjakoa7cd2492003-04-11 09:40:12 +0000498}
499
500int tun_addroute(struct tun_t *this,
501 struct in_addr *dst,
502 struct in_addr *gateway,
503 struct in_addr *mask)
504{
jjakoec89e9f2004-01-10 06:38:43 +0000505
jjako1ea66342004-01-28 09:27:34 +0000506
507 /* TODO: Learn how to set routing table on sun */
jjako409b8552004-02-04 22:57:41 +0000508
509#if defined(__linux__)
jjakoec89e9f2004-01-10 06:38:43 +0000510
jjakoa7cd2492003-04-11 09:40:12 +0000511 struct rtentry r;
512 int fd;
513
514 memset (&r, '\0', sizeof (r));
515 r.rt_flags = RTF_UP | RTF_GATEWAY; /* RTF_HOST not set */
516
517 /* Create a channel to the NET kernel. */
518 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
519 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
520 "socket() failed");
521 return -1;
522 }
523
524 r.rt_dst.sa_family = AF_INET;
525 r.rt_gateway.sa_family = AF_INET;
526 r.rt_genmask.sa_family = AF_INET;
527 ((struct sockaddr_in *) &r.rt_dst)->sin_addr.s_addr = dst->s_addr;
528 ((struct sockaddr_in *) &r.rt_gateway)->sin_addr.s_addr = gateway->s_addr;
529 ((struct sockaddr_in *) &r.rt_genmask)->sin_addr.s_addr = mask->s_addr;
530
531 if (ioctl(fd, SIOCADDRT, (void *) &r) < 0) { /* SIOCDELRT */
532 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
533 "ioctl(SIOCADDRT) failed");
534 close(fd);
535 return -1;
536 }
537 close(fd);
jjako409b8552004-02-04 22:57:41 +0000538 return 0;
jjakoec89e9f2004-01-10 06:38:43 +0000539
jjako1ea66342004-01-28 09:27:34 +0000540#elif defined(__FreeBSD__)
541
542struct {
543 struct rt_msghdr rt;
544 struct sockaddr_in dst;
545 struct sockaddr_in gate;
546 struct sockaddr_in mask;
547} req;
548
549 int fd;
550 struct rt_msghdr *rtm;
551
552 if((fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) {
553 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
554 "socket() failed");
555 return -1;
556 }
557
558 memset(&req, 0x00, sizeof(req));
559
560 rtm = &req.rt;
561
562 rtm->rtm_msglen = sizeof(req);
563 rtm->rtm_version = RTM_VERSION;
564 rtm->rtm_type = RTM_ADD;
565 rtm->rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; /* TODO */
566 rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
567 rtm->rtm_pid = getpid();
568 rtm->rtm_seq = 0044; /* TODO */
569
570 req.dst.sin_family = AF_INET;
571 req.dst.sin_len = sizeof(req.dst);
572 req.mask.sin_family = AF_INET;
573 req.mask.sin_len = sizeof(req.mask);
574 req.gate.sin_family = AF_INET;
575 req.gate.sin_len = sizeof(req.gate);
576
577 req.dst.sin_addr.s_addr = dst->s_addr;
578 req.mask.sin_addr.s_addr = mask->s_addr;
579 req.gate.sin_addr.s_addr = gateway->s_addr;
580
581 if(write(fd, rtm, rtm->rtm_msglen) < 0) {
582 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
583 "write() failed");
584 close(fd);
585 return -1;
586 }
587 close(fd);
jjako409b8552004-02-04 22:57:41 +0000588 return 0;
jjako1ea66342004-01-28 09:27:34 +0000589
jjako409b8552004-02-04 22:57:41 +0000590#elif defined(__sun__)
591 sys_err(LOG_WARNING, __FILE__, __LINE__, errno,
592 "Could not set up routing on Solaris. Please add route manually.");
593 return 0;
594
595#else
596#error "Unknown platform!"
jjakoec89e9f2004-01-10 06:38:43 +0000597#endif
598
jjakoa7cd2492003-04-11 09:40:12 +0000599}
600
jjakoa7cd2492003-04-11 09:40:12 +0000601int tun_new(struct tun_t **tun)
602{
jjakoec89e9f2004-01-10 06:38:43 +0000603
jjako409b8552004-02-04 22:57:41 +0000604#if defined(__linux__)
605 struct ifreq ifr;
606
jjako243bfe62004-01-26 23:06:05 +0000607#elif defined(__FreeBSD__)
608 char devname[IFNAMSIZ+5]; /* "/dev/" + ifname */
609 int devnum;
jjako409b8552004-02-04 22:57:41 +0000610
611#elif defined(__sun__)
612 int if_fd, ppa = -1;
613 static int ip_fd = 0;
614
jjako06e9f122004-01-19 18:37:58 +0000615#else
jjako409b8552004-02-04 22:57:41 +0000616#error "Unknown platform!"
jjakoec89e9f2004-01-10 06:38:43 +0000617#endif
jjakoa7cd2492003-04-11 09:40:12 +0000618
619 if (!(*tun = calloc(1, sizeof(struct tun_t)))) {
620 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "calloc() failed");
621 return EOF;
622 }
623
624 (*tun)->cb_ind = NULL;
625 (*tun)->addrs = 0;
jjako409b8552004-02-04 22:57:41 +0000626
627#if defined(__linux__)
jjakoec89e9f2004-01-10 06:38:43 +0000628 /* Open the actual tun device */
jjakoa7cd2492003-04-11 09:40:12 +0000629 if (((*tun)->fd = open("/dev/net/tun", O_RDWR)) < 0) {
630 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "open() failed");
631 return -1;
632 }
jjako409b8552004-02-04 22:57:41 +0000633
jjakoec89e9f2004-01-10 06:38:43 +0000634 /* Set device flags. For some weird reason this is also the method
635 used to obtain the network interface name */
jjako52c24142002-12-16 13:33:51 +0000636 memset(&ifr, 0, sizeof(ifr));
637 ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Tun device, no packet info */
jjako52c24142002-12-16 13:33:51 +0000638 if (ioctl((*tun)->fd, TUNSETIFF, (void *) &ifr) < 0) {
jjakoa7cd2492003-04-11 09:40:12 +0000639 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "ioctl() failed");
jjako52c24142002-12-16 13:33:51 +0000640 close((*tun)->fd);
641 return -1;
642 }
jjako409b8552004-02-04 22:57:41 +0000643
jjako52c24142002-12-16 13:33:51 +0000644 strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ);
jjakoa7cd2492003-04-11 09:40:12 +0000645 (*tun)->devname[IFNAMSIZ] = 0;
jjako409b8552004-02-04 22:57:41 +0000646
jjakoec89e9f2004-01-10 06:38:43 +0000647 ioctl((*tun)->fd, TUNSETNOCSUM, 1); /* Disable checksums */
jjako409b8552004-02-04 22:57:41 +0000648 return 0;
649
650#elif defined(__FreeBSD__)
jjakoec89e9f2004-01-10 06:38:43 +0000651
jjako409b8552004-02-04 22:57:41 +0000652 /* Find suitable device */
653 for (devnum = 0; devnum < 255; devnum++) { /* TODO 255 */
654 snprintf(devname, sizeof(devname), "/dev/tun%d", devnum);
655 devname[sizeof(devname)] = 0;
656 if (((*tun)->fd = open(devname, O_RDWR)) >= 0) break;
657 if (errno != EBUSY) break;
658 }
659 if ((*tun)->fd < 0) {
660 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Can't find tunnel device");
661 return -1;
662 }
jjakoec89e9f2004-01-10 06:38:43 +0000663
jjako409b8552004-02-04 22:57:41 +0000664 snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", devnum);
665 (*tun)->devname[sizeof((*tun)->devname)] = 0;
666 return 0;
667
668#elif defined(__sun__)
jjakoec89e9f2004-01-10 06:38:43 +0000669
670 if( (ip_fd = open("/dev/ip", O_RDWR, 0)) < 0){
671 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Can't open /dev/ip");
672 return -1;
673 }
674
675 if( ((*tun)->fd = open("/dev/tun", O_RDWR, 0)) < 0){
676 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Can't open /dev/tun");
677 return -1;
678 }
679
680 /* Assign a new PPA and get its unit number. */
681 if( (ppa = ioctl((*tun)->fd, TUNNEWPPA, -1)) < 0){
682 syslog(LOG_ERR, "Can't assign new interface");
683 return -1;
684 }
685
686 if( (if_fd = open("/dev/tun", O_RDWR, 0)) < 0){
687 syslog(LOG_ERR, "Can't open /dev/tun (2)");
688 return -1;
689 }
690 if(ioctl(if_fd, I_PUSH, "ip") < 0){
691 syslog(LOG_ERR, "Can't push IP module");
692 return -1;
693 }
694
695 /* Assign ppa according to the unit number returned by tun device */
696 if(ioctl(if_fd, IF_UNITSEL, (char *)&ppa) < 0){
697 syslog(LOG_ERR, "Can't set PPA %d", ppa);
698 return -1;
699 }
700
701 /* Link the two streams */
702 if(ioctl(ip_fd, I_LINK, if_fd) < 0){
703 syslog(LOG_ERR, "Can't link TUN device to IP");
704 return -1;
705 }
706
707 close (if_fd);
708
jjako243bfe62004-01-26 23:06:05 +0000709 snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", ppa);
710 (*tun)->devname[sizeof((*tun)->devname)] = 0;
jjakoa7cd2492003-04-11 09:40:12 +0000711 return 0;
jjako409b8552004-02-04 22:57:41 +0000712
713#else
714#error "Unknown platform!"
715#endif
716
jjako52c24142002-12-16 13:33:51 +0000717}
718
jjakoa7cd2492003-04-11 09:40:12 +0000719int tun_free(struct tun_t *tun)
jjako52c24142002-12-16 13:33:51 +0000720{
721 if (close(tun->fd)) {
jjakoa7cd2492003-04-11 09:40:12 +0000722 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "close() failed");
jjako52c24142002-12-16 13:33:51 +0000723 }
jjakoa7cd2492003-04-11 09:40:12 +0000724
jjakoec89e9f2004-01-10 06:38:43 +0000725 /* TODO: For solaris we need to unlink streams */
726
jjako52c24142002-12-16 13:33:51 +0000727 free(tun);
728 return 0;
729}
730
731
jjakoa7cd2492003-04-11 09:40:12 +0000732int tun_set_cb_ind(struct tun_t *this,
733 int (*cb_ind) (struct tun_t *tun, void *pack, unsigned len)) {
734 this->cb_ind = cb_ind;
735 return 0;
736}
737
738
739int tun_decaps(struct tun_t *this)
jjako52c24142002-12-16 13:33:51 +0000740{
jjakoa7cd2492003-04-11 09:40:12 +0000741 unsigned char buffer[PACKET_MAX];
742 int status;
743
744 if ((status = read(this->fd, buffer, sizeof(buffer))) <= 0) {
745 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "read() failed");
746 return -1;
747 }
748
749 if (this->cb_ind)
750 return this->cb_ind(this, buffer, status);
jjako52c24142002-12-16 13:33:51 +0000751
jjakoa7cd2492003-04-11 09:40:12 +0000752 return 0;
jjako52c24142002-12-16 13:33:51 +0000753}
754
755int tun_encaps(struct tun_t *tun, void *pack, unsigned len)
756{
jjakoa7cd2492003-04-11 09:40:12 +0000757 return write(tun->fd, pack, len);
758}
759
760int tun_runscript(struct tun_t *tun, char* script) {
761
762 char buf[TUN_SCRIPTSIZE];
763 char snet[TUN_ADDRSIZE];
764 char smask[TUN_ADDRSIZE];
765
766 strncpy(snet, inet_ntoa(tun->addr), sizeof(snet));
767 snet[sizeof(snet)-1] = 0;
768 strncpy(smask, inet_ntoa(tun->netmask), sizeof(smask));
769 smask[sizeof(smask)-1] = 0;
770
771 /* system("ipup /dev/tun0 192.168.0.10 255.255.255.0"); */
772 snprintf(buf, sizeof(buf), "%s %s %s %s",
773 script, tun->devname, snet, smask);
774 buf[sizeof(buf)-1] = 0;
775 system(buf);
776 return 0;
jjako52c24142002-12-16 13:33:51 +0000777}