blob: 43a02ee89eec1741e73013ebe7138d1ab4bff0da [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.
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 *
jjako52c24142002-12-16 13:33:51 +000010 */
11
12/*
jjakoa7cd2492003-04-11 09:40:12 +000013 * 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.
jjako52c24142002-12-16 13:33:51 +000016 *
jjako52c24142002-12-16 13:33:51 +000017 */
18
jjako52c24142002-12-16 13:33:51 +000019#include <syslog.h>
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>
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>
jjako52c24142002-12-16 13:33:51 +000041
jjako409b8552004-02-04 22:57:41 +000042#if defined(__linux__)
jjako0141d202004-01-09 15:19:20 +000043#include <linux/if.h>
44#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__)
49#include <net/if.h>
50#include <net/if_tun.h>
51
jjako0fe0df02004-09-17 11:30:40 +000052#elif defined (__APPLE__)
53#include <net/if.h>
54
jjako0141d202004-01-09 15:19:20 +000055#elif defined (__sun__)
jjakoec89e9f2004-01-10 06:38:43 +000056#include <stropts.h>
57#include <sys/sockio.h>
58#include <net/if.h>
jjako0141d202004-01-09 15:19:20 +000059#include <net/if_tun.h>
jjakoec89e9f2004-01-10 06:38:43 +000060/*#include "sun_if_tun.h"*/
jjako409b8552004-02-04 22:57:41 +000061
62#else
63#error "Unknown platform!"
jjako0141d202004-01-09 15:19:20 +000064#endif
65
jjako52c24142002-12-16 13:33:51 +000066#include "tun.h"
jjakoa7cd2492003-04-11 09:40:12 +000067#include "syserr.h"
jjako52c24142002-12-16 13:33:51 +000068
jjako409b8552004-02-04 22:57:41 +000069#if defined(__linux__)
70
jjakoa7cd2492003-04-11 09:40:12 +000071int tun_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, int dlen)
jjako52c24142002-12-16 13:33:51 +000072{
Harald Weltebed35df2011-11-02 13:06:18 +010073 int len = RTA_LENGTH(dlen);
74 int alen = NLMSG_ALIGN(n->nlmsg_len);
75 struct rtattr *rta = (struct rtattr *)(((void *)n) + alen);
76 if (alen + len > nsize)
77 return -1;
78 rta->rta_len = len;
79 rta->rta_type = type;
80 memcpy(RTA_DATA(rta), d, dlen);
81 n->nlmsg_len = alen + len;
82 return 0;
jjakoa7cd2492003-04-11 09:40:12 +000083}
84
Harald Weltebed35df2011-11-02 13:06:18 +010085int tun_gifindex(struct tun_t *this, __u32 * index)
86{
87 struct ifreq ifr;
88 int fd;
jjako52c24142002-12-16 13:33:51 +000089
Harald Weltebed35df2011-11-02 13:06:18 +010090 memset(&ifr, '\0', sizeof(ifr));
91 ifr.ifr_addr.sa_family = AF_INET;
92 ifr.ifr_dstaddr.sa_family = AF_INET;
93 ifr.ifr_netmask.sa_family = AF_INET;
94 strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
95 ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
96 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
97 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "socket() failed");
98 }
99 if (ioctl(fd, SIOCGIFINDEX, &ifr)) {
100 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "ioctl() failed");
101 close(fd);
102 return -1;
103 }
104 close(fd);
105 *index = ifr.ifr_ifindex;
106 return 0;
jjakoa7cd2492003-04-11 09:40:12 +0000107}
jjakoec89e9f2004-01-10 06:38:43 +0000108#endif
jjako52c24142002-12-16 13:33:51 +0000109
Harald Weltebed35df2011-11-02 13:06:18 +0100110int tun_sifflags(struct tun_t *this, int flags)
111{
112 struct ifreq ifr;
113 int fd;
jjakoa7cd2492003-04-11 09:40:12 +0000114
Harald Weltebed35df2011-11-02 13:06:18 +0100115 memset(&ifr, '\0', sizeof(ifr));
116 ifr.ifr_flags = flags;
117 strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
118 ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
119 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
120 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "socket() failed");
121 }
122 if (ioctl(fd, SIOCSIFFLAGS, &ifr)) {
123 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
124 "ioctl(SIOCSIFFLAGS) failed");
125 close(fd);
126 return -1;
127 }
128 close(fd);
129 return 0;
jjakoa7cd2492003-04-11 09:40:12 +0000130}
131
jjako0141d202004-01-09 15:19:20 +0000132/* Currently unused
jjakoa7cd2492003-04-11 09:40:12 +0000133int tun_addroute2(struct tun_t *this,
134 struct in_addr *dst,
135 struct in_addr *gateway,
136 struct in_addr *mask) {
137
138 struct {
139 struct nlmsghdr n;
140 struct rtmsg r;
141 char buf[TUN_NLBUFSIZE];
142 } req;
143
144 struct sockaddr_nl local;
145 int addr_len;
146 int fd;
147 int status;
148 struct sockaddr_nl nladdr;
149 struct iovec iov;
150 struct msghdr msg;
151
152 memset(&req, 0, sizeof(req));
153 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
154 req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
155 req.n.nlmsg_type = RTM_NEWROUTE;
156 req.r.rtm_family = AF_INET;
157 req.r.rtm_table = RT_TABLE_MAIN;
158 req.r.rtm_protocol = RTPROT_BOOT;
159 req.r.rtm_scope = RT_SCOPE_UNIVERSE;
160 req.r.rtm_type = RTN_UNICAST;
161 tun_nlattr(&req.n, sizeof(req), RTA_DST, dst, 4);
162 tun_nlattr(&req.n, sizeof(req), RTA_GATEWAY, gateway, 4);
163
164 if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
165 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
166 "socket() failed");
jjako52c24142002-12-16 13:33:51 +0000167 return -1;
168 }
169
jjakoa7cd2492003-04-11 09:40:12 +0000170 memset(&local, 0, sizeof(local));
171 local.nl_family = AF_NETLINK;
172 local.nl_groups = 0;
173
174 if (bind(fd, (struct sockaddr*)&local, sizeof(local)) < 0) {
175 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
176 "bind() failed");
177 close(fd);
178 return -1;
179 }
180
181 addr_len = sizeof(local);
182 if (getsockname(fd, (struct sockaddr*)&local, &addr_len) < 0) {
183 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
184 "getsockname() failed");
185 close(fd);
186 return -1;
187 }
188
189 if (addr_len != sizeof(local)) {
190 sys_err(LOG_ERR, __FILE__, __LINE__, 0,
191 "Wrong address length %d", addr_len);
192 close(fd);
193 return -1;
194 }
195
196 if (local.nl_family != AF_NETLINK) {
197 sys_err(LOG_ERR, __FILE__, __LINE__, 0,
198 "Wrong address family %d", local.nl_family);
199 close(fd);
200 return -1;
201 }
202
203 iov.iov_base = (void*)&req.n;
204 iov.iov_len = req.n.nlmsg_len;
205
206 msg.msg_name = (void*)&nladdr;
207 msg.msg_namelen = sizeof(nladdr),
208 msg.msg_iov = &iov;
209 msg.msg_iovlen = 1;
210 msg.msg_control = NULL;
211 msg.msg_controllen = 0;
212 msg.msg_flags = 0;
213
214 memset(&nladdr, 0, sizeof(nladdr));
215 nladdr.nl_family = AF_NETLINK;
216 nladdr.nl_pid = 0;
217 nladdr.nl_groups = 0;
218
219 req.n.nlmsg_seq = 0;
220 req.n.nlmsg_flags |= NLM_F_ACK;
221
jjako0141d202004-01-09 15:19:20 +0000222 status = sendmsg(fd, &msg, 0); * TODO: Error check *
jjakoa7cd2492003-04-11 09:40:12 +0000223 close(fd);
224 return 0;
225}
jjako0141d202004-01-09 15:19:20 +0000226*/
jjakoa7cd2492003-04-11 09:40:12 +0000227
228int tun_addaddr(struct tun_t *this,
229 struct in_addr *addr,
Harald Weltebed35df2011-11-02 13:06:18 +0100230 struct in_addr *dstaddr, struct in_addr *netmask)
231{
jjako0141d202004-01-09 15:19:20 +0000232
jjako409b8552004-02-04 22:57:41 +0000233#if defined(__linux__)
Harald Weltebed35df2011-11-02 13:06:18 +0100234 struct {
235 struct nlmsghdr n;
236 struct ifaddrmsg i;
237 char buf[TUN_NLBUFSIZE];
238 } req;
jjakoa7cd2492003-04-11 09:40:12 +0000239
Harald Weltebed35df2011-11-02 13:06:18 +0100240 struct sockaddr_nl local;
241 socklen_t addr_len;
242 int fd;
243 int status;
jjakoa7cd2492003-04-11 09:40:12 +0000244
Harald Weltebed35df2011-11-02 13:06:18 +0100245 struct sockaddr_nl nladdr;
246 struct iovec iov;
247 struct msghdr msg;
jjakoa7cd2492003-04-11 09:40:12 +0000248
Harald Weltebed35df2011-11-02 13:06:18 +0100249 if (!this->addrs) /* Use ioctl for first addr to make ping work */
250 return tun_setaddr(this, addr, dstaddr, netmask);
jjakoa7cd2492003-04-11 09:40:12 +0000251
Harald Weltebed35df2011-11-02 13:06:18 +0100252 memset(&req, 0, sizeof(req));
253 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
254 req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
255 req.n.nlmsg_type = RTM_NEWADDR;
256 req.i.ifa_family = AF_INET;
257 req.i.ifa_prefixlen = 32; /* 32 FOR IPv4 */
258 req.i.ifa_flags = 0;
259 req.i.ifa_scope = RT_SCOPE_HOST; /* TODO or 0 */
260 if (tun_gifindex(this, &req.i.ifa_index)) {
261 return -1;
262 }
jjakoa7cd2492003-04-11 09:40:12 +0000263
Harald Weltebed35df2011-11-02 13:06:18 +0100264 tun_nlattr(&req.n, sizeof(req), IFA_ADDRESS, addr, sizeof(addr));
265 tun_nlattr(&req.n, sizeof(req), IFA_LOCAL, dstaddr, sizeof(dstaddr));
jjakoa7cd2492003-04-11 09:40:12 +0000266
Harald Weltebed35df2011-11-02 13:06:18 +0100267 if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
268 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "socket() failed");
269 return -1;
270 }
jjakoa7cd2492003-04-11 09:40:12 +0000271
Harald Weltebed35df2011-11-02 13:06:18 +0100272 memset(&local, 0, sizeof(local));
273 local.nl_family = AF_NETLINK;
274 local.nl_groups = 0;
jjakoa7cd2492003-04-11 09:40:12 +0000275
Harald Weltebed35df2011-11-02 13:06:18 +0100276 if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
277 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "bind() failed");
278 close(fd);
279 return -1;
280 }
jjakoa7cd2492003-04-11 09:40:12 +0000281
Harald Weltebed35df2011-11-02 13:06:18 +0100282 addr_len = sizeof(local);
283 if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0) {
284 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
285 "getsockname() failed");
286 close(fd);
287 return -1;
288 }
jjakoa7cd2492003-04-11 09:40:12 +0000289
Harald Weltebed35df2011-11-02 13:06:18 +0100290 if (addr_len != sizeof(local)) {
291 sys_err(LOG_ERR, __FILE__, __LINE__, 0,
292 "Wrong address length %d", addr_len);
293 close(fd);
294 return -1;
295 }
jjakoa7cd2492003-04-11 09:40:12 +0000296
Harald Weltebed35df2011-11-02 13:06:18 +0100297 if (local.nl_family != AF_NETLINK) {
298 sys_err(LOG_ERR, __FILE__, __LINE__, 0,
299 "Wrong address family %d", local.nl_family);
300 close(fd);
301 return -1;
302 }
jjakoa7cd2492003-04-11 09:40:12 +0000303
Harald Weltebed35df2011-11-02 13:06:18 +0100304 iov.iov_base = (void *)&req.n;
305 iov.iov_len = req.n.nlmsg_len;
jjakoa7cd2492003-04-11 09:40:12 +0000306
Harald Weltebed35df2011-11-02 13:06:18 +0100307 msg.msg_name = (void *)&nladdr;
308 msg.msg_namelen = sizeof(nladdr), msg.msg_iov = &iov;
309 msg.msg_iovlen = 1;
310 msg.msg_control = NULL;
311 msg.msg_controllen = 0;
312 msg.msg_flags = 0;
313
314 memset(&nladdr, 0, sizeof(nladdr));
315 nladdr.nl_family = AF_NETLINK;
316 nladdr.nl_pid = 0;
317 nladdr.nl_groups = 0;
318
319 req.n.nlmsg_seq = 0;
320 req.n.nlmsg_flags |= NLM_F_ACK;
321
322 status = sendmsg(fd, &msg, 0); /* TODO Error check */
323
324 tun_sifflags(this, IFF_UP | IFF_RUNNING); /* TODO */
325 close(fd);
326 this->addrs++;
327 return 0;
jjako409b8552004-02-04 22:57:41 +0000328
jjako0fe0df02004-09-17 11:30:40 +0000329#elif defined (__FreeBSD__) || defined (__APPLE__)
jjako409b8552004-02-04 22:57:41 +0000330
Harald Weltebed35df2011-11-02 13:06:18 +0100331 int fd;
332 struct ifaliasreq areq;
jjako409b8552004-02-04 22:57:41 +0000333
Harald Weltebed35df2011-11-02 13:06:18 +0100334 /* TODO: Is this needed on FreeBSD? */
335 if (!this->addrs) /* Use ioctl for first addr to make ping work */
336 return tun_setaddr(this, addr, dstaddr, netmask); /* TODO dstaddr */
jjako409b8552004-02-04 22:57:41 +0000337
Harald Weltebed35df2011-11-02 13:06:18 +0100338 memset(&areq, 0, sizeof(areq));
jjako409b8552004-02-04 22:57:41 +0000339
Harald Weltebed35df2011-11-02 13:06:18 +0100340 /* Set up interface name */
341 strncpy(areq.ifra_name, this->devname, IFNAMSIZ);
342 areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
jjako409b8552004-02-04 22:57:41 +0000343
Harald Weltebed35df2011-11-02 13:06:18 +0100344 ((struct sockaddr_in *)&areq.ifra_addr)->sin_family = AF_INET;
345 ((struct sockaddr_in *)&areq.ifra_addr)->sin_len =
346 sizeof(areq.ifra_addr);
347 ((struct sockaddr_in *)&areq.ifra_addr)->sin_addr.s_addr = addr->s_addr;
jjako409b8552004-02-04 22:57:41 +0000348
Harald Weltebed35df2011-11-02 13:06:18 +0100349 ((struct sockaddr_in *)&areq.ifra_mask)->sin_family = AF_INET;
350 ((struct sockaddr_in *)&areq.ifra_mask)->sin_len =
351 sizeof(areq.ifra_mask);
352 ((struct sockaddr_in *)&areq.ifra_mask)->sin_addr.s_addr =
353 netmask->s_addr;
jjako409b8552004-02-04 22:57:41 +0000354
Harald Weltebed35df2011-11-02 13:06:18 +0100355 /* For some reason FreeBSD uses ifra_broadcast for specifying dstaddr */
356 ((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_family = AF_INET;
357 ((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_len =
358 sizeof(areq.ifra_broadaddr);
359 ((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_addr.s_addr =
360 dstaddr->s_addr;
jjako409b8552004-02-04 22:57:41 +0000361
Harald Weltebed35df2011-11-02 13:06:18 +0100362 /* Create a channel to the NET kernel. */
363 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
364 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "socket() failed");
365 return -1;
366 }
jjako409b8552004-02-04 22:57:41 +0000367
Harald Weltebed35df2011-11-02 13:06:18 +0100368 if (ioctl(fd, SIOCAIFADDR, (void *)&areq) < 0) {
369 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
370 "ioctl(SIOCAIFADDR) failed");
371 close(fd);
372 return -1;
373 }
374
375 close(fd);
376 this->addrs++;
377 return 0;
jjako409b8552004-02-04 22:57:41 +0000378
379#elif defined (__sun__)
Harald Weltebed35df2011-11-02 13:06:18 +0100380
381 if (!this->addrs) /* Use ioctl for first addr to make ping work */
382 return tun_setaddr(this, addr, dstaddr, netmask);
383
384 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
385 "Setting multiple addresses not possible on Solaris");
386 return -1;
jjako409b8552004-02-04 22:57:41 +0000387
388#else
389#error "Unknown platform!"
jjako0141d202004-01-09 15:19:20 +0000390#endif
jjakoa7cd2492003-04-11 09:40:12 +0000391
Harald Weltebed35df2011-11-02 13:06:18 +0100392}
jjakoa7cd2492003-04-11 09:40:12 +0000393
394int tun_setaddr(struct tun_t *this,
395 struct in_addr *addr,
Harald Weltebed35df2011-11-02 13:06:18 +0100396 struct in_addr *dstaddr, struct in_addr *netmask)
jjakoa7cd2492003-04-11 09:40:12 +0000397{
Harald Weltebed35df2011-11-02 13:06:18 +0100398 struct ifreq ifr;
399 int fd;
jjakoa7cd2492003-04-11 09:40:12 +0000400
Harald Weltebed35df2011-11-02 13:06:18 +0100401 memset(&ifr, '\0', sizeof(ifr));
402 ifr.ifr_addr.sa_family = AF_INET;
403 ifr.ifr_dstaddr.sa_family = AF_INET;
jjako409b8552004-02-04 22:57:41 +0000404
405#if defined(__linux__)
Harald Weltebed35df2011-11-02 13:06:18 +0100406 ifr.ifr_netmask.sa_family = AF_INET;
jjako409b8552004-02-04 22:57:41 +0000407
jjako0fe0df02004-09-17 11:30:40 +0000408#elif defined(__FreeBSD__) || defined (__APPLE__)
Harald Weltebed35df2011-11-02 13:06:18 +0100409 ((struct sockaddr_in *)&ifr.ifr_addr)->sin_len =
410 sizeof(struct sockaddr_in);
411 ((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_len =
412 sizeof(struct sockaddr_in);
jjako243bfe62004-01-26 23:06:05 +0000413#endif
jjako409b8552004-02-04 22:57:41 +0000414
Harald Weltebed35df2011-11-02 13:06:18 +0100415 strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
416 ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
jjakoa7cd2492003-04-11 09:40:12 +0000417
Harald Weltebed35df2011-11-02 13:06:18 +0100418 /* Create a channel to the NET kernel. */
419 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
420 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "socket() failed");
421 return -1;
422 }
jjakoa7cd2492003-04-11 09:40:12 +0000423
Harald Weltebed35df2011-11-02 13:06:18 +0100424 if (addr) { /* Set the interface address */
425 this->addr.s_addr = addr->s_addr;
426 memcpy(&((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr, addr,
427 sizeof(*addr));
428 if (ioctl(fd, SIOCSIFADDR, (void *)&ifr) < 0) {
429 if (errno != EEXIST) {
430 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
431 "ioctl(SIOCSIFADDR) failed");
432 } else {
433 sys_err(LOG_WARNING, __FILE__, __LINE__, errno,
434 "ioctl(SIOCSIFADDR): Address already exists");
435 }
436 close(fd);
437 return -1;
438 }
439 }
jjakoa7cd2492003-04-11 09:40:12 +0000440
Harald Weltebed35df2011-11-02 13:06:18 +0100441 if (dstaddr) { /* Set the destination address */
442 this->dstaddr.s_addr = dstaddr->s_addr;
443 memcpy(&((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_addr,
444 dstaddr, sizeof(*dstaddr));
445 if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) & ifr) < 0) {
446 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
447 "ioctl(SIOCSIFDSTADDR) failed");
448 close(fd);
449 return -1;
450 }
451 }
jjakoa7cd2492003-04-11 09:40:12 +0000452
Harald Weltebed35df2011-11-02 13:06:18 +0100453 if (netmask) { /* Set the netmask */
454 this->netmask.s_addr = netmask->s_addr;
jjako409b8552004-02-04 22:57:41 +0000455#if defined(__linux__)
Harald Weltebed35df2011-11-02 13:06:18 +0100456 memcpy(&((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr,
457 netmask, sizeof(*netmask));
jjako409b8552004-02-04 22:57:41 +0000458
jjako0fe0df02004-09-17 11:30:40 +0000459#elif defined(__FreeBSD__) || defined (__APPLE__)
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
463#elif defined(__sun__)
Harald Weltebed35df2011-11-02 13:06:18 +0100464 ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr =
465 netmask->s_addr;
jjako409b8552004-02-04 22:57:41 +0000466#else
467#error "Unknown platform!"
jjakoec89e9f2004-01-10 06:38:43 +0000468#endif
jjako409b8552004-02-04 22:57:41 +0000469
Harald Weltebed35df2011-11-02 13:06:18 +0100470 if (ioctl(fd, SIOCSIFNETMASK, (void *)&ifr) < 0) {
471 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
472 "ioctl(SIOCSIFNETMASK) failed");
473 close(fd);
474 return -1;
475 }
476 }
jjako1ea66342004-01-28 09:27:34 +0000477
Harald Weltebed35df2011-11-02 13:06:18 +0100478 close(fd);
479 this->addrs++;
jjako409b8552004-02-04 22:57:41 +0000480
Harald Weltebed35df2011-11-02 13:06:18 +0100481 /* On linux the route to the interface is set automatically
482 on FreeBSD we have to do this manually */
483
484 /* TODO: How does it work on Solaris? */
485
486 tun_sifflags(this, IFF_UP | IFF_RUNNING);
jjako163b4552004-12-30 15:33:58 +0000487
jjako0fe0df02004-09-17 11:30:40 +0000488#if defined(__FreeBSD__) || defined (__APPLE__)
Harald Weltebed35df2011-11-02 13:06:18 +0100489 tun_addroute(this, dstaddr, addr, netmask);
490 this->routes = 1;
jjako1ea66342004-01-28 09:27:34 +0000491#endif
492
Harald Weltebed35df2011-11-02 13:06:18 +0100493 return 0;
jjakoa7cd2492003-04-11 09:40:12 +0000494}
495
jjako163b4552004-12-30 15:33:58 +0000496int tun_route(struct tun_t *this,
497 struct in_addr *dst,
Harald Weltebed35df2011-11-02 13:06:18 +0100498 struct in_addr *gateway, struct in_addr *mask, int delete)
jjakoa7cd2492003-04-11 09:40:12 +0000499{
jjakoec89e9f2004-01-10 06:38:43 +0000500
Harald Weltebed35df2011-11-02 13:06:18 +0100501 /* TODO: Learn how to set routing table on sun */
jjako409b8552004-02-04 22:57:41 +0000502
503#if defined(__linux__)
jjakoec89e9f2004-01-10 06:38:43 +0000504
Harald Weltebed35df2011-11-02 13:06:18 +0100505 struct rtentry r;
506 int fd;
jjakoa7cd2492003-04-11 09:40:12 +0000507
Harald Weltebed35df2011-11-02 13:06:18 +0100508 memset(&r, '\0', sizeof(r));
509 r.rt_flags = RTF_UP | RTF_GATEWAY; /* RTF_HOST not set */
jjakoa7cd2492003-04-11 09:40:12 +0000510
Harald Weltebed35df2011-11-02 13:06:18 +0100511 /* Create a channel to the NET kernel. */
512 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
513 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "socket() failed");
514 return -1;
515 }
jjakoa7cd2492003-04-11 09:40:12 +0000516
Harald Weltebed35df2011-11-02 13:06:18 +0100517 r.rt_dst.sa_family = AF_INET;
518 r.rt_gateway.sa_family = AF_INET;
519 r.rt_genmask.sa_family = AF_INET;
520 memcpy(&((struct sockaddr_in *)&r.rt_dst)->sin_addr, dst, sizeof(*dst));
521 memcpy(&((struct sockaddr_in *)&r.rt_gateway)->sin_addr, gateway,
522 sizeof(*gateway));
523 memcpy(&((struct sockaddr_in *)&r.rt_genmask)->sin_addr, mask,
524 sizeof(*mask));
525
526 if (delete) {
527 if (ioctl(fd, SIOCDELRT, (void *)&r) < 0) {
528 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
529 "ioctl(SIOCDELRT) failed");
530 close(fd);
531 return -1;
532 }
533 } else {
534 if (ioctl(fd, SIOCADDRT, (void *)&r) < 0) {
535 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
536 "ioctl(SIOCADDRT) failed");
537 close(fd);
538 return -1;
539 }
540 }
541 close(fd);
542 return 0;
543
jjako0fe0df02004-09-17 11:30:40 +0000544#elif defined(__FreeBSD__) || defined (__APPLE__)
jjako3eaf4532004-01-27 19:47:49 +0000545
Harald Weltebed35df2011-11-02 13:06:18 +0100546 struct {
547 struct rt_msghdr rt;
548 struct sockaddr_in dst;
549 struct sockaddr_in gate;
550 struct sockaddr_in mask;
551 } req;
jjako3eaf4532004-01-27 19:47:49 +0000552
Harald Weltebed35df2011-11-02 13:06:18 +0100553 int fd;
554 struct rt_msghdr *rtm;
jjako1ea66342004-01-28 09:27:34 +0000555
Harald Weltebed35df2011-11-02 13:06:18 +0100556 if ((fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) {
557 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "socket() failed");
558 return -1;
559 }
560
561 memset(&req, 0x00, sizeof(req));
562
563 rtm = &req.rt;
564
565 rtm->rtm_msglen = sizeof(req);
566 rtm->rtm_version = RTM_VERSION;
567 if (delete) {
568 rtm->rtm_type = RTM_DELETE;
569 } else {
570 rtm->rtm_type = RTM_ADD;
571 }
572 rtm->rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; /* TODO */
573 rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
574 rtm->rtm_pid = getpid();
575 rtm->rtm_seq = 0044; /* TODO */
576
577 req.dst.sin_family = AF_INET;
578 req.dst.sin_len = sizeof(req.dst);
579 req.mask.sin_family = AF_INET;
580 req.mask.sin_len = sizeof(req.mask);
581 req.gate.sin_family = AF_INET;
582 req.gate.sin_len = sizeof(req.gate);
583
584 req.dst.sin_addr.s_addr = dst->s_addr;
585 req.mask.sin_addr.s_addr = mask->s_addr;
586 req.gate.sin_addr.s_addr = gateway->s_addr;
587
588 if (write(fd, rtm, rtm->rtm_msglen) < 0) {
589 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "write() failed");
590 close(fd);
591 return -1;
592 }
593 close(fd);
594 return 0;
jjako1ea66342004-01-28 09:27:34 +0000595
jjako409b8552004-02-04 22:57:41 +0000596#elif defined(__sun__)
Harald Weltebed35df2011-11-02 13:06:18 +0100597 sys_err(LOG_WARNING, __FILE__, __LINE__, errno,
598 "Could not set up routing on Solaris. Please add route manually.");
599 return 0;
600
jjako409b8552004-02-04 22:57:41 +0000601#else
602#error "Unknown platform!"
jjakoec89e9f2004-01-10 06:38:43 +0000603#endif
604
jjakoa7cd2492003-04-11 09:40:12 +0000605}
606
jjako163b4552004-12-30 15:33:58 +0000607int tun_addroute(struct tun_t *this,
608 struct in_addr *dst,
Harald Weltebed35df2011-11-02 13:06:18 +0100609 struct in_addr *gateway, struct in_addr *mask)
jjako163b4552004-12-30 15:33:58 +0000610{
Harald Weltebed35df2011-11-02 13:06:18 +0100611 return tun_route(this, dst, gateway, mask, 0);
jjako163b4552004-12-30 15:33:58 +0000612}
613
614int tun_delroute(struct tun_t *this,
615 struct in_addr *dst,
Harald Weltebed35df2011-11-02 13:06:18 +0100616 struct in_addr *gateway, struct in_addr *mask)
jjako163b4552004-12-30 15:33:58 +0000617{
Harald Weltebed35df2011-11-02 13:06:18 +0100618 return tun_route(this, dst, gateway, mask, 1);
jjako163b4552004-12-30 15:33:58 +0000619}
620
jjakoa7cd2492003-04-11 09:40:12 +0000621int tun_new(struct tun_t **tun)
622{
jjakoec89e9f2004-01-10 06:38:43 +0000623
jjako409b8552004-02-04 22:57:41 +0000624#if defined(__linux__)
Harald Weltebed35df2011-11-02 13:06:18 +0100625 struct ifreq ifr;
jjako409b8552004-02-04 22:57:41 +0000626
jjako0fe0df02004-09-17 11:30:40 +0000627#elif defined(__FreeBSD__) || defined (__APPLE__)
Harald Weltebed35df2011-11-02 13:06:18 +0100628 char devname[IFNAMSIZ + 5]; /* "/dev/" + ifname */
629 int devnum;
630 struct ifaliasreq areq;
631 int fd;
jjako409b8552004-02-04 22:57:41 +0000632
633#elif defined(__sun__)
Harald Weltebed35df2011-11-02 13:06:18 +0100634 int if_fd, ppa = -1;
635 static int ip_fd = 0;
636 int muxid;
637 struct ifreq ifr;
jjako409b8552004-02-04 22:57:41 +0000638
jjako06e9f122004-01-19 18:37:58 +0000639#else
jjako409b8552004-02-04 22:57:41 +0000640#error "Unknown platform!"
jjakoec89e9f2004-01-10 06:38:43 +0000641#endif
Harald Weltebed35df2011-11-02 13:06:18 +0100642
643 if (!(*tun = calloc(1, sizeof(struct tun_t)))) {
644 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "calloc() failed");
645 return EOF;
646 }
647
648 (*tun)->cb_ind = NULL;
649 (*tun)->addrs = 0;
650 (*tun)->routes = 0;
651
jjako409b8552004-02-04 22:57:41 +0000652#if defined(__linux__)
Harald Weltebed35df2011-11-02 13:06:18 +0100653 /* Open the actual tun device */
654 if (((*tun)->fd = open("/dev/net/tun", O_RDWR)) < 0) {
655 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "open() failed");
656 return -1;
657 }
658
659 /* Set device flags. For some weird reason this is also the method
660 used to obtain the network interface name */
661 memset(&ifr, 0, sizeof(ifr));
662 ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Tun device, no packet info */
663 if (ioctl((*tun)->fd, TUNSETIFF, (void *)&ifr) < 0) {
664 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "ioctl() failed");
665 close((*tun)->fd);
666 return -1;
667 }
668
669 strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ);
670 (*tun)->devname[IFNAMSIZ - 1] = 0;
671
672 ioctl((*tun)->fd, TUNSETNOCSUM, 1); /* Disable checksums */
673 return 0;
674
jjako0fe0df02004-09-17 11:30:40 +0000675#elif defined(__FreeBSD__) || defined (__APPLE__)
jjakoec89e9f2004-01-10 06:38:43 +0000676
Harald Weltebed35df2011-11-02 13:06:18 +0100677 /* Find suitable device */
678 for (devnum = 0; devnum < 255; devnum++) { /* TODO 255 */
679 snprintf(devname, sizeof(devname), "/dev/tun%d", devnum);
Harald Weltebed35df2011-11-02 13:06:18 +0100680 if (((*tun)->fd = open(devname, O_RDWR)) >= 0)
681 break;
682 if (errno != EBUSY)
683 break;
684 }
685 if ((*tun)->fd < 0) {
686 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
687 "Can't find tunnel device");
688 return -1;
689 }
jjakoec89e9f2004-01-10 06:38:43 +0000690
Harald Weltebed35df2011-11-02 13:06:18 +0100691 snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", devnum);
692 (*tun)->devname[sizeof((*tun)->devname)] = 0;
jjako1f158642004-02-05 20:39:57 +0000693
Harald Weltebed35df2011-11-02 13:06:18 +0100694 /* The tun device we found might have "old" IP addresses allocated */
695 /* We need to delete those. This problem is not present on Linux */
jjako1f158642004-02-05 20:39:57 +0000696
Harald Weltebed35df2011-11-02 13:06:18 +0100697 memset(&areq, 0, sizeof(areq));
jjako1f158642004-02-05 20:39:57 +0000698
Harald Weltebed35df2011-11-02 13:06:18 +0100699 /* Set up interface name */
700 strncpy(areq.ifra_name, (*tun)->devname, IFNAMSIZ);
701 areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
jjako1f158642004-02-05 20:39:57 +0000702
Harald Weltebed35df2011-11-02 13:06:18 +0100703 /* Create a channel to the NET kernel. */
704 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
705 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "socket() failed");
706 return -1;
707 }
jjako1f158642004-02-05 20:39:57 +0000708
Harald Weltebed35df2011-11-02 13:06:18 +0100709 /* Delete any IP addresses until SIOCDIFADDR fails */
710 while (ioctl(fd, SIOCDIFADDR, (void *)&areq) != -1) ;
711
712 close(fd);
713 return 0;
jjako409b8552004-02-04 22:57:41 +0000714
715#elif defined(__sun__)
jjakoec89e9f2004-01-10 06:38:43 +0000716
Harald Weltebed35df2011-11-02 13:06:18 +0100717 if ((ip_fd = open("/dev/udp", O_RDWR, 0)) < 0) {
718 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
719 "Can't open /dev/udp");
720 return -1;
721 }
jjakoec89e9f2004-01-10 06:38:43 +0000722
Harald Weltebed35df2011-11-02 13:06:18 +0100723 if (((*tun)->fd = open("/dev/tun", O_RDWR, 0)) < 0) {
724 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
725 "Can't open /dev/tun");
726 return -1;
727 }
jjakoec89e9f2004-01-10 06:38:43 +0000728
Harald Weltebed35df2011-11-02 13:06:18 +0100729 /* Assign a new PPA and get its unit number. */
730 if ((ppa = ioctl((*tun)->fd, TUNNEWPPA, -1)) < 0) {
731 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
732 "Can't assign new interface");
733 return -1;
734 }
jjakoc6762cf2004-04-28 14:52:58 +0000735
Harald Weltebed35df2011-11-02 13:06:18 +0100736 if ((if_fd = open("/dev/tun", O_RDWR, 0)) < 0) {
737 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
738 "Can't open /dev/tun (2)");
739 return -1;
740 }
741 if (ioctl(if_fd, I_PUSH, "ip") < 0) {
742 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
743 "Can't push IP module");
744 return -1;
745 }
jjakoc6762cf2004-04-28 14:52:58 +0000746
Harald Weltebed35df2011-11-02 13:06:18 +0100747 /* Assign ppa according to the unit number returned by tun device */
748 if (ioctl(if_fd, IF_UNITSEL, (char *)&ppa) < 0) {
749 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Can't set PPA %d",
750 ppa);
751 return -1;
752 }
753
754 /* Link the two streams */
755 if ((muxid = ioctl(ip_fd, I_LINK, if_fd)) < 0) {
756 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
757 "Can't link TUN device to IP");
758 return -1;
759 }
760
761 close(if_fd);
762
763 snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", ppa);
764 (*tun)->devname[sizeof((*tun)->devname)] = 0;
765
766 memset(&ifr, 0, sizeof(ifr));
767 strcpy(ifr.ifr_name, (*tun)->devname);
768 ifr.ifr_ip_muxid = muxid;
769
770 if (ioctl(ip_fd, SIOCSIFMUXID, &ifr) < 0) {
771 ioctl(ip_fd, I_PUNLINK, muxid);
772 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
773 "Can't set multiplexor id");
774 return -1;
775 }
776
777 /* if (fcntl (fd, F_SETFL, O_NONBLOCK) < 0)
778 msg (M_ERR, "Set file descriptor to non-blocking failed"); */
779
780 return 0;
jjako409b8552004-02-04 22:57:41 +0000781
782#else
783#error "Unknown platform!"
784#endif
785
jjako52c24142002-12-16 13:33:51 +0000786}
787
jjakoa7cd2492003-04-11 09:40:12 +0000788int tun_free(struct tun_t *tun)
jjako52c24142002-12-16 13:33:51 +0000789{
jjako163b4552004-12-30 15:33:58 +0000790
Harald Weltebed35df2011-11-02 13:06:18 +0100791 if (tun->routes) {
792 tun_delroute(tun, &tun->dstaddr, &tun->addr, &tun->netmask);
793 }
jjako163b4552004-12-30 15:33:58 +0000794
Harald Weltebed35df2011-11-02 13:06:18 +0100795 if (close(tun->fd)) {
796 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "close() failed");
797 }
jjakoa7cd2492003-04-11 09:40:12 +0000798
Harald Weltebed35df2011-11-02 13:06:18 +0100799 /* TODO: For solaris we need to unlink streams */
jjakoec89e9f2004-01-10 06:38:43 +0000800
Harald Weltebed35df2011-11-02 13:06:18 +0100801 free(tun);
802 return 0;
jjako52c24142002-12-16 13:33:51 +0000803}
804
Harald Weltebed35df2011-11-02 13:06:18 +0100805int tun_set_cb_ind(struct tun_t *this,
806 int (*cb_ind) (struct tun_t * tun, void *pack, unsigned len))
807{
808 this->cb_ind = cb_ind;
809 return 0;
jjakoa7cd2492003-04-11 09:40:12 +0000810}
811
jjakoa7cd2492003-04-11 09:40:12 +0000812int tun_decaps(struct tun_t *this)
jjako52c24142002-12-16 13:33:51 +0000813{
jjakoc6762cf2004-04-28 14:52:58 +0000814
jjako0fe0df02004-09-17 11:30:40 +0000815#if defined(__linux__) || defined (__FreeBSD__) || defined (__APPLE__)
jjakoc6762cf2004-04-28 14:52:58 +0000816
Harald Weltebed35df2011-11-02 13:06:18 +0100817 unsigned char buffer[PACKET_MAX];
818 int status;
jjako52c24142002-12-16 13:33:51 +0000819
Harald Weltebed35df2011-11-02 13:06:18 +0100820 if ((status = read(this->fd, buffer, sizeof(buffer))) <= 0) {
821 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "read() failed");
822 return -1;
823 }
824
825 if (this->cb_ind)
826 return this->cb_ind(this, buffer, status);
827
828 return 0;
jjakoc6762cf2004-04-28 14:52:58 +0000829
830#elif defined (__sun__)
831
Harald Weltebed35df2011-11-02 13:06:18 +0100832 unsigned char buffer[PACKET_MAX];
833 struct strbuf sbuf;
834 int f = 0;
jjakoc6762cf2004-04-28 14:52:58 +0000835
Harald Weltebed35df2011-11-02 13:06:18 +0100836 sbuf.maxlen = PACKET_MAX;
837 sbuf.buf = buffer;
838 if (getmsg(this->fd, NULL, &sbuf, &f) < 0) {
839 sys_err(LOG_ERR, __FILE__, __LINE__, errno, "getmsg() failed");
840 return -1;
841 }
jjakoc6762cf2004-04-28 14:52:58 +0000842
Harald Weltebed35df2011-11-02 13:06:18 +0100843 if (this->cb_ind)
844 return this->cb_ind(this, buffer, sbuf.len);
845
846 return 0;
847
jjakoc6762cf2004-04-28 14:52:58 +0000848#endif
849
jjako52c24142002-12-16 13:33:51 +0000850}
851
852int tun_encaps(struct tun_t *tun, void *pack, unsigned len)
853{
jjakoc6762cf2004-04-28 14:52:58 +0000854
jjako0fe0df02004-09-17 11:30:40 +0000855#if defined(__linux__) || defined (__FreeBSD__) || defined (__APPLE__)
jjakoc6762cf2004-04-28 14:52:58 +0000856
Harald Weltebed35df2011-11-02 13:06:18 +0100857 return write(tun->fd, pack, len);
jjakoc6762cf2004-04-28 14:52:58 +0000858
859#elif defined (__sun__)
860
Harald Weltebed35df2011-11-02 13:06:18 +0100861 struct strbuf sbuf;
862 sbuf.len = len;
863 sbuf.buf = pack;
864 return putmsg(tun->fd, NULL, &sbuf, 0);
jjakoc6762cf2004-04-28 14:52:58 +0000865
866#endif
jjakoa7cd2492003-04-11 09:40:12 +0000867}
868
Harald Weltebed35df2011-11-02 13:06:18 +0100869int tun_runscript(struct tun_t *tun, char *script)
870{
jjakoa7cd2492003-04-11 09:40:12 +0000871
Harald Weltebed35df2011-11-02 13:06:18 +0100872 char buf[TUN_SCRIPTSIZE];
873 char snet[TUN_ADDRSIZE];
874 char smask[TUN_ADDRSIZE];
875 int rc;
876
877 strncpy(snet, inet_ntoa(tun->addr), sizeof(snet));
878 snet[sizeof(snet) - 1] = 0;
879 strncpy(smask, inet_ntoa(tun->netmask), sizeof(smask));
880 smask[sizeof(smask) - 1] = 0;
881
882 /* system("ipup /dev/tun0 192.168.0.10 255.255.255.0"); */
883 snprintf(buf, sizeof(buf), "%s %s %s %s",
884 script, tun->devname, snet, smask);
885 buf[sizeof(buf) - 1] = 0;
886 rc = system(buf);
887 if (rc == -1) {
888 sys_err(LOG_ERR, __FILE__, __LINE__, errno,
889 "Error executing command %s", buf);
890 return -1;
891 }
892 return 0;
jjako52c24142002-12-16 13:33:51 +0000893}