* added tap version checking logics. Many tap32 versions are broken,
[oweals/gnunet.git] / src / vpn / gnunet-helper-vpn-windows.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010, 2012 Christian Grothoff
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * @file vpn/gnunet-helper-vpn-windows.c
23  * @brief the helper for the VPN service in win32 builds. 
24  * Opens a virtual network-interface, sends data received on the if to stdout, 
25  * sends data received on stdin to the interface
26  * @author Christian M. Fuchs
27  *
28  * The following list of people have reviewed this code and considered
29  * it safe since the last modification (if you reviewed it, please
30  * have your name added to the list):
31  *
32  */
33
34 #include <stdio.h>
35 #include <windows.h>
36 #include <setupapi.h>
37 #include <ddk/cfgmgr32.h>
38 #include "platform.h"
39 #include "tap-windows.h"
40 #include <Winsock2.h>
41
42 /**
43  * Need 'struct GNUNET_MessageHeader'.
44  */
45 #include "gnunet_common.h"
46
47 /**
48  * Need VPN message types.
49  */
50 #include "gnunet_protocols.h"
51
52 /**
53  * Should we print (interesting|debug) messages that can happen during
54  * normal operation?
55  */
56 #define DEBUG GNUNET_NO
57
58 /**
59  * Maximum size of a GNUnet message (GNUNET_SERVER_MAX_MESSAGE_SIZE)
60  */
61 #define MAX_SIZE 65536
62
63 /**
64  * Name or Path+Name of our driver in Unicode.
65  * The .sys and .cat files HAVE to be in the same location as this file!
66  */
67 #define INF_FILE "tapw32.inf"
68
69 /**
70  * Hardware ID used in the inf-file. 
71  * This might change over time, as openvpn advances their driver
72  */
73 #define HARDWARE_ID "TAP0901"
74
75 /**
76  * Component ID if our driver
77  */
78 #define TAP_WIN_COMPONENT_ID "tap0901"
79
80 /**
81  * Minimum major-id of the driver version we can work with
82  */
83 #define TAP_WIN_MIN_MAJOR 9
84
85 /**
86  * Minimum minor-id of the driver version we can work with. 
87  * v <= 7 has buggy IPv6.
88  * v == 8 is broken for small IPv4 Packets
89  */
90 #define TAP_WIN_MIN_MINOR 9
91
92 /**
93  * Time to wait for our virtual device to go up after telling it to do so.
94  * 
95  * openvpn doesn't specify a value, 4 seems sane for testing, even for openwrt
96  * (in fact, 4 was chosen by a fair dice roll...)
97  */
98 #define TAP32_POSTUP_WAITTIME 4
99
100 /**
101  * Location of the network interface list resides in registry.
102  * TODO: is this fixed on all version of windows? Checked with XP and 7
103  */
104 #define INTERFACE_REGISTRY_LOCATION "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
105
106 /**
107  * Our local process' PID. Used for creating a sufficiently unique additional 
108  * hardware ID for our device.
109  */
110 static char secondary_hwid[LINE_LEN / 2];
111
112 /**
113  * Device's visible Name, used to identify a network device in netsh.
114  * eg: "Local Area Connection 9"
115  */
116 static char device_visible_name[256];
117
118 /** 
119  * This is our own local instance of a virtual network interface
120  * It is (somewhat) equivalent to using tun/tap in unixoid systems
121  * 
122  * Upon initialization, we create such an device node.
123  * Upon termination, we remove it again.
124  * 
125  * If we crash this device might stay around.
126  */
127 static HDEVINFO DeviceInfo = INVALID_HANDLE_VALUE;
128
129 /**
130  * Registry Key we hand over to windows to spawn a new virtual interface
131  */
132 static SP_DEVINFO_DATA DeviceNode;
133
134 /**
135  * GUID of our virtual device in the form of 
136  * {12345678-1234-1234-1234-123456789abc} - in hex
137  */
138 static char device_guid[256];
139 /**
140  * inet_pton() wrapper for WSAStringToAddress()
141  *
142  * this is needed as long as we support WinXP, because only Vista+ support 
143  * inet_pton at all, and mingw does not yet offer inet_pton/ntop at all
144  * 
145  * @param af - IN - the aftype this address is supposed to be (v4/v6) 
146  * @param src - IN - the presentation form of the address
147  * @param dst - OUT - the numerical form of the address
148  * @return 0 on success, 1 on failure
149  */
150 #if WINVER >= 0x0600
151 int inet_pton (int af, const char *src, void *dst);
152 #else
153
154 int
155 inet_pton (int af, const char *src, void *dst)
156 {
157   struct sockaddr_storage addr;
158   int size = sizeof (addr);
159   char local_copy[INET6_ADDRSTRLEN + 1];
160
161   ZeroMemory (&addr, sizeof (addr));
162   /* stupid non-const API */
163   strncpy (local_copy, src, INET6_ADDRSTRLEN + 1);
164   local_copy[INET6_ADDRSTRLEN] = 0;
165
166   if (WSAStringToAddressA (local_copy, af, NULL, (struct sockaddr *) &addr, &size) == 0)
167     {
168       switch (af)
169         {
170         case AF_INET:
171           *(struct in_addr *) dst = ((struct sockaddr_in *) &addr)->sin_addr;
172           return 1;
173         case AF_INET6:
174           *(struct in6_addr *) dst = ((struct sockaddr_in6 *) &addr)->sin6_addr;
175           return 1;
176         }
177     }
178   return 0;
179 }
180 #endif
181
182 /**
183  * Wrapper for executing a shellcommand in windows.
184  * 
185  * @param command - the command + parameters to execute
186  * @return * exitcode of the program executed, 
187  *         * EINVAL (cmd/file not found)
188  *         * EPIPE (could not read STDOUT)
189  */
190 static int
191 execute_shellcommand (char * command)
192 {
193   FILE *pipe;
194
195   if (NULL == command ||
196       NULL == (pipe = _popen (command, "rt")))
197     return EINVAL;
198
199 #ifdef TESTING
200   {
201     char output[LINE_LEN];
202
203     printf ("executed command: %s", command);
204     while (NULL != fgets (output, sizeof (output), pipe))
205       printf (output);
206   }
207 #endif
208
209   if (!feof (pipe))
210     return EPIPE;
211
212   return _pclose (pipe);
213 }
214
215 /**
216  * @brief Sets the IPv6-Address given in address on the interface dev
217  *
218  * @param address the IPv6-Address
219  * @param prefix_len the length of the network-prefix
220  */
221 static void
222 set_address6 (const char *address, unsigned long prefix_len)
223 {
224   int ret = EINVAL;
225   char command[LINE_LEN];
226   struct sockaddr_in6 sa6;
227
228   /*
229    * parse the new address
230    */
231   memset (&sa6, 0, sizeof (struct sockaddr_in6));
232   sa6.sin6_family = AF_INET6;
233   if (1 != inet_pton (AF_INET6, address, &sa6.sin6_addr.s6_addr))
234     {
235       fprintf (stderr, "Failed to parse address `%s': %s\n", address,
236                strerror (errno));
237       exit (1);
238     }
239
240   /*
241    * prepare the command
242    */
243   snprintf (command, LINE_LEN,
244             "netsh interface ipv6 add address \"%s\" %s/%d",
245             device_visible_name, address, prefix_len);
246   /*
247    * Set the address
248    */
249   ret = execute_shellcommand (command);
250
251   /* Did it work?*/
252   if (0 != ret)
253     {
254       fprintf (stderr, "Setting IPv6 address failed: %s\n", strerror (ret));
255       exit (1); // FIXME: return error code, shut down interface / unload driver
256     }
257 }
258
259 /**
260  * @brief Sets the IPv4-Address given in address on the interface dev
261  *
262  * @param dev the interface to configure
263  * @param address the IPv4-Address
264  * @param mask the netmask
265  */
266 static void
267 set_address4 (const char *address, const char *mask)
268 {
269   int ret = EINVAL;
270   char command[LINE_LEN];
271
272   struct sockaddr_in addr;
273   addr.sin_family = AF_INET;
274
275   /*
276    * Parse the address
277    */
278   if (1 != inet_pton (AF_INET, address, &addr.sin_addr.s_addr))
279     {
280       fprintf (stderr, "Failed to parse address `%s': %s\n", address,
281                strerror (errno));
282       exit (1);
283     }
284
285   /*
286    * prepare the command
287    */
288   snprintf (command, LINE_LEN,
289             "netsh interface ipv4 add address \"%s\" %s %s",
290             device_visible_name, address, mask);
291   /*
292    * Set the address
293    */
294   ret = execute_shellcommand (command);
295
296   /* Did it work?*/
297   if (0 != ret)
298     {
299       fprintf (stderr, "Setting IPv4 address failed: %s\n", strerror (ret));
300       exit (1); // FIXME: return error code, shut down interface / unload driver
301     }
302 }
303
304 /**
305  * Setup a new virtual interface to use for tunneling. 
306  * 
307  * @return: TRUE if setup was successful, else FALSE
308  */
309 static boolean
310 setup_interface ()
311 {
312   /*
313    * where to find our inf-file. (+ the "full" path, after windows found")
314    * 
315    * We do not directly input all the props here, because openvpn will update
316    * these details over time.
317    */
318   char inf_file_path[MAX_PATH];
319   char hwidlist[LINE_LEN + 4];
320   char class_name[128];
321   GUID class_guid;
322   int str_lenth = 0;
323
324   /** 
325    * Set the device's hardware ID and add it to a list.
326    * This information will later on identify this device in registry. 
327    * 
328    * TODO: Currently we just use TAP0901 as HWID, 
329    * but we might want to add additional information
330    */
331   strncpy (hwidlist, HARDWARE_ID, LINE_LEN);
332   /**
333    * this is kind of over-complicated, but allows keeps things independent of 
334    * how the openvpn-hwid is actually stored. 
335    * 
336    * A HWID list is double-\0 terminated and \0 separated
337    */
338   str_lenth = strlen (hwidlist) + 1;
339   strncpy (&hwidlist[str_lenth], secondary_hwid, LINE_LEN - str_lenth);
340
341   /** 
342    * Locate the inf-file, we need to store it somewhere where the system can
343    * find it. A good choice would be CWD/PDW or %WINDIR$\system32\
344    * 
345    * TODO: How about win64 in the future? 
346    *       We need to use a different driver for amd64/i386 !
347    */
348   GetFullPathNameA (INF_FILE, MAX_PATH, inf_file_path, NULL);
349
350   /** 
351    * Bootstrap our device info using the drivers inf-file
352    */
353   if (!SetupDiGetINFClassA (inf_file_path,
354                             &class_guid,
355                             class_name, sizeof (class_name) / sizeof (char),
356                             NULL))
357     return FALSE;
358
359   /** 
360    * Collect all the other needed information... 
361    * let the system fill our this form 
362    */
363   DeviceInfo = SetupDiCreateDeviceInfoList (&class_guid, NULL);
364   if (DeviceInfo == INVALID_HANDLE_VALUE)
365     return FALSE;
366
367   DeviceNode.cbSize = sizeof (SP_DEVINFO_DATA);
368   if (!SetupDiCreateDeviceInfoA (DeviceInfo,
369                                  class_name,
370                                  &class_guid,
371                                  NULL,
372                                  NULL,
373                                  DICD_GENERATE_ID,
374                                  &DeviceNode))
375     return FALSE;
376
377   /* Deploy all the information collected into the registry */
378   if (!SetupDiSetDeviceRegistryPropertyA (DeviceInfo,
379                                           &DeviceNode,
380                                           SPDRP_HARDWAREID,
381                                           (LPBYTE) hwidlist,
382                                           (strlen (hwidlist) + 2) * sizeof (char)))
383     return FALSE;
384
385   /* Install our new class(=device) into the system */
386   if (!SetupDiCallClassInstaller (DIF_REGISTERDEVICE,
387                                   DeviceInfo,
388                                   &DeviceNode))
389     return FALSE;
390
391   return TRUE;
392 }
393
394 /**
395  * Remove our new virtual interface to use for tunneling. 
396  * This function must be called AFTER setup_interface!
397  * 
398  * @return: TRUE if destruction was successful, else FALSE
399  */
400 static boolean
401 remove_interface ()
402 {
403   SP_REMOVEDEVICE_PARAMS remove;
404
405   if (INVALID_HANDLE_VALUE == DeviceInfo)
406     return FALSE;
407
408   remove.ClassInstallHeader.cbSize = sizeof (SP_CLASSINSTALL_HEADER);
409   remove.HwProfile = 0;
410   remove.Scope = DI_REMOVEDEVICE_GLOBAL;
411   remove.ClassInstallHeader.InstallFunction = DIF_REMOVE;
412   /*
413    * 1. Prepare our existing device information set, and place the 
414    *    uninstall related information into the structure
415    */
416   if (!SetupDiSetClassInstallParamsA (DeviceInfo,
417                                       (PSP_DEVINFO_DATA) & DeviceNode,
418                                       &remove.ClassInstallHeader,
419                                       sizeof (remove)))
420     return FALSE;
421   /*
422    * 2. Uninstall the virtual interface using the class installer
423    */
424   if (!SetupDiCallClassInstaller (DIF_REMOVE,
425                                   DeviceInfo,
426                                   (PSP_DEVINFO_DATA) & DeviceNode))
427     return FALSE;
428
429   SetupDiDestroyDeviceInfoList (DeviceInfo);
430
431   return TRUE;
432 }
433
434 /**
435  * Do all the lookup necessary to retrieve the inteface's actual name
436  * off the registry. 
437  * 
438  * @return: TRUE if we were able to lookup the interface's name, else FALSE
439  */
440 static boolean
441 resolve_interface_name ()
442 {
443
444   SP_DEVINFO_LIST_DETAIL_DATA device_details;
445   char pnp_instance_id [MAX_DEVICE_ID_LEN];
446   HKEY adapter_key_handle;
447   LONG status;
448   DWORD len;
449   int i = 0;
450   boolean retval = FALSE;
451   char adapter[] = INTERFACE_REGISTRY_LOCATION;
452
453   /* We can obtain the PNP instance ID from our setupapi handle */
454   device_details.cbSize = sizeof (device_details);
455   if (CR_SUCCESS != CM_Get_Device_ID_ExA (DeviceNode.DevInst,
456                                           (PCHAR) pnp_instance_id,
457                                           MAX_DEVICE_ID_LEN,
458                                           0, //must be 0
459                                           NULL)) //hMachine, we are local
460     return FALSE;
461
462   /* Now we can use this ID to locate the correct networks interface in registry */
463   if (ERROR_SUCCESS != RegOpenKeyExA (
464                                       HKEY_LOCAL_MACHINE,
465                                       adapter,
466                                       0,
467                                       KEY_READ,
468                                       &adapter_key_handle))
469     return FALSE;
470
471   /* Of course there is a multitude of entries here, with arbitrary names, 
472    * thus we need to iterate through there.
473    */
474   while (!retval)
475     {
476       char instance_key[256];
477       char query_key [256];
478       HKEY instance_key_handle;
479       char pnpinstanceid_name[] = "PnpInstanceID";
480       char pnpinstanceid_value[256];
481       char adaptername_name[] = "Name";
482       DWORD data_type;
483
484       len = sizeof (adapter_key_handle);
485       /* optain a subkey of {4D36E972-E325-11CE-BFC1-08002BE10318} */
486       status = RegEnumKeyExA (
487                               adapter_key_handle,
488                               i,
489                               instance_key,
490                               &len,
491                               NULL,
492                               NULL,
493                               NULL,
494                               NULL);
495
496       /* this may fail due to one of two reasons: 
497        * we are at the end of the list*/
498       if (ERROR_NO_MORE_ITEMS == status)
499         break;
500       // * we found a broken registry key, continue with the next key.
501       if (ERROR_SUCCESS != status)
502         goto cleanup;
503
504       /* prepare our new query string: */
505       snprintf (query_key, 256, "%s\\%s\\Connection",
506                 INTERFACE_REGISTRY_LOCATION,
507                 instance_key);
508
509       /* look inside instance_key\\Connection */
510       status = RegOpenKeyExA (
511                               HKEY_LOCAL_MACHINE,
512                               query_key,
513                               0,
514                               KEY_READ,
515                               &instance_key_handle);
516
517       if (status != ERROR_SUCCESS)
518         continue;
519
520       /* now, read our PnpInstanceID */
521       len = sizeof (pnpinstanceid_value);
522       status = RegQueryValueExA (instance_key_handle,
523                                  pnpinstanceid_name,
524                                  NULL, //reserved, always NULL according to MSDN
525                                  &data_type,
526                                  (LPBYTE) pnpinstanceid_value,
527                                  &len);
528
529       if (status != ERROR_SUCCESS || data_type != REG_SZ)
530         goto cleanup;
531
532       /* compare the value we got to our devices PNPInstanceID*/
533       if (0 != strncmp (pnpinstanceid_value, pnp_instance_id,
534                         sizeof (pnpinstanceid_value) / sizeof (char)))
535         goto cleanup;
536
537       len = sizeof (device_visible_name);
538       status = RegQueryValueExA (
539                                  instance_key_handle,
540                                  adaptername_name,
541                                  NULL, //reserved, always NULL according to MSDN
542                                  &data_type,
543                                  (LPBYTE) device_visible_name,
544                                  &len);
545
546       if (status != ERROR_SUCCESS || data_type != REG_SZ)
547         goto cleanup;
548
549       /* 
550        * we have successfully found OUR instance, 
551        * save the device GUID before exiting
552        */
553
554       strncpy (device_guid, instance_key, 256);
555       retval = TRUE;
556
557 cleanup:
558       RegCloseKey (instance_key_handle);
559
560       ++i;
561     }
562
563   RegCloseKey (adapter_key_handle);
564
565   return retval;
566 }
567
568 static boolean
569 check_tapw32_version (HANDLE handle)
570 {
571   {
572     ULONG version[3];
573     DWORD len;
574     memset (&(version), 0, sizeof (version));
575
576
577     if (DeviceIoControl (handle, TAP_WIN_IOCTL_GET_VERSION,
578                          &version, sizeof (version),
579                          &version, sizeof (version), &len, NULL))
580       {
581 #ifdef TESTING
582         fprintf (stderr, "TAP-Windows Driver Version %d.%d %s",
583                  (int) version[0],
584                  (int) version[1],
585                  (version[2] ? "(DEBUG)" : ""));
586 #endif
587       }
588
589     if (version[0] != TAP_WIN_MIN_MAJOR || version[1] < TAP_WIN_MIN_MINOR)
590       {
591         fprintf (stderr, "ERROR:  This version of gnunet requires a TAP-Windows driver that is at least version %d.%d!\n",
592                  TAP_WIN_MIN_MAJOR,
593                  TAP_WIN_MIN_MINOR);
594         return FALSE;
595       }
596     return TRUE;
597   }
598 }
599
600 /**
601  * Creates a tun-interface called dev;
602  *
603  * @return the fd to the tun or -1 on error
604  */
605 static HANDLE
606 init_tun ()
607 {
608   char device_path[256];
609   HANDLE handle;
610
611   if (!setup_interface ())
612     {
613       errno = ENODEV;
614       return INVALID_HANDLE_VALUE;
615     }
616
617   if (!resolve_interface_name ())
618     {
619       errno = ENODEV;
620       return INVALID_HANDLE_VALUE;
621     }
622
623   /* Open Windows TAP-Windows adapter */
624   snprintf (device_path, sizeof (device_path), "%s%s%s",
625             USERMODEDEVICEDIR,
626             device_guid,
627             TAP_WIN_SUFFIX);
628
629   handle = CreateFile (
630                        device_path,
631                        GENERIC_READ | GENERIC_WRITE,
632                        0, /* was: FILE_SHARE_READ */
633                        0,
634                        OPEN_EXISTING,
635                        FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
636                        0
637                        );
638
639   if (handle == INVALID_HANDLE_VALUE)
640     {
641       fprintf (stderr, "CreateFile failed on TAP device: %s\n", device_path);
642       return handle;
643     }
644
645   /* get driver version info */
646   if (!check_tapw32_version (handle))
647     {
648       CloseHandle (handle);
649       return INVALID_HANDLE_VALUE;
650     }
651
652   return handle;
653 }
654
655 static boolean
656 tun_up (HANDLE handle)
657 {
658   ULONG status = TRUE;
659   DWORD len;
660   if (DeviceIoControl (handle, TAP_WIN_IOCTL_SET_MEDIA_STATUS,
661                        &status, sizeof (status),
662                        &status, sizeof (status), &len, NULL))
663     {
664       fprintf (stderr, "The TAP-Windows driver ignored our request to set the interface UP (TAP_WIN_IOCTL_SET_MEDIA_STATUS DeviceIoControl call)!\n");
665       return FALSE;
666     }
667
668   /* Wait for the device to go UP, might take some time. */
669   Sleep ((TAP32_POSTUP_WAITTIME)*1000);
670
671   return TRUE;
672
673 }
674
675 /**
676  * Start forwarding to and from the tunnel.
677  *
678  * @param fd_tun tunnel FD
679  */
680 static void
681 run (HANDLE handle)
682 {
683   /*
684    * The buffer filled by reading from fd_tun
685    */
686   unsigned char buftun[MAX_SIZE];
687   ssize_t buftun_size = 0;
688   unsigned char *buftun_read = NULL;
689
690   /*
691    * The buffer filled by reading from stdin
692    */
693   unsigned char bufin[MAX_SIZE];
694   ssize_t bufin_size = 0;
695   ssize_t bufin_rpos = 0;
696   unsigned char *bufin_read = NULL;
697
698   //openvpn  
699   // Set Device to Subnet-Mode? 
700   // do we really need tun.c:2925 ?
701   // Why do we also assign IPv4's there??? Foobar??
702
703   /* tun up: */
704   if (!tun_up (handle))
705     goto teardown;
706
707   // tun.c:3038 
708
709   // mainloop:
710   // tunnel_point_to_point
711   //openvpn.c:62
712
713   // init.c:3337
714   /* setup ansync IO */
715   //forward.c: 1515
716
717
718 teardown:
719   ;
720   //init.c:3472
721 }
722
723 /**
724  * Open VPN tunnel interface.
725  *
726  * @param argc must be 6
727  * @param argv 0: binary name (gnunet-helper-vpn)
728  *             1: tunnel interface name (gnunet-vpn)
729  *             2: IPv6 address (::1), "-" to disable
730  *             3: IPv6 netmask length in bits (64), ignored if #2 is "-"
731  *             4: IPv4 address (1.2.3.4), "-" to disable
732  *             5: IPv4 netmask (255.255.0.0), ignored if #4 is "-"
733  */
734 int
735 main (int argc, char **argv)
736 {
737   char hwid[LINE_LEN];
738   HANDLE handle;
739   int global_ret;
740
741   if (6 != argc)
742     {
743       fprintf (stderr, "Fatal: must supply 5 arguments!\n");
744       return 1;
745     }
746
747   strncpy (hwid, argv[1], LINE_LEN);
748   hwid[LINE_LEN - 1] = '\0';
749
750   /* 
751    * We use our PID for finding/resolving the control-panel name of our virtual 
752    * device. PIDs are (of course) unique at runtime, thus we can safely use it 
753    * as additional hardware-id for our device.
754    */
755   snprintf (secondary_hwid, LINE_LEN / 2, "%s-%d",
756             hwid,
757             _getpid ());
758
759   if (INVALID_HANDLE_VALUE == (handle = init_tun ()))
760     {
761       fprintf (stderr, "Fatal: could not initialize virtual-interface %s with IPv6 %s/%s and IPv4 %s/%s\n",
762                hwid,
763                argv[2],
764                argv[3],
765                argv[4],
766                argv[5]);
767       return 1;
768     }
769
770   if (0 != strcmp (argv[2], "-"))
771     {
772       const char *address = argv[2];
773       long prefix_len = atol (argv[3]);
774
775       if ((prefix_len < 1) || (prefix_len > 127))
776         {
777           fprintf (stderr, "Fatal: prefix_len out of range\n");
778           global_ret = -1;
779           goto cleanup;
780         }
781
782       set_address6 (address, prefix_len);
783     }
784
785   if (0 != strcmp (argv[4], "-"))
786     {
787       const char *address = argv[4];
788       const char *mask = argv[5];
789
790       set_address4 (address, mask);
791     }
792
793   //eventuell: 
794   // tap_allow_nonadmin_access
795   //tun.c:2023
796
797   run (handle);
798   global_ret = 0;
799 cleanup:
800   remove_interface ();
801
802   return global_ret;
803 }