* New upstream release:
[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 static void get_name(char *ifname, int namelen, char *dev_name);
36
37 #define TAP_CONTROL_CODE(request,method) CTL_CODE(FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
38 #define TAP_IOCTL_CONFIG_TUN       TAP_CONTROL_CODE(10, METHOD_BUFFERED)
39 #define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE(6, METHOD_BUFFERED)
40
41 #define TAP_ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
42 #define NETWORK_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
43 #define TAP_DEVICE_SPACE "\\\\.\\Global\\"
44 #define TAP_VERSION_ID_0801 "tap0801"
45 #define TAP_VERSION_ID_0901 "tap0901"
46 #define KEY_COMPONENT_ID "ComponentId"
47 #define NET_CFG_INST_ID "NetCfgInstanceId"
48 #else
49 #include <err.h>
50 #include <arpa/inet.h>
51 #include <netinet/in.h>
52
53 #define TUN_MAX_TRY 50
54 #endif
55
56 #include "tun.h"
57 #include "common.h"
58
59 char if_name[250];
60
61 #ifndef WINDOWS32
62 #ifdef LINUX
63
64 #include <sys/ioctl.h>
65 #include <net/if.h>
66 #include <linux/if_tun.h>
67
68 int 
69 open_tun(const char *tun_device) 
70 {
71         int i;
72         int tun_fd;
73         struct ifreq ifreq;
74         char *tunnel = "/dev/net/tun";
75
76         if ((tun_fd = open(tunnel, O_RDWR)) < 0) {
77                 warn("open_tun: %s: %s", tunnel, strerror(errno));
78                 return -1;
79         }
80
81         memset(&ifreq, 0, sizeof(ifreq));
82
83         ifreq.ifr_flags = IFF_TUN; 
84
85         if (tun_device != NULL) {
86                 strncpy(ifreq.ifr_name, tun_device, IFNAMSIZ);
87                 ifreq.ifr_name[IFNAMSIZ-1] = '\0';
88                 strncpy(if_name, tun_device, sizeof(if_name));
89                 if_name[sizeof(if_name)-1] = '\0';
90
91                 if (ioctl(tun_fd, TUNSETIFF, (void *) &ifreq) != -1) {
92                         fprintf(stderr, "Opened %s\n", ifreq.ifr_name);
93                         return tun_fd;
94                 }
95
96                 if (errno != EBUSY) {
97                         warn("open_tun: ioctl[TUNSETIFF]: %s", strerror(errno));
98                         return -1;
99                 }
100         } else {
101                 for (i = 0; i < TUN_MAX_TRY; i++) {
102                         snprintf(ifreq.ifr_name, IFNAMSIZ, "dns%d", i);
103
104                         if (ioctl(tun_fd, TUNSETIFF, (void *) &ifreq) != -1) {
105                                 fprintf(stderr, "Opened %s\n", ifreq.ifr_name);
106                                 snprintf(if_name, sizeof(if_name), "dns%d", i);
107                                 return tun_fd;
108                         }
109
110                         if (errno != EBUSY) {
111                                 warn("open_tun: ioctl[TUNSETIFF]: %s", strerror(errno));
112                                 return -1;
113                         }
114                 }
115
116                 warn("open_tun: Couldn't set interface name");
117         }
118         warn("error when opening tun");
119         return -1;
120 }
121
122 #else /* BSD */
123
124 int 
125 open_tun(const char *tun_device) 
126 {
127         int i;
128         int tun_fd;
129         char tun_name[50];
130
131         if (tun_device != NULL) {
132                 snprintf(tun_name, sizeof(tun_name), "/dev/%s", tun_device);
133                 strncpy(if_name, tun_device, sizeof(if_name));
134                 if_name[sizeof(if_name)-1] = '\0';
135
136                 if ((tun_fd = open(tun_name, O_RDWR)) < 0) {
137                         warn("open_tun: %s: %s", tun_name, strerror(errno));
138                         return -1;
139                 }
140
141                 fprintf(stderr, "Opened %s\n", tun_name);
142                 return tun_fd;
143         } else {
144                 for (i = 0; i < TUN_MAX_TRY; i++) {
145                         snprintf(tun_name, sizeof(tun_name), "/dev/tun%d", i);
146
147                         if ((tun_fd = open(tun_name, O_RDWR)) >= 0) {
148                                 fprintf(stderr, "Opened %s\n", tun_name);
149                                 snprintf(if_name, sizeof(if_name), "tun%d", i);
150                                 return tun_fd;
151                         }
152
153                         if (errno == ENOENT)
154                                 break;
155                 }
156
157                 warn("open_tun: Failed to open tunneling device");
158         }
159
160         return -1;
161 }
162
163 #endif /* !LINUX */
164 #else /* WINDOWS32 */
165 static void
166 get_device(char *device, int device_len, const char *wanted_dev)
167 {
168         LONG status;
169         HKEY adapter_key;
170         int index;
171
172         index = 0;
173         status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TAP_ADAPTER_KEY, 0, KEY_READ, &adapter_key);
174
175         if (status != ERROR_SUCCESS) {
176                 warnx("Error opening registry key " TAP_ADAPTER_KEY );
177                 return;
178         }
179         
180         while (TRUE) {
181                 char name[256];
182                 char unit[256];
183                 char component[256];
184
185                 char cid_string[256] = KEY_COMPONENT_ID;
186                 HKEY device_key;
187                 DWORD datatype;
188                 DWORD len;
189
190                 /* Iterate through all adapter of this kind */
191                 len = sizeof(name);
192                 status = RegEnumKeyEx(adapter_key, index, name, &len, NULL, NULL, NULL, NULL);
193                 if (status == ERROR_NO_MORE_ITEMS) {
194                         break;
195                 } else if (status != ERROR_SUCCESS) {
196                         warnx("Error enumerating subkeys of registry key " TAP_ADAPTER_KEY );
197                         break;
198                 }
199
200                 snprintf(unit, sizeof(unit), TAP_ADAPTER_KEY "\\%s", name);
201                 status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, unit, 0, KEY_READ, &device_key);
202                 if (status != ERROR_SUCCESS) {
203                         warnx("Error opening registry key %s", unit);
204                         goto next;
205                 }
206
207                 /* Check component id */
208                 len = sizeof(component);
209                 status = RegQueryValueEx(device_key, cid_string, NULL, &datatype, (LPBYTE)component, &len);
210                 if (status != ERROR_SUCCESS || datatype != REG_SZ) {
211                         goto next;
212                 }
213                 if (strncmp(TAP_VERSION_ID_0801, component, strlen(TAP_VERSION_ID_0801)) == 0 ||
214                         strncmp(TAP_VERSION_ID_0901, component, strlen(TAP_VERSION_ID_0901)) == 0) {
215                         /* We found a TAP32 device, get its NetCfgInstanceId */
216                         char iid_string[256] = NET_CFG_INST_ID;
217                         
218                         status = RegQueryValueEx(device_key, iid_string, NULL, &datatype, (LPBYTE) device, (DWORD *) &device_len);
219                         if (status != ERROR_SUCCESS || datatype != REG_SZ) {
220                                 warnx("Error reading registry key %s\\%s on TAP device", unit, iid_string);
221                         } else {
222                                 /* Done getting GUID of TAP device,
223                                  * now check if the name is the requested one */
224                                 if (wanted_dev) {
225                                         char name[250];
226                                         get_name(name, sizeof(name), device);
227                                         if (strncmp(name, wanted_dev, strlen(wanted_dev))) {
228                                                 /* Skip if name mismatch */
229                                                 goto next;
230                                         }
231                                 }
232                                 /* Get the if name */
233                                 get_name(if_name, sizeof(if_name), device);
234                                 RegCloseKey(device_key);
235                                 return;
236                         }
237                 }
238 next:
239                 RegCloseKey(device_key);
240                 index++;
241         }
242         RegCloseKey(adapter_key);
243 }
244
245 static void
246 get_name(char *ifname, int namelen, char *dev_name)
247 {
248         char path[256];
249         char name_str[256] = "Name";
250         LONG status;
251         HKEY conn_key;
252         DWORD len;
253         DWORD datatype;
254
255         memset(ifname, 0, namelen);
256
257         snprintf(path, sizeof(path), NETWORK_KEY "\\%s\\Connection", dev_name);
258         status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, path, 0, KEY_READ, &conn_key);
259         if (status != ERROR_SUCCESS) {
260                 fprintf(stderr, "Could not look up name of interface %s: error opening key\n", dev_name);
261                 RegCloseKey(conn_key);
262                 return;
263         }
264         len = namelen;
265         status = RegQueryValueEx(conn_key, name_str, NULL, &datatype, (LPBYTE)ifname, &len);
266         if (status != ERROR_SUCCESS || datatype != REG_SZ) {
267                 fprintf(stderr, "Could not look up name of interface %s: error reading value\n", dev_name);
268                 RegCloseKey(conn_key);
269                 return;
270         }
271         RegCloseKey(conn_key);
272 }
273
274 DWORD WINAPI tun_reader(LPVOID arg)
275 {
276         struct tun_data *tun = arg;
277         char buf[64*1024];
278         int len;
279         int res;
280         OVERLAPPED olpd;
281         int sock;
282
283         sock = open_dns(0, INADDR_ANY);
284
285         olpd.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
286
287         while(TRUE) {
288                 olpd.Offset = 0;
289                 olpd.OffsetHigh = 0;
290                 res = ReadFile(tun->tun, buf, sizeof(buf), (LPDWORD) &len, &olpd);
291                 if (!res) {
292                         WaitForSingleObject(olpd.hEvent, INFINITE);
293                         res = GetOverlappedResult(dev_handle, &olpd, (LPDWORD) &len, FALSE);
294                         res = sendto(sock, buf, len, 0, (struct sockaddr*) &(tun->addr), 
295                                 sizeof(struct sockaddr_in));
296                 }
297         }
298
299         return 0;
300 }
301
302 int 
303 open_tun(const char *tun_device) 
304 {
305         char adapter[256];
306         char tapfile[512];
307         int tunfd;
308         in_addr_t local;
309
310         memset(adapter, 0, sizeof(adapter));
311         memset(if_name, 0, sizeof(if_name));
312         get_device(adapter, sizeof(adapter), tun_device);
313
314         if (strlen(adapter) == 0 || strlen(if_name) == 0) {
315                 if (tun_device) {
316                         warnx("No TAP adapters found. Try without -d.");
317                 } else {
318                         warnx("No TAP adapters found. Version 0801 and 0901 are supported.");
319                 }
320                 return -1;
321         }
322         
323         fprintf(stderr, "Opening device %s\n", if_name);
324         snprintf(tapfile, sizeof(tapfile), "%s%s.tap", TAP_DEVICE_SPACE, adapter);
325         dev_handle = CreateFile(tapfile, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, NULL);
326         if (dev_handle == INVALID_HANDLE_VALUE) {
327                 warnx("Could not open device!");
328                 return -1;
329         }
330
331         /* Use a UDP connection to forward packets from tun,
332          * so we can still use select() in main code.
333          * A thread does blocking reads on tun device and 
334          * sends data as udp to this socket */
335         
336         local = htonl(0x7f000001); /* 127.0.0.1 */
337         tunfd = open_dns(55353, local);
338
339         data.tun = dev_handle;
340         memset(&(data.addr), 0, sizeof(data.addr));
341         data.addr.sin_family = AF_INET;
342         data.addr.sin_port = htons(55353);
343         data.addr.sin_addr.s_addr = local;
344         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)tun_reader, &data, 0, NULL);
345         
346         return tunfd;
347 }
348 #endif 
349
350 void 
351 close_tun(int tun_fd) 
352 {
353         if (tun_fd >= 0)
354                 close(tun_fd);
355 }
356
357 int 
358 write_tun(int tun_fd, char *data, size_t len) 
359 {
360 #if defined (FREEBSD) || defined (DARWIN) || defined(NETBSD) || defined(WINDOWS32)
361         data += 4;
362         len -= 4;
363 #else /* !FREEBSD/DARWIN */
364 #ifdef LINUX
365         data[0] = 0x00;
366         data[1] = 0x00;
367         data[2] = 0x08;
368         data[3] = 0x00;
369 #else /* OPENBSD */
370         data[0] = 0x00;
371         data[1] = 0x00;
372         data[2] = 0x00;
373         data[3] = 0x02;
374 #endif /* !LINUX */
375 #endif /* FREEBSD */
376
377 #ifndef WINDOWS32
378         if (write(tun_fd, data, len) != len) {
379                 warn("write_tun");
380                 return 1;
381         }
382 #else /* WINDOWS32 */
383         {
384                 DWORD written;
385                 DWORD res;
386                 OVERLAPPED olpd;
387
388                 olpd.Offset = 0;
389                 olpd.OffsetHigh = 0;
390                 olpd.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
391                 res = WriteFile(dev_handle, data, len, &written, &olpd);
392                 if (!res && GetLastError() == ERROR_IO_PENDING) {
393                         WaitForSingleObject(olpd.hEvent, INFINITE);
394                         res = GetOverlappedResult(dev_handle, &olpd, &written, FALSE);
395                         if (written != len) {
396                                 return -1;
397                         }
398                 }
399         }
400 #endif
401         return 0;
402 }
403
404 ssize_t
405 read_tun(int tun_fd, char *buf, size_t len) 
406 {
407 #if defined (FREEBSD) || defined (DARWIN) || defined(NETBSD) || defined(WINDOWS32)
408         /* FreeBSD/Darwin/NetBSD has no header */
409         int bytes;
410         memset(buf, 0, 4);
411 #ifdef WINDOWS32
412         /* Windows needs recv() since it is local UDP socket */
413         bytes = recv(tun_fd, buf + 4, len - 4, 0);
414 #else
415         /* The other need read() because fd is not a socket */
416         bytes = read(tun_fd, buf + 4, len - 4);
417 #endif /*WINDOWS32*/
418         if (bytes < 0) {
419                 return bytes;
420         } else {
421                 return bytes + 4;
422         }
423 #else /* !FREEBSD */
424         return read(tun_fd, buf, len);
425 #endif /* !FREEBSD */
426 }
427
428 int
429 tun_setip(const char *ip, const char *remoteip, int netbits)
430 {
431         char cmdline[512];
432         int netmask;
433         struct in_addr net;
434         int i;
435 #ifndef LINUX
436         int r;
437 #endif
438 #ifdef WINDOWS32
439         DWORD status;
440         DWORD ipdata[3];
441         struct in_addr addr;
442         DWORD len;
443 #endif
444
445         netmask = 0;
446         for (i = 0; i < netbits; i++) {
447                 netmask = (netmask << 1) | 1;
448         }
449         netmask <<= (32 - netbits);
450         net.s_addr = htonl(netmask);
451
452         if (inet_addr(ip) == INADDR_NONE) {
453                 fprintf(stderr, "Invalid IP: %s!\n", ip);
454                 return 1;
455         }
456 #ifndef WINDOWS32
457         snprintf(cmdline, sizeof(cmdline), 
458                         "/sbin/ifconfig %s %s %s netmask %s",
459                         if_name,
460                         ip,
461 #ifdef FREEBSD
462                         remoteip, /* FreeBSD wants other IP as second IP */
463 #else
464                         ip,
465 #endif
466                         inet_ntoa(net));
467         
468         fprintf(stderr, "Setting IP of %s to %s\n", if_name, ip);
469 #ifndef LINUX
470         r = system(cmdline);
471         if(r != 0) {
472                 return r;
473         } else {
474                 snprintf(cmdline, sizeof(cmdline),
475                                 "/sbin/route add %s/%d %s",
476                                 ip, netbits, ip);
477         }
478         fprintf(stderr, "Adding route %s/%d to %s\n", ip, netbits, ip);
479 #endif
480         return system(cmdline);
481 #else /* WINDOWS32 */
482
483         /* Set device as connected */
484         fprintf(stderr, "Enabling interface '%s'\n", if_name);
485         status = 1;
486         r = DeviceIoControl(dev_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, 
487                 sizeof(status), &status, sizeof(status), &len, NULL);
488         if (!r) {
489                 fprintf(stderr, "Failed to enable interface\n");
490                 return -1;
491         }
492         
493         if (inet_aton(ip, &addr)) {
494                 ipdata[0] = (DWORD) addr.s_addr;   /* local ip addr */
495                 ipdata[1] = net.s_addr & ipdata[0]; /* network addr */
496                 ipdata[2] = (DWORD) net.s_addr;    /* netmask */
497         } else {
498                 return -1;
499         }
500
501         /* Tell ip/networkaddr/netmask to device for arp use */
502         r = DeviceIoControl(dev_handle, TAP_IOCTL_CONFIG_TUN, &ipdata, 
503                 sizeof(ipdata), &ipdata, sizeof(ipdata), &len, NULL);
504         if (!r) {
505                 fprintf(stderr, "Failed to set interface in TUN mode\n");
506                 return -1;
507         }
508
509         /* use netsh to set ip address */
510         fprintf(stderr, "Setting IP of interface '%s' to %s (can take a few seconds)...\n", if_name, ip);
511         snprintf(cmdline, sizeof(cmdline), "netsh interface ip set address \"%s\" static %s %s",
512                 if_name, ip, inet_ntoa(net));
513         return system(cmdline);
514 #endif
515 }
516
517 int 
518 tun_setmtu(const unsigned mtu)
519 {
520 #ifndef WINDOWS32
521         char cmdline[512];
522
523         if (mtu > 200 && mtu <= 1500) {
524                 snprintf(cmdline, sizeof(cmdline), 
525                                 "/sbin/ifconfig %s mtu %u",
526                                 if_name,
527                                 mtu);
528                 
529                 fprintf(stderr, "Setting MTU of %s to %u\n", if_name, mtu);
530                 return system(cmdline);
531         } else {
532                 warn("MTU out of range: %u\n", mtu);
533         }
534
535         return 1;
536 #else /* WINDOWS32 */
537
538         return 0;
539 #endif
540 }
541