[svn-upgrade] Integrating new upstream version, iodine (0.5.1)
[debian/iodine.git] / src / tun.c
1 /*
2  * Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <stdint.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26
27 #ifdef WINDOWS32
28 #include <winsock2.h>
29 #include <winioctl.h>
30 #include "windows.h"
31
32 HANDLE dev_handle;
33 struct tun_data data;
34
35 #define TAP_CONTROL_CODE(request,method) CTL_CODE(FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
36 #define TAP_IOCTL_CONFIG_TUN       TAP_CONTROL_CODE(10, METHOD_BUFFERED)
37 #define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE(6, METHOD_BUFFERED)
38
39 #define TAP_ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
40 #define TAP_DEVICE_SPACE "\\\\.\\Global\\"
41 #define TAP_VERSION_ID_0801 "tap0801"
42 #define TAP_VERSION_ID_0901 "tap0901"
43 #define KEY_COMPONENT_ID "ComponentId"
44 #define NET_CFG_INST_ID "NetCfgInstanceId"
45 #else
46 #include <err.h>
47 #include <arpa/inet.h>
48 #include <netinet/in.h>
49
50 #define TUN_MAX_TRY 50
51 #endif
52
53 #include "tun.h"
54 #include "common.h"
55
56 char if_name[50];
57
58 #ifndef WINDOWS32
59 #ifdef LINUX
60
61 #include <sys/ioctl.h>
62 #include <net/if.h>
63 #include <linux/if_tun.h>
64
65 int 
66 open_tun(const char *tun_device) 
67 {
68         int i;
69         int tun_fd;
70         struct ifreq ifreq;
71         char *tunnel = "/dev/net/tun";
72
73         if ((tun_fd = open(tunnel, O_RDWR)) < 0) {
74                 warn("open_tun: %s: %s", tunnel, strerror(errno));
75                 return -1;
76         }
77
78         memset(&ifreq, 0, sizeof(ifreq));
79
80         ifreq.ifr_flags = IFF_TUN; 
81
82         if (tun_device != NULL) {
83                 strncpy(ifreq.ifr_name, tun_device, IFNAMSIZ);
84                 ifreq.ifr_name[IFNAMSIZ-1] = '\0';
85                 strncpy(if_name, tun_device, sizeof(if_name));
86                 if_name[sizeof(if_name)-1] = '\0';
87
88                 if (ioctl(tun_fd, TUNSETIFF, (void *) &ifreq) != -1) {
89                         fprintf(stderr, "Opened %s\n", ifreq.ifr_name);
90                         return tun_fd;
91                 }
92
93                 if (errno != EBUSY) {
94                         warn("open_tun: ioctl[TUNSETIFF]: %s", strerror(errno));
95                         return -1;
96                 }
97         } else {
98                 for (i = 0; i < TUN_MAX_TRY; i++) {
99                         snprintf(ifreq.ifr_name, IFNAMSIZ, "dns%d", i);
100
101                         if (ioctl(tun_fd, TUNSETIFF, (void *) &ifreq) != -1) {
102                                 fprintf(stderr, "Opened %s\n", ifreq.ifr_name);
103                                 snprintf(if_name, sizeof(if_name), "dns%d", i);
104                                 return tun_fd;
105                         }
106
107                         if (errno != EBUSY) {
108                                 warn("open_tun: ioctl[TUNSETIFF]: %s", strerror(errno));
109                                 return -1;
110                         }
111                 }
112
113                 warn("open_tun: Couldn't set interface name");
114         }
115         return -1;
116 }
117
118 #else /* BSD */
119
120 int 
121 open_tun(const char *tun_device) 
122 {
123         int i;
124         int tun_fd;
125         char tun_name[50];
126
127         if (tun_device != NULL) {
128                 snprintf(tun_name, sizeof(tun_name), "/dev/%s", tun_device);
129                 strncpy(if_name, tun_device, sizeof(if_name));
130                 if_name[sizeof(if_name)-1] = '\0';
131
132                 if ((tun_fd = open(tun_name, O_RDWR)) < 0) {
133                         warn("open_tun: %s: %s", tun_name, strerror(errno));
134                         return -1;
135                 }
136
137                 fprintf(stderr, "Opened %s\n", tun_name);
138                 return tun_fd;
139         } else {
140                 for (i = 0; i < TUN_MAX_TRY; i++) {
141                         snprintf(tun_name, sizeof(tun_name), "/dev/tun%d", i);
142
143                         if ((tun_fd = open(tun_name, O_RDWR)) >= 0) {
144                                 fprintf(stderr, "Opened %s\n", tun_name);
145                                 snprintf(if_name, sizeof(if_name), "tun%d", i);
146                                 return tun_fd;
147                         }
148
149                         if (errno == ENOENT)
150                                 break;
151                 }
152
153                 warn("open_tun: Failed to open tunneling device");
154         }
155
156         return -1;
157 }
158
159 #endif /* !LINUX */
160 #else /* WINDOWS32 */
161 static void
162 get_device(char *device, int device_len)
163 {
164         LONG status;
165         HKEY adapter_key;
166         int index;
167
168         index = 0;
169         status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TAP_ADAPTER_KEY, 0, KEY_READ, &adapter_key);
170
171         if (status != ERROR_SUCCESS) {
172                 warnx("Error opening registry key " TAP_ADAPTER_KEY );
173                 return;
174         }
175         
176         while (TRUE) {
177                 char name[256];
178                 char unit[256];
179                 char component[256];
180
181                 char cid_string[256] = KEY_COMPONENT_ID;
182                 HKEY device_key;
183                 DWORD datatype;
184                 DWORD len;
185
186                 /* Iterate through all adapter of this kind */
187                 len = sizeof(name);
188                 status = RegEnumKeyEx(adapter_key, index, name, &len, NULL, NULL, NULL, NULL);
189                 if (status == ERROR_NO_MORE_ITEMS) {
190                         break;
191                 } else if (status != ERROR_SUCCESS) {
192                         warnx("Error enumerating subkeys of registry key " TAP_ADAPTER_KEY );
193                         break;
194                 }
195
196                 snprintf(unit, sizeof(unit), TAP_ADAPTER_KEY "\\%s", name);
197                 status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, unit, 0, KEY_READ, &device_key);
198                 if (status != ERROR_SUCCESS) {
199                         warnx("Error opening registry key %s", unit);
200                         goto next;
201                 }
202
203                 /* Check component id */
204                 len = sizeof(component);
205                 status = RegQueryValueEx(device_key, cid_string, NULL, &datatype, (LPBYTE)component, &len);
206                 if (status != ERROR_SUCCESS || datatype != REG_SZ) {
207                         goto next;
208                 }
209                 if (strncmp(TAP_VERSION_ID_0801, component, strlen(TAP_VERSION_ID_0801)) == 0 ||
210                         strncmp(TAP_VERSION_ID_0901, component, strlen(TAP_VERSION_ID_0901)) == 0) {
211                         /* We found a TAP32 device, get its NetCfgInstanceId */
212                         char iid_string[256] = NET_CFG_INST_ID;
213                         
214                         status = RegQueryValueEx(device_key, iid_string, NULL, &datatype, (LPBYTE) device, (DWORD *) &device_len);
215                         if (status != ERROR_SUCCESS || datatype != REG_SZ) {
216                                 warnx("Error reading registry key %s\\%s on TAP device", unit, iid_string);
217                         } else {
218                                 /* Done getting name of TAP device */
219                                 RegCloseKey(device_key);
220                                 return;
221                         }
222                 }
223 next:
224                 RegCloseKey(device_key);
225                 index++;
226         }
227         RegCloseKey(adapter_key);
228 }
229
230 DWORD WINAPI tun_reader(LPVOID arg)
231 {
232         struct tun_data *tun = arg;
233         char buf[64*1024];
234         int len;
235         int res;
236         OVERLAPPED olpd;
237         int sock;
238
239         sock = open_dns(0, INADDR_ANY);
240
241         olpd.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
242
243         while(TRUE) {
244                 olpd.Offset = 0;
245                 olpd.OffsetHigh = 0;
246                 res = ReadFile(tun->tun, buf, sizeof(buf), (LPDWORD) &len, &olpd);
247                 if (!res) {
248                         WaitForSingleObject(olpd.hEvent, INFINITE);
249                         res = GetOverlappedResult(dev_handle, &olpd, (LPDWORD) &len, FALSE);
250                         res = sendto(sock, buf, len, 0, (struct sockaddr*) &(tun->addr), 
251                                 sizeof(struct sockaddr_in));
252                 }
253         }
254
255         return 0;
256 }
257
258 int 
259 open_tun(const char *tun_device) 
260 {
261         char adapter[256];
262         char tapfile[512];
263         int tunfd;
264         in_addr_t local;
265
266         memset(adapter, 0, sizeof(adapter));
267         get_device(adapter, sizeof(adapter));
268
269         if (strlen(adapter) == 0) {
270                 warnx("No TAP adapters found. See README-win32.txt for help.\n");
271                 return -1;
272         }
273         
274         snprintf(tapfile, sizeof(tapfile), "%s%s.tap", TAP_DEVICE_SPACE, adapter);
275         fprintf(stderr, "Opening device %s\n", tapfile);
276         dev_handle = CreateFile(tapfile, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, NULL);
277         if (dev_handle == INVALID_HANDLE_VALUE) {
278                 return -1;
279         }
280
281         /* TODO get name of interface */
282         strncpy(if_name, "dns", MIN(4, sizeof(if_name)));
283
284         /* Use a UDP connection to forward packets from tun,
285          * so we can still use select() in main code.
286          * A thread does blocking reads on tun device and 
287          * sends data as udp to this socket */
288         
289         local = htonl(0x7f000001); /* 127.0.0.1 */
290         tunfd = open_dns(55353, local);
291
292         data.tun = dev_handle;
293         memset(&(data.addr), 0, sizeof(data.addr));
294         data.addr.sin_family = AF_INET;
295         data.addr.sin_port = htons(55353);
296         data.addr.sin_addr.s_addr = local;
297         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)tun_reader, &data, 0, NULL);
298         
299         return tunfd;
300 }
301 #endif 
302
303 void 
304 close_tun(int tun_fd) 
305 {
306         if (tun_fd >= 0)
307                 close(tun_fd);
308 }
309
310 int 
311 write_tun(int tun_fd, char *data, size_t len) 
312 {
313 #if defined (FREEBSD) || defined (DARWIN) || defined(NETBSD) || defined(WINDOWS32)
314         data += 4;
315         len -= 4;
316 #else /* !FREEBSD/DARWIN */
317 #ifdef LINUX
318         data[0] = 0x00;
319         data[1] = 0x00;
320         data[2] = 0x08;
321         data[3] = 0x00;
322 #else /* OPENBSD */
323         data[0] = 0x00;
324         data[1] = 0x00;
325         data[2] = 0x00;
326         data[3] = 0x02;
327 #endif /* !LINUX */
328 #endif /* FREEBSD */
329
330 #ifndef WINDOWS32
331         if (write(tun_fd, data, len) != len) {
332                 warn("write_tun");
333                 return 1;
334         }
335 #else /* WINDOWS32 */
336         {
337                 DWORD written;
338                 DWORD res;
339                 OVERLAPPED olpd;
340
341                 olpd.Offset = 0;
342                 olpd.OffsetHigh = 0;
343                 olpd.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
344                 res = WriteFile(dev_handle, data, len, &written, &olpd);
345                 if (!res && GetLastError() == ERROR_IO_PENDING) {
346                         WaitForSingleObject(olpd.hEvent, INFINITE);
347                         res = GetOverlappedResult(dev_handle, &olpd, &written, FALSE);
348                         if (written != len) {
349                                 return -1;
350                         }
351                 }
352         }
353 #endif
354         return 0;
355 }
356
357 ssize_t
358 read_tun(int tun_fd, char *buf, size_t len) 
359 {
360 #if defined (FREEBSD) || defined (DARWIN) || defined(NETBSD) || defined(WINDOWS32)
361         /* FreeBSD/Darwin/NetBSD has no header */
362         int bytes;
363         bytes = recv(tun_fd, buf + 4, len, 0);
364         if (bytes < 0) {
365                 return bytes;
366         } else {
367                 return bytes + 4;
368         }
369 #else /* !FREEBSD */
370         return read(tun_fd, buf, len);
371 #endif /* !FREEBSD */
372 }
373
374 int
375 tun_setip(const char *ip, int netbits)
376 {
377         char cmdline[512];
378         int netmask;
379         struct in_addr net;
380         int i;
381 #ifndef LINUX
382         int r;
383 #endif
384 #ifdef WINDOWS32
385         DWORD status;
386         DWORD ipdata[3];
387         struct in_addr addr;
388         DWORD len;
389 #endif
390
391         netmask = 0;
392         for (i = 0; i < netbits; i++) {
393                 netmask = (netmask << 1) | 1;
394         }
395         netmask <<= (32 - netbits);
396         net.s_addr = htonl(netmask);
397
398         if (inet_addr(ip) == INADDR_NONE) {
399                 fprintf(stderr, "Invalid IP: %s!\n", ip);
400                 return 1;
401         }
402 #ifndef WINDOWS32
403         snprintf(cmdline, sizeof(cmdline), 
404                         "/sbin/ifconfig %s %s %s netmask %s",
405                         if_name,
406                         ip,
407                         ip,
408                         inet_ntoa(net));
409         
410         fprintf(stderr, "Setting IP of %s to %s\n", if_name, ip);
411 #ifndef LINUX
412         r = system(cmdline);
413         if(r != 0) {
414                 return r;
415         } else {
416                 snprintf(cmdline, sizeof(cmdline),
417                                 "/sbin/route add %s/%d %s",
418                                 ip, netbits, ip);
419         }
420         fprintf(stderr, "Adding route %s/%d to %s\n", ip, netbits, ip);
421 #endif
422         return system(cmdline);
423 #else /* WINDOWS32 */
424
425         /* Set device as connected */
426         fprintf(stderr, "Enabling interface '%s'\n", if_name);
427         status = 1;
428         r = DeviceIoControl(dev_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, 
429                 sizeof(status), &status, sizeof(status), &len, NULL);
430         if (!r) {
431                 fprintf(stderr, "Failed to enable interface\n");
432                 return -1;
433         }
434         
435         if (inet_aton(ip, &addr)) {
436                 ipdata[0] = (DWORD) addr.s_addr;   /* local ip addr */
437                 ipdata[1] = net.s_addr & ipdata[0]; /* network addr */
438                 ipdata[2] = (DWORD) net.s_addr;    /* netmask */
439         } else {
440                 return -1;
441         }
442
443         /* Tell ip/networkaddr/netmask to device for arp use */
444         r = DeviceIoControl(dev_handle, TAP_IOCTL_CONFIG_TUN, &ipdata, 
445                 sizeof(ipdata), &ipdata, sizeof(ipdata), &len, NULL);
446         if (!r) {
447                 fprintf(stderr, "Failed to set interface in TUN mode\n");
448                 return -1;
449         }
450
451         /* use netsh to set ip address */
452         fprintf(stderr, "Setting IP of interface '%s' to %s (can take a few seconds)...\n", if_name, ip);
453         snprintf(cmdline, sizeof(cmdline), "netsh interface ip set address \"%s\" static %s %s",
454                 if_name, ip, inet_ntoa(net));
455         return system(cmdline);
456 #endif
457 }
458
459 int 
460 tun_setmtu(const unsigned mtu)
461 {
462 #ifndef WINDOWS32
463         char cmdline[512];
464
465         if (mtu > 200 && mtu <= 1500) {
466                 snprintf(cmdline, sizeof(cmdline), 
467                                 "/sbin/ifconfig %s mtu %u",
468                                 if_name,
469                                 mtu);
470                 
471                 fprintf(stderr, "Setting MTU of %s to %u\n", if_name, mtu);
472                 return system(cmdline);
473         } else {
474                 warn("MTU out of range: %u\n", mtu);
475         }
476
477         return 1;
478 #else /* WINDOWS32 */
479
480         return 0;
481 #endif
482 }
483